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
87 lines
3.3 KiB
TypeScript
87 lines
3.3 KiB
TypeScript
import { Component } from 'react';
|
|
import type { ErrorInfo, ReactNode } from 'react';
|
|
import { AlertTriangle, RotateCcw } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
const REPO_URL = 'https://git.karti.ai/PIG/PIG-Demo';
|
|
|
|
export interface DemoErrorBoundaryProps {
|
|
children: ReactNode;
|
|
/** Named in the fallback copy, so the visitor knows what broke. */
|
|
demoTitle?: string;
|
|
/** Link to the exact source, if the caller knows it. Falls back to the repo. */
|
|
sourceHref?: string;
|
|
/** Called when the visitor asks to try again; use it to reset shell state. */
|
|
onReset?: () => void;
|
|
}
|
|
|
|
interface DemoErrorBoundaryState {
|
|
error: Error | null;
|
|
}
|
|
|
|
/**
|
|
* One broken demo must never take the site down.
|
|
*
|
|
* This is a class because there is still no hook for `componentDidCatch`; that
|
|
* is the entire reason for the exception to the function-component rule here.
|
|
*
|
|
* The fallback is deliberately calm and specific. A site whose pitch is
|
|
* "here are the receipts" cannot answer a crash with a shrug: it names the
|
|
* demo, links the source, and lets the visitor retry without a full reload.
|
|
*/
|
|
export class DemoErrorBoundary extends Component<DemoErrorBoundaryProps, DemoErrorBoundaryState> {
|
|
override state: DemoErrorBoundaryState = { error: null };
|
|
|
|
static getDerivedStateFromError(error: Error): DemoErrorBoundaryState {
|
|
return { error };
|
|
}
|
|
|
|
override componentDidCatch(error: Error, info: ErrorInfo) {
|
|
// No telemetry endpoint on a static site, and none is wanted. The console
|
|
// is the only place a maintainer can see this, so keep the component stack.
|
|
console.error('[pig-demo] a demo surface threw', error, info.componentStack);
|
|
}
|
|
|
|
private handleReset = () => {
|
|
this.setState({ error: null });
|
|
this.props.onReset?.();
|
|
};
|
|
|
|
override render() {
|
|
const { error } = this.state;
|
|
if (!error) return this.props.children;
|
|
|
|
const { demoTitle, sourceHref } = this.props;
|
|
return (
|
|
<div role="alert" className="mx-auto my-10 max-w-xl rounded-xl border border-border bg-surface p-5 sm:p-6">
|
|
<div className="flex items-center gap-2 text-warning">
|
|
<AlertTriangle className="size-5" aria-hidden="true" />
|
|
<h2 className="text-h3 text-fg">
|
|
{demoTitle ? `${demoTitle} failed to render` : 'This demo failed to render'}
|
|
</h2>
|
|
</div>
|
|
<p className="mt-3 max-w-measure text-prose text-fg-2">
|
|
Something in this demo threw while drawing. The rest of the site is unaffected — every
|
|
other demo is a separate module. The environment and the recorded runs behind this page
|
|
are in the repository either way, and you can run them yourself.
|
|
</p>
|
|
<pre className="mt-3 whitespace-pre-wrap break-words rounded-md border border-border bg-surface-3 px-3 py-2 font-mono text-code text-fg-2">
|
|
{error.message || 'Unknown error'}
|
|
</pre>
|
|
<div className="mt-5 flex flex-wrap gap-2">
|
|
<Button size="touch" onClick={this.handleReset}>
|
|
<RotateCcw aria-hidden="true" />
|
|
Try again
|
|
</Button>
|
|
<Button variant="outline" size="touch" asChild>
|
|
<a href={sourceHref ?? REPO_URL} rel="noreferrer">
|
|
Read the source
|
|
<span aria-hidden="true">↗</span>
|
|
</a>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|