Skip to content

Commit 753834e

Browse files
authored
style(dashboard): sharpen dark results command center (#1723)
* style(dashboard): sharpen dark results command center * fix(dashboard): remove mobile eval detail overflow
1 parent 79d156e commit 753834e

8 files changed

Lines changed: 134 additions & 68 deletions

File tree

apps/dashboard/src/components/EvalDetail.tsx

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ export function EvalDetail({
174174
<div className="flex h-full min-h-0 min-w-0 flex-col">
175175
{/* Tab navigation — at the top so Files tab editor fills maximum height */}
176176
<div className="min-w-0 border-b border-gray-800">
177-
<div className="flex min-w-0 gap-1 overflow-x-auto px-4">
177+
<div className="av-scrollbar-none flex min-w-0 gap-1 overflow-x-auto px-4">
178178
{tabs.map((tab) => (
179179
<button
180180
type="button"
@@ -331,7 +331,7 @@ function SourceTab({ result }: { result: EvalResult }) {
331331
const traceability: SourceTraceability | undefined = result.source_traceability;
332332
if (!traceability || traceability.status !== 'captured') {
333333
return (
334-
<div className="rounded-lg border border-gray-800 bg-gray-900 p-4">
334+
<div className="rounded-lg border border-gray-800 bg-gray-950/80 p-4 ring-1 ring-white/5">
335335
<h4 className="text-sm font-medium text-gray-300">Source metadata</h4>
336336
<p className="mt-2 text-sm text-gray-500">
337337
{traceability?.message ?? 'Source metadata was not captured for this run.'}
@@ -493,7 +493,7 @@ function ChecksTab({ result, projectId }: { result: EvalResult; projectId?: stri
493493
return (
494494
<div className="space-y-6">
495495
{/* Overall score */}
496-
<div className="rounded-lg border border-gray-800 bg-gray-900 p-4">
496+
<div className="rounded-lg border border-gray-800 bg-gray-950/80 p-4 ring-1 ring-white/5">
497497
<div className="flex items-center gap-4">
498498
<span className="text-sm font-medium text-gray-400">Overall score</span>
499499
<div className="flex-1">
@@ -567,9 +567,9 @@ function ChecksTab({ result, projectId }: { result: EvalResult; projectId?: stri
567567

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

786786
function gradingVerdictClass(pass: boolean | undefined): string {
787-
if (pass === true) return 'border-emerald-900/50 bg-emerald-950/20 text-emerald-300';
788-
if (pass === false) return 'border-red-900/50 bg-red-950/20 text-red-300';
789-
return 'border-gray-800 bg-gray-950/50 text-gray-300';
787+
if (pass === true) return 'border-emerald-800/60 bg-emerald-950/30 text-emerald-200';
788+
if (pass === false) return 'border-red-700/70 bg-red-950/40 text-red-200';
789+
return 'border-gray-700 bg-gray-950/70 text-gray-300';
790+
}
791+
792+
function gradingPanelClass(pass: boolean | undefined): string {
793+
if (pass === true) return 'border-emerald-900/50 bg-emerald-950/15';
794+
if (pass === false) return 'border-red-900/60 bg-red-950/15';
795+
return 'border-gray-800 bg-gray-900/70';
790796
}
791797

792798
function assertionLabel(
@@ -815,17 +821,27 @@ function NamedScoresGrid({ scores }: { scores: Record<string, number> | undefine
815821
if (!scores || Object.keys(scores).length === 0) return null;
816822
return (
817823
<div className="grid gap-3 md:grid-cols-3">
818-
{Object.entries(scores).map(([name, value]) => (
819-
<RunMetricRow key={name} label={name} value={formatPercent(value)} />
820-
))}
824+
{Object.entries(scores).map(([name, value]) => {
825+
const tone =
826+
value >= 0.8 ? 'text-emerald-300' : value >= 0.5 ? 'text-amber-300' : 'text-red-300';
827+
return (
828+
<div
829+
key={name}
830+
className="rounded-lg border border-gray-800 bg-gray-950/70 p-3 ring-1 ring-white/5"
831+
>
832+
<div className="text-xs font-medium uppercase tracking-wider text-gray-500">{name}</div>
833+
<div className={`mt-1 font-mono text-sm ${tone}`}>{formatPercent(value)}</div>
834+
</div>
835+
);
836+
})}
821837
</div>
822838
);
823839
}
824840

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

844860
function GradingAggregateSummary({ result }: { result: GradingComponentResult }) {
845861
return (
846-
<div className="space-y-4 rounded-lg border border-gray-800 bg-gray-900 p-4">
862+
<div
863+
className={`space-y-4 rounded-lg border p-4 ring-1 ring-white/5 ${gradingPanelClass(
864+
result.pass,
865+
)}`}
866+
>
847867
<div className="grid gap-3 md:grid-cols-[minmax(9rem,12rem)_1fr] md:items-center">
848868
<div
849-
className={`rounded-lg border px-3 py-2 text-sm font-medium ${gradingVerdictClass(
869+
className={`rounded-lg border px-3 py-2 text-sm font-semibold ${gradingVerdictClass(
850870
result.pass,
851871
)}`}
852872
>
@@ -855,12 +875,12 @@ function GradingAggregateSummary({ result }: { result: GradingComponentResult })
855875
<div className="min-w-0">
856876
<div className="mb-1 flex items-center justify-between gap-3 text-xs text-gray-500">
857877
<span>Aggregate score</span>
858-
<span className="font-mono">{formatPercent(result.score)}</span>
878+
<span className="font-mono text-gray-300">{formatPercent(result.score)}</span>
859879
</div>
860880
<OptionalScoreBar score={result.score} />
861881
</div>
862882
</div>
863-
{result.reason ? <p className="text-sm text-gray-300">{result.reason}</p> : null}
883+
{result.reason ? <p className="text-sm text-gray-200">{result.reason}</p> : null}
864884
<NamedScoresGrid scores={result.namedScores} />
865885
<MetadataBlock metadata={result.metadata} />
866886
</div>
@@ -917,7 +937,7 @@ function GradingComponentNode({
917937
<div className="grid gap-3 md:grid-cols-[minmax(10rem,1fr)_minmax(8rem,12rem)] md:items-center">
918938
<div className="min-w-0">
919939
<div className="flex min-w-0 flex-wrap items-center gap-2">
920-
<span className="font-medium text-gray-200">{label.title}</span>
940+
<span className="font-medium text-gray-100">{label.title}</span>
921941
<span
922942
className={`rounded-md border px-1.5 py-0.5 text-[11px] font-medium ${gradingVerdictClass(
923943
component.pass,
@@ -935,20 +955,20 @@ function GradingComponentNode({
935955
) : null}
936956
</div>
937957
<div className="min-w-0">
938-
<div className="mb-1 text-right font-mono text-xs text-gray-500">
958+
<div className="mb-1 text-right font-mono text-xs text-gray-400">
939959
{formatPercent(component.score)}
940960
</div>
941961
<OptionalScoreBar score={component.score} />
942962
</div>
943963
</div>
944-
{component.reason ? <p className="text-sm text-gray-300">{component.reason}</p> : null}
964+
{component.reason ? <p className="mt-1 text-sm text-gray-200">{component.reason}</p> : null}
945965
</div>
946966
);
947967

948968
return (
949969
<details
950970
open={depth === 0}
951-
className="rounded-lg border border-gray-800 bg-gray-900 p-3"
971+
className={`rounded-lg border p-3 ring-1 ring-white/5 ${gradingPanelClass(component.pass)}`}
952972
style={{ marginLeft: depth > 0 ? `${Math.min(depth, 4) * 1}rem` : undefined }}
953973
>
954974
<summary className="flex cursor-pointer list-none items-start gap-3">
@@ -1030,7 +1050,7 @@ function ArtifactGradingTab({
10301050
<button
10311051
type="button"
10321052
onClick={() => onOpenFile(gradingPath)}
1033-
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"
1053+
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"
10341054
>
10351055
Open grading JSON
10361056
</button>

apps/dashboard/src/components/ResultTable.tsx

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ export function ResultTable({
317317
</p>
318318
</div>
319319

320-
<div className="space-y-3 rounded-lg border border-gray-800 bg-gray-900/40 p-3">
320+
<div className="space-y-3 rounded-lg border border-gray-800 bg-gray-950/80 p-3 ring-1 ring-white/5">
321321
<div className="flex flex-wrap gap-2">
322322
{RESULT_TABLE_VIEW_PRESETS.map((preset) => (
323323
<button
@@ -326,8 +326,8 @@ export function ResultTable({
326326
onClick={() => updateState({ view: preset.id })}
327327
className={`inline-flex items-center gap-2 rounded-md border px-2.5 py-1.5 text-sm transition-colors ${
328328
model.state.view === preset.id
329-
? 'border-cyan-900/60 bg-cyan-950/30 text-cyan-300'
330-
: 'border-gray-800 bg-gray-950/70 text-gray-400 hover:border-gray-700 hover:text-gray-200'
329+
? 'border-cyan-700/70 bg-cyan-950/40 text-cyan-200'
330+
: 'border-gray-800 bg-gray-950/80 text-gray-400 hover:border-gray-600 hover:bg-gray-900/70 hover:text-gray-100'
331331
}`}
332332
>
333333
<span>{preset.label}</span>
@@ -494,12 +494,12 @@ export function ResultRowsTable({
494494
onOpenTrialDetail: (rowKey: string, trial: EvalCaseTrial) => void;
495495
}) {
496496
return (
497-
<div className="max-w-full overflow-x-auto rounded-lg border border-gray-800">
497+
<div className="max-w-full overflow-x-auto rounded-lg border border-gray-800 bg-gray-950/80 ring-1 ring-white/5">
498498
<table
499499
className="w-full whitespace-nowrap text-left text-sm"
500500
style={{ minWidth: `${resultTableMinWidth(visibleColumns)}px` }}
501501
>
502-
<thead className="border-b border-gray-800 bg-gray-900/50">
502+
<thead className="border-b border-gray-800 bg-gray-900/80">
503503
<tr>
504504
{visibleColumns.map((column) => (
505505
<th key={column.id} className={columnHeaderClassName(column.id)} title={column.label}>
@@ -514,7 +514,7 @@ export function ResultRowsTable({
514514
))}
515515
</tr>
516516
</thead>
517-
<tbody className="divide-y divide-gray-800/50">
517+
<tbody className="divide-y divide-gray-800/70">
518518
{rows.map((row) => {
519519
const repeatGroup = repeatGroupsByRowKey.get(row.key);
520520
const isSelected = selectedRowKey === row.key && !selectedTrialPath;
@@ -523,8 +523,12 @@ export function ResultRowsTable({
523523
<Fragment key={row.key}>
524524
<tr
525525
className={`cursor-pointer transition-colors ${
526-
isSelected ? 'bg-cyan-950/20' : 'hover:bg-gray-900/30'
527-
} ${repeatGroup ? 'bg-gray-950/20' : ''}`}
526+
isSelected
527+
? 'bg-cyan-950/30 ring-1 ring-inset ring-cyan-800/60'
528+
: row.status === 'failing'
529+
? 'bg-red-950/10 hover:bg-red-950/20'
530+
: 'hover:bg-gray-900/50'
531+
} ${repeatGroup ? 'bg-gray-950/30' : ''}`}
528532
onClick={() => onOpenDetail(row.key)}
529533
onKeyDown={(event) => {
530534
if (event.key === 'Enter' || event.key === ' ') {
@@ -760,9 +764,16 @@ function ResultStatusSymbol({ status, label }: { status: string; label: string }
760764
const passing = status === 'passing';
761765
const warning = status === 'error' || status === 'partial';
762766
const symbol = passing ? CHECK_MARK : CROSS_MARK;
763-
const tone = passing ? 'text-emerald-300' : warning ? 'text-amber-300' : 'text-red-300';
767+
const tone = passing
768+
? 'border-emerald-900/60 bg-emerald-950/30 text-emerald-300'
769+
: warning
770+
? 'border-amber-900/60 bg-amber-950/30 text-amber-300'
771+
: 'border-red-800/70 bg-red-950/40 text-red-300';
764772
return (
765-
<span className={`inline-flex text-base font-semibold ${tone}`} title={label}>
773+
<span
774+
className={`inline-flex h-6 w-6 items-center justify-center rounded-md border text-base font-semibold ${tone}`}
775+
title={label}
776+
>
766777
{symbol}
767778
</span>
768779
);
@@ -829,7 +840,10 @@ function RepeatScoreCell({ group }: { group: RepeatRunGroup }) {
829840
<div className="flex min-w-0 flex-col items-end gap-1">
830841
<PassRatePill rate={group.passRate} />
831842
<div className="text-xs text-gray-500">Attempt success</div>
832-
<div className="text-xs tabular-nums text-gray-600">
843+
<div
844+
className="text-xs tabular-nums text-gray-600"
845+
title={`Mean score ${formatPercent(group.meanScore)}`}
846+
>
833847
Mean score {formatPercent(group.meanScore)}
834848
</div>
835849
</div>
@@ -1021,11 +1035,13 @@ function ResultDetailPanel({
10211035
<aside
10221036
key={panelScrollKey}
10231037
ref={scrollPanelIntoView}
1024-
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)]"
1038+
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)]"
10251039
>
1026-
<div className="flex min-w-0 items-start justify-between gap-3 border-b border-gray-800 px-4 py-3">
1040+
<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">
10271041
<div className="min-w-0">
1028-
<p className="text-xs font-medium uppercase tracking-wider text-gray-500">Row detail</p>
1042+
<p className="text-xs font-medium uppercase tracking-wider text-cyan-500/80">
1043+
Row detail
1044+
</p>
10291045
<h4 className="mt-1 truncate text-base font-semibold text-white" title={title}>
10301046
{title}
10311047
</h4>
@@ -1037,14 +1053,14 @@ function ResultDetailPanel({
10371053
<div className="flex shrink-0 items-center gap-2">
10381054
<a
10391055
href={evalDetailHref}
1040-
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"
1056+
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"
10411057
>
10421058
Full page
10431059
</a>
10441060
<button
10451061
type="button"
10461062
onClick={onClose}
1047-
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"
1063+
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"
10481064
>
10491065
Close
10501066
</button>

apps/dashboard/src/components/RunDetail.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ export function RunDetail({ results, runId, projectId }: RunDetailProps) {
8181
{/* Category Breakdown */}
8282
<div>
8383
<h3 className="mb-3 text-sm font-medium text-gray-400">Category Breakdown</h3>
84-
<div className="max-w-full overflow-x-auto rounded-lg border border-gray-800">
84+
<div className="max-w-full overflow-x-auto rounded-lg border border-gray-800 bg-gray-950/80 ring-1 ring-white/5">
8585
<table className="min-w-[620px] w-full whitespace-nowrap text-left text-sm">
86-
<thead className="border-b border-gray-800 bg-gray-900/50">
86+
<thead className="border-b border-gray-800 bg-gray-900/80">
8787
<tr>
8888
<th className="px-4 py-2.5 font-medium text-gray-400">Category</th>
8989
<th className="px-4 py-2.5 font-medium text-gray-400">Pass Rate</th>
@@ -95,11 +95,11 @@ export function RunDetail({ results, runId, projectId }: RunDetailProps) {
9595
<th className="px-4 py-2.5 text-right font-medium text-gray-400">Total</th>
9696
</tr>
9797
</thead>
98-
<tbody className="divide-y divide-gray-800/50">
98+
<tbody className="divide-y divide-gray-800/70">
9999
{visibleCategories.map((cat) => {
100100
const expanded = expandedCategories[cat.name] === true;
101101
return (
102-
<tr key={cat.name} className="transition-colors hover:bg-gray-900/30">
102+
<tr key={cat.name} className="transition-colors hover:bg-gray-900/50">
103103
<td className="w-[18rem] max-w-[18rem] px-4 py-2.5 font-medium text-gray-200">
104104
<span className="flex min-w-0 items-center gap-2">
105105
<span

apps/dashboard/src/components/ScoreBar.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,31 @@
88
interface ScoreBarProps {
99
score: number;
1010
className?: string;
11+
showLabel?: boolean;
1112
}
1213

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

1623
return (
1724
<div className={`flex items-center gap-3 ${className}`}>
18-
<div className="h-2 flex-1 overflow-hidden rounded-full bg-gray-800">
25+
<div className="h-2 flex-1 overflow-hidden rounded-full bg-gray-800/90 ring-1 ring-white/5">
1926
<div
20-
className="h-full rounded-full bg-gradient-to-r from-cyan-400 to-blue-500 transition-all duration-300"
27+
className={`h-full rounded-full bg-gradient-to-r ${tone} transition-all duration-300`}
2128
style={{ width: `${pct}%` }}
2229
/>
2330
</div>
24-
<span className="w-12 text-right text-sm font-medium tabular-nums text-gray-300">{pct}%</span>
31+
{showLabel ? (
32+
<span className="w-12 text-right text-sm font-medium tabular-nums text-gray-200">
33+
{pct}%
34+
</span>
35+
) : null}
2536
</div>
2637
);
2738
}

apps/dashboard/src/components/StatsCards.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ export function StatsCards({
2323
totalCost,
2424
}: StatsCardsProps) {
2525
const pct = Math.round(passRate * 100);
26-
const rateColor = pct >= 80 ? 'text-cyan-400' : pct >= 60 ? 'text-amber-400' : 'text-red-400';
26+
const rateColor = pct >= 80 ? 'text-cyan-300' : pct >= 60 ? 'text-amber-300' : 'text-red-300';
2727

2828
return (
29-
<div className="flex flex-wrap items-center gap-6 rounded-lg border border-gray-800 bg-gray-900/60 px-5 py-3">
29+
<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">
3030
<Stat label="Pass Rate" value={`${pct}%`} accent={rateColor} large />
31-
<div className="h-6 w-px bg-gray-700" />
31+
<div className="h-8 w-px bg-cyan-900/50" />
3232
<Stat label="Passed" value={String(passed)} accent="text-emerald-400" />
3333
<Stat label="Failures" value={String(failed)} accent="text-red-400" />
3434
{executionErrors > 0 && (
@@ -37,7 +37,7 @@ export function StatsCards({
3737
<Stat label="Total" value={String(total)} />
3838
{totalCost !== undefined && (
3939
<>
40-
<div className="h-6 w-px bg-gray-700" />
40+
<div className="h-8 w-px bg-cyan-900/50" />
4141
<Stat label="Cost" value={`$${totalCost.toFixed(4)}`} accent="text-amber-400" />
4242
</>
4343
)}
@@ -58,9 +58,9 @@ function Stat({
5858
}) {
5959
return (
6060
<div className="flex flex-col">
61-
<span className="text-xs text-gray-500">{label}</span>
61+
<span className="text-xs font-medium text-gray-500">{label}</span>
6262
<span
63-
className={`tabular-nums font-semibold ${large ? 'text-2xl' : 'text-lg'} ${accent ?? 'text-white'}`}
63+
className={`tabular-nums font-semibold ${large ? 'text-2xl' : 'text-lg'} ${accent ?? 'text-gray-100'}`}
6464
>
6565
{value}
6666
</span>

0 commit comments

Comments
 (0)