516685526c
The ledger answers which contracted capacity is sold, to whom, at what margin. It says nothing about the motion — the repeatable practice that turns a customer conversation into a scoped deployment, and turns that deployment into something the next one reuses. Motion is deliberately not a parallel entity tree. DEMAND_STAGES already is the motion, so Motion binds reusable artefacts to the stages of a demand deal that already exists: an engagement hangs off one deal, cascade deleted, one per deal by unique constraint. Nine closed kinds, each declaring which stages it serves, and a starter library of twelve templates covering all eight open stages. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
76 lines
2.8 KiB
TypeScript
76 lines
2.8 KiB
TypeScript
/**
|
|
* Source with a copy button — the fence chrome, in one place.
|
|
*
|
|
* Reference architectures arrive as `mermaid`, both in a template's structured
|
|
* `fields` and inside the markdown body a promoted artefact brings with it, and
|
|
* neither is rendered as a picture. Bundling mermaid means roughly two
|
|
* megabytes and evaluating author-supplied text, and the proxy allows exactly
|
|
* one inline script by hash (AGENTS.md §5), so it would drag a CSP change onto
|
|
* the deployment host too. Source with a copy button gets the reader into their
|
|
* own diagram tool in two clicks; the picture is tracked as follow-up in
|
|
* `docs/motion.md`.
|
|
*
|
|
* The copy button is here rather than in each caller because a diagram in a
|
|
* body and a diagram in a field are the same thing to the person reading it,
|
|
* and one of the two silently lacking the button is the sort of difference
|
|
* nobody reports.
|
|
*/
|
|
import { useState } from 'react';
|
|
import { Check, Copy } from 'lucide-react';
|
|
import { toast } from 'sonner';
|
|
import { Button } from '@/components/ui';
|
|
|
|
export function CodeBlock({
|
|
source,
|
|
language,
|
|
label,
|
|
}: {
|
|
source: string;
|
|
/** The fence's language, shown as the block's caption. Absent for a bare fence. */
|
|
language?: string | null;
|
|
/** What the copy button says it copies, for a screen reader. */
|
|
label?: string;
|
|
}) {
|
|
const [copied, setCopied] = useState(false);
|
|
|
|
const copy = async () => {
|
|
// `navigator.clipboard` is absent outside a secure context, which the LAN
|
|
// dev server is — so this branch is reached routinely, not exceptionally.
|
|
if (!navigator.clipboard) {
|
|
toast.error('The browser refused clipboard access. Select the source and copy it by hand.');
|
|
return;
|
|
}
|
|
try {
|
|
await navigator.clipboard.writeText(source);
|
|
setCopied(true);
|
|
toast.success('Source copied.');
|
|
window.setTimeout(() => setCopied(false), 2000);
|
|
} catch {
|
|
toast.error('The browser refused clipboard access. Select the source and copy it by hand.');
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="min-w-0 overflow-hidden rounded-lg border border-border bg-surface-2">
|
|
<div className="flex min-w-0 items-center justify-between gap-2 border-b border-border pl-3">
|
|
<span className="min-w-0 truncate font-mono text-[11px] lowercase text-muted">
|
|
{language ?? 'source'}
|
|
</span>
|
|
<Button
|
|
type="button"
|
|
size="sm"
|
|
variant="ghost"
|
|
onClick={() => void copy()}
|
|
aria-label={label ? `Copy the ${label} source` : 'Copy the source'}
|
|
>
|
|
{copied ? <Check className="size-4" aria-hidden /> : <Copy className="size-4" aria-hidden />}
|
|
{copied ? 'Copied' : 'Copy'}
|
|
</Button>
|
|
</div>
|
|
<pre className="scroll-x p-3 text-xs leading-5">
|
|
<code className="font-mono">{source}</code>
|
|
</pre>
|
|
</div>
|
|
);
|
|
}
|