Skip to content
Merged
Show file tree
Hide file tree
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
64 changes: 42 additions & 22 deletions apps/dashboard/src/components/EvalDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export function EvalDetail({
<div className="flex h-full min-h-0 min-w-0 flex-col">
{/* Tab navigation — at the top so Files tab editor fills maximum height */}
<div className="min-w-0 border-b border-gray-800">
<div className="flex min-w-0 gap-1 overflow-x-auto px-4">
<div className="av-scrollbar-none flex min-w-0 gap-1 overflow-x-auto px-4">
{tabs.map((tab) => (
<button
type="button"
Expand Down Expand Up @@ -331,7 +331,7 @@ function SourceTab({ result }: { result: EvalResult }) {
const traceability: SourceTraceability | undefined = result.source_traceability;
if (!traceability || traceability.status !== 'captured') {
return (
<div className="rounded-lg border border-gray-800 bg-gray-900 p-4">
<div className="rounded-lg border border-gray-800 bg-gray-950/80 p-4 ring-1 ring-white/5">
<h4 className="text-sm font-medium text-gray-300">Source metadata</h4>
<p className="mt-2 text-sm text-gray-500">
{traceability?.message ?? 'Source metadata was not captured for this run.'}
Expand Down Expand Up @@ -493,7 +493,7 @@ function ChecksTab({ result, projectId }: { result: EvalResult; projectId?: stri
return (
<div className="space-y-6">
{/* Overall score */}
<div className="rounded-lg border border-gray-800 bg-gray-900 p-4">
<div className="rounded-lg border border-gray-800 bg-gray-950/80 p-4 ring-1 ring-white/5">
<div className="flex items-center gap-4">
<span className="text-sm font-medium text-gray-400">Overall score</span>
<div className="flex-1">
Expand Down Expand Up @@ -567,9 +567,9 @@ function ChecksTab({ result, projectId }: { result: EvalResult; projectId?: stri

function RunMetricRow({ label, value }: { label: string; value: string | undefined }) {
return (
<div className="rounded-lg border border-gray-800 bg-gray-900 p-3">
<div className="rounded-lg border border-gray-800 bg-gray-950/80 p-3 ring-1 ring-white/5">
<div className="text-xs font-medium uppercase tracking-wider text-gray-500">{label}</div>
<div className="mt-1 font-mono text-sm text-gray-200">{value ?? '-'}</div>
<div className="mt-1 font-mono text-sm text-gray-100">{value ?? '-'}</div>
</div>
);
}
Expand Down Expand Up @@ -784,9 +784,15 @@ function gradingVerdictLabel(pass: boolean | undefined): string {
}

function gradingVerdictClass(pass: boolean | undefined): string {
if (pass === true) return 'border-emerald-900/50 bg-emerald-950/20 text-emerald-300';
if (pass === false) return 'border-red-900/50 bg-red-950/20 text-red-300';
return 'border-gray-800 bg-gray-950/50 text-gray-300';
if (pass === true) return 'border-emerald-800/60 bg-emerald-950/30 text-emerald-200';
if (pass === false) return 'border-red-700/70 bg-red-950/40 text-red-200';
return 'border-gray-700 bg-gray-950/70 text-gray-300';
}

function gradingPanelClass(pass: boolean | undefined): string {
if (pass === true) return 'border-emerald-900/50 bg-emerald-950/15';
if (pass === false) return 'border-red-900/60 bg-red-950/15';
return 'border-gray-800 bg-gray-900/70';
}

function assertionLabel(
Expand Down Expand Up @@ -815,17 +821,27 @@ function NamedScoresGrid({ scores }: { scores: Record<string, number> | undefine
if (!scores || Object.keys(scores).length === 0) return null;
return (
<div className="grid gap-3 md:grid-cols-3">
{Object.entries(scores).map(([name, value]) => (
<RunMetricRow key={name} label={name} value={formatPercent(value)} />
))}
{Object.entries(scores).map(([name, value]) => {
const tone =
value >= 0.8 ? 'text-emerald-300' : value >= 0.5 ? 'text-amber-300' : 'text-red-300';
return (
<div
key={name}
className="rounded-lg border border-gray-800 bg-gray-950/70 p-3 ring-1 ring-white/5"
>
<div className="text-xs font-medium uppercase tracking-wider text-gray-500">{name}</div>
<div className={`mt-1 font-mono text-sm ${tone}`}>{formatPercent(value)}</div>
</div>
);
})}
</div>
);
}

function MetadataBlock({ metadata }: { metadata: GradingJsonObject | undefined }) {
if (!metadata || Object.keys(metadata).length === 0) return null;
return (
<details className="rounded-lg border border-gray-800 bg-gray-950/50 p-3">
<details className="rounded-lg border border-gray-800 bg-gray-950/80 p-3">
<summary className="cursor-pointer text-sm font-medium text-gray-300">Metadata</summary>
<pre className="mt-3 max-h-64 overflow-auto text-xs text-gray-300">
{JSON.stringify(metadata, null, 2)}
Expand All @@ -838,15 +854,19 @@ function OptionalScoreBar({ score }: { score: number | undefined }) {
if (score == null) {
return <div className="h-2 rounded-full bg-gray-800" />;
}
return <ScoreBar score={score} />;
return <ScoreBar score={score} showLabel={false} />;
}

function GradingAggregateSummary({ result }: { result: GradingComponentResult }) {
return (
<div className="space-y-4 rounded-lg border border-gray-800 bg-gray-900 p-4">
<div
className={`space-y-4 rounded-lg border p-4 ring-1 ring-white/5 ${gradingPanelClass(
result.pass,
)}`}
>
<div className="grid gap-3 md:grid-cols-[minmax(9rem,12rem)_1fr] md:items-center">
<div
className={`rounded-lg border px-3 py-2 text-sm font-medium ${gradingVerdictClass(
className={`rounded-lg border px-3 py-2 text-sm font-semibold ${gradingVerdictClass(
result.pass,
)}`}
>
Expand All @@ -855,12 +875,12 @@ function GradingAggregateSummary({ result }: { result: GradingComponentResult })
<div className="min-w-0">
<div className="mb-1 flex items-center justify-between gap-3 text-xs text-gray-500">
<span>Aggregate score</span>
<span className="font-mono">{formatPercent(result.score)}</span>
<span className="font-mono text-gray-300">{formatPercent(result.score)}</span>
</div>
<OptionalScoreBar score={result.score} />
</div>
</div>
{result.reason ? <p className="text-sm text-gray-300">{result.reason}</p> : null}
{result.reason ? <p className="text-sm text-gray-200">{result.reason}</p> : null}
<NamedScoresGrid scores={result.namedScores} />
<MetadataBlock metadata={result.metadata} />
</div>
Expand Down Expand Up @@ -917,7 +937,7 @@ function GradingComponentNode({
<div className="grid gap-3 md:grid-cols-[minmax(10rem,1fr)_minmax(8rem,12rem)] md:items-center">
<div className="min-w-0">
<div className="flex min-w-0 flex-wrap items-center gap-2">
<span className="font-medium text-gray-200">{label.title}</span>
<span className="font-medium text-gray-100">{label.title}</span>
<span
className={`rounded-md border px-1.5 py-0.5 text-[11px] font-medium ${gradingVerdictClass(
component.pass,
Expand All @@ -935,20 +955,20 @@ function GradingComponentNode({
) : null}
</div>
<div className="min-w-0">
<div className="mb-1 text-right font-mono text-xs text-gray-500">
<div className="mb-1 text-right font-mono text-xs text-gray-400">
{formatPercent(component.score)}
</div>
<OptionalScoreBar score={component.score} />
</div>
</div>
{component.reason ? <p className="text-sm text-gray-300">{component.reason}</p> : null}
{component.reason ? <p className="mt-1 text-sm text-gray-200">{component.reason}</p> : null}
</div>
);

return (
<details
open={depth === 0}
className="rounded-lg border border-gray-800 bg-gray-900 p-3"
className={`rounded-lg border p-3 ring-1 ring-white/5 ${gradingPanelClass(component.pass)}`}
style={{ marginLeft: depth > 0 ? `${Math.min(depth, 4) * 1}rem` : undefined }}
>
<summary className="flex cursor-pointer list-none items-start gap-3">
Expand Down Expand Up @@ -1030,7 +1050,7 @@ function ArtifactGradingTab({
<button
type="button"
onClick={() => onOpenFile(gradingPath)}
className="rounded-md border border-gray-700 px-3 py-1.5 text-xs text-gray-300 transition-colors hover:border-cyan-900/60 hover:text-cyan-300"
className="rounded-md border border-gray-700 bg-gray-950/80 px-3 py-1.5 text-xs text-gray-300 transition-colors hover:border-cyan-800/70 hover:text-cyan-200"
>
Open grading JSON
</button>
Expand Down
48 changes: 32 additions & 16 deletions apps/dashboard/src/components/ResultTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ export function ResultTable({
</p>
</div>

<div className="space-y-3 rounded-lg border border-gray-800 bg-gray-900/40 p-3">
<div className="space-y-3 rounded-lg border border-gray-800 bg-gray-950/80 p-3 ring-1 ring-white/5">
<div className="flex flex-wrap gap-2">
{RESULT_TABLE_VIEW_PRESETS.map((preset) => (
<button
Expand All @@ -326,8 +326,8 @@ export function ResultTable({
onClick={() => updateState({ view: preset.id })}
className={`inline-flex items-center gap-2 rounded-md border px-2.5 py-1.5 text-sm transition-colors ${
model.state.view === preset.id
? 'border-cyan-900/60 bg-cyan-950/30 text-cyan-300'
: 'border-gray-800 bg-gray-950/70 text-gray-400 hover:border-gray-700 hover:text-gray-200'
? 'border-cyan-700/70 bg-cyan-950/40 text-cyan-200'
: 'border-gray-800 bg-gray-950/80 text-gray-400 hover:border-gray-600 hover:bg-gray-900/70 hover:text-gray-100'
}`}
>
<span>{preset.label}</span>
Expand Down Expand Up @@ -494,12 +494,12 @@ export function ResultRowsTable({
onOpenTrialDetail: (rowKey: string, trial: EvalCaseTrial) => void;
}) {
return (
<div className="max-w-full overflow-x-auto rounded-lg border border-gray-800">
<div className="max-w-full overflow-x-auto rounded-lg border border-gray-800 bg-gray-950/80 ring-1 ring-white/5">
<table
className="w-full whitespace-nowrap text-left text-sm"
style={{ minWidth: `${resultTableMinWidth(visibleColumns)}px` }}
>
<thead className="border-b border-gray-800 bg-gray-900/50">
<thead className="border-b border-gray-800 bg-gray-900/80">
<tr>
{visibleColumns.map((column) => (
<th key={column.id} className={columnHeaderClassName(column.id)} title={column.label}>
Expand All @@ -514,7 +514,7 @@ export function ResultRowsTable({
))}
</tr>
</thead>
<tbody className="divide-y divide-gray-800/50">
<tbody className="divide-y divide-gray-800/70">
{rows.map((row) => {
const repeatGroup = repeatGroupsByRowKey.get(row.key);
const isSelected = selectedRowKey === row.key && !selectedTrialPath;
Expand All @@ -523,8 +523,12 @@ export function ResultRowsTable({
<Fragment key={row.key}>
<tr
className={`cursor-pointer transition-colors ${
isSelected ? 'bg-cyan-950/20' : 'hover:bg-gray-900/30'
} ${repeatGroup ? 'bg-gray-950/20' : ''}`}
isSelected
? 'bg-cyan-950/30 ring-1 ring-inset ring-cyan-800/60'
: row.status === 'failing'
? 'bg-red-950/10 hover:bg-red-950/20'
: 'hover:bg-gray-900/50'
} ${repeatGroup ? 'bg-gray-950/30' : ''}`}
onClick={() => onOpenDetail(row.key)}
onKeyDown={(event) => {
if (event.key === 'Enter' || event.key === ' ') {
Expand Down Expand Up @@ -760,9 +764,16 @@ function ResultStatusSymbol({ status, label }: { status: string; label: string }
const passing = status === 'passing';
const warning = status === 'error' || status === 'partial';
const symbol = passing ? CHECK_MARK : CROSS_MARK;
const tone = passing ? 'text-emerald-300' : warning ? 'text-amber-300' : 'text-red-300';
const tone = passing
? 'border-emerald-900/60 bg-emerald-950/30 text-emerald-300'
: warning
? 'border-amber-900/60 bg-amber-950/30 text-amber-300'
: 'border-red-800/70 bg-red-950/40 text-red-300';
return (
<span className={`inline-flex text-base font-semibold ${tone}`} title={label}>
<span
className={`inline-flex h-6 w-6 items-center justify-center rounded-md border text-base font-semibold ${tone}`}
title={label}
>
{symbol}
</span>
);
Expand Down Expand Up @@ -829,7 +840,10 @@ function RepeatScoreCell({ group }: { group: RepeatRunGroup }) {
<div className="flex min-w-0 flex-col items-end gap-1">
<PassRatePill rate={group.passRate} />
<div className="text-xs text-gray-500">Attempt success</div>
<div className="text-xs tabular-nums text-gray-600">
<div
className="text-xs tabular-nums text-gray-600"
title={`Mean score ${formatPercent(group.meanScore)}`}
>
Mean score {formatPercent(group.meanScore)}
</div>
</div>
Expand Down Expand Up @@ -1021,11 +1035,13 @@ function ResultDetailPanel({
<aside
key={panelScrollKey}
ref={scrollPanelIntoView}
className="min-w-0 max-w-full overflow-hidden rounded-lg border border-gray-800 bg-gray-950/80 xl:sticky xl:top-4 xl:max-h-[calc(100vh-2rem)]"
className="min-w-0 max-w-full overflow-hidden rounded-lg border border-cyan-950/70 bg-gray-950/95 ring-1 ring-white/10 xl:sticky xl:top-4 xl:max-h-[calc(100vh-2rem)]"
>
<div className="flex min-w-0 items-start justify-between gap-3 border-b border-gray-800 px-4 py-3">
<div className="flex min-w-0 items-start justify-between gap-3 border-b border-gray-800 bg-gray-900/60 px-4 py-3">
<div className="min-w-0">
<p className="text-xs font-medium uppercase tracking-wider text-gray-500">Row detail</p>
<p className="text-xs font-medium uppercase tracking-wider text-cyan-500/80">
Row detail
</p>
<h4 className="mt-1 truncate text-base font-semibold text-white" title={title}>
{title}
</h4>
Expand All @@ -1037,14 +1053,14 @@ function ResultDetailPanel({
<div className="flex shrink-0 items-center gap-2">
<a
href={evalDetailHref}
className="rounded-md border border-gray-800 px-2.5 py-1.5 text-xs text-gray-400 transition-colors hover:border-gray-700 hover:text-gray-200"
className="rounded-md border border-gray-700 bg-gray-950/80 px-2.5 py-1.5 text-xs text-gray-300 transition-colors hover:border-cyan-800/70 hover:text-cyan-200"
>
Full page
</a>
<button
type="button"
onClick={onClose}
className="rounded-md border border-gray-800 px-2.5 py-1.5 text-xs text-gray-400 transition-colors hover:border-gray-700 hover:text-gray-200"
className="rounded-md border border-gray-700 bg-gray-950/80 px-2.5 py-1.5 text-xs text-gray-300 transition-colors hover:border-gray-500 hover:text-gray-100"
>
Close
</button>
Expand Down
8 changes: 4 additions & 4 deletions apps/dashboard/src/components/RunDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ export function RunDetail({ results, runId, projectId }: RunDetailProps) {
{/* Category Breakdown */}
<div>
<h3 className="mb-3 text-sm font-medium text-gray-400">Category Breakdown</h3>
<div className="max-w-full overflow-x-auto rounded-lg border border-gray-800">
<div className="max-w-full overflow-x-auto rounded-lg border border-gray-800 bg-gray-950/80 ring-1 ring-white/5">
<table className="min-w-[620px] w-full whitespace-nowrap text-left text-sm">
<thead className="border-b border-gray-800 bg-gray-900/50">
<thead className="border-b border-gray-800 bg-gray-900/80">
<tr>
<th className="px-4 py-2.5 font-medium text-gray-400">Category</th>
<th className="px-4 py-2.5 font-medium text-gray-400">Pass Rate</th>
Expand All @@ -95,11 +95,11 @@ export function RunDetail({ results, runId, projectId }: RunDetailProps) {
<th className="px-4 py-2.5 text-right font-medium text-gray-400">Total</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-800/50">
<tbody className="divide-y divide-gray-800/70">
{visibleCategories.map((cat) => {
const expanded = expandedCategories[cat.name] === true;
return (
<tr key={cat.name} className="transition-colors hover:bg-gray-900/30">
<tr key={cat.name} className="transition-colors hover:bg-gray-900/50">
<td className="w-[18rem] max-w-[18rem] px-4 py-2.5 font-medium text-gray-200">
<span className="flex min-w-0 items-center gap-2">
<span
Expand Down
19 changes: 15 additions & 4 deletions apps/dashboard/src/components/ScoreBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,31 @@
interface ScoreBarProps {
score: number;
className?: string;
showLabel?: boolean;
}

export function ScoreBar({ score, className = '' }: ScoreBarProps) {
export function ScoreBar({ score, className = '', showLabel = true }: ScoreBarProps) {
const pct = Math.round(Math.max(0, Math.min(1, score)) * 100);
const tone =
pct >= 80
? 'from-cyan-300 via-sky-400 to-blue-500'
: pct >= 50
? 'from-amber-300 via-orange-400 to-red-400'
: 'from-red-400 via-rose-500 to-fuchsia-500';

return (
<div className={`flex items-center gap-3 ${className}`}>
<div className="h-2 flex-1 overflow-hidden rounded-full bg-gray-800">
<div className="h-2 flex-1 overflow-hidden rounded-full bg-gray-800/90 ring-1 ring-white/5">
<div
className="h-full rounded-full bg-gradient-to-r from-cyan-400 to-blue-500 transition-all duration-300"
className={`h-full rounded-full bg-gradient-to-r ${tone} transition-all duration-300`}
style={{ width: `${pct}%` }}
/>
</div>
<span className="w-12 text-right text-sm font-medium tabular-nums text-gray-300">{pct}%</span>
{showLabel ? (
<span className="w-12 text-right text-sm font-medium tabular-nums text-gray-200">
{pct}%
</span>
) : null}
</div>
);
}
12 changes: 6 additions & 6 deletions apps/dashboard/src/components/StatsCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ export function StatsCards({
totalCost,
}: StatsCardsProps) {
const pct = Math.round(passRate * 100);
const rateColor = pct >= 80 ? 'text-cyan-400' : pct >= 60 ? 'text-amber-400' : 'text-red-400';
const rateColor = pct >= 80 ? 'text-cyan-300' : pct >= 60 ? 'text-amber-300' : 'text-red-300';

return (
<div className="flex flex-wrap items-center gap-6 rounded-lg border border-gray-800 bg-gray-900/60 px-5 py-3">
<div className="flex flex-wrap items-center gap-5 rounded-lg border border-cyan-950/70 bg-gray-950/80 px-5 py-3 ring-1 ring-white/5">
<Stat label="Pass Rate" value={`${pct}%`} accent={rateColor} large />
<div className="h-6 w-px bg-gray-700" />
<div className="h-8 w-px bg-cyan-900/50" />
<Stat label="Passed" value={String(passed)} accent="text-emerald-400" />
<Stat label="Failures" value={String(failed)} accent="text-red-400" />
{executionErrors > 0 && (
Expand All @@ -37,7 +37,7 @@ export function StatsCards({
<Stat label="Total" value={String(total)} />
{totalCost !== undefined && (
<>
<div className="h-6 w-px bg-gray-700" />
<div className="h-8 w-px bg-cyan-900/50" />
<Stat label="Cost" value={`$${totalCost.toFixed(4)}`} accent="text-amber-400" />
</>
)}
Expand All @@ -58,9 +58,9 @@ function Stat({
}) {
return (
<div className="flex flex-col">
<span className="text-xs text-gray-500">{label}</span>
<span className="text-xs font-medium text-gray-500">{label}</span>
<span
className={`tabular-nums font-semibold ${large ? 'text-2xl' : 'text-lg'} ${accent ?? 'text-white'}`}
className={`tabular-nums font-semibold ${large ? 'text-2xl' : 'text-lg'} ${accent ?? 'text-gray-100'}`}
>
{value}
</span>
Expand Down
Loading
Loading