"use client"; import * as React from "react"; import { EyeOff, FileJson, FileText, History, MoreHorizontal, Play, RotateCcw, Share2, Trash2, Trophy } from "lucide-react"; import { useApp } from "@/components/app/store"; import { ProviderIcon } from "@/components/brand/provider-icon"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/misc"; import { Tooltip } from "@/components/ui/tooltip"; import { ActionSheet } from "@/components/ui/sheet"; import { ConfirmDialog } from "@/components/common/confirm-dialog"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; import { useIsMobile, useLongPress } from "@/lib/client/hooks"; import { cn, formatRelative, formatUsd, truncate } from "@/lib/utils"; import { sessionBlind, sessionCost, sessionWinner, type ArenaSessionDto } from "./types"; interface Props { sessions: ArenaSessionDto[] | undefined; loading: boolean; activeId: string | null; disabled?: boolean; onOpen: (s: ArenaSessionDto) => void; onRerun: (s: ArenaSessionDto) => void; onDelete: (s: ArenaSessionDto) => Promise; onExport: (s: ArenaSessionDto, format: "markdown" | "json") => void; onShare: (s: ArenaSessionDto) => void; className?: string; } const PAGE = 8; /** Past comparisons: stacked rows on phones (tap = open, long-press/… = actions), table from `md` up. */ export function ArenaHistory({ sessions, loading, activeId, disabled, onOpen, onRerun, onDelete, onExport, onShare, className }: Props) { const { modelsByKey, preferences } = useApp(); const isMobile = useIsMobile(); const [showAll, setShowAll] = React.useState(false); const [menuFor, setMenuFor] = React.useState(null); const [deleting, setDeleting] = React.useState(null); const list = showAll ? sessions ?? [] : (sessions ?? []).slice(0, PAGE); const nameOf = (k: string) => modelsByKey.get(k)?.displayName ?? k.split("/").slice(1).join("/"); const providerOf = (k: string) => modelsByKey.get(k)?.provider ?? k.split("/")[0]; const rowData = (s: ArenaSessionDto) => { const winner = sessionWinner(s); return { winner, winnerName: winner ? nameOf(winner.modelKey) : null, winnerProvider: winner ? providerOf(winner.modelKey) : null, cost: sessionCost(s), blind: sessionBlind(s), active: s.id === activeId }; }; const actions = (s: ArenaSessionDto) => [ { key: "open", label: "Open (read-only)", icon: , onSelect: () => onOpen(s), disabled }, { key: "rerun", label: "Run again", icon: , onSelect: () => onRerun(s), disabled }, "separator" as const, { key: "md", label: "Export Markdown", icon: , onSelect: () => onExport(s, "markdown") }, { key: "json", label: "Export JSON", icon: , onSelect: () => onExport(s, "json") }, { key: "share", label: "Share…", icon: , onSelect: () => onShare(s) }, "separator" as const, { key: "delete", label: "Delete", icon: , destructive: true, onSelect: () => setDeleting(s) }, ]; return (

History

{sessions?.length ? {sessions.length} : null}
{loading && !sessions ? (
{[0, 1, 2].map((i) => ( ))}
) : !sessions?.length ? (

Your past comparisons will show up here — prompt, models, winner, cost.

) : isMobile ? (
    {list.map((s, i) => ( onOpen(s)} onMore={() => setMenuFor(s)} /> ))}
) : (
{preferences.showCosts ? : null} {list.map((s) => { const d = rowData(s); return ( {preferences.showCosts ? : null} ); })}
Prompt Models WinnerCostDate
{d.blind ? ( blind ) : null} {s.modelKeys.map((k) => ( ))} {d.winner ? ( {d.winnerName} ) : ( — )} {d.cost > 0 ? `≈ ${formatUsd(d.cost, { precise: d.cost < 0.01 })}` : "—"}
{actions(s).map((a, i) => a === "separator" ? ( ) : ( {a.icon} {a.label} ), )}
)} {(sessions?.length ?? 0) > PAGE ? ( ) : null} !o && setMenuFor(null)} title={menuFor ? truncate(menuFor.prompt, 80) : undefined} items={menuFor ? actions(menuFor) : []} /> !o && setDeleting(null)} title="Delete this comparison?" description="The prompt, every response, your votes and any public link are removed. Usage records are kept for your cost history." confirmLabel="Delete" destructive onConfirm={async () => { if (deleting) await onDelete(deleting); }} />
); } interface RowData { winner: ReturnType; winnerName: string | null; winnerProvider: string | null; cost: number; blind: boolean; active: boolean; } function HistoryRow({ s, data: d, first, disabled, nameOf, providerOf, showCosts, onOpen, onMore }: { s: ArenaSessionDto; data: RowData; first: boolean; disabled?: boolean; nameOf: (k: string) => string; providerOf: (k: string) => string; showCosts: boolean; onOpen: () => void; onMore: () => void }) { const press = useLongPress({ onLongPress: onMore, onClick: () => !disabled && onOpen() }); return (
  • e.key === "Enter" && onOpen()}>

    {s.prompt}

    {s.modelKeys.map((k) => ( ))} {d.blind ? : null} {showCosts && d.cost > 0 ? ≈ {formatUsd(d.cost, { precise: d.cost < 0.01 })} : null} {d.winner ? ( {d.winnerName} ) : null}
  • ); }