"use client"; import * as React from "react"; import { Trophy } from "lucide-react"; import { ProviderIcon } from "@/components/brand/provider-icon"; import type { LiveMetrics } from "@/lib/arena/metrics"; import { criterionById, type Criterion } from "@/lib/arena/scoring"; import { cn, formatMs, formatTokens, formatUsd } from "@/lib/utils"; import { STATUS_META, StatusDot, formatTps } from "./metrics-strip"; import { BlindAvatar } from "./blind"; export interface ComparisonRow { key: string; name: string; provider: string | null; /** Blind: identity hidden, show letter avatar. */ hidden?: boolean; blindIndex: number; metrics: LiveMetrics; criteriaWon: string[]; isWinner: boolean; } interface Props { rows: ComparisonRow[]; showCosts?: boolean; customCriteria?: Criterion[]; className?: string; } type Col = { id: string; label: string; get: (r: ComparisonRow) => string; best?: (rows: ComparisonRow[]) => string | null }; function bestBy(rows: ComparisonRow[], get: (r: ComparisonRow) => number | null, dir: "min" | "max"): string | null { const cands = rows.map((r) => ({ key: r.key, v: get(r) })).filter((x): x is { key: string; v: number } => typeof x.v === "number" && Number.isFinite(x.v)); if (cands.length < 2) return null; cands.sort((a, b) => (dir === "min" ? a.v - b.v : b.v - a.v)); return cands[0].v === cands[1].v ? null : cands[0].key; } /** * End-of-run comparison. Table from `md` up; stacked metric rows on phones (never a 4-column table on a phone). */ export function ComparisonTable({ rows, showCosts = true, customCriteria = [], className }: Props) { const cols = React.useMemo
Best value per column is highlighted. Costs are estimates from list prices unless the provider reports them.
{/* Phone: metric rows, models inline */}| Model | {cols.map((c) => ({c.label} | ))}
|---|---|
|
|
{cols.map((c) => (
{c.id === "status" ? (
|
))}