import { Gauge } from "lucide-react" import { useVoiceTrace } from "@/lib/queries" import type { VoiceTurn } from "@/lib/api" // Balken-Stufen in kanonischer Reihenfolge (zeitlich disjunkt). Mem0 ist ein Unter-Detail // INNERHALB von „Hirn" und wird NICHT als eigenes Segment addiert (sonst Doppelzählung). const SEGMENTS = [ { key: "stt_ms", label: "STT", color: "#f59e0b" }, { key: "vision_ms", label: "Sehen", color: "#a78bfa" }, { key: "hirn_ms", label: "Hirn", color: "#2dd4bf" }, { key: "gen_ms", label: "Antwort", color: "#60a5fa" }, ] as const const fmt = (ms: number | null | undefined): string => ms == null ? "–" : `${(ms / 1000).toLocaleString("de-DE", { minimumFractionDigits: 1, maximumFractionDigits: 1 })} s` function ago(ts: number): string { const s = Math.max(0, Date.now() / 1000 - ts) if (s < 60) return `vor ${Math.round(s)} s` if (s < 3600) return `vor ${Math.round(s / 60)} min` return `vor ${Math.round(s / 3600)} h` } /** Summe der sichtbaren Balken-Stufen (STT gehört zur gefühlten Latenz dazu; total_ms des Backends * misst nur den Chat-Request ohne STT). So stimmen Balkenbreite und angezeigte Zahl immer überein. */ function rowTotal(t: VoiceTurn): number { return SEGMENTS.reduce((sum, s) => sum + ((t[s.key] as number | null) ?? 0), 0) } /** Dominante Stufe eines Turns = der „Täter". */ function culprit(t: VoiceTurn): { label: string; color: string; ms: number } | null { let best: { label: string; color: string; ms: number } | null = null for (const s of SEGMENTS) { const ms = t[s.key] as number | null if (ms != null && (best == null || ms > best.ms)) best = { label: s.label, color: s.color, ms } } return best } function TurnRow({ t, scale }: { t: VoiceTurn; scale: number }) { const title = SEGMENTS.map((s) => `${s.label} ${fmt(t[s.key] as number | null)}`).join(" · ") + (t.mem0_ms != null ? ` (davon Gedächtnis ${fmt(t.mem0_ms)})` : "") return (