d36548ff39
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
59 lines
2.7 KiB
TypeScript
59 lines
2.7 KiB
TypeScript
import type { Group, WorkspaceView, SurfaceState } from "./layoutTypes";
|
|
|
|
const RING: Record<SurfaceState | "stopped", string> = {
|
|
error: "#F4544E", wait: "#F2B84B", work: "#4C8DFF", done: "#3FB950", idle: "#5A6573", stopped: "#5A6573",
|
|
};
|
|
|
|
function aggregate(w: WorkspaceView): SurfaceState | "stopped" {
|
|
const order: SurfaceState[] = ["error", "wait", "work", "done", "idle"];
|
|
const running = Object.values(w.surfaces).filter((s) => s.running);
|
|
if (running.length === 0) return "stopped";
|
|
for (const st of order) {
|
|
if (running.some((s) => s.state === st)) return st;
|
|
}
|
|
return "idle";
|
|
}
|
|
|
|
export function Sidebar({
|
|
groups, workspaces, activeId, onSelect, onNew,
|
|
}: {
|
|
groups: Group[];
|
|
workspaces: WorkspaceView[];
|
|
activeId: string | null;
|
|
onSelect: (id: string) => void;
|
|
onNew: () => void;
|
|
}) {
|
|
const byGroup = (gid: string | null) => workspaces.filter((w) => (w.group_id ?? null) === gid).sort((a, b) => a.order - b.order);
|
|
const ungrouped = byGroup(null);
|
|
|
|
const row = (w: WorkspaceView) => (
|
|
<div key={w.id} onClick={() => onSelect(w.id)}
|
|
style={{
|
|
display: "flex", alignItems: "center", gap: 9, padding: "6px 8px", borderRadius: 6, cursor: "pointer",
|
|
background: w.id === activeId ? "#1A2029" : "transparent", fontFamily: "Inter", fontSize: 13,
|
|
color: w.id === activeId ? "#E6EDF3" : "#8B97A6",
|
|
}}>
|
|
<span style={{ width: 10, height: 10, borderRadius: "50%", border: `2px solid ${RING[aggregate(w)]}`, boxSizing: "border-box" }} />
|
|
<span style={{ flex: 1 }}>{w.name}</span>
|
|
{w.unread && <span style={{ width: 7, height: 7, borderRadius: "50%", background: "#4C8DFF" }} />}
|
|
<span style={{ fontFamily: "monospace", fontSize: 11, color: "#5A6573" }}>{Object.keys(w.surfaces).length}</span>
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<div style={{ width: 248, background: "#13171F", height: "100%", padding: 14, boxSizing: "border-box", overflowY: "auto" }}>
|
|
<button onClick={onNew} style={{ width: "100%", padding: 8, marginBottom: 16, background: "#1A2029", color: "#E6EDF3", border: "1px solid #323C49", borderRadius: 7 }}>+ New workspace</button>
|
|
{groups.sort((a, b) => a.order - b.order).map((g) => (
|
|
<div key={g.id} style={{ marginBottom: 12 }}>
|
|
<div style={{ display: "flex", alignItems: "center", gap: 7, padding: "0 4px", marginBottom: 4 }}>
|
|
<span style={{ width: 8, height: 8, borderRadius: 2, background: g.color }} />
|
|
<span style={{ fontFamily: "Inter", fontSize: 11, fontWeight: 700, letterSpacing: 0.5, color: "#8B97A6" }}>{g.name.toUpperCase()}</span>
|
|
</div>
|
|
{byGroup(g.id).map(row)}
|
|
</div>
|
|
))}
|
|
{ungrouped.length > 0 && <div style={{ marginTop: 8 }}>{ungrouped.map(row)}</div>}
|
|
</div>
|
|
);
|
|
}
|