"use client"; import { useState } from "react"; import MetricCard from "@/components/ui/MetricCard"; import SplitToggle from "@/components/ui/SplitToggle"; import { NORMALIZER_VERSION, splitLabels } from "@/lib/data"; import type { ModelRow, Provenance, Split, SplitInfo, SplitMetrics, } from "@/lib/types"; import SectionLabel from "./SectionLabel"; export interface MetricPanelProps { /** dataset these results are scoped to, e.g. "ViMedCSS" — the hub hosts multiple datasets, so the section header names the one being shown */ datasetName: string; metrics?: ModelRow["metrics"]; splits: { test: SplitInfo; hard: SplitInfo }; provenance: Provenance; } const VALUE_KEYS = [ "wer", "csWer", "nWer", "cer", "mtrExact", "rtfx", ] as const; const METRIC_CHIPS = ["WER", "CS-WER", "N-WER", "CER", "MTR", "RTFx"] as const; function emptyNote(provenance: Provenance, split: Split): string { if (provenance === "running") return "No committed measurements yet — the pilot run executing now will populate this panel."; if (provenance === "planned") return "No measurements yet — this model is on the evaluation roadmap."; return `No measurements recorded on the ${splitLabels[split]} split yet.`; } /** Per-card microcopy for a missing value, by row provenance. */ function gapReason(provenance: Provenance): string { if (provenance === "paper-imported") return "not published"; if (provenance === "running" || provenance === "planned") return "awaiting pilot run"; return "not recorded"; } /** * Split-toggleable metric grid: WER / CS-WER / N-WER / CER / MTR (exact) / * RTFx as count-up MetricCards, with 95% CI sub-lines where measured. */ export default function MetricPanel({ datasetName, metrics, splits, provenance, }: MetricPanelProps) { const [split, setSplit] = useState("test"); const m: SplitMetrics | undefined = metrics?.[split]; const info = splits[split]; const hasAny = VALUE_KEYS.some((k) => typeof m?.[k] === "number"); return (
{hasAny ? ( /* Stable keys: toggling splits updates values in place instead of remounting cards and replaying the one-shot count-up. */
) : ( /* flatlined-ECG empty state: one composed panel instead of a checkerboard of em-dash cards */

no trace yet — awaiting platform run

{emptyNote(provenance, split)}

    {METRIC_CHIPS.map((c) => (
  • {c}
  • ))}
)}

WER family in %, lower is better · MTR higher is better · RTFx = audio s ÷ wall s, higher is faster · normalizer {NORMALIZER_VERSION}

); }