import { useState } from "react"; import { Check, Hourglass, X, Power, Send, MessageSquare } from "lucide-react"; import { COLORS, FONT } from "./theme"; import type { EventRecord } from "./socketBridge"; const ICON: Record = { done: , wait: , error: , exit: , }; const COLOR: Record = { done: COLORS.stDone, wait: COLORS.stWait, error: COLORS.stError, exit: COLORS.textMuted, }; type Tab = "all" | "unread" | "errors"; const TABS: { id: Tab; label: string }[] = [ { id: "all", label: "All" }, { id: "unread", label: "Unread" }, { id: "errors", label: "Errors" }, ]; function rel(ts: number): string { const s = Math.max(0, Math.floor((Date.now() - ts) / 1000)); if (s < 60) return `${s}s`; if (s < 3600) return `${Math.floor(s / 60)}m`; if (s < 86400) return `${Math.floor(s / 3600)}h`; return `${Math.floor(s / 86400)}d`; } export function EventCenter({ events, onMarkAllRead, onSelect, }: { events: EventRecord[]; onMarkAllRead: () => void; onSelect: (surfaceId: string, id: number) => void; }) { const [tab, setTab] = useState("all"); const shown = tab === "unread" ? events.filter((e) => !e.read) : tab === "errors" ? events.filter((e) => e.kind === "error") : events; return (
Event Center Mark all read
{TABS.map((t) => { const on = t.id === tab; return ( ); })}
{shown.length === 0 &&
No events yet.
} {shown.map((e) => (
onSelect(e.surface_id, e.id)} style={{ display: "flex", gap: 9, padding: 10, borderRadius: 8, border: `1px solid ${COLORS.borderSubtle}`, cursor: "pointer", opacity: e.read ? 0.55 : 1 }}> {ICON[e.kind] ?? null}
{e.workspace_name} · {e.agent_label ?? "shell"}
{e.kind} {rel(e.ts)}
{!e.read && }
))}
{/* External notification channels — mocked until the daemon subscriber lands (SP5). */}
EXTERNAL NOTIFY
{[ { name: "Telegram", icon: }, { name: "MAX", icon: }, ].map((c) => (
{c.icon} {c.name}
))}
); }