a21596b3e4
Eighteen agents: three design directions judged on whether a COO on a phone actually learns what an environment is, on craft, and on landing without a rewrite; one spec; a foundation of measured tokens; six build lanes; three browser verifiers; a final gate pass. The materials are Prime Intellect's, measured from their site: near-black grounds, one green, sharp radii, mono small-caps labels, Geist and Geist Mono self-hosted because production CSP is font-src 'self'. Two of their own greys fail contrast on their own ground (#737373 is 4.02:1, #6E6E6E is 3.73:1 on #0F0F0F), so --muted is lifted and the CSS comment carries the number — or someone will 'correct' it back. Every text-on-ground pair in both themes is tabulated in src/index.css with its measured ratio. The two rules that resolved every conflict: data is mono, sentences are sans; the language wins on materials, the lesson wins on legibility. Light mode is a finished paper theme, not an inversion. What did not change: the derived-tabs contract, the honesty markers, the isolation lint, every gate. 419 contract checks, entry chunk at 74% of budget, zero horizontal overflow on any route at 390/1024/1440 in either theme. Also flips Alert Triage to status 'live' — the pipeline built it but never promoted it, so it was badged SPEC on its own playable page and the home page counted one environment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019mt6sHQHEnEYrJZvoMCJSB
133 lines
4.3 KiB
TypeScript
133 lines
4.3 KiB
TypeScript
import {
|
|
CartesianGrid,
|
|
Line,
|
|
LineChart,
|
|
ReferenceLine,
|
|
ResponsiveContainer,
|
|
Tooltip,
|
|
XAxis,
|
|
YAxis,
|
|
} from 'recharts';
|
|
import type { TooltipProps } from 'recharts';
|
|
import { formatNumber } from './format';
|
|
|
|
export interface MetricPoint {
|
|
/** Whatever the x axis is counting: checkpoint, step, arm name. */
|
|
x: string | number;
|
|
y: number;
|
|
}
|
|
|
|
export interface MetricChartProps {
|
|
data: MetricPoint[];
|
|
/** The rule the headline number is being compared against. */
|
|
baseline?: { value: number; label: string };
|
|
height: number;
|
|
/** Off under `prefers-reduced-motion`; recharts animates on mount by default. */
|
|
animate: boolean;
|
|
digits?: number;
|
|
}
|
|
|
|
/*
|
|
* Every colour is an `hsl(var(--…))` string read from the token layer at paint
|
|
* time, so the chart follows the theme toggle without a re-render and no hex
|
|
* ever lands in this file. The series takes `--tab-underline`, which is the
|
|
* one token that is the accent fill in dark and the accent INK in light — a
|
|
* 2px line on the page ground is a state indicator, and the raw accent is
|
|
* 1.47:1 against white.
|
|
*/
|
|
const SERIES = 'hsl(var(--tab-underline))';
|
|
const GRID = 'hsl(var(--border))';
|
|
const BASELINE = 'hsl(var(--border-strong))';
|
|
const TICK = 'hsl(var(--muted))';
|
|
|
|
/**
|
|
* The chart, in its own module so `React.lazy` can hold recharts out of the
|
|
* entry chunk. Nothing else in the shell may import this file directly — an
|
|
* ordinary import here defeats the whole arrangement and the entry chunk grows
|
|
* by ~100 kB without anyone noticing.
|
|
*/
|
|
export default function MetricChart({
|
|
data,
|
|
baseline,
|
|
height,
|
|
animate,
|
|
digits = 3,
|
|
}: MetricChartProps) {
|
|
return (
|
|
<div style={{ height }} className="w-full font-mono">
|
|
<ResponsiveContainer width="100%" height="100%">
|
|
<LineChart data={data} margin={{ top: 8, right: 12, bottom: 4, left: 4 }}>
|
|
<CartesianGrid stroke={GRID} strokeDasharray="2 4" vertical={false} />
|
|
<XAxis
|
|
dataKey="x"
|
|
tick={{ fill: TICK, fontSize: 11, fontFamily: 'inherit' }}
|
|
tickLine={false}
|
|
axisLine={{ stroke: GRID }}
|
|
minTickGap={12}
|
|
/>
|
|
<YAxis
|
|
tick={{ fill: TICK, fontSize: 11, fontFamily: 'inherit' }}
|
|
tickLine={false}
|
|
axisLine={false}
|
|
width={40}
|
|
tickFormatter={(value: number) => formatNumber(value, digits > 2 ? 2 : digits)}
|
|
/>
|
|
{baseline ? (
|
|
<ReferenceLine
|
|
y={baseline.value}
|
|
stroke={BASELINE}
|
|
strokeDasharray="5 4"
|
|
// Right-anchored: a baseline near zero sits on the x axis, and
|
|
// a left-anchored label lands on the first tick's text.
|
|
label={{
|
|
value: baseline.label,
|
|
position: 'insideTopRight',
|
|
fill: TICK,
|
|
fontSize: 11,
|
|
fontFamily: 'inherit',
|
|
}}
|
|
/>
|
|
) : null}
|
|
<Tooltip
|
|
cursor={{ stroke: BASELINE }}
|
|
content={(props: TooltipProps<number, string>) => (
|
|
<ChartTooltip {...props} digits={digits} />
|
|
)}
|
|
/>
|
|
<Line
|
|
// `linear`, not `monotone`: a spline invents curvature between two
|
|
// measured points, and on a categorical axis (one point per
|
|
// recorded arm) that curve is a claim nobody measured.
|
|
type="linear"
|
|
dataKey="y"
|
|
stroke={SERIES}
|
|
strokeWidth={2}
|
|
dot={{ r: 3, fill: SERIES, strokeWidth: 0 }}
|
|
activeDot={{ r: 4.5, fill: SERIES, strokeWidth: 0 }}
|
|
isAnimationActive={animate}
|
|
animationDuration={400}
|
|
/>
|
|
</LineChart>
|
|
</ResponsiveContainer>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ChartTooltip({
|
|
active,
|
|
payload,
|
|
label,
|
|
digits,
|
|
}: TooltipProps<number, string> & { digits: number }) {
|
|
if (!active || !payload || payload.length === 0) return null;
|
|
const value = payload[0]?.value;
|
|
return (
|
|
<div className="rounded-md border border-border bg-surface px-2.5 py-1.5 font-mono text-caption shadow-overlay">
|
|
<p className="text-muted">{String(label)}</p>
|
|
<p className="font-semibold text-fg">
|
|
{typeof value === 'number' ? formatNumber(value, digits) : '—'}
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|