import { useEffect, useState } from "react"; import { cn } from "@/lib/utils"; export type ConnectionStatus = | "live" | "connecting" | "reconnecting" | "closed" | "stalled" | "error"; const STATUS_TONE: Record< ConnectionStatus, { dot: string; ring: string; label: string } > = { live: { dot: "bg-[hsl(var(--success))]", ring: "ring-[hsl(var(--success))]/40", label: "Live", }, connecting: { dot: "bg-[hsl(var(--signal))]", ring: "ring-[hsl(var(--signal))]/40", label: "Connecting", }, reconnecting: { dot: "bg-[hsl(var(--signal))]", ring: "ring-[hsl(var(--signal))]/40", label: "Reconnecting", }, closed: { dot: "bg-muted-foreground", ring: "ring-muted-foreground/30", label: "Closed", }, stalled: { dot: "bg-destructive", ring: "ring-destructive/40", label: "Stalled", }, error: { dot: "bg-destructive", ring: "ring-destructive/40", label: "Error", }, }; /** * Refined status pill with an animated dot. The dot pulses for any * non-terminal status and stays solid for stable states. Sits inline * with other header chrome and reads as a one-word status, not a * multi-line status panel. */ export function StatusPill({ status, label, className, }: { status: ConnectionStatus; label?: string; className?: string; }) { const tone = STATUS_TONE[status]; const isPulsing = status === "live" || status === "connecting" || status === "reconnecting"; return ( {isPulsing ? ( ) : null} {label ?? tone.label} ); } /** * Re-tick every second so a "Last event: 12s ago" subtitle stays * current without a parent re-render. Mount-once interval. */ export function useTickingNow(intervalMs = 1000): number { const [now, setNow] = useState(() => Date.now()); useEffect(() => { const id = setInterval(() => setNow(Date.now()), intervalMs); return () => clearInterval(id); }, [intervalMs]); return now; } export function formatAge(seconds: number): string { if (seconds < 0) return "0s"; if (seconds < 60) return `${seconds}s`; const m = Math.floor(seconds / 60); const s = seconds % 60; if (m < 60) return s === 0 ? `${m}m` : `${m}m ${s}s`; const h = Math.floor(m / 60); return `${h}h ${m % 60}m`; }