import { useHive } from "../store"; import { enterReplay, exitReplay, seekReplay, playReplay, pauseReplay, setReplaySpeed } from "../store/replay"; // Session replay transport (Phase F / K5). When active it drives the Overview's // topology, activity feed, and chart from the replay slice (that wiring lives in // Overview); this component is only the controls: enter/exit, play/pause, speed, // and the scrubber. The replay store slice is separate from live state, so SSE // keeps updating the live view underneath. export default function Replay({ sessionId }: { sessionId: string }) { const replay = useHive((s) => s.replay); const active = replay.active && replay.sessionId === sessionId; if (!active) { return (
); } return ; } function ReplayControls() { const replay = useHive((s) => s.replay); const { events, cursor, loading, loadedCount, playing, speed, truncatedStart, historyStartsAt } = replay; if (loading) { return (
Loading replay…
{loadedCount} events loaded
); } if (!events.length) { return (
No events to replay
); } const cur = events[cursor]; const curTs = cur ? new Date(cur.ts) : null; return (
Replay transport · event {cursor + 1} / {events.length}
{truncatedStart && (
History starts at {historyStartsAt ? new Date(historyStartsAt).toLocaleString() : "an unknown point"} — earlier events were pruned.
)} {/* Transport controls */}
{([1, 10, 60] as const).map((sp) => ( ))}
{curTs ? curTs.toLocaleTimeString() : "—"}
{/* Scrubber, indexed by event, labeled by wall-clock ts */} seekReplay(Number(e.target.value))} className="w-full accent-[var(--brand)]" aria-label="Replay position" />
); }