/** * dashboard-client/src/components/EventStream.tsx — SSE event list with filters. * * Type badge colored by event type. Timestamp + expandable detail row. * Type filter chips (all + per-type). Virtualization: manual windowing for * 500+ events (render last N visible). */ import type React from "react"; import { useState, useMemo } from "react"; import type { SseEvent } from "@contracts"; import { Toggle } from "../components/ui/toggle"; export interface EventStreamProps { events: SseEvent[]; } const TYPE_COLORS: Record = { compact_start: "ev-compact", compact_end: "ev-compact", compact_trigger: "ev-trigger", compact_skip: "ev-trigger", tier_changed: "ev-tier", model_changed: "ev-tier", pressure_lifted: "ev-tier", checkpoint_persisted: "ev-checkpoint", recall_inject: "ev-recall", anchors_updated: "ev-recall", config_updated: "ev-config", config_preset: "ev-config", crew_presence_changed: "ev-crew", crew_turn_changed: "ev-crew", crew_bandit_chosen: "ev-crew", session_sample: "ev-recall", game_ritual_start: "ev-game", game_ritual_stage: "ev-game", game_ritual_end: "ev-game", game_mode_changed: "ev-game", game_render: "ev-game", }; // Filterable event types (spec: compact_start/end, recall_inject, checkpoint_persisted + all). const FILTER_TYPES = [ "all", "compact_start", "compact_end", "recall_inject", "checkpoint_persisted", ] as const; type FilterType = (typeof FILTER_TYPES)[number]; const RENDER_WINDOW = 200; // render last 200 to keep DOM light under 500+ events function summarize(ev: SseEvent): string { switch (ev.type) { case "compact_start": return `trigger=${ev.trigger} session=${ev.sessionId.slice(0, 8)}`; case "compact_end": return `freed=${ev.tokensFreed} ok=${ev.success} cp=${ev.checkpointId.slice(0, 8)}`; case "compact_trigger": return `pressure=${ev.pressure}% threshold=${ev.threshold}% armed=${ev.armed}`; case "compact_skip": return `reason=${ev.reason}`; case "tier_changed": return `${ev.from} → ${ev.to} ctx=${ev.contextPct}%`; case "model_changed": return `${ev.providerName}/${ev.model}`; case "pressure_lifted": return `${ev.beforePct}% → ${ev.afterPct}%`; case "checkpoint_persisted": return `cp=${ev.checkpointId.slice(0, 8)} sessionTok=${ev.sessionTokens}`; case "recall_inject": return `q="${ev.query.slice(0, 40)}" chunks=${ev.chunks} tok=${ev.tokens}`; case "anchors_updated": return `count=${ev.count} pinned=${ev.pinned}`; case "config_updated": return `key=${ev.key}`; case "config_preset": return `preset=${ev.preset}`; case "crew_presence_changed": return `agents=${ev.activeAgents} turn=${ev.currentTurn}`; case "crew_turn_changed": return `turn=${ev.turnIndex} agent=${ev.agentName}`; case "crew_bandit_chosen": return `agent=${ev.chosenAgent} score=${ev.score} regret=${ev.regret}`; case "session_sample": return `sess=${ev.sessionId.slice(0, 8)} tok=${ev.tokens.toLocaleString()} ctx=${Math.round(ev.percent)}%`; case "game_ritual_start": case "game_ritual_stage": case "game_ritual_end": case "game_mode_changed": case "game_render": return `stage=${ev.type}`; default: return `event=${ev.type ?? "unknown"}`; } } function formatTs(ts: string): string { try { const d = new Date(ts); return d.toLocaleTimeString(); } catch { return ts; } } export function EventStream({ events }: EventStreamProps): React.ReactElement { const [filter, setFilter] = useState("all"); const [expanded, setExpanded] = useState(null); const filtered = useMemo(() => { const list = filter === "all" ? events : events.filter((e) => e.type === filter); // Newest first; render only the last RENDER_WINDOW to keep DOM light. return list.slice(-RENDER_WINDOW).reverse(); }, [events, filter]); return (
{FILTER_TYPES.map((t) => ( setFilter(t)} > {t === "all" ? "all" : t.replace(/_/g, " ")} ))} {events.length} buffered
    {filtered.length === 0 && (
  • No events yet.
  • )} {filtered.map((ev, idx) => { const cls = TYPE_COLORS[ev.type] ?? "ev-default"; return (
  • setExpanded(expanded === idx ? null : idx)} > {formatTs(ev.ts)}{" "} {ev.type}{" "} {summarize(ev)} {expanded === idx && (
    									{JSON.stringify(ev, null, 2)}
    								
    )}
  • ); })}
); }