Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 35 additions & 7 deletions web/app/data/catalog/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
Activity,
AlertTriangle,
BookOpen,
Download,
} from "lucide-react";

const FEATURE_DISPLAY: Record<string, { label: string; unit: string; description: string }> = {
Expand Down Expand Up @@ -58,6 +59,26 @@ function formatNumber(value: number | null | undefined, digits = 2): string {
return value.toFixed(digits);
}

/** A stored input or output value exactly as recorded, without rounding. */
function formatStoredValue(value: unknown): string {
if (value === null || value === undefined) return "—";
if (typeof value === "number") return Number.isNaN(value) ? "—" : String(value);
return String(value);
}

/** Save the run's stored metadata (everything the page loaded) as JSON. */
function downloadRunMetadata(run: V2Run) {
const blob = new Blob([JSON.stringify(run, null, 2)], { type: "application/json" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = `run-${run.run_id}-metadata.json`;
document.body.appendChild(link);
link.click();
link.remove();
URL.revokeObjectURL(url);
}

function formatTimestamp(value: string | null | undefined): string {
if (!value) return "—";
const sourceText = value.trim();
Expand Down Expand Up @@ -206,7 +227,15 @@ export default function RunDetailPage({ params }: { params: Promise<{ id: string
</div>
</div>
</div>
<div className="flex flex-wrap gap-2 justify-end">
<div className="flex flex-wrap gap-2 justify-end items-center">
<button
type="button"
onClick={() => downloadRunMetadata(run)}
className="inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full border border-[hsl(var(--border))] hover:bg-[hsl(var(--muted))]"
>
<Download className="w-3 h-3" />
Download metadata
</button>
{badges.map((b) => (
<span
key={b.label}
Expand Down Expand Up @@ -310,11 +339,7 @@ export default function RunDetailPage({ params }: { params: Promise<{ id: string
<p className="text-xs uppercase tracking-wide truncate">{key}</p>
</div>
<p className="text-2xl font-semibold break-words">
{typeof value === "number"
? formatNumber(value, 2)
: value === null || value === undefined
? "—"
: String(value)}
{formatStoredValue(value)}
{!supplemented.has(key) && outputUnits.get(key) && (
<span className="text-sm font-normal text-[hsl(var(--muted-foreground))] ml-1">
{outputUnits.get(key)}
Expand Down Expand Up @@ -401,7 +426,10 @@ export default function RunDetailPage({ params }: { params: Promise<{ id: string
<td className="py-2 pr-4 text-[hsl(var(--muted-foreground))]">
{fromSummary ? [meta.description, "from the canonical summary"].filter(Boolean).join(" · ") : meta.description}
</td>
<td className="py-2 pr-4 font-mono">{typeof value === "number" ? formatNumber(value, 2) : value === null || value === undefined ? "—" : String(value)}</td>
<td className="py-2 pr-4 font-mono" title={formatStoredValue(value)}>
{/* Etcher inputs are computed step averages, so they stay rounded; recorded set-points show exactly as stored. */}
{isEtcher && typeof value === "number" ? formatNumber(value, 2) : formatStoredValue(value)}
</td>
<td className="py-2 pr-4 text-[hsl(var(--muted-foreground))]">{unit}</td>
</tr>
);
Expand Down