Redesign in Prime Intellect's design language, both themes, three viewports
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
This commit is contained in:
+222
-115
@@ -1,8 +1,9 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import type { ComponentType, ReactNode } from 'react';
|
||||
import type { ComponentProps, ComponentType, ReactNode } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { TermScope } from '@/components/site/Term';
|
||||
import { listRuns, loadEpisode, rewardTotal } from '@/lib/demo-kit/episode';
|
||||
import { usePlayer } from '@/lib/demo-kit/player';
|
||||
import { loadDemoModule } from '@/lib/demo-kit/registry';
|
||||
@@ -12,6 +13,7 @@ import { useRunParam, useSpeedParam, useStepParam, useTabParam } from '@/lib/url
|
||||
import * as st from '@/content/styles';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { BlindCompare } from './BlindCompare';
|
||||
import { Chip, Label, StatusPill } from './chrome';
|
||||
import { CodeReceipt } from './CodeReceipt';
|
||||
import { DemoErrorBoundary } from './DemoErrorBoundary';
|
||||
import { DemoTabBar, TabClaim, resolveTab, visibleTabs } from './DemoTabs';
|
||||
@@ -33,14 +35,14 @@ import { StatStrip } from './StatStrip';
|
||||
import type { Stat } from './StatStrip';
|
||||
import { RecordedBadge, TracePlayer } from './TracePlayer';
|
||||
import { VerifyBadge } from './VerifyBadge';
|
||||
import { formatOrDash, useIsDesktop } from './format';
|
||||
import { formatDate, formatOrDash, useIsDesktop } from './format';
|
||||
|
||||
const REPO_BLOB = 'https://git.karti.ai/PIG/PIG-Demo/src/branch/main/';
|
||||
|
||||
/**
|
||||
* The panel the step-detail strip inside the Watch tab opens on.
|
||||
*
|
||||
* This control is deliberately NOT in the URL. `?tab=` now belongs to the page's
|
||||
* This control is deliberately NOT in the URL. `?tab=` belongs to the page's
|
||||
* four top-level tabs, and one param cannot address two nested controls without
|
||||
* one of them silently winning; a permalink to `?tab=call` would land the reader
|
||||
* on a page with no such top-level tab.
|
||||
@@ -50,6 +52,18 @@ const DEFAULT_DETAIL_PANEL = 'reasoning';
|
||||
/** Reserved slug for the shell's own hand-written demo. Dev builds only. */
|
||||
const MOCK_SLUG = '__mock';
|
||||
|
||||
/**
|
||||
* A top-level tab panel. Each one is its own `TermScope`, so "first occurrence
|
||||
* per panel gets the underline" is literally per panel rather than per page.
|
||||
*/
|
||||
function Panel({ children, ...props }: ComponentProps<typeof TabsContent>) {
|
||||
return (
|
||||
<TabsContent {...props}>
|
||||
<TermScope>{children}</TermScope>
|
||||
</TabsContent>
|
||||
);
|
||||
}
|
||||
|
||||
export interface DemoBundle {
|
||||
demo: AnyDemoModule;
|
||||
runs: RunRef[];
|
||||
@@ -58,7 +72,7 @@ export interface DemoBundle {
|
||||
|
||||
type LoadState =
|
||||
| { status: 'loading' }
|
||||
| { status: 'ready'; bundle: DemoBundle }
|
||||
| { status: 'ready'; bundle: DemoBundle; dropped: number }
|
||||
| { status: 'error'; message: string };
|
||||
|
||||
/**
|
||||
@@ -67,15 +81,20 @@ type LoadState =
|
||||
* `loadDemoModule` and `loadEpisode` both cache their promises, so the route
|
||||
* loader having already fetched the module makes this resolve without a second
|
||||
* request. Runs are loaded with `allSettled` on purpose: one unreadable trace
|
||||
* drops that arm rather than blanking the page.
|
||||
* drops that arm rather than blanking the page — and the page SAYS it dropped
|
||||
* one, because a missing arm that goes unmentioned is a missing arm nobody can
|
||||
* ask about.
|
||||
*/
|
||||
async function loadBundle(slug: string): Promise<DemoBundle> {
|
||||
async function loadBundle(slug: string): Promise<{ bundle: DemoBundle; dropped: number }> {
|
||||
if (slug === MOCK_SLUG) {
|
||||
// Dynamic, so the mock lands in its own chunk and production never fetches
|
||||
// it. A static import would ship several hundred lines of fake trace to
|
||||
// every visitor of every real demo.
|
||||
const mock = await import('./mock');
|
||||
return { demo: mock.mockDemo, runs: mock.mockRuns, episodes: mock.mockEpisodes };
|
||||
return {
|
||||
bundle: { demo: mock.mockDemo, runs: mock.mockRuns, episodes: mock.mockEpisodes },
|
||||
dropped: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const demo = await loadDemoModule(slug);
|
||||
@@ -83,14 +102,21 @@ async function loadBundle(slug: string): Promise<DemoBundle> {
|
||||
const settled = await Promise.allSettled(runs.map((run) => loadEpisode(run)));
|
||||
|
||||
const episodes: Record<string, DemoEpisode> = {};
|
||||
let dropped = 0;
|
||||
settled.forEach((outcome, index) => {
|
||||
const run = runs[index];
|
||||
if (!run) return;
|
||||
if (outcome.status === 'fulfilled') episodes[run.id] = outcome.value;
|
||||
else console.error(`[pig-demo] dropped run "${run.id}":`, outcome.reason);
|
||||
else {
|
||||
dropped += 1;
|
||||
console.error(`[pig-demo] dropped run "${run.id}":`, outcome.reason);
|
||||
}
|
||||
});
|
||||
|
||||
return { demo, runs: runs.filter((run) => episodes[run.id] !== undefined), episodes };
|
||||
return {
|
||||
bundle: { demo, runs: runs.filter((run) => episodes[run.id] !== undefined), episodes },
|
||||
dropped,
|
||||
};
|
||||
}
|
||||
|
||||
export interface DemoShellProps {
|
||||
@@ -112,19 +138,19 @@ export function DemoShell({ slug: slugProp, bundle }: DemoShellProps) {
|
||||
const params = useParams();
|
||||
const slug = slugProp ?? params['slug'] ?? '';
|
||||
const [state, setState] = useState<LoadState>(
|
||||
bundle ? { status: 'ready', bundle } : { status: 'loading' },
|
||||
bundle ? { status: 'ready', bundle, dropped: 0 } : { status: 'loading' },
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (bundle) {
|
||||
setState({ status: 'ready', bundle });
|
||||
setState({ status: 'ready', bundle, dropped: 0 });
|
||||
return;
|
||||
}
|
||||
let live = true;
|
||||
setState({ status: 'loading' });
|
||||
loadBundle(slug)
|
||||
.then((loaded) => {
|
||||
if (live) setState({ status: 'ready', bundle: loaded });
|
||||
if (live) setState({ status: 'ready', bundle: loaded.bundle, dropped: loaded.dropped });
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
if (!live) return;
|
||||
@@ -142,11 +168,11 @@ export function DemoShell({ slug: slugProp, bundle }: DemoShellProps) {
|
||||
if (state.status === 'error') {
|
||||
return (
|
||||
<main className={cn(st.shell, 'py-16')}>
|
||||
<div role="alert" className="card max-w-xl p-6">
|
||||
<h1 className={st.h2}>That demo is not here</h1>
|
||||
<p className={cn(st.prose, 'mt-3')}>{state.message}</p>
|
||||
<div role="alert" className="max-w-xl rounded-xl border border-border bg-surface p-5 sm:p-6">
|
||||
<h1 className="text-h2 text-fg">That environment is not here</h1>
|
||||
<p className="mt-3 max-w-measure text-prose text-fg-2">{state.message}</p>
|
||||
<a href="/gallery" className={cn(st.btnSecondary, 'mt-6')}>
|
||||
Back to the demos
|
||||
All environments
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
@@ -158,12 +184,12 @@ export function DemoShell({ slug: slugProp, bundle }: DemoShellProps) {
|
||||
// so a crash names it, and resetting re-renders the surfaces rather than
|
||||
// re-navigating.
|
||||
<DemoErrorBoundary demoTitle={state.bundle.demo.meta.title}>
|
||||
<DemoBody bundle={state.bundle} />
|
||||
<DemoBody bundle={state.bundle} dropped={state.dropped} />
|
||||
</DemoErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
function DemoBody({ bundle }: { bundle: DemoBundle }) {
|
||||
function DemoBody({ bundle, dropped }: { bundle: DemoBundle; dropped: number }) {
|
||||
const { demo, runs, episodes } = bundle;
|
||||
const isDesktop = useIsDesktop();
|
||||
|
||||
@@ -271,25 +297,39 @@ function DemoBody({ bundle }: { bundle: DemoBundle }) {
|
||||
// agent's. With no recording to match, the demo's own first board will do.
|
||||
const playSeed = run?.seed ?? 0;
|
||||
|
||||
// The newest capture, for the Evidence readout. `runs` is manifest order,
|
||||
// which is not date order.
|
||||
const newestCapture = useMemo(
|
||||
() =>
|
||||
runs.reduce<string | null>(
|
||||
(newest, candidate) =>
|
||||
newest === null || candidate.capturedAt > newest ? candidate.capturedAt : newest,
|
||||
null,
|
||||
),
|
||||
[runs],
|
||||
);
|
||||
|
||||
const headerStats: Stat[] = episode
|
||||
? [
|
||||
{
|
||||
label: 'Outcome',
|
||||
value: episode.outcome,
|
||||
tone: episode.outcome === 'solved' ? 'positive' : 'warning',
|
||||
...(episode.truncated ? { title: 'Truncated before a terminal state' } : {}),
|
||||
title: episode.truncated
|
||||
? 'Truncated before a terminal state'
|
||||
: 'How the recorded run ended',
|
||||
},
|
||||
{
|
||||
label: 'Reward',
|
||||
value: formatOrDash(rewardTotal(episode.rewards, demo.reward.components)),
|
||||
tone: 'brand',
|
||||
title: 'Total under the shipped weights',
|
||||
title: 'Reward — total under the shipped weights',
|
||||
},
|
||||
{ label: 'Steps', value: steps.length, title: 'Model calls in this run' },
|
||||
{ label: 'Steps', value: steps.length, title: 'Steps — model calls in this run' },
|
||||
{
|
||||
label: 'Seed',
|
||||
value: episode.seed,
|
||||
title: 'The same seed reproduces this board',
|
||||
title: 'Seed — the same seed reproduces this board',
|
||||
},
|
||||
]
|
||||
: [];
|
||||
@@ -322,19 +362,7 @@ function DemoBody({ bundle }: { bundle: DemoBundle }) {
|
||||
{
|
||||
id: 'call',
|
||||
label: 'Model call',
|
||||
content: (
|
||||
<div className="space-y-3">
|
||||
<ModelCallPanel call={current?.call ?? null} />
|
||||
{current?.reply ? (
|
||||
<div className="card p-3">
|
||||
<h3 className="text-sm font-semibold">Reply</h3>
|
||||
<p className="mt-1 whitespace-pre-wrap font-mono text-xs leading-relaxed">
|
||||
{current.reply}
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
),
|
||||
content: <ModelCallPanel call={current?.call ?? null} reply={current?.reply ?? null} />,
|
||||
},
|
||||
// A demo's own extra panels ride alongside the step detail, where they sit
|
||||
// next to the step they are almost always about. With no recording there is
|
||||
@@ -347,19 +375,6 @@ function DemoBody({ bundle }: { bundle: DemoBundle }) {
|
||||
? detailPanel
|
||||
: DEFAULT_DETAIL_PANEL;
|
||||
|
||||
const timeline = (
|
||||
<StepTimeline
|
||||
steps={steps}
|
||||
current={player.index}
|
||||
onSelect={(next) => {
|
||||
player.pause();
|
||||
player.seek(next);
|
||||
}}
|
||||
Surface={Surface}
|
||||
onTogglePlay={player.toggle}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<main className={cn(st.shell, 'pb-16')}>
|
||||
{/*
|
||||
@@ -371,15 +386,23 @@ function DemoBody({ bundle }: { bundle: DemoBundle }) {
|
||||
{current?.announce ?? ''}
|
||||
</div>
|
||||
|
||||
<header className="pt-6 sm:pt-8">
|
||||
<p className={cn(st.eyebrow, 'text-accent-fg')}>For {demo.meta.persona}</p>
|
||||
<h1 className={cn(st.h2, 'mt-1')}>{demo.meta.title}</h1>
|
||||
<p className={cn(st.lede, 'mt-2 max-w-prose')}>{demo.meta.tagline}</p>
|
||||
<header className="pt-6">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Label className="text-accent-fg">For {demo.meta.persona}</Label>
|
||||
<StatusPill status={demo.meta.status} />
|
||||
</div>
|
||||
<h1 className="mt-1.5 text-h1 text-fg lg:text-h1-lg">{demo.meta.title}</h1>
|
||||
<p className="mt-2 max-w-measure-wide text-pretty text-lede text-fg-2 lg:text-lede-lg">
|
||||
{demo.meta.tagline}
|
||||
</p>
|
||||
{/* The buyer's question, kept quiet on purpose: it is the thing they
|
||||
walked in with, not the thing this page is asserting. */}
|
||||
<p className="mt-2 max-w-prose text-sm italic leading-relaxed text-muted">
|
||||
{demo.narrative.anxiety}
|
||||
</p>
|
||||
<div className="mt-4 border-l-2 border-border-strong pl-3">
|
||||
<Label>The question you walked in with</Label>
|
||||
<p className="mt-0.5 max-w-[60ch] text-pretty text-prose text-fg-2">
|
||||
{demo.narrative.anxiety}
|
||||
</p>
|
||||
</div>
|
||||
{/*
|
||||
The stats describe the RECORDED RUN, so they only belong on the tabs
|
||||
whose subject is that run. On Play they sat above the visitor's own
|
||||
@@ -387,23 +410,22 @@ function DemoBody({ bundle }: { bundle: DemoBundle }) {
|
||||
having already failed before you have touched a key.
|
||||
*/}
|
||||
{run && headerStats.length > 0 && activeTab !== 'play' ? (
|
||||
// One line that scrolls itself rather than a block that wraps: every
|
||||
// row this header spends is a row of the interactive board pushed
|
||||
// below the fold on a 390px phone.
|
||||
<div className="mt-3 flex items-center gap-2 overflow-x-auto pb-1 sm:flex-wrap sm:overflow-visible sm:pb-0">
|
||||
<div className="mt-4 flex flex-wrap items-center gap-x-4 gap-y-2">
|
||||
<StatStrip stats={headerStats} />
|
||||
<RecordedBadge
|
||||
model={run.model}
|
||||
capturedAt={run.capturedAt}
|
||||
{...(run.intervention ? { intervention: run.intervention } : {})}
|
||||
className="ml-0 shrink-0 flex-nowrap"
|
||||
{...(player.timingIsReal || activeTab !== 'watch'
|
||||
? {}
|
||||
: { timingNote: 'pacing approximate' })}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<SlotRegion id="hero-aside" />
|
||||
</header>
|
||||
|
||||
<Tabs value={activeTab} onValueChange={setTabParam} className="mt-4 sm:mt-5">
|
||||
<Tabs value={activeTab} onValueChange={setTabParam} className="mt-5 sm:mt-6">
|
||||
<DemoTabBar tabs={tabs} />
|
||||
|
||||
{tabs.includes('play') ? (
|
||||
@@ -412,23 +434,45 @@ function DemoBody({ bundle }: { bundle: DemoBundle }) {
|
||||
// Reward and back. Radix leaves the hiding to the author under
|
||||
// `forceMount`, which is what the `data-[state=inactive]` class does —
|
||||
// it is load-bearing, not belt-and-braces.
|
||||
<TabsContent
|
||||
<Panel
|
||||
value="play"
|
||||
forceMount
|
||||
className="mt-5 space-y-5 data-[state=inactive]:hidden sm:mt-6 sm:space-y-6"
|
||||
className="mt-5 space-y-6 data-[state=inactive]:hidden sm:mt-6"
|
||||
>
|
||||
<TabClaim>{claims.play}</TabClaim>
|
||||
<PlayYourself demo={demo} seed={playSeed} />
|
||||
<PlayYourself
|
||||
demo={demo}
|
||||
seed={playSeed}
|
||||
// The bridge to Watch, rendered only once the visitor has acted
|
||||
// and only when there is a Watch tab to bridge to. No slug, no
|
||||
// narrative literal: the same sentence on every environment.
|
||||
{...(tabs.includes('watch')
|
||||
? {
|
||||
afterMove: (
|
||||
<p className="max-w-measure text-prose text-fg-2">
|
||||
The score on this board comes from the grader, not from us.{' '}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setTabParam('watch')}
|
||||
className="font-medium text-accent-fg underline decoration-border-strong underline-offset-4 hover:decoration-accent-fg"
|
||||
>
|
||||
Watch a model take the same board →
|
||||
</button>
|
||||
</p>
|
||||
),
|
||||
}
|
||||
: {})}
|
||||
/>
|
||||
<SlotRegion id="below-board" />
|
||||
<div className="space-y-2">
|
||||
<h2 className={st.h3}>The machine you are inside</h2>
|
||||
<div className="space-y-3">
|
||||
<Label as="h2">The machine you are inside</Label>
|
||||
<EnvAnatomy anatomy={demo.anatomy} rewardLine={demo.meta.rewardLine} compact />
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Panel>
|
||||
) : null}
|
||||
|
||||
{tabs.includes('watch') && run && episode ? (
|
||||
<TabsContent value="watch" className="mt-6 space-y-4">
|
||||
<Panel value="watch" className="mt-5 space-y-4 sm:mt-6">
|
||||
<TabClaim>{claims.watch}</TabClaim>
|
||||
|
||||
{runs.length > 1 ? (
|
||||
@@ -472,13 +516,15 @@ function DemoBody({ bundle }: { bundle: DemoBundle }) {
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 gap-3 lg:grid-cols-[auto_minmax(0,1fr)] lg:items-start">
|
||||
<div className="card w-fit p-4">
|
||||
<div className="w-fit max-w-full rounded-xl border border-border bg-surface p-3 sm:p-4">
|
||||
{current ? <Surface state={current.state} /> : null}
|
||||
</div>
|
||||
|
||||
<div className="min-w-0">
|
||||
<Tabs value={activeDetail} onValueChange={setDetailPanel}>
|
||||
<TabsList aria-label="Details for this step" className="w-full overflow-x-auto">
|
||||
{/* The nested list keeps the PILL segment style, so the
|
||||
primary underline bar and this never look alike. */}
|
||||
<TabsList aria-label="Details for this step" className="max-w-full overflow-x-auto">
|
||||
{detailPanels.map((panel) => (
|
||||
<TabsTrigger key={panel.id} value={panel.id}>
|
||||
{panel.label}
|
||||
@@ -486,7 +532,7 @@ function DemoBody({ bundle }: { bundle: DemoBundle }) {
|
||||
))}
|
||||
</TabsList>
|
||||
{detailPanels.map((panel) => (
|
||||
<TabsContent key={panel.id} value={panel.id}>
|
||||
<TabsContent key={panel.id} value={panel.id} className="mt-3">
|
||||
{panel.content}
|
||||
</TabsContent>
|
||||
))}
|
||||
@@ -494,13 +540,27 @@ function DemoBody({ bundle }: { bundle: DemoBundle }) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{timeline}
|
||||
<StepTimeline
|
||||
steps={steps}
|
||||
current={player.index}
|
||||
onSelect={(next) => {
|
||||
player.pause();
|
||||
player.seek(next);
|
||||
}}
|
||||
Surface={Surface}
|
||||
onTogglePlay={player.toggle}
|
||||
/>
|
||||
{player.timingIsReal ? null : (
|
||||
<p className="text-xs text-muted">
|
||||
<p className="max-w-measure text-small text-muted">
|
||||
Some steps in this run carried no recorded latency, so their dwell on the timeline
|
||||
is the player's fallback rather than a measurement.
|
||||
</p>
|
||||
)}
|
||||
{dropped > 0 ? (
|
||||
<p className="font-mono text-caption text-muted">
|
||||
{dropped} recorded {dropped === 1 ? 'run' : 'runs'} could not be loaded
|
||||
</p>
|
||||
) : null}
|
||||
<SlotRegion id="below-timeline" />
|
||||
|
||||
{blindPair ? (
|
||||
@@ -529,10 +589,10 @@ function DemoBody({ bundle }: { bundle: DemoBundle }) {
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</TabsContent>
|
||||
</Panel>
|
||||
) : null}
|
||||
|
||||
<TabsContent value="reward" className="mt-6 space-y-4">
|
||||
<Panel value="reward" className="mt-5 space-y-4 sm:mt-6">
|
||||
<TabClaim>{claims.reward}</TabClaim>
|
||||
|
||||
{episode ? (
|
||||
@@ -543,7 +603,7 @@ function DemoBody({ bundle }: { bundle: DemoBundle }) {
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<p className={cn(st.prose, 'max-w-prose')}>
|
||||
<p className="max-w-measure text-prose text-fg-2">
|
||||
No recorded run has been scored for this environment yet, so every term below
|
||||
reads as not scored rather than as zero. The weights are the ones the environment
|
||||
ships.
|
||||
@@ -553,11 +613,10 @@ function DemoBody({ bundle }: { bundle: DemoBundle }) {
|
||||
)}
|
||||
|
||||
{/* The editor carries the ranking it re-orders in its own right-hand
|
||||
column, so the two are never on screen apart. */}
|
||||
column, so the two are never on screen apart. Absent, not greyed,
|
||||
when there is nothing to rank. */}
|
||||
{arms.length > 1 ? <RewardEditor spec={demo.reward} arms={arms} /> : null}
|
||||
|
||||
{episode ? <VerifyBadge demo={demo} episode={episode} /> : null}
|
||||
|
||||
{run && episode ? (
|
||||
<HeadlineMetric
|
||||
label={`Total reward — ${run.label}`}
|
||||
@@ -568,9 +627,9 @@ function DemoBody({ bundle }: { bundle: DemoBundle }) {
|
||||
) : null}
|
||||
|
||||
<SlotRegion id="beside-reward" />
|
||||
</TabsContent>
|
||||
</Panel>
|
||||
|
||||
<TabsContent value="evidence" className="mt-6 space-y-6">
|
||||
<Panel value="evidence" className="mt-5 space-y-6 sm:mt-6">
|
||||
<TabClaim>{claims.evidence}</TabClaim>
|
||||
|
||||
{/*
|
||||
@@ -580,10 +639,45 @@ function DemoBody({ bundle }: { bundle: DemoBundle }) {
|
||||
tab a visitor opens to read rather than to do — Play stays a board
|
||||
above the fold, which is the one thing a paragraph here would cost.
|
||||
*/}
|
||||
<p className={cn(st.prose, 'max-w-prose')}>{demo.narrative.thesis}</p>
|
||||
<div className="space-y-3">
|
||||
<p className="max-w-measure text-pretty text-prose text-fg-2">{demo.narrative.thesis}</p>
|
||||
{/* The readout that left the landing. Every value is data. */}
|
||||
<p className="flex flex-wrap gap-x-2 gap-y-1 font-mono text-caption text-muted">
|
||||
<span>
|
||||
<span className="uppercase tracking-label">verifiers</span>{' '}
|
||||
<span className="text-fg">{demo.provenance.verifiersVersion}</span>
|
||||
</span>
|
||||
{newestCapture ? (
|
||||
<>
|
||||
<span aria-hidden="true">·</span>
|
||||
<span>
|
||||
<span className="uppercase tracking-label">captured</span>{' '}
|
||||
<span className="text-fg">{formatDate(newestCapture)}</span>
|
||||
</span>
|
||||
</>
|
||||
) : null}
|
||||
{run ? (
|
||||
<>
|
||||
<span aria-hidden="true">·</span>
|
||||
<span title="The same seed reproduces this board">
|
||||
<span className="uppercase tracking-label">seed</span>{' '}
|
||||
<span className="text-fg">{run.seed}</span>
|
||||
</span>
|
||||
</>
|
||||
) : null}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<EnvAnatomy anatomy={demo.anatomy} rewardLine={demo.meta.rewardLine} />
|
||||
|
||||
{/*
|
||||
The receipt sits on Evidence, not Reward: the Evidence claim both
|
||||
demos make is "here is the grader, and here is proof your browser
|
||||
ran it", and that proof belongs next to the printed grader rather
|
||||
than under a slider. Reward keeps the breakdown and the editor.
|
||||
*/}
|
||||
{episode ? <VerifyBadge demo={demo} episode={episode} /> : null}
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2 lg:items-start">
|
||||
<ProvenanceCard provenance={demo.provenance} {...(run ? { run } : {})} />
|
||||
<CodeReceipt
|
||||
@@ -605,7 +699,7 @@ function DemoBody({ bundle }: { bundle: DemoBundle }) {
|
||||
|
||||
<SlotRegion id="before-limits" />
|
||||
<LimitsCallout limits={demo.narrative.limits} />
|
||||
</TabsContent>
|
||||
</Panel>
|
||||
</Tabs>
|
||||
</main>
|
||||
);
|
||||
@@ -689,33 +783,46 @@ function RunSwitcher({
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap items-center gap-x-4 gap-y-2">
|
||||
<SegmentedControl
|
||||
label="Agent"
|
||||
options={arms.map((arm) => {
|
||||
const intervention = arm.runs.find((r) => r.intervention)?.intervention;
|
||||
return {
|
||||
value: arm.label,
|
||||
label: arm.label,
|
||||
...(intervention ? { title: intervention } : {}),
|
||||
};
|
||||
})}
|
||||
value={activeArm.label}
|
||||
onChange={pickArm}
|
||||
className="border border-border p-1"
|
||||
optionClassName="tap px-3 text-sm"
|
||||
/>
|
||||
{activeArm.runs.length > 1 ? (
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Label as="span">Agent</Label>
|
||||
<SegmentedControl
|
||||
label="Hidden word"
|
||||
options={activeArm.runs.map((run) => ({
|
||||
value: run.id,
|
||||
label: `#${run.seed}`,
|
||||
}))}
|
||||
value={active.id}
|
||||
onChange={onSelect}
|
||||
className="border border-border p-1"
|
||||
optionClassName="tap px-2.5 text-xs nums"
|
||||
label="Agent"
|
||||
options={arms.map((arm) => {
|
||||
const intervention = arm.runs.find((r) => r.intervention)?.intervention;
|
||||
return {
|
||||
value: arm.label,
|
||||
label: intervention ? (
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
{arm.label}
|
||||
{/* PROMPTED: this arm's runs were intervened on. The chip
|
||||
travels with the arm so a switch never hides it. */}
|
||||
<Chip variant="default">prompted</Chip>
|
||||
</span>
|
||||
) : (
|
||||
arm.label
|
||||
),
|
||||
...(intervention ? { title: intervention } : {}),
|
||||
};
|
||||
})}
|
||||
value={activeArm.label}
|
||||
onChange={pickArm}
|
||||
/>
|
||||
</div>
|
||||
{activeArm.runs.length > 1 ? (
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Label as="span">Seed</Label>
|
||||
<SegmentedControl
|
||||
label="Seed"
|
||||
options={activeArm.runs.map((run) => ({
|
||||
value: run.id,
|
||||
label: `#${run.seed}`,
|
||||
title: `Seed ${run.seed} — the same seed reproduces this board`,
|
||||
}))}
|
||||
value={active.id}
|
||||
onChange={onSelect}
|
||||
optionClassName="px-2.5 font-mono text-caption"
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
@@ -730,23 +837,23 @@ function ShellSkeleton() {
|
||||
return (
|
||||
<div className={cn(st.shell, 'py-10')} aria-busy="true">
|
||||
<p className="sr-only">Loading the recorded run.</p>
|
||||
<Skeleton className="h-8 w-64" />
|
||||
<Skeleton className="mt-3 h-4 w-full max-w-md" />
|
||||
<Skeleton className="mt-4 h-10 w-full max-w-sm" />
|
||||
<Skeleton className="h-3 w-40" />
|
||||
<Skeleton className="mt-3 h-8 w-64" />
|
||||
<Skeleton className="mt-3 h-5 w-full max-w-md" />
|
||||
<Skeleton className="mt-6 h-11 w-full max-w-md" />
|
||||
<Skeleton className="mt-6 h-80" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The run a visitor sees before choosing one.
|
||||
*
|
||||
* `runs[0]` is the manifest's first entry — the weakest agent on seed 0, which
|
||||
* for the first demo shipped was a failed game with thinking off. So the Watch tab opened on a
|
||||
* loss with an empty reasoning panel and the Reward tab on a row of zeros:
|
||||
* the model's least interesting attempt, chosen by accident of sort order.
|
||||
* for the first demo shipped was a failed game with thinking off. So the Watch
|
||||
* tab opened on a loss with an empty reasoning panel and the Reward tab on a
|
||||
* row of zeros: the model's least interesting attempt, chosen by accident of
|
||||
* sort order.
|
||||
*
|
||||
* Prefer, in order: a run with recorded reasoning (there is something to
|
||||
* stream), then a solved one (the board reaches a conclusion), then the
|
||||
|
||||
Reference in New Issue
Block a user