Files
PIG-Demo/src/components/demo/DemoErrorBoundary.tsx
T
karti-ai a21596b3e4
ci / web (push) Successful in 2m42s
ci / python (push) Successful in 3m8s
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
2026-08-28 21:41:15 -07:00

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>
);
}
}