@vitavision/ui 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["SIZE_CLASSES"],"sources":["../src/theme.ts","../src/tone.ts","../src/components/ui/cn.ts","../src/components/ui/Tooltip.tsx","../src/components/ThemeToggle.tsx","../src/components/ui/Badge.tsx","../src/components/ui/Density.tsx","../src/components/ui/Button.tsx","../src/components/ui/Dialog.tsx","../src/components/ui/Disclosure.tsx","../src/components/ui/Kbd.tsx","../src/components/ui/DropdownMenu.tsx","../src/components/ui/Feedback.tsx","../src/components/ui/Field.tsx","../src/components/ui/highlightSpans.ts","../src/components/ui/HighlightedText.tsx","../src/components/ui/numberText.ts","../src/components/ui/Input.tsx","../src/components/ui/listboxModel.ts","../src/components/ui/Listbox.tsx","../src/components/ui/Panel.tsx","../src/components/ui/Progress.tsx","../src/components/ui/Popover.tsx","../src/components/ui/VectorInput.tsx","../src/components/ui/PoseInput.tsx","../src/components/ui/SegmentedControl.tsx","../src/components/ui/Select.tsx","../src/components/ui/Slider.tsx","../src/components/ui/tableSort.ts","../src/components/ui/tableWindow.ts","../src/components/ui/Table.tsx","../src/components/ui/Textarea.tsx","../src/components/ui/Toggle.tsx","../src/components/ui/ToggleChip.tsx","../src/components/ui/toastStore.ts","../src/components/ui/Toaster.tsx","../src/components/Tabs.tsx"],"sourcesContent":["/*\n * Which of the two palettes is on screen.\n *\n * Judging an anomaly map, or a measurement overlay drawn over a low-contrast part, against\n * a bright room and against a dark one are different tasks, and a researcher switches\n * between them within a session -- so dark mode is a real, storable choice here rather than\n * only `prefers-color-scheme`.\n *\n * Three states, not two. \"system\" is a real choice and has to survive a reload as itself,\n * otherwise the first time the OS flips at sunset the app disagrees with everything else on\n * the machine and there is no way back to following it.\n *\n * The class is applied to <html> rather than <body> so the page background painted before\n * React mounts is already correct. `applyTheme` (below, as `paint`) has to be duplicated as\n * an inline script in the consumer's `index.html` for exactly that reason -- see the\n * package README for the exact snippet, and keep it in agreement with this module if either\n * changes.\n */\n\n/**\n * A stored theme preference: a palette, or `system` to follow the OS\n * (`prefers-color-scheme`) and keep following it.\n */\nexport type ThemeChoice = \"light\" | \"dark\" | \"system\";\n\n/**\n * The `localStorage` key a consumer's theme choice is kept under.\n *\n * Every function here takes it as an optional last argument and defaults to this constant,\n * so a single app needs to pass nothing, while two apps sharing a browser profile -- or one\n * app that wants a namespaced key -- can each choose their own without stepping on the\n * other's stored preference.\n */\nexport const DEFAULT_THEME_STORAGE_KEY = \"vitavision-theme\";\n\n// Every DOM access here is inside a function, and guarded: this module is imported on the\n// server too.\nconst query = () =>\n typeof window !== \"undefined\" && typeof window.matchMedia === \"function\" ? window.matchMedia(\"(prefers-color-scheme: dark)\") : null;\n\n/**\n * The stored preference.\n *\n * @param storageKey - The `localStorage` key. Defaults to `DEFAULT_THEME_STORAGE_KEY`.\n * @returns The stored choice, or `system` when nothing valid is stored or storage cannot be\n * read (private browsing, the server).\n */\nexport function readThemeChoice(storageKey: string = DEFAULT_THEME_STORAGE_KEY): ThemeChoice {\n try {\n const stored = window.localStorage.getItem(storageKey);\n if (stored === \"light\" || stored === \"dark\" || stored === \"system\") return stored;\n } catch {\n // Private browsing, or a WebView with storage disabled. Following the OS is a fine\n // answer to \"I could not read your preference\", and better than refusing to render.\n }\n return \"system\";\n}\n\n/**\n * What `choice` actually resolves to right now.\n *\n * @param choice - The preference.\n * @returns `choice` itself for `light`/`dark`; for `system`, the OS palette (`light` where\n * it cannot be queried).\n */\nexport function resolveTheme(choice: ThemeChoice): \"light\" | \"dark\" {\n if (choice !== \"system\") return choice;\n return query()?.matches ? \"dark\" : \"light\";\n}\n\nfunction paint(choice: ThemeChoice): void {\n if (typeof document === \"undefined\") return;\n document.documentElement.classList.toggle(\"dark\", resolveTheme(choice) === \"dark\");\n}\n\n/**\n * Store a preference and paint it (the `dark` class on `<html>`).\n *\n * @param choice - The new preference.\n * @param storageKey - The `localStorage` key. Defaults to `DEFAULT_THEME_STORAGE_KEY`.\n */\nexport function setThemeChoice(choice: ThemeChoice, storageKey: string = DEFAULT_THEME_STORAGE_KEY): void {\n try {\n window.localStorage.setItem(storageKey, choice);\n } catch {\n // Not worth failing the click over; the choice still applies for this session.\n }\n paint(choice);\n}\n\n/**\n * Paint the stored choice and keep following the OS while the choice is \"system\".\n *\n * @param storageKey - The `localStorage` key. Defaults to `DEFAULT_THEME_STORAGE_KEY`.\n * @returns An unsubscribe, so a test can tear the listener down; a long-lived app never does.\n */\nexport function initTheme(storageKey: string = DEFAULT_THEME_STORAGE_KEY): () => void {\n paint(readThemeChoice(storageKey));\n\n const media = query();\n if (media === null) return () => {};\n\n const onSystemChange = () => {\n if (readThemeChoice(storageKey) === \"system\") paint(\"system\");\n };\n media.addEventListener(\"change\", onSystemChange);\n return () => media.removeEventListener(\"change\", onSystemChange);\n}\n","/*\n * The verdict vocabulary, for the components that paint outside of `className`.\n *\n * Every Tailwind-driven primitive under `components/ui` reaches the palette through a\n * class name, which only works where Tailwind's own build sees the class. `MeasureOverlay`\n * and `LineProfile` paint through raw SVG `stroke`/`fill` attributes instead — a caliper\n * box or an edge mark is drawn from data the consumer supplies at runtime, not from a\n * fixed set of class names a bundler can scan for. So these two read the same five tokens\n * `styles.css` defines (`--signal`, `--normal`, `--defect`, `--warn`, `--fg-subtle`)\n * directly as CSS custom properties, which resolves correctly in both themes without\n * needing Tailwind involved in the overlay's own render path at all.\n */\n\n/**\n * A paint for SVG overlays and charts: the accent (`signal`), a verdict colour, or `muted`.\n *\n * Named `MeasureTone` rather than `Tone` — `components/ui/Badge.tsx` already owns that name\n * for the verdict-badge vocabulary (`neutral`/`normal`/`defect`/`unlabeled`/`warning`/\n * `info`), and both are re-exported from the package root, where two types of the same name\n * would collide.\n */\nexport type MeasureTone = \"signal\" | \"normal\" | \"defect\" | \"warn\" | \"muted\";\n\nconst TONE_VAR: Record<MeasureTone, string> = {\n signal: \"var(--signal)\",\n normal: \"var(--normal)\",\n defect: \"var(--defect)\",\n warn: \"var(--warn)\",\n muted: \"var(--fg-subtle)\",\n};\n\n/**\n * The CSS paint value for a tone — usable directly as a `stroke`, `fill` or `color`. It is a\n * `var(--token)` reference, so it follows the theme without a re-render.\n *\n * @param tone - The tone; `undefined` falls back.\n * @param fallback - The tone used when `tone` is `undefined`. Defaults to `signal`.\n * @returns A `var(--…)` CSS value.\n */\nexport function toneColor(tone: MeasureTone | undefined, fallback: MeasureTone = \"signal\"): string {\n return TONE_VAR[tone ?? fallback];\n}\n","/**\n * Class composition, and the shared focus treatment.\n *\n * A naive primitive *appends* the caller's `className` to its own base string, so\n * `<Button className=\"px-6\">` produces `px-3 … px-6` and wins or loses on source order\n * rather than on intent. `twMerge` resolves that by conflict group: the caller's `px-6`\n * replaces the base `px-3` and leaves everything else alone.\n */\n\nimport { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Compose class names: `clsx` for the conditionals, then `tailwind-merge` so a later class\n * replaces an earlier one of the same conflict group (the caller's `px-6` beats the base\n * `px-3`). Every primitive merges its caller's `className` through this.\n *\n * @param inputs - Class values: strings, arrays, and `{ class: condition }` records.\n * @returns One class string with conflicts resolved.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n\n/**\n * One focus treatment for every interactive element in the design system.\n *\n * `outline` rather than `ring` because an outline follows the element's own border-radius\n * and needs no offset *colour* -- a ring offset has to know which surface it sits on, and\n * these primitives are used on `ground`, `surface` and `raised` alike.\n *\n * `focus-visible` rather than `focus`, so a mouse click does not leave a ring behind.\n */\nexport const focusRing = \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-signal\";\n\n/**\n * The focus treatment for controls whose own box is the hit target, where an offset ring\n * would clip: the same outline as {@link focusRing}, drawn with no offset.\n */\nexport const focusRingInset = \"focus-visible:outline-2 focus-visible:outline-offset-0 focus-visible:outline-signal\";\n","/*\n * A short explanation, on demand.\n *\n * Replaces the native `title` attribute, which appears after an unpredictable delay, in the\n * OS palette, at a size nobody chose, and never at all for a keyboard user.\n *\n * A tooltip is for text that helps but is not needed to operate the control. Anything a\n * reader must have in order to answer the question belongs in the field's description,\n * where it is always visible.\n */\n\nimport * as RadixTooltip from \"@radix-ui/react-tooltip\";\nimport { HelpCircle, type LucideIcon } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\n\nimport { cn, focusRing } from \"./cn\";\n\n/**\n * Shares one open delay (200 ms) across the tooltips below it. Mount it once near the root:\n * `Tooltip` and `InfoHint` need it above them.\n */\nexport function TooltipProvider({\n children,\n}: {\n /** The app, or the region that uses tooltips. */\n children: ReactNode;\n}) {\n return <RadixTooltip.Provider delayDuration={200}>{children}</RadixTooltip.Provider>;\n}\n\n/**\n * A short explanation on hover or focus of its child, for text that helps but is not needed\n * to operate the control (that belongs in a field's description).\n *\n * The child must be one focusable element that accepts a ref (Radix `asChild`). Radix\n * exposes `data-state` (`closed`/`delayed-open`/`instant-open`) on it.\n */\nexport function Tooltip({\n content,\n children,\n}: {\n /** The explanation. */\n content: ReactNode;\n /** The one element it explains. */\n children: ReactNode;\n}) {\n return (\n <RadixTooltip.Root>\n <RadixTooltip.Trigger asChild>{children}</RadixTooltip.Trigger>\n <RadixTooltip.Portal>\n <RadixTooltip.Content\n sideOffset={6}\n collisionPadding={8}\n className={cn(\n \"z-50 max-w-xs rounded-control border border-line bg-overlay px-2.5 py-1.5\",\n \"text-xs leading-snug text-fg shadow-lg shadow-black/25\",\n )}\n >\n {content}\n <RadixTooltip.Arrow className=\"fill-overlay\" />\n </RadixTooltip.Content>\n </RadixTooltip.Portal>\n </RadixTooltip.Root>\n );\n}\n\n/**\n * The standard affordance: a quiet mark that yields the explanation on hover or focus.\n *\n * `icon` exists because the mark answers two different questions. The default `?` offers\n * help with a control; `Info` offers *facts about the thing on screen* -- where a dataset\n * came from, which method produced a measurement -- which the tone demotes here rather\n * than spending a permanent line on. Reading them as the same affordance would be a small\n * lie.\n */\nexport function InfoHint({\n children,\n label = \"More information\",\n icon: Icon = HelpCircle,\n}: {\n /** The explanation shown on hover or focus. */\n children: ReactNode;\n /** The button's accessible name. Defaults to \"More information\". */\n label?: string | undefined;\n /** The mark: `HelpCircle` (the default) for help, `Info` for facts about the thing. */\n icon?: LucideIcon | undefined;\n}) {\n return (\n <Tooltip content={children}>\n <button type=\"button\" aria-label={label} className={cn(\"rounded-full text-fg-subtle transition-colors hover:text-fg\", focusRing)}>\n <Icon className=\"size-3.5\" />\n </button>\n </Tooltip>\n );\n}\n","/*\n * The palette switch.\n *\n * Three states, not two, and cycled by one button rather than spread across a\n * segmented control: this sits in a header next to the thing the app is\n * actually about, and a control that costs one glyph is the right size for a\n * preference that is set once a session. \"System\" is a real state — an app that\n * silently stops following the OS the first time you touch the switch has taken\n * something away.\n *\n * One shared control rather than a copy per app: two copies of a control that\n * writes to `localStorage` is two chances to disagree about the key, the cycle\n * order, or which icon means which state.\n *\n * `storageKey` is per app on purpose — `theme.ts` takes the key as an argument\n * for the same reason. Two apps sharing a browser profile each keep their own\n * preference, and an app that already had one under its own key does not lose\n * it by adopting this component.\n */\n\nimport { Moon, Sun, SunMoon } from \"lucide-react\";\nimport { useState, useSyncExternalStore } from \"react\";\n\nimport { DEFAULT_THEME_STORAGE_KEY, readThemeChoice, setThemeChoice, type ThemeChoice } from \"../theme\";\nimport { Tooltip } from \"./ui/Tooltip\";\nimport { cn, focusRing } from \"./ui/cn\";\n\nconst ORDER: ThemeChoice[] = [\"system\", \"light\", \"dark\"];\nconst ICON = { system: SunMoon, light: Sun, dark: Moon };\nconst LABEL: Record<ThemeChoice, string> = {\n system: \"Theme: following the system\",\n light: \"Theme: light\",\n dark: \"Theme: dark\",\n};\n\n/** Another tab (or window) changing the stored choice re-renders the toggle. */\nfunction subscribe(onChange: () => void): () => void {\n window.addEventListener(\"storage\", onChange);\n return () => window.removeEventListener(\"storage\", onChange);\n}\n\n/** What the server, and a hydrating client's first render, show: no stored choice read yet. */\nconst serverChoice = (): ThemeChoice => \"system\";\n\n/**\n * The palette switch: one button cycling system → light → dark, stored in `localStorage`\n * under `storageKey` and painted as the `dark` class on `<html>`.\n *\n * Its `aria-label` (repeated in a tooltip) names the current state; it needs a\n * `TooltipProvider` above it. The stored choice is read as an external store, so the server\n * render and the hydrating first client render agree (\"system\") and the stored choice\n * follows without a mismatch. The state is exposed as `data-theme-choice`.\n */\nexport function ThemeToggle({\n storageKey = DEFAULT_THEME_STORAGE_KEY,\n className,\n}: {\n /** The `localStorage` key; per app, so two apps sharing a profile keep their own choice. */\n storageKey?: string | undefined;\n /** Merged with the button's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const stored = useSyncExternalStore(subscribe, () => readThemeChoice(storageKey), serverChoice);\n // What this toggle last set, so it keeps cycling even where storage is unavailable and the\n // stored value cannot follow.\n const [chosen, setChosen] = useState<{ key: string; choice: ThemeChoice } | null>(null);\n const choice = chosen?.key === storageKey ? chosen.choice : stored;\n\n const advance = () => {\n const next = ORDER[(ORDER.indexOf(choice) + 1) % ORDER.length]!;\n setChosen({ key: storageKey, choice: next });\n setThemeChoice(next, storageKey);\n };\n\n const Icon = ICON[choice];\n\n return (\n <Tooltip content={LABEL[choice]}>\n <button\n type=\"button\"\n onClick={advance}\n aria-label={LABEL[choice]}\n data-theme-choice={choice}\n className={cn(\"rounded-control p-1.5 text-fg-muted transition-colors hover:bg-raised hover:text-fg\", focusRing, className)}\n >\n <Icon className=\"size-4\" />\n </button>\n </Tooltip>\n );\n}\n","/*\n * Status colour, in one place.\n *\n * The tone vocabulary is deliberately small and deliberately *about verdicts*. The chrome\n * carries no saturation, so a colour on screen means something: green is a normal sample\n * (or an in-tolerance measurement), red is a defect (or an out-of-tolerance one), amber is\n * a caveat. `info` is the one tone that borrows the interaction accent, and it is reserved\n * for statements of fact about a method -- \"produces anomaly maps\", \"caliper axis fixed\" --\n * rather than for anything a reader must act on.\n */\n\nimport type { ReactNode } from \"react\";\n\nimport { cn } from \"./cn\";\n\n/**\n * The badge and status-dot vocabulary: the verdicts (`normal`, `defect`, `warning`),\n * `unlabeled` for a sample with no verdict yet, `info` for a statement of fact about a\n * method, and `neutral` for everything else.\n */\nexport type Tone = \"neutral\" | \"normal\" | \"defect\" | \"unlabeled\" | \"warning\" | \"info\";\n\nconst TONE_CLASSES: Record<Tone, string> = {\n neutral: \"bg-raised text-fg-muted ring-line\",\n normal: \"bg-normal/12 text-normal ring-normal/25\",\n defect: \"bg-defect/12 text-defect ring-defect/25\",\n unlabeled: \"bg-raised text-fg-subtle ring-line\",\n warning: \"bg-warn/12 text-warn ring-warn/25\",\n info: \"bg-signal/12 text-signal ring-signal/25\",\n};\n\nconst DOT_CLASSES: Record<Tone, string> = {\n neutral: \"bg-fg-subtle\",\n normal: \"bg-normal\",\n defect: \"bg-defect\",\n unlabeled: \"bg-line-strong\",\n warning: \"bg-warn\",\n info: \"bg-signal\",\n};\n\n/**\n * A short label in a tone: a verdict, a status, a count.\n *\n * The tone is exposed as `data-tone`, so a stylesheet or a test can read it without parsing\n * class names.\n */\nexport function Badge({\n tone = \"neutral\",\n className,\n children,\n}: {\n /** Which colour, by meaning. Defaults to `neutral`. */\n tone?: Tone | undefined;\n /** Merged with the badge's own classes through `cn`. */\n className?: string | undefined;\n /** The label. */\n children: ReactNode;\n}) {\n return (\n <span\n data-tone={tone}\n className={cn(\n \"inline-flex items-center gap-1 rounded-control px-1.5 py-0.5 text-xs font-medium ring-1 ring-inset\",\n TONE_CLASSES[tone],\n className,\n )}\n >\n {children}\n </span>\n );\n}\n\n/**\n * A status as a dot and a word.\n *\n * The mapping from a domain status to a tone belongs to each caller -- only it knows\n * whether \"running\" is good news -- but the *rendering* of the answer does not.\n */\nexport function StatusDot({\n tone,\n className,\n children,\n}: {\n /** The status, as a tone; also exposed as `data-tone`. */\n tone: Tone;\n /** Merged with the row's own classes through `cn`. */\n className?: string | undefined;\n /** The word beside the dot. The dot alone is decoration: say the status in words. */\n children?: ReactNode;\n}) {\n return (\n <span data-tone={tone} className={cn(\"inline-flex items-center gap-1.5 text-xs text-fg-muted\", className)}>\n <span className={cn(\"size-1.5 shrink-0 rounded-full\", DOT_CLASSES[tone])} aria-hidden />\n {children}\n </span>\n );\n}\n\n/** Counts as a compact `12 normal · 9 defect` run of badges, skipping the zeroes. */\nexport function CountRun({\n counts,\n className,\n}: {\n /** `[name, count, tone]` triples, in display order. Zero counts are dropped. */\n counts: [string, number, Tone][];\n /** Merged with the run's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const shown = counts.filter(([, value]) => value > 0);\n if (shown.length === 0) {\n return <span className={cn(\"text-xs text-fg-subtle\", className)}>empty</span>;\n }\n return (\n <span className={cn(\"flex flex-wrap items-center gap-1.5\", className)}>\n {shown.map(([name, value, tone]) => (\n <Badge key={name} tone={tone}>\n <span className=\"font-mono\">{value}</span> {name}\n </Badge>\n ))}\n </span>\n );\n}\n","/*\n * How much room the chrome takes, as one decision per region rather than per component.\n *\n * The default spacing is written for a page: a form you read through once, where generous\n * padding is what makes it legible. An inspector column beside a canvas is the opposite\n * problem — it is a permanent surface that accumulates tools, and there the same padding is\n * simply less of the instrument on screen. Nesting compounds it: a panel body's `p-4`\n * inside a section's `gap-3` inside a column's own padding is three margins deep before any\n * control appears.\n *\n * `compact` is not \"the same design, smaller\". It drops the leading and the padding a\n * reading surface needs and keeps the type sizes and hit targets a control needs, so a\n * dense column stays clickable. Below this there is no honest saving left — the next step\n * down is a control too small to hit.\n *\n * The default is `comfortable`, so a consumer that never mentions density gets exactly what\n * it had.\n */\n\nimport { createContext, use } from \"react\";\nimport type { ReactNode } from \"react\";\n\n/**\n * How much room the chrome takes: `comfortable` (the default, for a page you read through)\n * or `compact` (for a permanent tool surface such as an inspector column).\n */\nexport type Density = \"comfortable\" | \"compact\";\n\nconst DensityContext = createContext<Density>(\"comfortable\");\n\n/**\n * The density in force here. Every primitive that has two spacings reads this.\n *\n * @returns The value of the nearest `DensityProvider`, or `comfortable` outside one.\n */\nexport function useDensity(): Density {\n return use(DensityContext);\n}\n\n/**\n * Pick one of two values by the density in force — the primitives' own idiom.\n *\n * @param density - The density in force, from `useDensity()`.\n * @param comfortable - The value for `comfortable`.\n * @param compact - The value for `compact`.\n * @returns `compact` when the density is compact, else `comfortable`.\n */\nexport function byDensity<T>(density: Density, comfortable: T, compact: T): T {\n return density === \"compact\" ? compact : comfortable;\n}\n\n/**\n * Sets the density for a region: every primitive below it reads the value through\n * `useDensity`. Nest providers to switch a sub-region back.\n */\nexport function DensityProvider({\n value,\n children,\n}: {\n /** The density for everything below. */\n value: Density;\n /** The region. */\n children: ReactNode;\n}) {\n return <DensityContext value={value}>{children}</DensityContext>;\n}\n","import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport { Loader2 } from \"lucide-react\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { byDensity, useDensity } from \"./Density\";\nimport { cn, focusRing } from \"./cn\";\n\n/**\n * A button's look, by what the action is: `primary` for the one action a screen is for,\n * `secondary` for the rest, `ghost` for toolbar actions, `danger` for an action that\n * destroys something.\n */\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"ghost\" | \"danger\";\n\n/**\n * A control's height and padding, shared by `Button`, `SegmentedControl` and `ToggleChip`. For a\n * button, `md` is the comfortable density's size and `sm` the compact one's.\n */\nexport type ButtonSize = \"sm\" | \"md\";\n\nconst VARIANT_CLASSES: Record<ButtonVariant, string> = {\n primary: \"bg-signal text-signal-fg hover:bg-signal-strong\",\n secondary: \"bg-surface text-fg ring-1 ring-inset ring-line-strong hover:bg-raised\",\n ghost: \"text-fg-muted hover:bg-raised hover:text-fg\",\n // Soft rather than a solid red block. The verdict red is also the colour of a defect\n // badge, and a filled button of it reads as an alarm on a screen that is mostly grey.\n // Genuinely destructive actions are behind a confirmation dialog regardless. The hover\n // tint stops at /15 so the red label keeps 4.5:1 on it.\n danger: \"bg-defect/10 text-defect ring-1 ring-inset ring-defect/30 hover:bg-defect/15\",\n};\n\nconst SIZE_CLASSES: Record<ButtonSize, string> = {\n sm: \"h-7 gap-1.5 px-2.5 text-xs\",\n md: \"h-8 gap-2 px-3 text-sm\",\n};\n\n/**\n * The classes that make a control look like a button, for the one element that has to be\n * something else. Prefer `Button` and `ButtonLink`; this exists so a third shape — a\n * `<label>` wrapping a file input, say — does not have to copy the variant table.\n *\n * @param options - The variant (default `secondary`), the size (default `md`) and the\n * caller's own classes, merged last.\n * @returns The class string.\n */\nexport function buttonClasses({\n variant = \"secondary\",\n size = \"md\",\n className,\n}: {\n variant?: ButtonVariant | undefined;\n size?: ButtonSize | undefined;\n className?: string | undefined;\n} = {}): string {\n return cn(\n \"inline-flex shrink-0 items-center justify-center rounded-control font-medium transition-colors\",\n \"disabled:cursor-not-allowed disabled:opacity-45\",\n SIZE_CLASSES[size],\n VARIANT_CLASSES[variant],\n focusRing,\n className,\n );\n}\n\nfunction ButtonIcon({ icon }: { icon: ReactNode }) {\n return (\n <span className=\"shrink-0 [&>svg]:size-3.5\" aria-hidden>\n {icon}\n </span>\n );\n}\n\n/**\n * An action. `asChild` renders the button's look onto the single child element instead of a\n * `<button>` — how a router's link becomes a button-shaped navigation without this package\n * knowing about routers: `<Button asChild><Link to=\"/runs\">Runs</Link></Button>`.\n * For a plain URL, `ButtonLink` is the shorter spelling.\n *\n * Takes every `<button>` prop, `ref` included. State is exposed as `data-variant` and, while\n * `loading`, `data-loading` (plus `aria-busy`).\n */\nexport function Button({\n variant = \"secondary\",\n size,\n type = \"button\",\n loading = false,\n icon,\n asChild = false,\n className,\n children,\n disabled,\n ...rest\n}: ComponentProps<\"button\"> & {\n /** What kind of action this is. Defaults to `secondary`. */\n variant?: ButtonVariant | undefined;\n /** Defaults to the density in force — `md` comfortable, `sm` compact. */\n size?: ButtonSize | undefined;\n /** Swaps the icon for a spinner and blocks the click, without the label changing. */\n loading?: boolean | undefined;\n /** A leading icon, decorative (`aria-hidden`): the label carries the meaning. */\n icon?: ReactNode;\n /**\n * Render onto the one child element (Radix `Slot`) rather than a `<button>`. The child\n * keeps its own semantics — a link stays a link — so `type`, `disabled` and `loading` do\n * not apply.\n */\n asChild?: boolean | undefined;\n}) {\n const density = useDensity();\n const resolved = size ?? byDensity(density, \"md\", \"sm\");\n\n if (asChild) {\n return (\n <Slot {...rest} data-variant={variant} className={buttonClasses({ variant, size: resolved, className })}>\n {icon && <ButtonIcon icon={icon} />}\n <Slottable>{children}</Slottable>\n </Slot>\n );\n }\n\n return (\n <button\n type={type}\n disabled={disabled || loading}\n aria-busy={loading || undefined}\n data-variant={variant}\n data-loading={loading ? \"\" : undefined}\n {...rest}\n className={buttonClasses({ variant, size: resolved, className })}\n >\n {loading ? <Loader2 className=\"size-3.5 shrink-0 animate-spin\" aria-hidden /> : icon && <ButtonIcon icon={icon} />}\n {children}\n </button>\n );\n}\n\n/**\n * A navigation that looks like a button: one `<a>`, styled by the same variants and density.\n *\n * The alternative every consumer reached for was `<Link><Button/></Link>` — a button inside\n * an anchor, which is invalid HTML, gives a keyboard two tab stops for one action, and makes\n * a screen reader announce a button that navigates. There is no `disabled` or `loading`:\n * a link that cannot be followed should not be rendered as one, and a navigation has no\n * pending state of its own.\n *\n * It renders a plain `<a href>`, and takes every `<a>` prop, `ref` included. For a router's\n * link, pass it as the only child with `asChild` —\n * `<ButtonLink asChild><Link to=\"/runs\">Runs</Link></ButtonLink>` — so client-side\n * navigation keeps working and this package still never imports a router.\n */\nexport function ButtonLink({\n variant = \"secondary\",\n size,\n icon,\n asChild = false,\n className,\n children,\n ...rest\n}: ComponentProps<\"a\"> & {\n /** What kind of action this is. Defaults to `secondary`. */\n variant?: ButtonVariant | undefined;\n /** Defaults to the density in force — `md` comfortable, `sm` compact. */\n size?: ButtonSize | undefined;\n /** A leading icon, decorative (`aria-hidden`): the label carries the meaning. */\n icon?: ReactNode;\n /** Render onto the one child element — a router's `<Link>` — instead of an `<a>`. */\n asChild?: boolean | undefined;\n}) {\n const density = useDensity();\n const resolved = size ?? byDensity(density, \"md\", \"sm\");\n const Comp = asChild ? Slot : \"a\";\n\n return (\n <Comp {...rest} data-variant={variant} className={buttonClasses({ variant, size: resolved, className })}>\n {icon && <ButtonIcon icon={icon} />}\n <Slottable>{children}</Slottable>\n </Comp>\n );\n}\n","/*\n * Confirmation for the things that cannot be undone, and a shell for the things that need\n * a form.\n *\n * `Dialog` is a modal with no verdict of its own: a title, a body the caller fills, and a\n * footer it chooses. `ConfirmDialog` is one arrangement of it -- the destructive-action\n * confirmation every app in this family needs at least once (deleting a dataset, discarding\n * an in-progress measurement session) -- rather than a second copy of the overlay, the\n * centring and the width cap.\n */\n\nimport * as RadixDialog from \"@radix-ui/react-dialog\";\nimport { useCallback, useState, type ReactNode } from \"react\";\n\nimport { Button } from \"./Button\";\nimport { cn, focusRingInset } from \"./cn\";\n\n/**\n * Whether the body overflows its height cap, kept current as the dialog or its content\n * resizes. A scrolling body has to be reachable by keyboard (so it can be scrolled without\n * a pointer); a body that fits must not add a tab stop.\n */\nfunction useOverflowing(): [(node: HTMLDivElement | null) => (() => void) | undefined, boolean] {\n const [overflowing, setOverflowing] = useState(false);\n const ref = useCallback((node: HTMLDivElement | null) => {\n if (node === null) return undefined;\n const measure = () => setOverflowing(node.scrollHeight > node.clientHeight + 1);\n measure();\n if (typeof ResizeObserver === \"undefined\") return undefined;\n const observer = new ResizeObserver(measure);\n observer.observe(node);\n // The body is capped, so once it hits the cap only the content keeps changing size.\n if (node.firstElementChild) observer.observe(node.firstElementChild);\n return () => observer.disconnect();\n }, []);\n return [ref, overflowing];\n}\n\n/**\n * A modal: a title, a body the caller fills, and a footer it chooses.\n *\n * Controlled — the caller owns `open`. The body scrolls under a height cap so the footer\n * stays on screen; while it overflows it is a keyboard tab stop (with the focus ring) and\n * carries `data-overflowing`, so it can be scrolled without a pointer.\n */\nexport function Dialog({\n open,\n onOpenChange,\n title,\n description,\n footer,\n className,\n children,\n}: {\n /** Whether the dialog is shown. */\n open: boolean;\n /** Called with `false` on Escape, an overlay click or a `DialogClose`. */\n onOpenChange: (open: boolean) => void;\n /** Names the dialog (`aria-labelledby`). */\n title: string;\n /** Optional: what the reader needs before they can answer, not decoration. */\n description?: ReactNode;\n /** The action row, right-aligned below the body. */\n footer?: ReactNode;\n /** Merged with the dialog panel's own classes through `cn`. */\n className?: string | undefined;\n /** The body. */\n children?: ReactNode;\n}) {\n const [bodyRef, overflowing] = useOverflowing();\n\n return (\n <RadixDialog.Root open={open} onOpenChange={onOpenChange}>\n <RadixDialog.Portal>\n <RadixDialog.Overlay className=\"fixed inset-0 z-50 bg-black/50 backdrop-blur-[1px]\" />\n <RadixDialog.Content\n className={cn(\n \"fixed top-1/2 left-1/2 z-50 w-[min(28rem,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2\",\n // A ceiling and a column, so a dialog that lists things keeps its footer on\n // screen and scrolls its body instead of growing past the viewport. Every\n // short dialog is far below the cap and is unaffected. This is a scroller\n // inside an overlay, not a second page scroller.\n \"flex max-h-[calc(100vh-4rem)] flex-col\",\n \"rounded-panel border border-line bg-overlay p-5 shadow-xl shadow-black/40\",\n \"focus:outline-none\",\n className,\n )}\n >\n <RadixDialog.Title className=\"text-sm font-semibold tracking-tight text-fg\">{title}</RadixDialog.Title>\n {/* Radix warns when a dialog has no description; render an empty one rather than\n inventing prose for a form whose fields already carry their own labels. */}\n <RadixDialog.Description className={cn(\"mt-2 text-sm leading-relaxed text-fg-muted\", description === undefined && \"sr-only\")}>\n {description ?? title}\n </RadixDialog.Description>\n <div\n ref={bodyRef}\n tabIndex={overflowing ? 0 : undefined}\n data-overflowing={overflowing ? \"\" : undefined}\n className={cn(\"min-h-0 overflow-y-auto\", focusRingInset)}\n >\n <div>{children}</div>\n </div>\n {footer && <div className=\"mt-5 flex shrink-0 justify-end gap-2\">{footer}</div>}\n </RadixDialog.Content>\n </RadixDialog.Portal>\n </RadixDialog.Root>\n );\n}\n\n/**\n * The destructive-action confirmation: a `Dialog` whose footer is Cancel and one confirm\n * button. Say in `description` what will actually happen.\n */\nexport function ConfirmDialog({\n open,\n onOpenChange,\n title,\n description,\n confirmLabel,\n onConfirm,\n loading = false,\n disabled = false,\n destructive = false,\n className,\n}: {\n /** Whether the dialog is shown. */\n open: boolean;\n /** Called with `false` on Cancel, Escape or an overlay click. */\n onOpenChange: (open: boolean) => void;\n /** The question, e.g. \"Delete dataset?\". */\n title: string;\n /** Say what will actually happen, naming the thing by the name the reader gave it. */\n description: ReactNode;\n /** The confirm button's label: the verb, e.g. \"Delete\". */\n confirmLabel: string;\n /** Called on confirm. The dialog stays open; close it (or set `loading`) yourself. */\n onConfirm: () => void;\n /** Shows the confirm button's spinner and blocks it while the action runs. */\n loading?: boolean;\n /** Blocks the confirm button. */\n disabled?: boolean;\n /** Renders the confirm button as `danger` rather than `primary`. */\n destructive?: boolean;\n /** Merged with the dialog panel's own classes through `cn`. */\n className?: string | undefined;\n}) {\n return (\n <Dialog\n open={open}\n onOpenChange={onOpenChange}\n title={title}\n description={description}\n className={className}\n footer={\n <>\n <RadixDialog.Close asChild>\n <Button variant=\"ghost\">Cancel</Button>\n </RadixDialog.Close>\n <Button variant={destructive ? \"danger\" : \"primary\"} loading={loading} disabled={disabled} onClick={onConfirm}>\n {confirmLabel}\n </Button>\n </>\n }\n />\n );\n}\n\n/**\n * The dialog's own dismiss, for a footer the caller builds: wrap a button in it with\n * `asChild` — `<DialogClose asChild><Button>Done</Button></DialogClose>`.\n */\nexport const DialogClose = RadixDialog.Close;\n","/*\n * Content folded away, with a caret we draw ourselves.\n *\n * The base layer (`styles.css`) removes the native `<details>` marker; this supplies one\n * that rotates with the element's own open state, so no JavaScript is involved in the\n * animation.\n *\n * `<details>` rather than a headless package on purpose: the element already has the\n * semantics, the keyboard behaviour and the find-in-page integration.\n */\n\nimport { ChevronRight } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\n\nimport { cn } from \"./cn\";\n\n/**\n * Content folded away behind a summary line, on a native `<details>`, with a caret that\n * rotates with the element's own `open` state (no JavaScript involved).\n *\n * Uncontrolled: `defaultOpen` sets the initial state and the element owns it after that.\n */\nexport function Disclosure({\n summary,\n count,\n defaultOpen = false,\n className,\n children,\n}: {\n /** The always-visible line that toggles the content. */\n summary: ReactNode;\n /** How many things are inside, where the number is the reason to open it. */\n count?: number;\n /** Whether it starts open. */\n defaultOpen?: boolean;\n /** Merged with the `<details>` element's own classes through `cn`. */\n className?: string | undefined;\n /** The folded content. */\n children: ReactNode;\n}) {\n return (\n <details open={defaultOpen} className={cn(\"group\", className)}>\n <summary\n className={cn(\n \"flex cursor-pointer items-center gap-1.5 rounded-control py-1 text-xs font-medium text-fg-muted\",\n \"transition-colors hover:text-fg\",\n \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-signal\",\n )}\n >\n <ChevronRight className=\"size-3.5 shrink-0 transition-transform group-open:rotate-90\" aria-hidden />\n {summary}\n {count !== undefined && <span className=\"font-mono text-fg-subtle tabular-nums\">{count}</span>}\n </summary>\n <div className=\"pt-3\">{children}</div>\n </details>\n );\n}\n","/*\n * A key, as the keyboard shows it: the shortcut next to a command, the key a hint refers to.\n */\n\nimport type { ComponentProps } from \"react\";\n\nimport { cn } from \"./cn\";\n\n/**\n * A keyboard key or chord (`F`, `⌘K`, `[`), set in mono on a raised key cap.\n *\n * Renders `<kbd>`, which screen readers announce as text. Write the key as the user's keyboard\n * labels it; for a chord, one `Kbd` with the whole chord reads better than several. Takes\n * every `<kbd>` prop, `ref` included.\n */\nexport function Kbd({ className, ...rest }: ComponentProps<\"kbd\">) {\n return (\n <kbd\n {...rest}\n className={cn(\n \"inline-flex min-w-[1.5em] items-center justify-center rounded-control border border-line-strong\",\n \"bg-raised px-1 font-mono text-[10px] leading-4 text-fg-muted\",\n className,\n )}\n />\n );\n}\n","/*\n * A menu of commands or toggles behind a trigger: a layers menu, an overflow menu.\n *\n * Use it for things a person *does* or *switches*. To pick one value from a set use `Select`,\n * or a `Listbox` in a `Popover` when the options need more than a label.\n */\n\nimport * as RadixMenu from \"@radix-ui/react-dropdown-menu\";\nimport { Check } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\n\nimport { Kbd } from \"./Kbd\";\nimport { cn } from \"./cn\";\n\n/** Props of `DropdownMenu`. */\nexport interface DropdownMenuProps {\n /** The element that opens it: one focusable element that accepts a ref (Radix `asChild`). */\n trigger: ReactNode;\n /** `MenuItem`, `MenuCheckboxItem`, `MenuLabel` and `MenuSeparator` elements. */\n children: ReactNode;\n /** Controlled open state. */\n open?: boolean | undefined;\n /** Called when it opens or closes. */\n onOpenChange?: ((open: boolean) => void) | undefined;\n /** Its alignment along the trigger. Defaults to `start`. */\n align?: \"start\" | \"center\" | \"end\" | undefined;\n /** The trigger's side it opens on. Defaults to `bottom`. */\n side?: \"top\" | \"right\" | \"bottom\" | \"left\" | undefined;\n /** Merged with the menu's own classes through `cn`. */\n className?: string | undefined;\n}\n\n/**\n * A menu (`role=\"menu\"`) that opens from `trigger`, with arrow-key navigation, typeahead,\n * Escape or a press outside to close, and focus returned to the trigger. It is non-modal: the\n * page behind stays readable and operable. Toggling a `MenuCheckboxItem` keeps the menu open,\n * so several layers can be switched in one visit.\n */\nexport function DropdownMenu({ trigger, children, open, onOpenChange, align = \"start\", side = \"bottom\", className }: DropdownMenuProps) {\n return (\n <RadixMenu.Root\n // Non-modal: a modal menu hides the rest of the page from assistive technology while\n // its trigger stays focusable under it, and a layers menu is used next to the canvas\n // it controls.\n modal={false}\n {...(open === undefined ? {} : { open })}\n {...(onOpenChange === undefined ? {} : { onOpenChange })}\n >\n <RadixMenu.Trigger asChild>{trigger}</RadixMenu.Trigger>\n <RadixMenu.Portal>\n <RadixMenu.Content\n side={side}\n align={align}\n sideOffset={6}\n collisionPadding={8}\n className={cn(\n \"z-50 min-w-44 rounded-panel border border-line bg-overlay p-1 text-sm text-fg shadow-lg shadow-black/25\",\n className,\n )}\n >\n {children}\n </RadixMenu.Content>\n </RadixMenu.Portal>\n </RadixMenu.Root>\n );\n}\n\nconst itemClasses = cn(\n \"relative flex h-7 cursor-default items-center gap-2 rounded-control pr-2 pl-7 text-sm outline-none select-none\",\n \"data-[highlighted]:bg-raised data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n);\n\n/** Props of `MenuItem`. */\nexport interface MenuItemProps {\n /** The item's text. */\n children: ReactNode;\n /** Runs the command. */\n onSelect?: (() => void) | undefined;\n /** An icon before the text, in the indicator column. */\n icon?: ReactNode;\n /** `defect` for a destructive command. */\n tone?: \"defect\" | undefined;\n /** A shortcut shown at the end of the row, as a `Kbd`. Display only: binding it is the app's. */\n shortcut?: string | undefined;\n /** Blocks the item. */\n disabled?: boolean | undefined;\n}\n\n/**\n * A command. `onSelect` runs when it is clicked or chosen with Enter, and the menu then\n * closes. `tone=\"defect\"` marks a destructive command.\n */\nexport function MenuItem({ children, onSelect, shortcut, disabled, icon, tone }: MenuItemProps) {\n return (\n <RadixMenu.Item\n {...(disabled === undefined ? {} : { disabled })}\n onSelect={() => onSelect?.()}\n className={cn(itemClasses, tone === \"defect\" && \"text-defect\")}\n >\n {icon && (\n <span aria-hidden className=\"absolute left-2 flex size-3.5 items-center [&>svg]:size-3.5\">\n {icon}\n </span>\n )}\n <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n {shortcut && <Kbd>{shortcut}</Kbd>}\n </RadixMenu.Item>\n );\n}\n\n/** Props of `MenuCheckboxItem`. */\nexport interface MenuCheckboxItemProps {\n /** The item's text. */\n children: ReactNode;\n /** Whether it is on. */\n checked: boolean;\n /** Called with the new state. */\n onCheckedChange: (checked: boolean) => void;\n /** A shortcut shown at the end of the row, as a `Kbd`. Display only: binding it is the app's. */\n shortcut?: string | undefined;\n /** Blocks the item. */\n disabled?: boolean | undefined;\n}\n\n/**\n * A toggle (`role=\"menuitemcheckbox\"`): a layer on or off. A check marks it on. Choosing it\n * leaves the menu open.\n */\nexport function MenuCheckboxItem({ children, checked, onCheckedChange, shortcut, disabled }: MenuCheckboxItemProps) {\n return (\n <RadixMenu.CheckboxItem\n checked={checked}\n onCheckedChange={onCheckedChange}\n {...(disabled === undefined ? {} : { disabled })}\n // A toggle in a menu of toggles: keep the menu open for the next one.\n onSelect={(event) => event.preventDefault()}\n className={itemClasses}\n >\n <RadixMenu.ItemIndicator className=\"absolute left-2 flex items-center text-signal\">\n <Check className=\"size-3.5\" aria-hidden />\n </RadixMenu.ItemIndicator>\n <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n {shortcut && <Kbd>{shortcut}</Kbd>}\n </RadixMenu.CheckboxItem>\n );\n}\n\n/** A heading over a group of items, in the eyebrow role. Not focusable. */\nexport function MenuLabel({\n children,\n}: {\n /** The heading. */\n children: ReactNode;\n}) {\n return (\n <RadixMenu.Label className=\"px-2 pt-1.5 pb-1 text-[11px] font-semibold tracking-wider text-fg-muted uppercase\">\n {children}\n </RadixMenu.Label>\n );\n}\n\n/** A divider between groups of items. */\nexport function MenuSeparator() {\n return <RadixMenu.Separator className=\"my-1 h-px bg-line\" />;\n}\n","/*\n * What the screen says when it has something other than data to show.\n *\n * An error explains what happened and what to do about it; it does not apologise and it is\n * never vague. An empty state is an invitation to act, so it carries the action rather than\n * describing it. And loading is a shape, not the word \"Loading…\" -- a skeleton or a\n * progress bar keeps a slow request from looking identical to a broken one.\n */\n\nimport { AlertTriangle, CheckCircle2, Info, XCircle } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\n\nimport { cn } from \"./cn\";\n\n/** A callout's kind: a fact (`info`), a caveat (`warning`), a failure (`error`) or `success`. */\nexport type CalloutTone = \"info\" | \"warning\" | \"error\" | \"success\";\n\nconst CALLOUT: Record<CalloutTone, { classes: string; Icon: typeof Info }> = {\n info: { classes: \"border-signal/30 bg-signal/8 text-fg\", Icon: Info },\n warning: { classes: \"border-warn/30 bg-warn/8 text-fg\", Icon: AlertTriangle },\n error: { classes: \"border-defect/30 bg-defect/8 text-fg\", Icon: XCircle },\n success: { classes: \"border-normal/30 bg-normal/8 text-fg\", Icon: CheckCircle2 },\n};\n\nconst ICON_TONE: Record<CalloutTone, string> = {\n info: \"text-signal\",\n warning: \"text-warn\",\n error: \"text-defect\",\n success: \"text-normal\",\n};\n\n/**\n * A boxed message with a tone icon, an optional title and optional actions.\n *\n * An `error` callout is a `role=\"alert\"` (announced at once); the others are\n * `role=\"status\"`. The tone is exposed as `data-tone`.\n */\nexport function Callout({\n tone = \"info\",\n title,\n actions,\n className,\n children,\n}: {\n /** Defaults to `info`. */\n tone?: CalloutTone | undefined;\n /** A one-line summary above the message. */\n title?: ReactNode;\n /** Buttons for what to do about it, below the message. */\n actions?: ReactNode;\n /** Merged with the callout's own classes through `cn`. */\n className?: string | undefined;\n /** The message: what happened and what to do about it. */\n children: ReactNode;\n}) {\n const { classes, Icon } = CALLOUT[tone];\n return (\n <div\n role={tone === \"error\" ? \"alert\" : \"status\"}\n data-tone={tone}\n className={cn(\"flex items-start gap-2.5 rounded-control border px-3 py-2.5 text-sm leading-snug\", classes, className)}\n >\n <Icon className={cn(\"mt-0.5 size-4 shrink-0\", ICON_TONE[tone])} aria-hidden />\n <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n {title && <p className=\"font-medium\">{title}</p>}\n <div className=\"min-w-0 text-fg-muted\">{children}</div>\n {actions && <div className=\"mt-1 flex flex-wrap items-center gap-2\">{actions}</div>}\n </div>\n </div>\n );\n}\n\n/** The common error shape: an `error` `Callout` with only a message. */\nexport function ErrorBox({\n className,\n children,\n}: {\n /** Merged with the callout's own classes through `cn`. */\n className?: string | undefined;\n /** What went wrong and what to do about it. */\n children: ReactNode;\n}) {\n return (\n <Callout tone=\"error\" className={className}>\n {children}\n </Callout>\n );\n}\n\n/** An empty state: a sentence saying why there is nothing here, and the action that fixes it. */\nexport function Empty({\n action,\n className,\n children,\n}: {\n /** The action — usually a primary `Button` — rather than a description of it. */\n action?: ReactNode;\n /** Merged with the empty state's own classes through `cn`. */\n className?: string | undefined;\n /** Why there is nothing to show. */\n children: ReactNode;\n}) {\n return (\n <div className={cn(\"flex flex-col items-center gap-3 py-10 text-center\", className)}>\n <p className=\"text-sm text-fg-muted\">{children}</p>\n {action}\n </div>\n );\n}\n\n/** One placeholder bar for content on its way (`aria-hidden`; size it with `className`). */\nexport function Skeleton({\n className,\n}: {\n /** Sets the bar's size; merged with its own classes through `cn`. */\n className?: string | undefined;\n}) {\n return <div aria-hidden className={cn(\"animate-pulse rounded-control bg-line\", className)} />;\n}\n\n/** A stand-in for a panel's worth of content, so the layout does not jump when it lands. */\nexport function SkeletonRows({\n rows = 3,\n className,\n}: {\n /** How many bars. Defaults to 3; the last is shorter. */\n rows?: number | undefined;\n /** Merged with the column's own classes through `cn`. */\n className?: string | undefined;\n}) {\n return (\n <div role=\"status\" aria-label=\"Loading\" className={cn(\"flex flex-col gap-2\", className)}>\n {Array.from({ length: rows }, (_, index) => (\n <Skeleton key={index} className={cn(\"h-4\", index === rows - 1 ? \"w-2/3\" : \"w-full\")} />\n ))}\n </div>\n );\n}\n\n/**\n * A known fraction done, as a bar and (with `label`) a percentage line.\n *\n * A `role=\"progressbar\"` with `aria-valuenow` in percent. Its accessible name is\n * `aria-label` if given, else `label`, else \"Progress\" — pass one that names the job when\n * a screen shows more than one bar.\n *\n * @deprecated Use `Progress`: a value out of a `max` with a \"412 of 2,556\" value text, an\n * indeterminate state, and a track that follows the density.\n */\nexport function ProgressBar({\n fraction,\n label,\n \"aria-label\": ariaLabel,\n className,\n}: {\n /** Done, from 0 to 1. Clamped. */\n fraction: number;\n /** What is progressing, printed after the percentage: \"42 of 100 images\". */\n label?: string | undefined;\n /** The bar's accessible name. Defaults to `label`, then to \"Progress\". */\n \"aria-label\"?: string | undefined;\n /** Merged with the wrapper's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const percent = Math.round(Math.min(Math.max(fraction, 0), 1) * 100);\n return (\n <div className={cn(\"flex flex-col gap-1.5\", className)}>\n <div\n role=\"progressbar\"\n aria-label={ariaLabel ?? label ?? \"Progress\"}\n aria-valuenow={percent}\n aria-valuemin={0}\n aria-valuemax={100}\n data-complete={percent === 100 ? \"\" : undefined}\n className=\"h-1.5 overflow-hidden rounded-full bg-line\"\n >\n <div className=\"h-full rounded-full bg-signal transition-[width] duration-300\" style={{ width: `${percent}%` }} />\n </div>\n {label && (\n <span className=\"font-mono text-xs text-fg-muted tabular-nums\">\n {percent}% · {label}\n </span>\n )}\n </div>\n );\n}\n","/*\n * A labelled control.\n *\n * Two things a naive label cannot express, both of which the schema form needs. A required\n * field's marker is a separate span rather than text concatenated onto the label string, so\n * it can be styled, read out as a marker, or dropped. And a field's description (and its\n * error) is wired to the control through `aria-describedby`, so a screen reader connects\n * the two.\n *\n * `as=\"group\"` exists because a `<label>` cannot label a set of radios or a segmented\n * control; that case needs a labelled group instead, and getting it wrong is the difference\n * between a control that announces itself and one that announces nothing.\n */\n\nimport { createContext, use, useId, useMemo, type ReactNode } from \"react\";\n\nimport { byDensity, useDensity } from \"./Density\";\nimport { cn } from \"./cn\";\n\n/** What a `Field` tells the control inside it: the ids that describe it, and whether it is invalid. */\ninterface FieldControl {\n describedBy: string | undefined;\n invalid: boolean;\n}\n\nconst FieldContext = createContext<FieldControl>({ describedBy: undefined, invalid: false });\n\n/**\n * The `aria-describedby` and `aria-invalid` a control inside a `Field` should carry, merged\n * with the control's own `aria-describedby`. Used by this package's controls; not exported\n * from the package.\n */\nexport function useFieldDescription(own?: string): {\n \"aria-describedby\": string | undefined;\n \"aria-invalid\": true | undefined;\n} {\n const { describedBy, invalid } = use(FieldContext);\n const ids = [own, describedBy].filter(Boolean).join(\" \");\n return { \"aria-describedby\": ids === \"\" ? undefined : ids, \"aria-invalid\": invalid || undefined };\n}\n\n/**\n * A label, the control, and optionally a description, an error and a compact annotation.\n *\n * The label text and the annotation name the control (the `<label>` wraps them and the\n * control); the description and the error are outside the label and wired into the\n * control's `aria-describedby` instead (the error also sets `aria-invalid`): directly for\n * this package's controls — `Input`, `NumberInput`, `Textarea`, `Select`, `Slider`,\n * `SegmentedControl`, `Checkbox` — and, with `as=\"group\"`, on the group itself. A control of your own inside a `label` field does not\n * pick them up; give it `aria-describedby` yourself. State is exposed as `data-invalid` and\n * `data-required`.\n */\nexport function Field({\n label,\n description,\n error,\n required = false,\n annotation,\n as = \"label\",\n className,\n children,\n}: {\n /** The field's name, shown above the control. */\n label: string;\n /** What the value means or does. Always visible, and read with the control. */\n description?: ReactNode;\n /** What is wrong with the current value. Shown in the verdict red, announced as an alert. */\n error?: ReactNode;\n /** Marks the field required (a `*` after the label). */\n required?: boolean | undefined;\n /** A compact fact about the accepted values -- a range, a unit -- kept out of the prose. */\n annotation?: ReactNode;\n /**\n * `label` (the default) wraps the control in a `<label>`; `group` renders a\n * `role=\"group\"` named by `label`, for a set of radios or a segmented control.\n */\n as?: \"label\" | \"group\";\n /** Merged with the field's own classes through `cn`. */\n className?: string | undefined;\n /** The control. */\n children: ReactNode;\n}) {\n const descriptionId = useId();\n const errorId = useId();\n const density = useDensity();\n\n const hasError = error !== undefined && error !== null && error !== false;\n const describedBy = [description ? descriptionId : undefined, hasError ? errorId : undefined].filter(Boolean).join(\" \") || undefined;\n\n // A group carries the description itself; its controls would only repeat it.\n const control = useMemo(\n () => ({ describedBy: as === \"label\" ? describedBy : undefined, invalid: hasError }),\n [as, describedBy, hasError],\n );\n\n const gap = byDensity(density, \"gap-1.5\", \"gap-1\");\n\n const labelled = (\n <>\n <span className=\"flex items-baseline gap-2\">\n <span className={cn(\"font-medium text-fg\", byDensity(density, \"text-xs\", \"text-[11px]\"))}>\n {label}\n {required && (\n <span className=\"ml-0.5 text-defect\" title=\"Required\">\n *\n </span>\n )}\n </span>\n {annotation && <span className=\"ml-auto font-mono text-[11px] text-fg-subtle\">{annotation}</span>}\n </span>\n <FieldContext value={control}>{children}</FieldContext>\n </>\n );\n\n return (\n <div\n className={cn(\"flex min-w-0 flex-col\", gap, className)}\n data-invalid={hasError ? \"\" : undefined}\n data-required={required ? \"\" : undefined}\n {...(as === \"group\" ? { role: \"group\", \"aria-label\": label, \"aria-describedby\": describedBy } : {})}\n >\n {/* The description and the error sit outside the <label>: inside it they would be read\n as part of the control's name as well as its description. */}\n {as === \"label\" ? <label className={cn(\"flex min-w-0 flex-col\", gap)}>{labelled}</label> : labelled}\n\n {description && (\n <span id={descriptionId} className=\"text-xs leading-snug text-fg-muted\">\n {description}\n </span>\n )}\n {hasError && (\n <span id={errorId} role=\"alert\" className=\"text-xs leading-snug text-defect\">\n {error}\n </span>\n )}\n </div>\n );\n}\n","/*\n * The model behind `HighlightedText`: spans over a string, cut into the runs that render\n * alike, and spans found by regular expressions. Pure, so it is tested without a DOM.\n *\n * Offsets are UTF-16 code-unit indices, as `String.prototype.slice` and `RegExp` use them.\n */\n\n/** What a highlighted span is. Each kind has its own shape on screen, not only its own colour. */\nexport type HighlightKind = \"placeholder\" | \"tag\" | \"protected-term\" | \"mismatch\";\n\n/** A highlighted range of the text: `[start, end)` in UTF-16 code units. */\nexport interface HighlightSpan {\n /** The offset of the span's first code unit. */\n start: number;\n /** The offset just past the span's last code unit. */\n end: number;\n /** What the span is; it decides the span's shape and its default label. */\n kind: HighlightKind;\n /**\n * What a screen reader hears after the span, instead of the kind's default label: say\n * *why* it is marked (\"missing from the translation\").\n */\n label?: string | undefined;\n}\n\n/** A run of the text covered by one set of spans; two spans side by side are two runs. */\nexport interface TextSegment {\n /** The run's characters. */\n text: string;\n /** Where the run starts in the whole text. */\n start: number;\n /** Where it ends. */\n end: number;\n /** The spans covering the run, in their order in the input. Empty for plain text. */\n spans: HighlightSpan[];\n}\n\n/** The kinds in the order their styles stack: a later one wins where two would clash. */\nexport const HIGHLIGHT_KINDS: readonly HighlightKind[] = [\"tag\", \"protected-term\", \"placeholder\", \"mismatch\"];\n\n/**\n * Cuts `text` into runs that are each covered by one fixed set of spans. Spans are clamped\n * to the text; empty, inverted and non-finite ones are dropped. Overlapping spans are kept:\n * the overlap is a run covered by both.\n *\n * @param text - The string, shown as text.\n * @param spans - The highlights, in any order.\n * @returns The runs, in order, together covering the whole text.\n */\nexport function segmentText(text: string, spans: readonly HighlightSpan[]): TextSegment[] {\n const length = text.length;\n const clamp = (offset: number) => Math.floor(Math.max(0, Math.min(length, offset)));\n const valid = spans\n .map((span) => ({ span, start: clamp(span.start), end: clamp(span.end) }))\n .filter(({ start, end }) => Number.isFinite(start) && Number.isFinite(end) && end > start);\n const cuts = new Set<number>([0, length]);\n for (const { start, end } of valid) {\n cuts.add(start);\n cuts.add(end);\n }\n const points = [...cuts].sort((a, b) => a - b);\n const segments: TextSegment[] = [];\n for (let i = 0; i + 1 < points.length; i++) {\n const start = points[i]!;\n const end = points[i + 1]!;\n const covering = valid.filter((v) => v.start <= start && v.end >= end).map((v) => v.span);\n segments.push({ text: text.slice(start, end), start, end, spans: covering });\n }\n return segments;\n}\n\n/** A pattern whose matches become spans of one kind. */\nexport interface HighlightRule {\n /** Matched against the whole text, every match (the `g` flag is added if missing). */\n pattern: RegExp;\n /** The kind every match gets. */\n kind: HighlightKind;\n /** The spans' `label`, if they need one. */\n label?: string | undefined;\n}\n\n/**\n * Spans for every match of every rule: `%1`, `<b>`, a glossary term. Empty matches are\n * skipped. The result is sorted by start, then longest first.\n *\n * @param text - The string to search.\n * @param rules - Each pattern and the kind its matches get.\n * @returns The spans, ready for `HighlightedText`.\n *\n * @example\n * ```ts\n * findSpans(\"Open %1 in <b>ParamsManager</b>\", [\n * { pattern: /%\\d+/, kind: \"placeholder\" },\n * { pattern: /<\\/?[a-z][^>]*>/i, kind: \"tag\" },\n * { pattern: /\\bParamsManager\\b/, kind: \"protected-term\" },\n * ]);\n * ```\n */\nexport function findSpans(text: string, rules: readonly HighlightRule[]): HighlightSpan[] {\n const spans: HighlightSpan[] = [];\n for (const rule of rules) {\n const flags = rule.pattern.flags.includes(\"g\") ? rule.pattern.flags : `${rule.pattern.flags}g`;\n for (const match of text.matchAll(new RegExp(rule.pattern.source, flags))) {\n if (match[0] === \"\") continue;\n const start = match.index;\n spans.push({ start, end: start + match[0].length, kind: rule.kind, ...(rule.label === undefined ? {} : { label: rule.label }) });\n }\n }\n return spans.sort((a, b) => a.start - b.start || b.end - a.end);\n}\n","/*\n * A string with marked spans: the placeholders, tags and protected terms of a UI string, and\n * where a translation does not match its source.\n *\n * The string is only ever text. It is cut into runs and each run is a React text child, so\n * `<b>` in a string is shown as the four characters `<b>`, never parsed. There is no\n * `dangerouslySetInnerHTML` here, nor anywhere in the package.\n *\n * Each kind differs in shape, not only in colour (a box, a mono face, an underline style),\n * and a screen reader hears the kind after the span. That label is selection-proof: a reader\n * copying the string gets the string.\n */\n\nimport type { ComponentProps } from \"react\";\n\nimport { cn } from \"./cn\";\nimport { HIGHLIGHT_KINDS, segmentText, type HighlightKind, type HighlightSpan } from \"./highlightSpans\";\n\n/** What a screen reader hears after a span of each kind, by default. */\nconst LABELS: Record<HighlightKind, string> = {\n placeholder: \"placeholder\",\n tag: \"markup\",\n \"protected-term\": \"protected term\",\n mismatch: \"mismatch\",\n};\n\n/** The look of each kind; listed in `HIGHLIGHT_KINDS` order, so a later kind wins a clash. */\nconst STYLES: Record<HighlightKind, string> = {\n // Code-like, in a quiet box.\n tag: \"rounded-sm bg-raised px-px font-mono text-fg-muted\",\n // Read as a name: a dotted underline, the face unchanged.\n \"protected-term\": \"underline decoration-fg-subtle decoration-dotted decoration-1 underline-offset-[3px]\",\n // A slot the code fills: mono, outlined.\n placeholder: \"rounded-sm bg-raised px-px font-mono text-fg ring-1 ring-line-strong ring-inset\",\n // A verdict the app made: a wavy underline and a tint, in the defect colour.\n mismatch: \"bg-defect/12 text-fg underline decoration-defect decoration-wavy decoration-1 underline-offset-[3px]\",\n};\n\n/** Props of `HighlightedText`: the text, its spans, and every `<span>` prop but `children`. */\nexport type HighlightedTextProps = Omit<ComponentProps<\"span\">, \"children\"> & {\n /** The string. Shown as text, whitespace and line breaks kept; markup in it is not interpreted. */\n text: string;\n /** The ranges to mark, in UTF-16 offsets, in any order; overlaps are allowed. `findSpans` builds them from patterns. */\n spans?: readonly HighlightSpan[] | undefined;\n /**\n * What a screen reader hears after a span of each kind: \"placeholder\", \"markup\",\n * \"protected term\" and \"mismatch\" by default. `null` silences a kind. A span's own `label` wins.\n */\n labels?: Partial<Record<HighlightKind, string | null>> | undefined;\n};\n\n/**\n * A string with highlighted spans: placeholders (`%1`, `{count}`), markup tags (`<b>`),\n * protected terms (`ParamsManager`) and mismatches, each drawn in its own shape. The caller\n * supplies the spans, or finds them with `findSpans`.\n *\n * The text is rendered as text, never as HTML. Each span is a `<mark>` with `data-kind` (the\n * kinds covering it, space-separated), followed by its label for screen readers (\"%1,\n * placeholder\"), which a copy of the text leaves out.\n */\nexport function HighlightedText({ text, spans = [], labels, className, ...rest }: HighlightedTextProps) {\n const segments = segmentText(text, spans);\n return (\n <span {...rest} className={cn(\"break-words whitespace-pre-wrap\", className)}>\n {segments.map((segment) => {\n if (segment.spans.length === 0) return segment.text;\n const kinds = HIGHLIGHT_KINDS.filter((kind) => segment.spans.some((span) => span.kind === kind));\n const said = segment.spans\n .map((span) => span.label ?? (labels?.[span.kind] === undefined ? LABELS[span.kind] : labels[span.kind]))\n .filter((label): label is string => typeof label === \"string\" && label !== \"\");\n return (\n <mark\n key={segment.start}\n data-kind={kinds.join(\" \")}\n className={cn(\"bg-transparent text-inherit\", ...kinds.map((kind) => STYLES[kind]))}\n >\n {segment.text}\n {said.length > 0 && <span className=\"sr-only select-none\">{`, ${[...new Set(said)].join(\", \")}`}</span>}\n </mark>\n );\n })}\n </span>\n );\n}\n","/*\n * Numbers as a field shows them and as a person types them. Used by `NumberInput`'s\n * number-valued mode, `VectorInput` and `PoseInput`, and exported for an app's own fields.\n *\n * A field bound straight to a number re-formats under the cursor: typing `0.` into a field\n * showing `0.000` becomes `0` before the next digit arrives. So the multi-number fields keep\n * the typed text while they have focus and show the formatted value otherwise; these are the\n * two conversions between the two.\n */\n\n/**\n * A number at a fixed number of decimals, without a negative zero (`-0.000` reads as a sign\n * error in a coordinate readout).\n *\n * @param value - The number. Non-finite values print as `NaN`, `Infinity`, `-Infinity`.\n * @param precision - Decimals, 0–20.\n * @returns The text.\n */\nexport function formatNumber(value: number, precision: number): string {\n if (!Number.isFinite(value)) return String(value);\n const text = value.toFixed(precision);\n return /^-0(?:\\.0*)?$/.test(text) ? text.slice(1) : text;\n}\n\n/**\n * The number a field's text means, if it means one.\n *\n * @param text - What is in the field.\n * @returns The number, or `null` for empty, partial (`-`, `1e`) or non-numeric text.\n */\nexport function parseNumber(text: string): number | null {\n const trimmed = text.trim();\n if (trimmed === \"\") return null;\n const value = Number(trimmed);\n return Number.isFinite(value) ? value : null;\n}\n","/*\n * Text entry.\n *\n * `controlClasses` is exported so a native control that isn't one of the wrapped ones below\n * can still pick up the shared palette and focus treatment.\n */\n\nimport { useId, useState, type ComponentProps, type KeyboardEvent } from \"react\";\n\nimport { byDensity, useDensity } from \"./Density\";\nimport { useFieldDescription } from \"./Field\";\nimport { cn, focusRingInset } from \"./cn\";\nimport { formatNumber, parseNumber } from \"./numberText\";\n\n/**\n * The shared look of a text-like control — border, palette, placeholder, hover, disabled and\n * focus — for a native control that is not one of `Input`, `NumberInput` or `Textarea`.\n * Add the height with `useControlHeight()`.\n */\nexport const controlClasses = cn(\n \"w-full rounded-control border border-line-strong bg-raised px-2.5 text-sm text-fg\",\n \"placeholder:text-fg-subtle\",\n \"transition-colors hover:border-fg-subtle\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n focusRingInset,\n);\n\n/**\n * The former name of `controlClasses`, kept as an alias for existing consumers.\n *\n * @deprecated Use `Input`, `NumberInput`, `Textarea` or `Select`, or `controlClasses`.\n */\nexport const inputClasses = controlClasses;\n\n/**\n * The control height in force — exported so an app's own native control can match.\n *\n * @returns A Tailwind height class: `h-8` comfortable, `h-7` compact.\n */\nexport function useControlHeight(): string {\n return byDensity(useDensity(), \"h-8\", \"h-7\");\n}\n\n/**\n * A single-line text input. Takes every `<input>` prop, `ref` included; inside a `Field` it\n * is described by the field's description and error.\n */\nexport function Input({ className, \"aria-describedby\": describedBy, ...rest }: ComponentProps<\"input\">) {\n return <input {...useFieldDescription(describedBy)} {...rest} className={cn(controlClasses, useControlHeight(), className)} />;\n}\n\n/**\n * Props of `NumberInput`: every `<input>` prop, `ref` included, with numeric bounds, a `unit`,\n * and an optional number-valued API (`onValueChange`, `precision`).\n */\nexport type NumberInputProps = Omit<ComponentProps<\"input\">, \"min\" | \"max\" | \"value\"> & {\n /**\n * The value. As text, it is the native input's `value`. With `onValueChange` it is a\n * number, and `null` shows an empty field.\n */\n value?: string | number | readonly string[] | null | undefined;\n /**\n * Makes the field number-valued.\n *\n * - **While focused** it keeps the text as typed. A keystroke is reported only when the\n * text is a finite number inside `[min, max]` and differs from `value`. Empty, partial\n * (`-`, `1e`) and out-of-range text is never reported, so clearing a field to retype it\n * does not pass through 0, and typing `5` on the way to `50` does not report a value under\n * `min`.\n * - **Enter** shows the value at `precision`. **Escape** restores the value the field had\n * on focus.\n * - **On leaving**, the field shows `value` again.\n */\n onValueChange?: ((value: number) => void) | undefined;\n /**\n * Called when a number-valued field is committed empty (left, or Enter pressed, with no\n * text), for a quantity that may be unset. Without it, emptying the field and leaving it\n * restores `value`.\n */\n onClear?: (() => void) | undefined;\n /**\n * Decimals shown when a number-valued field is not being edited. Without it the number is\n * shown as JavaScript prints it.\n */\n precision?: number | undefined;\n /** The smallest accepted value. */\n min?: number | undefined;\n /** The largest accepted value. */\n max?: number | undefined;\n /**\n * The quantity's unit (`mm`, `m`, `°`, `px`), written inside the field after the number, in\n * mono and muted. It is not part of the value. It is announced as the field's description\n * (`aria-describedby`): after the caller's own `aria-describedby`, before a surrounding\n * `Field`'s description and error.\n */\n unit?: string | undefined;\n};\n\n/**\n * A number, with the schema's own bounds attached and, optionally, its unit written in the\n * field.\n *\n * `font-mono` because these are read as quantities and compared down a column. Takes every\n * `<input>` prop, `ref` included (`type` is always `number`); `className`, `style` and `ref`\n * always go to the `<input>`.\n *\n * With a `unit`, the input sits in a `relative` full-width wrapper carrying `data-unit`, keeps\n * room on its right for the unit, and is described by it; size such a field through its\n * container. Without one — or with `\"\"` — the markup is exactly the plain input.\n *\n * With `onValueChange` the field is number-valued: `value` is a number (`null` shows empty), the\n * typed text survives while the field has focus, and only finite numbers inside `[min, max]`\n * are reported (see `onValueChange`; `onClear` reports an emptied field). Without it the field\n * is the native text-valued input.\n */\nexport function NumberInput({ onValueChange, onClear, precision, ...props }: NumberInputProps) {\n if (onValueChange) {\n return <ValueNumberInput {...props} onValueChange={onValueChange} onClear={onClear} precision={precision} />;\n }\n const { unit, ...rest } = props;\n // A `null` text value is an empty field, not a switch to uncontrolled.\n const text = (rest.value === null ? { ...rest, value: \"\" } : rest) as TextNumberInputProps;\n return unit === undefined || unit === \"\" ? <PlainNumberInput {...text} /> : <UnitNumberInput unit={unit} {...text} />;\n}\n\ntype TextNumberInputProps = Omit<NumberInputProps, \"unit\" | \"value\" | \"onValueChange\" | \"onClear\" | \"precision\"> & {\n value?: string | number | readonly string[] | undefined;\n};\n\n/** The number-valued field: formatted at rest, the typed text while focused. */\nfunction ValueNumberInput({\n value,\n onValueChange,\n onClear,\n precision,\n step,\n min,\n max,\n onFocus,\n onBlur,\n onChange,\n onKeyDown,\n ...rest\n}: Omit<NumberInputProps, \"onValueChange\"> & { onValueChange: (value: number) => void }) {\n const numeric = typeof value === \"number\" ? value : null;\n const show = (v: number | null) => (v === null ? \"\" : precision === undefined ? String(v) : formatNumber(v, precision));\n // Non-null while the field is being edited: the text as typed, and the value on focus.\n const [draft, setDraft] = useState<{ text: string; initial: number | null } | null>(null);\n\n /** The number `text` commits, or `null` while it is empty, partial or out of range. */\n const parse = (text: string) => {\n const parsed = parseNumber(text);\n if (parsed === null) return null;\n if ((min !== undefined && parsed < min) || (max !== undefined && parsed > max)) return null;\n return parsed;\n };\n\n /** An emptied field, committed: cleared if the caller allows it and it was not already empty. */\n const commitEmpty = (text: string) => {\n if (text.trim() === \"\" && numeric !== null) onClear?.();\n };\n\n const keyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n if (draft && event.key === \"Enter\") {\n commitEmpty(draft.text);\n setDraft({ ...draft, text: draft.text.trim() === \"\" && onClear ? \"\" : show(numeric) });\n } else if (draft && event.key === \"Escape\") {\n event.preventDefault();\n if (draft.initial !== numeric) {\n if (draft.initial === null) onClear?.();\n else onValueChange(draft.initial);\n }\n setDraft({ ...draft, text: show(draft.initial) });\n }\n onKeyDown?.(event);\n };\n\n return (\n <NumberInput\n {...rest}\n value={draft?.text ?? show(numeric)}\n step={step ?? \"any\"}\n min={min}\n max={max}\n onFocus={(event) => {\n setDraft({ text: show(numeric), initial: numeric });\n onFocus?.(event);\n }}\n onBlur={(event) => {\n if (draft) commitEmpty(draft.text);\n setDraft(null);\n onBlur?.(event);\n }}\n onKeyDown={keyDown}\n onChange={(event) => {\n const text = event.currentTarget.value;\n setDraft((current) => ({ text, initial: current?.initial ?? numeric }));\n const parsed = parse(text);\n if (parsed !== null && parsed !== numeric) onValueChange(parsed);\n onChange?.(event);\n }}\n />\n );\n}\n\nfunction PlainNumberInput({ className, \"aria-describedby\": describedBy, ...rest }: TextNumberInputProps) {\n return (\n <input\n {...useFieldDescription(describedBy)}\n {...rest}\n type=\"number\"\n inputMode=\"decimal\"\n className={cn(controlClasses, useControlHeight(), \"font-mono tabular-nums\", className)}\n />\n );\n}\n\n/*\n * A component of its own so that only a field with a unit calls `useId`: a plain\n * `NumberInput` stays byte-identical to the one before `unit` existed, down to the ids React\n * hands the elements rendered after it.\n */\nfunction UnitNumberInput({ unit, style, \"aria-describedby\": describedBy, ...rest }: TextNumberInputProps & { unit: string }) {\n const unitId = useId();\n return (\n <span data-unit={unit} className=\"relative flex w-full min-w-0 items-center\">\n <PlainNumberInput\n {...rest}\n aria-describedby={describedBy ? `${describedBy} ${unitId}` : unitId}\n // Room for the unit: its width in the unit's own characters, plus the field's padding.\n style={{ paddingInlineEnd: `calc(${unit.length}ch + 1rem)`, ...style }}\n />\n {/* `aria-hidden` keeps the unit out of the field's *name* when a `<label>` wraps it;\n `aria-describedby` still reads it, as the description. */}\n <span\n id={unitId}\n aria-hidden\n className=\"pointer-events-none absolute inset-y-0 right-2.5 flex items-center font-mono text-xs text-fg-subtle select-none\"\n >\n {unit}\n </span>\n </span>\n );\n}\n","/*\n * Keyboard movement in a single-select listbox, as a pure function so it is tested without a\n * DOM. `Listbox` calls it on every key press.\n */\n\n/** The keys that move the active option. */\nexport type ListboxKey = \"ArrowDown\" | \"ArrowUp\" | \"Home\" | \"End\" | \"PageDown\" | \"PageUp\";\n\n/**\n * The option a key moves the active one to, skipping disabled options.\n *\n * Arrows move by one and stop at the ends (no wrap: a long list that wraps loses its place).\n * Home and End go to the first and last enabled option. Page keys move by `page` options.\n * From no active option (`-1`), Down and Page Down go to the first enabled option, and Up and\n * Page Up to the last.\n *\n * @param disabled - Per option, whether it is disabled.\n * @param from - The active index, or -1.\n * @param key - The key pressed.\n * @param page - Options per page, for Page Up/Down. Defaults to 10.\n * @returns The new active index; `from` when there is nowhere to go; -1 when every option is\n * disabled.\n */\nexport function moveActive(disabled: readonly boolean[], from: number, key: ListboxKey, page = 10): number {\n const enabled = disabled.flatMap((off, index) => (off ? [] : [index]));\n if (enabled.length === 0) return -1;\n const first = enabled[0]!;\n const last = enabled[enabled.length - 1]!;\n if (key === \"Home\") return first;\n if (key === \"End\") return last;\n if (from < 0) return key === \"ArrowDown\" || key === \"PageDown\" ? first : last;\n\n const forward = key === \"ArrowDown\" || key === \"PageDown\";\n const steps = key === \"PageDown\" || key === \"PageUp\" ? Math.max(1, page) : 1;\n let target = from;\n let moved = 0;\n for (let index = from + (forward ? 1 : -1); index >= 0 && index < disabled.length; index += forward ? 1 : -1) {\n if (disabled[index]) continue;\n target = index;\n moved += 1;\n if (moved === steps) break;\n }\n return target;\n}\n\n/**\n * The first enabled option at or after `index`, or before it when none is after. Used to place\n * the active option when the list opens or changes.\n *\n * @param disabled - Per option, whether it is disabled.\n * @param index - The preferred index.\n * @returns An enabled index, or -1 when every option is disabled.\n */\nexport function nearestEnabled(disabled: readonly boolean[], index: number): number {\n for (let i = Math.max(0, index); i < disabled.length; i += 1) if (!disabled[i]) return i;\n for (let i = Math.min(index, disabled.length) - 1; i >= 0; i -= 1) if (!disabled[i]) return i;\n return -1;\n}\n","/*\n * Pick one of a list of options that need more than a label: a frame with its thumbnail and\n * size, a model with its point counts.\n *\n * `Select` is the compact choice of a value by name. A `Listbox` shows its options in place —\n * on its own in a panel, or inside a `Popover` behind a trigger — and each option can render\n * whatever identifies it.\n */\n\nimport { useId, useRef, useState, type KeyboardEvent, type ReactNode } from \"react\";\n\nimport { cn, focusRing } from \"./cn\";\nimport { moveActive, nearestEnabled, type ListboxKey } from \"./listboxModel\";\n\n/** One option of a `Listbox`. */\nexport interface ListboxOption {\n /** Its value, unique in the list. */\n value: string;\n /** Its name: the default rendering, and what a screen reader announces. */\n label: string;\n /** A second line under the label, in the default rendering. */\n description?: string | undefined;\n /** Shown but not choosable. */\n disabled?: boolean | undefined;\n}\n\n/** What `renderOption` is told about an option. */\nexport interface ListboxOptionState {\n /** It is the value. */\n selected: boolean;\n /** It has the keyboard cursor or the pointer. */\n active: boolean;\n}\n\n/** Props of `Listbox`. */\nexport interface ListboxProps {\n /** The options, in order. */\n options: readonly ListboxOption[];\n /** The selected value, or `null` for none. */\n value: string | null;\n /** Called with the value of the option chosen by click, Enter or Space. */\n onValueChange?: ((value: string) => void) | undefined;\n /**\n * Renders an option's content (the row keeps its own padding, states and accessibility).\n * Defaults to the label, with the description under it.\n */\n renderOption?: ((option: ListboxOption, state: ListboxOptionState) => ReactNode) | undefined;\n /** Names the list. */\n \"aria-label\": string;\n /** Focus the list when it mounts, e.g. when it opens in a `Popover`. */\n autoFocus?: boolean | undefined;\n /** Merged with the list's own classes through `cn`. */\n className?: string | undefined;\n}\n\nconst KEYS = new Set<string>([\"ArrowDown\", \"ArrowUp\", \"Home\", \"End\", \"PageDown\", \"PageUp\"]);\n\n/**\n * A single-select list (`role=\"listbox\"`) of options that each render their own content.\n *\n * The list is one tab stop. The arrow keys, Home, End and Page keys move the active option\n * (`aria-activedescendant`), skipping disabled ones, and Enter or Space choose it. A click\n * chooses the option under the pointer. Controlled: the caller keeps `value`. Each option\n * carries `aria-selected`, `data-selected` and `data-active`; the list scrolls the active\n * option into view.\n */\nexport function Listbox({\n options,\n value,\n onValueChange,\n renderOption = defaultRender,\n \"aria-label\": ariaLabel,\n autoFocus,\n className,\n}: ListboxProps) {\n const id = useId();\n const listRef = useRef<HTMLDivElement>(null);\n const disabled = options.map((option) => option.disabled === true);\n const selectedIndex = options.findIndex((option) => option.value === value);\n const [active, setActive] = useState(() => nearestEnabled(disabled, Math.max(0, selectedIndex)));\n // Options can change under the list; keep the cursor on an enabled option that exists.\n const current = active >= 0 && active < options.length && !disabled[active] ? active : nearestEnabled(disabled, active);\n\n const optionId = (index: number) => `${id}-option-${index}`;\n\n const activate = (index: number) => {\n setActive(index);\n listRef.current?.querySelector(`#${CSS.escape(optionId(index))}`)?.scrollIntoView({ block: \"nearest\" });\n };\n\n const choose = (index: number) => {\n const option = options[index];\n if (!option || option.disabled) return;\n onValueChange?.(option.value);\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (KEYS.has(event.key)) {\n event.preventDefault();\n const next = moveActive(disabled, current, event.key as ListboxKey);\n if (next >= 0) activate(next);\n } else if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n choose(current);\n }\n };\n\n return (\n <div\n ref={listRef}\n role=\"listbox\"\n aria-label={ariaLabel}\n tabIndex={0}\n autoFocus={autoFocus}\n aria-activedescendant={current >= 0 ? optionId(current) : undefined}\n onKeyDown={onKeyDown}\n className={cn(\"flex flex-col gap-px rounded-control p-0.5 outline-none\", focusRing, className)}\n >\n {options.map((option, index) => {\n const selected = index === selectedIndex;\n const isActive = index === current;\n return (\n <div\n key={option.value}\n id={optionId(index)}\n role=\"option\"\n aria-selected={selected}\n aria-disabled={option.disabled ? true : undefined}\n data-selected={selected ? \"\" : undefined}\n data-active={isActive ? \"\" : undefined}\n onPointerMove={() => {\n if (!option.disabled && !isActive) setActive(index);\n }}\n onClick={() => choose(index)}\n className={cn(\n \"flex min-w-0 cursor-default items-center gap-2 rounded-control px-2 py-1 text-sm text-fg\",\n // Selected is a bar on the leading edge plus the accent text; active (keyboard\n // cursor or pointer) is the raised fill. The two can coincide and each stays\n // visible, and neither puts muted text on a tint it fails contrast on.\n \"data-[selected]:text-signal data-[selected]:shadow-[inset_2px_0_0_var(--color-signal)]\",\n \"data-[active]:bg-raised\",\n option.disabled && \"opacity-50\",\n )}\n >\n {renderOption(option, { selected, active: isActive })}\n </div>\n );\n })}\n </div>\n );\n}\n\nfunction defaultRender(option: ListboxOption) {\n return (\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"truncate\">{option.label}</span>\n {option.description && <span className=\"truncate text-xs text-fg-muted\">{option.description}</span>}\n </span>\n );\n}\n","/*\n * The page furniture: what holds a screen together above the level of a single control.\n *\n * One heading size and one gap rule, defined once so every route in every consuming app\n * agrees with the others by construction rather than by luck.\n */\n\nimport { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport { isValidElement, type ReactElement, type ReactNode } from \"react\";\n\nimport { byDensity, useDensity } from \"./Density\";\nimport { cn } from \"./cn\";\n\n/**\n * A bordered surface with an optional header (a title and actions) above a padded body.\n * The header and the padding follow the density in force.\n */\nexport function Panel({\n title,\n actions,\n className,\n bodyClassName,\n children,\n}: {\n /** The header's heading (an `<h2>`). */\n title?: ReactNode;\n /** Controls at the header's right end. */\n actions?: ReactNode;\n /** Merged with the panel's own classes through `cn`. */\n className?: string | undefined;\n /** Merged with the body's own classes (its padding) through `cn`. */\n bodyClassName?: string | undefined;\n /** The body. */\n children: ReactNode;\n}) {\n const density = useDensity();\n\n return (\n <section className={cn(\"rounded-panel border border-line bg-surface\", className)}>\n {(title || actions) && (\n <header\n className={cn(\n \"flex items-center justify-between gap-4 border-b border-line\",\n byDensity(density, \"min-h-11 px-4 py-2.5\", \"min-h-8 px-2.5 py-1\"),\n )}\n >\n <h2\n className={cn(\n \"truncate font-semibold text-fg\",\n byDensity(density, \"text-sm tracking-tight\", \"text-[11px] tracking-wider uppercase text-fg-muted\"),\n )}\n >\n {title}\n </h2>\n {actions && <div className=\"flex shrink-0 items-center gap-1.5\">{actions}</div>}\n </header>\n )}\n <div className={cn(byDensity(density, \"p-4\", \"p-2.5\"), bodyClassName)}>{children}</div>\n </section>\n );\n}\n\n/**\n * A numbered step inside a form.\n *\n * The number is not decoration: it marks a genuine ordering, where each choice narrows the\n * next. Do not reach for this where the parts are merely adjacent.\n */\nexport function Section({\n step,\n title,\n hint,\n actions,\n className,\n children,\n}: {\n /** The step's number, shown zero-padded before the title (decorative, `aria-hidden`). */\n step?: number | undefined;\n /** The step's heading (an `<h3>`). */\n title: string;\n /** A short line after the title. */\n hint?: ReactNode;\n /** Controls at the heading row's right end. */\n actions?: ReactNode;\n /** Merged with the section's own classes through `cn`. */\n className?: string | undefined;\n /** The step's content. */\n children: ReactNode;\n}) {\n const density = useDensity();\n\n return (\n <section className={cn(\"flex flex-col\", byDensity(density, \"gap-3\", \"gap-1.5\"), className)}>\n <div className={cn(\"flex items-baseline\", byDensity(density, \"gap-2.5\", \"gap-2\"))}>\n {step !== undefined && (\n <span className=\"font-mono text-xs text-fg-subtle tabular-nums\" aria-hidden>\n {String(step).padStart(2, \"0\")}\n </span>\n )}\n <h3 className=\"text-sm font-semibold tracking-tight text-fg\">{title}</h3>\n {hint && <p className=\"text-xs text-fg-muted\">{hint}</p>}\n {actions && <div className=\"ml-auto flex items-center gap-2\">{actions}</div>}\n </div>\n {children}\n </section>\n );\n}\n\n/**\n * Where `PageHeader`'s back link goes: `{ href, label }` for a plain URL, or the app's own\n * link element (a router's `<Link>`).\n */\nexport type BackLink =\n | { href: string; label: string }\n /** A link element — typically a router's `<Link to=\"…\">Label</Link>` — styled and prefixed with an arrow. */\n | ReactElement;\n\nconst BACK_CLASSES = \"w-fit text-xs text-fg-muted transition-colors hover:text-signal\";\n\n/**\n * The top of a screen: an optional back link, the `<h1>` title with actions beside it, and\n * a line of meta facts below. Links go through `back`, never a router import.\n */\nexport function PageHeader({\n title,\n meta,\n actions,\n back,\n className,\n}: {\n /** The screen's heading (the `<h1>`). */\n title: ReactNode;\n /** Small facts under the title — an id, a date, a status badge. */\n meta?: ReactNode;\n /** The screen's actions, beside the title. */\n actions?: ReactNode;\n /**\n * The way up. `{ href, label }` renders a plain `<a>`; for client-side navigation pass the\n * router's own link — `back={<Link to=\"/experiments\">Experiments</Link>}` — which is\n * rendered with the back-link style and arrow, so this package never imports a router.\n */\n back?: BackLink | undefined;\n /** Merged with the header's own classes through `cn`. */\n className?: string | undefined;\n}) {\n return (\n <header className={cn(\"flex flex-col gap-1.5\", className)}>\n {back &&\n (isHrefLink(back) ? (\n <a href={back.href} className={BACK_CLASSES}>\n ← {back.label}\n </a>\n ) : (\n <Slot className={BACK_CLASSES}>\n {\"← \"}\n <Slottable>{back}</Slottable>\n </Slot>\n ))}\n <div className=\"flex flex-wrap items-center justify-between gap-x-4 gap-y-2\">\n <h1 className=\"text-xl font-semibold tracking-tight text-fg\">{title}</h1>\n {actions && <div className=\"flex shrink-0 items-center gap-2\">{actions}</div>}\n </div>\n {meta && <div className=\"flex flex-wrap items-center gap-2 text-xs text-fg-muted\">{meta}</div>}\n </header>\n );\n}\n\nfunction isHrefLink(back: BackLink): back is { href: string; label: string } {\n return !isValidElement(back);\n}\n\n/** One fact on a `ReadoutStrip`: a value, optionally labelled and optionally a link. */\nexport type ReadoutItem = {\n /** A quiet prefix naming what the value is, where the value alone is ambiguous. */\n label?: string | undefined;\n /** The fact. An item whose value is `null` or `undefined` is skipped. */\n value: ReactNode;\n /** Makes the value a plain `<a href>`. */\n href?: string | undefined;\n /**\n * Makes the value a link through the app's own element — a router's `<Link to=\"…\" />`,\n * given without children: the value becomes its content. Takes precedence over `href`.\n */\n link?: ReactElement | undefined;\n};\n\nconst READOUT_LINK = \"text-fg transition-colors hover:text-signal\";\n\n/**\n * The instrument's display line: the facts about what is on screen, in one fixed slot.\n *\n * Stated in the same place on every screen, in mono, the way a piece of measuring equipment\n * has one readout rather than a label per panel. Rendered as a list because it is one.\n */\nexport function ReadoutStrip({\n items,\n className,\n}: {\n /** The facts, in order. Items with no value are skipped. */\n items: ReadoutItem[];\n /** Merged with the list's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const shown = items.filter((item) => item.value !== null && item.value !== undefined);\n if (shown.length === 0) return null;\n\n return (\n <ol className={cn(\"flex flex-wrap items-center gap-x-2 gap-y-1 font-mono text-xs text-fg-muted\", className)}>\n {shown.map((item, index) => (\n // The items are positional facts with no identity of their own (a label is optional\n // and values repeat), so their place in the strip is the key.\n // oxlint-disable-next-line react/no-array-index-key -- positional list, see above\n <li key={index} className=\"flex items-center gap-2\">\n {index > 0 && (\n <span className=\"text-fg-subtle\" aria-hidden>\n ·\n </span>\n )}\n {item.label && <span className=\"text-fg-subtle\">{item.label}</span>}\n {item.link ? (\n <Slot className={READOUT_LINK}>\n <Slottable>{item.link}</Slottable>\n {item.value}\n </Slot>\n ) : item.href ? (\n <a href={item.href} className={READOUT_LINK}>\n {item.value}\n </a>\n ) : (\n <span className=\"text-fg\">{item.value}</span>\n )}\n </li>\n ))}\n </ol>\n );\n}\n","/*\n * How far a long operation has got.\n *\n * The bar is in `signal`, not a verdict colour: progress is not a pass or a fail, and a bar\n * that turned green at 100 % would claim a verdict nobody made. The value text is \"412 of\n * 2,556\" rather than a bare percentage, because the count is what a reader checks against\n * the work (\"2,556 strings\"), and a screen reader says it as written.\n */\n\nimport type { ComponentProps } from \"react\";\n\nimport { byDensity, useDensity } from \"./Density\";\nimport { cn } from \"./cn\";\n\n/** Counts written the same on the server and in every browser: `2,556`, `0.5`. */\nconst count = new Intl.NumberFormat(\"en-US\", { maximumFractionDigits: 2 });\n\n/**\n * The accessible value text of a progress: `\"412 of 2,556\"`, or `null` while indeterminate.\n *\n * @param value - Done so far; `null` or `undefined` for an unknown amount.\n * @param max - The whole. Defaults to 100.\n * @returns The text, with the value clamped to `[0, max]`.\n */\nexport function progressText(value: number | null | undefined, max = 100): string | null {\n if (value === null || value === undefined || !Number.isFinite(value)) return null;\n const whole = max > 0 ? max : 100;\n return `${count.format(Math.min(Math.max(value, 0), whole))} of ${count.format(whole)}`;\n}\n\n/** Props of `Progress`: a value out of a `max`, or none for an unknown amount, plus the `<div>` props. */\nexport type ProgressProps = Omit<ComponentProps<\"div\">, \"children\" | \"role\"> & {\n /** Done so far, from 0 to `max`; clamped. `null` or left out: indeterminate, the amount unknown. */\n value?: number | null | undefined;\n /** The whole. Defaults to 100; a value ≤ 0 counts as 100. */\n max?: number | undefined;\n /**\n * What a screen reader says for the value. Defaults to `\"412 of 2,556\"`; give your own when\n * the unit matters (\"412 of 2,556 strings\").\n */\n valueText?: string | undefined;\n};\n\n/**\n * A progress bar: determinate when it has a `value` (a `max`, 100 by default), indeterminate\n * without one. Name it with `aria-label` or `aria-labelledby`.\n *\n * A `progressbar` with `aria-valuenow`, `aria-valuemax` and `aria-valuetext` (\"412 of 2,556\",\n * or your `valueText`); indeterminate, it has none of them, as the ARIA pattern asks.\n * The track follows the density in force. State is exposed as `data-state`\n * (`\"indeterminate\"`, `\"loading\"` or `\"complete\"`). With reduced motion the indeterminate bar\n * holds still.\n */\nexport function Progress({ value, max = 100, valueText, className, ...rest }: ProgressProps) {\n const density = useDensity();\n const whole = max > 0 ? max : 100;\n const text = progressText(value, whole);\n const done = text === null ? null : Math.min(Math.max(value as number, 0), whole);\n const state = done === null ? \"indeterminate\" : done >= whole ? \"complete\" : \"loading\";\n return (\n <div\n role=\"progressbar\"\n aria-valuemin={done === null ? undefined : 0}\n aria-valuemax={done === null ? undefined : whole}\n aria-valuenow={done ?? undefined}\n aria-valuetext={done === null ? undefined : (valueText ?? text ?? undefined)}\n data-state={state}\n {...rest}\n className={cn(\"relative w-full overflow-hidden rounded-full bg-line\", byDensity(density, \"h-1.5\", \"h-1\"), className)}\n >\n {done === null ? (\n // A third of the track sweeping across it. Its resting place (reduced motion, a\n // screenshot) is the left third, still visibly \"in progress\".\n <div className=\"absolute inset-y-0 left-0 w-1/3 animate-progress-sweep rounded-full bg-signal\" />\n ) : (\n <div className=\"h-full rounded-full bg-signal transition-[width] duration-300\" style={{ width: `${(done / whole) * 100}%` }} />\n )}\n </div>\n );\n}\n","/*\n * Floating content anchored to a trigger: a picker, a small form, a list to choose from.\n *\n * A popover is for content a person works in. A *menu* of commands is `DropdownMenu`, and a\n * short explanation is `Tooltip`. It closes on Escape, on a press outside, or when the caller\n * sets `open` to false.\n */\n\nimport * as RadixPopover from \"@radix-ui/react-popover\";\nimport type { ReactNode } from \"react\";\n\nimport { cn } from \"./cn\";\n\n/** Props of `Popover`. */\nexport interface PopoverProps {\n /**\n * The element that opens it: one focusable element that accepts a ref (Radix `asChild`).\n * It carries `aria-expanded` and `data-state` (`open` / `closed`).\n */\n trigger: ReactNode;\n /** The content. */\n children: ReactNode;\n /** Controlled open state. */\n open?: boolean | undefined;\n /** Initial open state when uncontrolled. */\n defaultOpen?: boolean | undefined;\n /** Called when it opens or closes. */\n onOpenChange?: ((open: boolean) => void) | undefined;\n /** The trigger's side it opens on. Defaults to `bottom`. */\n side?: \"top\" | \"right\" | \"bottom\" | \"left\" | undefined;\n /** Its alignment along that side. Defaults to `start`. */\n align?: \"start\" | \"center\" | \"end\" | undefined;\n /** Names the content (`aria-label`), when nothing inside it does. */\n \"aria-label\"?: string | undefined;\n /** Merged with the content's own classes through `cn`. */\n className?: string | undefined;\n}\n\n/**\n * Floating content anchored to `trigger`, on the `overlay` surface with a shadow (floating\n * layers are the only ones that cast one).\n *\n * Controlled (`open` / `onOpenChange`) or not (`defaultOpen`). Focus moves into the content\n * when it opens and back to the trigger when it closes. Escape and a press outside close it.\n * The content carries `data-state` and `data-side`.\n */\nexport function Popover({\n trigger,\n children,\n open,\n defaultOpen,\n onOpenChange,\n side = \"bottom\",\n align = \"start\",\n \"aria-label\": ariaLabel,\n className,\n}: PopoverProps) {\n return (\n <RadixPopover.Root\n {...(open === undefined ? {} : { open })}\n {...(defaultOpen === undefined ? {} : { defaultOpen })}\n {...(onOpenChange === undefined ? {} : { onOpenChange })}\n >\n <RadixPopover.Trigger asChild>{trigger}</RadixPopover.Trigger>\n <RadixPopover.Portal>\n <RadixPopover.Content\n side={side}\n align={align}\n sideOffset={6}\n collisionPadding={8}\n aria-label={ariaLabel}\n className={cn(\n \"z-50 max-h-[var(--radix-popover-content-available-height)] overflow-y-auto\",\n \"rounded-panel border border-line bg-overlay p-1 text-sm text-fg shadow-lg shadow-black/25\",\n className,\n )}\n >\n {children}\n </RadixPopover.Content>\n </RadixPopover.Portal>\n </RadixPopover.Root>\n );\n}\n\n/**\n * Closes the surrounding `Popover` when its child is activated. Wraps one element (Radix\n * `asChild`).\n */\nexport function PopoverClose({\n children,\n}: {\n /** The one element that closes the popover. */\n children: ReactNode;\n}) {\n return <RadixPopover.Close asChild>{children}</RadixPopover.Close>;\n}\n","/*\n * A small vector — a position, a set of angles, a scale — edited as one row.\n *\n * Three separate `Field`s for x, y and z spend three labels and three rows on what a reader\n * thinks of as one quantity. This is one row: a quiet axis letter before each number, the\n * unit once at the end, and one name for the whole group.\n *\n * Each field keeps the text being typed while it has focus and shows the formatted value\n * otherwise (see `numberText.ts`), so `precision` never fights the cursor. Every valid\n * keystroke is reported, so whatever the vector drives follows the typing.\n */\n\nimport { useId, useState } from \"react\";\nimport type { KeyboardEvent } from \"react\";\n\nimport { NumberInput } from \"./Input\";\nimport { ReadoutStrip } from \"./Panel\";\nimport { cn } from \"./cn\";\nimport { formatNumber, parseNumber } from \"./numberText\";\n\nconst DEFAULT_LABELS = [\"x\", \"y\", \"z\", \"w\"];\n\n/** Props of `VectorInput`. */\nexport interface VectorInputProps {\n /** The components. Its length is the number of fields. */\n value: readonly number[];\n /** Called with the whole vector (a new array) whenever one component changes to a valid number. */\n onValueChange?: ((value: number[]) => void) | undefined;\n /** Each component's label, shown before its field and naming it. Defaults to x, y, z, w. */\n labels?: readonly string[] | undefined;\n /** The unit of every component, shown once after the row (`m`, `mm`, `°`). */\n unit?: string | undefined;\n /** The arrow-key step. Defaults to `any` (the browser then steps by 1). */\n step?: number | undefined;\n /** Decimals shown when a field is not being edited. Defaults to 3. */\n precision?: number | undefined;\n /** The smallest accepted component. */\n min?: number | undefined;\n /** The largest accepted component. */\n max?: number | undefined;\n /** Show the values as a compact mono readout instead of fields. */\n readOnly?: boolean | undefined;\n /** Blocks every field. */\n disabled?: boolean | undefined;\n /** Names the group (`role=\"group\"`). */\n \"aria-label\": string;\n /** Merged with the row's own classes through `cn`. */\n className?: string | undefined;\n}\n\n/**\n * A row of number fields editing one small vector, with the axis labels before the fields\n * and the unit once after them.\n *\n * Controlled. A `role=\"group\"` named by `aria-label`. Each field is named by the group and its\n * label (\"Translation x\"), so two vectors on one screen stay apart, and is described by the unit. While a field has focus it shows what is typed; on blur or Enter it\n * shows the value at `precision`; Escape restores the value it had on focus. With `readOnly`\n * it renders a `ReadoutStrip` (`x 0.100 m · y …`) instead. The row carries `data-readonly`\n * and `data-disabled`.\n */\nexport function VectorInput({\n value,\n onValueChange,\n labels = DEFAULT_LABELS,\n unit,\n step,\n precision = 3,\n min,\n max,\n readOnly = false,\n disabled = false,\n \"aria-label\": ariaLabel,\n className,\n}: VectorInputProps) {\n const unitId = useId();\n const labelOf = (index: number) => labels[index] ?? String(index);\n\n if (readOnly) {\n return (\n <div role=\"group\" aria-label={ariaLabel} data-readonly=\"\" className={cn(\"min-w-0\", className)}>\n <ReadoutStrip\n items={value.map((component, index) => ({\n label: labelOf(index),\n value: unit ? `${formatNumber(component, precision)} ${unit}` : formatNumber(component, precision),\n }))}\n />\n </div>\n );\n }\n\n const change = (index: number, component: number) => {\n if (component === value[index]) return;\n onValueChange?.(value.map((current, i) => (i === index ? component : current)));\n };\n\n return (\n <div\n role=\"group\"\n aria-label={ariaLabel}\n data-disabled={disabled ? \"\" : undefined}\n className={cn(\"flex min-w-0 items-center gap-1.5\", className)}\n >\n {value.map((component, index) => (\n // The components are positional: index is their identity.\n // oxlint-disable-next-line react/no-array-index-key -- see above\n <label key={index} className=\"flex min-w-0 flex-1 items-center gap-1\">\n <span className=\"shrink-0 font-mono text-[11px] text-fg-subtle\">{labelOf(index)}</span>\n <ComponentField\n name={`${ariaLabel} ${labelOf(index)}`}\n value={component}\n precision={precision}\n step={step}\n min={min}\n max={max}\n disabled={disabled}\n describedBy={unit ? unitId : undefined}\n onCommit={(next) => change(index, next)}\n />\n </label>\n ))}\n {unit && (\n <span id={unitId} className=\"shrink-0 font-mono text-xs text-fg-subtle\">\n {unit}\n </span>\n )}\n </div>\n );\n}\n\n/** One component: formatted at rest, the typed text while focused. */\nfunction ComponentField({\n name,\n value,\n precision,\n step,\n min,\n max,\n disabled,\n describedBy,\n onCommit,\n}: {\n name: string;\n value: number;\n precision: number;\n step: number | undefined;\n min: number | undefined;\n max: number | undefined;\n disabled: boolean;\n describedBy: string | undefined;\n onCommit: (value: number) => void;\n}) {\n // Non-null while the field is being edited: the text as typed, and the value on focus.\n const [draft, setDraft] = useState<{ text: string; initial: number } | null>(null);\n const formatted = formatNumber(value, precision);\n\n const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n if (event.key === \"Enter\") {\n setDraft((current) => current && { ...current, text: formatted });\n } else if (event.key === \"Escape\" && draft) {\n event.preventDefault();\n onCommit(draft.initial);\n setDraft({ ...draft, text: formatNumber(draft.initial, precision) });\n }\n };\n\n return (\n <NumberInput\n value={draft?.text ?? formatted}\n step={step ?? \"any\"}\n min={min}\n max={max}\n disabled={disabled}\n aria-label={name}\n aria-describedby={describedBy}\n onFocus={() => setDraft({ text: formatted, initial: value })}\n onBlur={() => setDraft(null)}\n onKeyDown={onKeyDown}\n onChange={(event) => {\n const text = event.currentTarget.value;\n setDraft((current) => ({ text, initial: current?.initial ?? value }));\n const parsed = parseNumber(text);\n if (parsed !== null) onCommit(parsed);\n }}\n className=\"min-w-0 px-1.5\"\n />\n );\n}\n","/*\n * A rigid transform, edited the way people think about one.\n *\n * Poses travel as `{ rotation: [qx, qy, qz, qw], translation: [tx, ty, tz] }` — the SE(3) wire\n * form — because a quaternion is the honest representation. Nobody edits one by hand,\n * though: a rotation is read and typed as roll, pitch and yaw in degrees. This component\n * shows that view and writes back the wire form.\n *\n * It owns no rotation mathematics. Which Euler convention \"roll, pitch, yaw\" means — the\n * axis order, intrinsic or extrinsic — is a decision of the 3D package that defines the\n * frames, so the conversion is passed in as a `RotationView`. This package stays pure UI.\n */\n\nimport { useState } from \"react\";\n\nimport { byDensity, useDensity } from \"./Density\";\nimport { ReadoutStrip } from \"./Panel\";\nimport { cn } from \"./cn\";\nimport { formatNumber } from \"./numberText\";\nimport { VectorInput } from \"./VectorInput\";\n\n/** Three numbers: a translation `[tx, ty, tz]`, or Euler angles. */\nexport type Vec3 = readonly [number, number, number];\n\n/** A unit quaternion in the wire order `[qx, qy, qz, qw]` (scalar last). */\nexport type Quaternion = readonly [number, number, number, number];\n\n/**\n * An SE(3) pose in the wire form (`a_se3_b` as nalgebra's `Isometry3` serialises it):\n * translation in metres, rotation as a unit quaternion `[qx, qy, qz, qw]`.\n */\nexport interface PoseValue {\n /** The rotation, `[qx, qy, qz, qw]`. */\n rotation: Quaternion;\n /** The translation `[tx, ty, tz]`, in metres. */\n translation: Vec3;\n}\n\n/**\n * How a rotation is shown as three angles: the conversion both ways, and the angles' names.\n * Supplied by the package that owns the frame conventions; `PoseInput` only calls it.\n */\nexport interface RotationView {\n /** The quaternion as three angles, in radians, in the order of `labels`. */\n toEuler: (rotation: Quaternion) => Vec3;\n /** Three angles in radians, in the order of `labels`, as a unit quaternion `[qx, qy, qz, qw]`. */\n fromEuler: (angles: Vec3) => Quaternion;\n /** The angles' names, shown before each field. Defaults to roll, pitch, yaw. */\n labels?: readonly [string, string, string] | undefined;\n}\n\n/** Props of `PoseInput`. */\nexport interface PoseInputProps {\n /** The pose, in the wire form. */\n value: PoseValue;\n /** Called with a new pose (new arrays) whenever a component changes. */\n onValueChange?: ((value: PoseValue) => void) | undefined;\n /** The quaternion ↔ angles conversion and the angles' names. Required: see `RotationView`. */\n rotationView: RotationView;\n /** Show and edit the translation in metres (the default) or millimetres. The value is always metres. */\n translationUnit?: \"m\" | \"mm\" | undefined;\n /** Decimals of the translation. Defaults to 4 for m, 1 for mm. */\n translationPrecision?: number | undefined;\n /** Decimals of the angles, in degrees. Defaults to 2. */\n rotationPrecision?: number | undefined;\n /** Show a compact mono readout instead of fields. */\n readOnly?: boolean | undefined;\n /** Blocks every field. */\n disabled?: boolean | undefined;\n /** Names the pose (`role=\"group\"`). Defaults to \"Pose\". */\n \"aria-label\"?: string | undefined;\n /** Merged with the component's own classes through `cn`. */\n className?: string | undefined;\n}\n\nconst DEGREES = 180 / Math.PI;\nconst DEFAULT_ANGLE_LABELS = [\"roll\", \"pitch\", \"yaw\"] as const;\n\nconst sameQuaternion = (a: Quaternion, b: Quaternion) => a.every((component, index) => component === b[index]);\n\n/**\n * An SE(3) pose shown and edited as a translation (m or mm) and three angles in degrees,\n * reading and writing the wire form `{ rotation: [qx, qy, qz, qw], translation: [tx, ty, tz] }`.\n *\n * The angles come from `rotationView.toEuler` and go back through `rotationView.fromEuler`.\n * The angles as typed are kept while the pose they produced is the one shown, so a yaw of\n * 190° is not redisplayed as −170° and a gimbal-locked pitch does not scramble the other two\n * angles mid-edit.\n *\n * A `role=\"group\"` named by `aria-label`, holding two `VectorInput` groups (\"Translation\",\n * \"Rotation\"). With `readOnly` it renders a `ReadoutStrip` instead\n * (`t 0.1000 0.2000 0.3000 m · rpy 0.00 90.00 0.00 °`). The root carries `data-readonly`.\n */\nexport function PoseInput({\n value,\n onValueChange,\n rotationView,\n translationUnit = \"m\",\n translationPrecision,\n rotationPrecision = 2,\n readOnly = false,\n disabled = false,\n \"aria-label\": ariaLabel = \"Pose\",\n className,\n}: PoseInputProps) {\n const density = useDensity();\n // The angles last typed, and the quaternion they became.\n const [typed, setTyped] = useState<{ rotation: Quaternion; angles: Vec3 } | null>(null);\n\n const scale = translationUnit === \"mm\" ? 1000 : 1;\n const tPrecision = translationPrecision ?? (translationUnit === \"mm\" ? 1 : 4);\n const translation = value.translation.map((component) => component * scale);\n const radians = typed !== null && sameQuaternion(typed.rotation, value.rotation) ? typed.angles : rotationView.toEuler(value.rotation);\n const degrees = radians.map((angle) => angle * DEGREES);\n const angleLabels = rotationView.labels ?? DEFAULT_ANGLE_LABELS;\n\n if (readOnly) {\n const join = (values: readonly number[], precision: number) => values.map((component) => formatNumber(component, precision)).join(\" \");\n return (\n <div role=\"group\" aria-label={ariaLabel} data-readonly=\"\" className={cn(\"min-w-0\", className)}>\n <ReadoutStrip\n items={[\n { label: \"t\", value: `${join(translation, tPrecision)} ${translationUnit}` },\n { label: \"rpy\", value: `${join(degrees, rotationPrecision)} °` },\n ]}\n />\n </div>\n );\n }\n\n const onTranslation = (next: number[]) => {\n const [x = 0, y = 0, z = 0] = next.map((component) => component / scale);\n onValueChange?.({ rotation: value.rotation, translation: [x, y, z] });\n };\n\n const onRotation = (next: number[]) => {\n const [a = 0, b = 0, c = 0] = next.map((angle) => angle / DEGREES);\n const angles: Vec3 = [a, b, c];\n const rotation = rotationView.fromEuler(angles);\n setTyped({ rotation, angles });\n onValueChange?.({ rotation, translation: value.translation });\n };\n\n const rowLabel = cn(\"text-fg-muted\", byDensity(density, \"text-xs\", \"text-[11px]\"));\n\n return (\n <div\n role=\"group\"\n aria-label={ariaLabel}\n className={cn(\n \"grid min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center gap-x-2\",\n byDensity(density, \"gap-y-2\", \"gap-y-1\"),\n className,\n )}\n >\n <span aria-hidden className={rowLabel}>\n Translation\n </span>\n <VectorInput\n aria-label=\"Translation\"\n value={translation}\n onValueChange={onTranslation}\n unit={translationUnit}\n precision={tPrecision}\n step={translationUnit === \"mm\" ? 1 : 0.001}\n disabled={disabled}\n />\n <span aria-hidden className={rowLabel}>\n Rotation\n </span>\n <VectorInput\n aria-label=\"Rotation\"\n value={degrees}\n onValueChange={onRotation}\n labels={angleLabels}\n unit=\"°\"\n precision={rotationPrecision}\n step={1}\n disabled={disabled}\n />\n </div>\n );\n}\n","/*\n * A choice among two or three, shown as two or three things.\n *\n * The control for a closed set the app can enumerate up front -- `Literal[\"small\", \"medium\"]`, an axis choice -- shown all at once, no popup, no typing, and no way to enter\n * a value the backend or algorithm would reject.\n *\n * Built on native radios rather than a headless package: a radio group in one `name` gets\n * roving focus and arrow-key navigation from the platform, which is the entire reason to\n * reach for a library here.\n *\n * **`\"\"` means unset**, and this control keeps that true without showing it. The segment\n * highlighted is always the *effective* value, so the reader sees what will happen;\n * choosing the segment that matches the default stores `\"\"` rather than the value, so the\n * default is still sent by nobody and defined only in one place. See `api/schemaForm.ts`\n * for why that rule is worth this much care.\n */\n\nimport { useId } from \"react\";\n\nimport type { ButtonSize } from \"./Button\";\nimport { useFieldDescription } from \"./Field\";\nimport { cn } from \"./cn\";\n\n/**\n * The segment's own box, by size. `sm` is the toolbar size and the default. `md` sets the\n * height explicitly rather than leaving it to padding plus line-height: the strip around it\n * is `h-8` with a 1px border and `p-0.5`, which leaves 26px of content box, and `h-full`\n * fills exactly that whatever the font's line height turns out to be.\n */\nconst SEGMENT_SIZE_CLASSES: Record<ButtonSize, string> = {\n sm: \"px-2.5 py-1 text-xs\",\n md: \"flex h-full items-center px-3.5 text-sm\",\n};\n\n/**\n * A choice among two or three, shown as a strip of segments (native radios in one group, so\n * arrow keys move between them).\n *\n * **`\"\"` means unset.** The highlighted segment is always the *effective* value — `value`,\n * or `defaultValue` while `value` is `\"\"` — and choosing the segment that matches\n * `defaultValue` reports `\"\"`, so the default stays defined in one place. Each segment\n * carries `data-state=\"checked\"` or `\"unchecked\"`; the group carries `data-disabled`.\n *\n * Toolbar-sized by default. In a row with a comfortable-density `Input`, `Select` or\n * `Button`, pass `size=\"md\"` and the strip's outer height matches theirs.\n */\nexport function SegmentedControl({\n value,\n defaultValue,\n options,\n onValueChange,\n disabled = false,\n \"aria-label\": ariaLabel,\n size = \"sm\",\n className,\n}: {\n /** `\"\"` when the field is untouched. */\n value: string;\n /** What applies when nothing is sent. Highlighted while `value` is `\"\"`. */\n defaultValue?: string | undefined;\n /** The segments, in order. */\n options: { value: string; label: string }[];\n /** Called with the chosen value, or `\"\"` when the default's segment is chosen. */\n onValueChange: (value: string) => void;\n /** Blocks every segment. */\n disabled?: boolean | undefined;\n /** Names the radio group. */\n \"aria-label\"?: string | undefined;\n /**\n * The strip's height: `sm` (the default) is the toolbar size, about 30px with `text-xs`\n * labels; `md` is exactly 32px (`h-8`) outside, border included, with `text-sm` labels, the\n * height of a comfortable-density `Input` or `Button`. Not derived from the density in\n * force, so a consumer that never passes it renders as it always did.\n */\n size?: ButtonSize | undefined;\n /** Merged with the strip's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const nameId = useId();\n const described = useFieldDescription();\n const effective = value === \"\" ? defaultValue : value;\n\n return (\n <div\n role=\"radiogroup\"\n aria-label={ariaLabel}\n aria-describedby={described[\"aria-describedby\"]}\n data-disabled={disabled ? \"\" : undefined}\n className={cn(\n // `shrink-0` because this is a control, not filler: in a crowded toolbar a flex item at\n // its default `1 1 auto` gives up width first, and the segment labels are what pay for\n // it. Let the row scroll instead of squeezing the choice.\n \"inline-flex w-fit max-w-full shrink-0 items-center gap-0.5 rounded-control border border-line-strong bg-raised p-0.5\",\n size === \"md\" && \"h-8\",\n disabled && \"opacity-50\",\n className,\n )}\n >\n {options.map((option) => {\n const checked = option.value === effective;\n return (\n <label\n key={option.value}\n data-state={checked ? \"checked\" : \"unchecked\"}\n className={cn(\n // `whitespace-nowrap` or a two-word label's min-content width is its longest\n // *word*, and \"Side by side\" is free to stack onto three lines inside a\n // fixed-height strip that then clips it.\n \"relative cursor-pointer whitespace-nowrap rounded-[0.3rem]\",\n SEGMENT_SIZE_CLASSES[size],\n \"font-medium transition-colors\",\n \"has-focus-visible:outline-2 has-focus-visible:outline-offset-1 has-focus-visible:outline-signal\",\n checked ? \"bg-surface text-fg shadow-sm\" : \"text-fg-muted hover:text-fg\",\n disabled && \"cursor-not-allowed\",\n )}\n >\n <input\n type=\"radio\"\n name={nameId}\n value={option.value}\n checked={checked}\n disabled={disabled}\n onChange={() =>\n // Selecting the default returns the field to unset: same effective value,\n // nothing sent, and the default keeps living in exactly one place.\n onValueChange(option.value === defaultValue ? \"\" : option.value)\n }\n className=\"absolute inset-0 cursor-pointer opacity-0\"\n />\n {option.label}\n </label>\n );\n })}\n </div>\n );\n}\n","/*\n * A picker with a list you can actually see.\n *\n * A native `<select>` cannot style its option list on macOS, so the popup arrives in the OS\n * palette in the middle of an otherwise deliberate instrument panel, and it cannot render a\n * two-line option or a disabled one with a reason.\n *\n * The one contract worth reading before changing this: **`\"\"` means unset.** Radix reserves\n * the empty string internally -- an `Item` may not carry it -- so an explicit \"use the\n * default\" entry travels under a sentinel and is mapped back at the boundary. Callers only\n * ever see `\"\"`.\n */\n\nimport * as RadixSelect from \"@radix-ui/react-select\";\nimport { Check, ChevronDown } from \"lucide-react\";\nimport { useState } from \"react\";\n\nimport { useFieldDescription } from \"./Field\";\nimport { useControlHeight } from \"./Input\";\nimport { cn, focusRingInset } from \"./cn\";\n\nconst UNSET_SENTINEL = \"__unset__\";\n\n/** One entry of a `Select`'s list. */\nexport type SelectOption = {\n /** What `onValueChange` reports. Must not be `\"\"`, which means unset. */\n value: string;\n /** What the list and the trigger show. */\n label: string;\n /** Shown quietly after the label -- a strategy, a count, a reason it is unavailable. */\n note?: string;\n /** Shown but not choosable; say why in `note`. */\n disabled?: boolean;\n};\n\n/**\n * A picker whose option list is styled with the app, with room for a quiet note per option.\n *\n * Controlled, and **`\"\"` means unset**: the trigger then shows `placeholder`, and the\n * `unsetLabel` entry (when given) reports `\"\"`. The trigger carries Radix's `data-state`\n * (`open`/`closed`), `data-placeholder` while unset and `data-disabled`; inside a `Field` it\n * is described by the field's description and error.\n */\nexport function Select({\n value,\n onValueChange,\n options,\n placeholder = \"Choose…\",\n unsetLabel,\n disabled = false,\n \"aria-label\": ariaLabel,\n \"aria-describedby\": ownDescribedBy,\n className,\n}: {\n /** The chosen option's value, or `\"\"` for unset. */\n value: string;\n /** Called with the chosen value, or `\"\"` for the unset entry. */\n onValueChange: (value: string) => void;\n /** The list, in order. */\n options: SelectOption[];\n /** Shown on the trigger while the value is unset. Defaults to \"Choose…\". */\n placeholder?: string | undefined;\n /** When given, adds a leading entry that returns the field to unset. */\n unsetLabel?: string | undefined;\n /** Blocks the trigger. */\n disabled?: boolean | undefined;\n /** Names the trigger, where no `<label>` does. */\n \"aria-label\"?: string | undefined;\n /** Ids of elements describing the trigger, merged with a surrounding `Field`'s. */\n \"aria-describedby\"?: string | undefined;\n /** Merged with the trigger's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const [open, setOpen] = useState(false);\n const described = useFieldDescription(ownDescribedBy);\n // The trigger is a control like any input: 32 px comfortable, 28 px compact.\n const height = useControlHeight();\n\n return (\n <RadixSelect.Root\n // Controlled for its whole lifetime: Radix treats `value=\"\"` as \"no selection\" and shows\n // the placeholder, which is exactly this component's unset.\n value={value}\n onValueChange={(next) => onValueChange(next === UNSET_SENTINEL ? \"\" : next)}\n open={open}\n onOpenChange={setOpen}\n disabled={disabled}\n >\n <RadixSelect.Trigger\n aria-label={ariaLabel}\n {...described}\n // While the list is open Radix hides everything outside it from assistive technology\n // (`aria-hidden`), this trigger included; a hidden element must not stay in the tab\n // order. Focus returns to it on close all the same.\n tabIndex={open ? -1 : undefined}\n className={cn(\n \"flex w-full items-center justify-between gap-2 rounded-control border border-line-strong bg-raised px-2.5 text-sm text-fg\",\n height,\n \"transition-colors hover:border-fg-subtle\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n \"data-[placeholder]:text-fg-subtle\",\n focusRingInset,\n className,\n )}\n >\n <RadixSelect.Value placeholder={placeholder} />\n <RadixSelect.Icon className=\"shrink-0 text-fg-subtle\">\n <ChevronDown className=\"size-3.5\" />\n </RadixSelect.Icon>\n </RadixSelect.Trigger>\n\n <RadixSelect.Portal>\n <RadixSelect.Content\n position=\"popper\"\n sideOffset={4}\n className={cn(\n \"z-50 max-h-72 min-w-(--radix-select-trigger-width) overflow-hidden\",\n \"rounded-panel border border-line bg-overlay shadow-lg shadow-black/25\",\n )}\n >\n <RadixSelect.Viewport className=\"p-1\">\n {unsetLabel !== undefined && <SelectItem value={UNSET_SENTINEL} label={unsetLabel} muted />}\n {options.map((option) => (\n <SelectItem key={option.value} value={option.value} label={option.label} note={option.note} disabled={option.disabled} />\n ))}\n </RadixSelect.Viewport>\n </RadixSelect.Content>\n </RadixSelect.Portal>\n </RadixSelect.Root>\n );\n}\n\nfunction SelectItem({\n value,\n label,\n note,\n disabled,\n muted = false,\n}: {\n value: string;\n label: string;\n note?: string | undefined;\n disabled?: boolean | undefined;\n muted?: boolean;\n}) {\n return (\n <RadixSelect.Item\n value={value}\n disabled={disabled ?? false}\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-control py-1.5 pr-2 pl-7 text-sm outline-none select-none\",\n \"data-highlighted:bg-raised data-highlighted:text-fg\",\n \"data-disabled:pointer-events-none data-disabled:opacity-45\",\n muted ? \"text-fg-muted\" : \"text-fg\",\n )}\n >\n <RadixSelect.ItemIndicator className=\"absolute left-2 text-signal\">\n <Check className=\"size-3.5\" />\n </RadixSelect.ItemIndicator>\n <RadixSelect.ItemText>{label}</RadixSelect.ItemText>\n {note && <span className=\"ml-auto font-mono text-xs text-fg-subtle\">{note}</span>}\n </RadixSelect.Item>\n );\n}\n","/*\n * A continuous value, with its number always on screen.\n *\n * A slider that drives what you are looking at -- a results threshold, an overlay opacity,\n * a caliper search range -- is a measurement instrument: the number matters as much as the\n * position, so it is rendered beside the track rather than left to a separate line of prose.\n */\n\nimport * as RadixSlider from \"@radix-ui/react-slider\";\nimport type { ReactNode } from \"react\";\n\nimport { useFieldDescription } from \"./Field\";\nimport { cn, focusRing } from \"./cn\";\n\n/**\n * A continuous value, with its number rendered beside the track.\n *\n * Controlled. The name (`aria-label`) and a surrounding `Field`'s description go on the\n * thumb — the element with `role=\"slider\"` that a keyboard and a screen reader operate.\n * Radix exposes `data-disabled` and `data-orientation` on the parts.\n */\nexport function Slider({\n value,\n onValueChange,\n onValueCommit,\n min = 0,\n max = 1,\n step = 0.01,\n readout,\n disabled = false,\n \"aria-label\": ariaLabel,\n \"aria-describedby\": ownDescribedBy,\n className,\n}: {\n /** The current value. */\n value: number;\n /** Called on every move. */\n onValueChange: (value: number) => void;\n /** Called once when a drag or a key press ends — for updates too expensive to run per move. */\n onValueCommit?: ((value: number) => void) | undefined;\n /** Defaults to 0. */\n min?: number | undefined;\n /** Defaults to 1. */\n max?: number | undefined;\n /** Defaults to 0.01. */\n step?: number | undefined;\n /** Rendered to the right of the track. Give it a fixed-width mono span for stability. */\n readout?: ReactNode;\n /** Blocks the thumb and dims the track. */\n disabled?: boolean | undefined;\n /** Names the slider (set on the thumb). */\n \"aria-label\"?: string | undefined;\n /** Ids of elements describing the slider, merged with a surrounding `Field`'s. */\n \"aria-describedby\"?: string | undefined;\n /** Merged with the row's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const described = useFieldDescription(ownDescribedBy);\n\n return (\n // Grows by default: a slider that does not fill the space it is given collapses to a\n // few pixels of track, which is unusable and reads as a rendering fault.\n <div className={cn(\"flex min-w-0 flex-1 items-center gap-3\", className)}>\n <RadixSlider.Root\n value={[value]}\n onValueChange={([next]) => next !== undefined && onValueChange(next)}\n onValueCommit={([next]) => next !== undefined && onValueCommit?.(next)}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n className={cn(\"relative flex h-5 min-w-0 flex-1 touch-none items-center select-none\", \"data-disabled:opacity-50\")}\n >\n <RadixSlider.Track className=\"relative h-1 grow overflow-hidden rounded-full bg-line\">\n <RadixSlider.Range className=\"absolute h-full bg-signal\" />\n </RadixSlider.Track>\n <RadixSlider.Thumb\n aria-label={ariaLabel}\n {...described}\n className={cn(\n \"block size-3.5 rounded-full border-2 border-signal bg-surface transition-shadow\",\n \"hover:shadow-[0_0_0_4px] hover:shadow-signal/20\",\n \"disabled:cursor-not-allowed\",\n focusRing,\n )}\n />\n </RadixSlider.Root>\n {readout && <span className=\"shrink-0 font-mono text-xs text-fg tabular-nums\">{readout}</span>}\n </div>\n );\n}\n","/*\n * The order of a sorted `Table`, as pure functions so it is tested without a DOM, and so an app\n * can reproduce the table's order (next/previous navigation, an export, a server that sorts the\n * same way) with `sortRows`.\n *\n * The order is deterministic across machines: strings go through one collator with a fixed\n * locale, so a server render and the browser that hydrates it put the rows in the same order.\n * A `Collator` is not DOM, so building it once at module scope keeps the package SSR-safe.\n */\n\nimport type { Column } from \"./Table\";\n\n/** Which way a column is sorted. Named as `aria-sort` names them: `\"ascending\"` puts the smallest first. */\nexport type SortDirection = \"ascending\" | \"descending\";\n\n/** The column a table is sorted by, and which way. Where a sort is expected, `null` means the rows' own order. */\nexport interface TableSort {\n /** The sorted column's `key`. */\n key: string;\n /** Which way. */\n direction: SortDirection;\n}\n\n/** The column fields that decide the order. */\ntype SortColumn<Row> = Pick<Column<Row>, \"key\" | \"sortable\" | \"sortValue\" | \"compare\">;\n\n/** Strings by a fixed English collation that reads digit runs as numbers: \"frame 9\" before \"frame 10\". */\nconst collator = new Intl.Collator(\"en\", { numeric: true });\n\n/**\n * Whether a column's header sorts the table: `sortable` if given, otherwise whether the column\n * says how to order (`sortValue` or `compare`).\n */\nexport function isSortable<Row>(column: Pick<Column<Row>, \"sortable\" | \"sortValue\" | \"compare\">): boolean {\n return column.sortable ?? (column.sortValue !== undefined || column.compare !== undefined);\n}\n\n/** The sort after a click on `column`'s header: its first direction, then the other, then none. */\nexport function nextSort<Row>(current: TableSort | null, column: Pick<Column<Row>, \"key\" | \"firstSort\">): TableSort | null {\n const first = column.firstSort ?? \"ascending\";\n if (current?.key !== column.key) return { key: column.key, direction: first };\n if (current.direction !== first) return null;\n return { key: column.key, direction: first === \"ascending\" ? \"descending\" : \"ascending\" };\n}\n\n/** A value with no place in the order: `null`, `undefined` or `NaN`. */\nfunction missing(value: number | string | null | undefined): boolean {\n return value === null || value === undefined || Number.isNaN(value);\n}\n\n/**\n * Two present values in ascending order: numbers by value (compared, not subtracted, so two\n * infinities tie), strings by the collator, and every number before every string.\n */\nfunction compareValues(a: number | string, b: number | string): number {\n if (typeof a === \"number\" && typeof b === \"number\") return a < b ? -1 : a > b ? 1 : 0;\n if (typeof a === \"string\" && typeof b === \"string\") return collator.compare(a, b);\n return typeof a === \"number\" ? -1 : 1;\n}\n\n/**\n * The display order of `rows` under `sort`, as indices into `rows`. `Table` renders through it\n * so each row keeps its original index for the row callbacks.\n */\nexport function sortOrder<Row>(rows: readonly Row[], columns: readonly SortColumn<Row>[], sort: TableSort | null | undefined): number[] {\n const order = rows.map((_, index) => index);\n if (!sort) return order;\n const column = columns.find((candidate) => candidate.key === sort.key);\n if (!column || !isSortable(column)) return order;\n const sign = sort.direction === \"descending\" ? -1 : 1;\n\n // Every comparison falls back to the original index, so equal rows keep their order in both\n // directions: descending is not ascending reversed, it is ascending with each run of ties kept.\n const { compare, sortValue } = column;\n if (compare) return order.sort((a, b) => sign * compare(rows[a]!, rows[b]!) || a - b);\n if (!sortValue) return order;\n\n // One call per row, not two per comparison: a `sortValue` may parse or look something up.\n const values = rows.map((row) => sortValue(row));\n return order.sort((a, b) => {\n const left = values[a];\n const right = values[b];\n const leftMissing = missing(left);\n const rightMissing = missing(right);\n // Missing values go last whichever way the column is sorted: a reader sorting by score in\n // either direction wants the rows that have one.\n if (leftMissing || rightMissing) return leftMissing === rightMissing ? a - b : leftMissing ? 1 : -1;\n return sign * compareValues(left!, right!) || a - b;\n });\n}\n\n/**\n * The rows in the order a `Table` with these columns shows them under `sort`.\n *\n * The sort is stable in both directions: rows that compare equal keep their order in `rows`.\n * With the column's `compare`, that function decides (reversed for `\"descending\"`). With its\n * `sortValue`, numbers compare by value and strings by a fixed English collation that reads\n * digit runs as numbers (\"frame 9\" before \"frame 10\"), the same on a server as in any\n * browser; numbers come before strings; and `null`, `undefined` and `NaN` go last in both\n * directions. A `null` sort, a `key` that names no column, and a column that is not sortable or\n * has neither function leave the order as given.\n *\n * @param rows - The rows, in their own order. Not modified.\n * @param columns - The table's columns; only `key`, `sortable`, `sortValue` and `compare` are read.\n * @param sort - The column and direction, or `null` for the rows' own order.\n * @returns A new array of the same rows, sorted.\n */\nexport function sortRows<Row>(\n rows: readonly Row[],\n columns: readonly Pick<Column<Row>, \"key\" | \"sortable\" | \"sortValue\" | \"compare\">[],\n sort: TableSort | null | undefined,\n): Row[] {\n return sortOrder(rows, columns, sort).map((index) => rows[index]!);\n}\n","/*\n * The arithmetic of a windowed table: which rows to render for a scroll position, and where\n * to scroll to bring a row into view. Pure, so it is tested on its own (`tableWindow.test.ts`,\n * `tableWindow.property.test.ts`) rather than through layout.\n *\n * Every row is `rowHeight` tall. The body starts below a sticky header `headerHeight` tall, and\n * the scroll container shows `viewportHeight` of the whole (header included).\n */\n\n/** The rows to render, as a half-open range of positions, and the space around them. */\nexport interface RowWindow {\n /** The first rendered position. */\n start: number;\n /** One past the last rendered position. */\n end: number;\n /** The height, in px, of the rows before `start`. */\n before: number;\n /** The height, in px, of the rows from `end` on. */\n after: number;\n}\n\n/** What the window depends on. Lengths in px. */\nexport interface WindowInput {\n rowCount: number;\n rowHeight: number;\n /** The scroll container's `scrollTop`. */\n scrollTop: number;\n /** The scroll container's visible height, header included. */\n viewportHeight: number;\n /** The sticky header's height. */\n headerHeight: number;\n /** Rows rendered beyond each edge of the visible ones. */\n overscan: number;\n}\n\n/**\n * The rows that intersect the visible part of the body, plus `overscan` on each side.\n * Degenerate input (no rows, a row height ≤ 0, NaN) gives an empty window.\n */\nexport function rowWindow({ rowCount, rowHeight, scrollTop, viewportHeight, headerHeight, overscan }: WindowInput): RowWindow {\n const count = Number.isFinite(rowCount) ? Math.max(0, Math.floor(rowCount)) : 0;\n if (count === 0 || !(rowHeight > 0)) return { start: 0, end: 0, before: 0, after: 0 };\n const extra = Math.max(0, Math.floor(overscan) || 0);\n // In the body's own coordinates the visible part is [scrollTop, scrollTop + viewport − header]:\n // the sticky header covers the top of the container.\n const top = Math.max(0, scrollTop || 0);\n const bottom = Math.max(top, top + Math.max(0, viewportHeight || 0) - Math.max(0, headerHeight || 0));\n const first = Math.min(count, Math.floor(top / rowHeight));\n const last = Math.min(count, Math.max(first + 1, Math.ceil(bottom / rowHeight)));\n const start = Math.max(0, first - extra);\n const end = Math.min(count, last + extra);\n return { start, end, before: start * rowHeight, after: (count - end) * rowHeight };\n}\n\n/**\n * The `scrollTop` that shows the row at `position` in full below the sticky header, moving\n * as little as possible: unchanged when it is already in view, else aligned to the nearer\n * edge. A viewport shorter than one row aligns the row's top.\n */\nexport function scrollTopFor(\n position: number,\n { rowHeight, scrollTop, viewportHeight, headerHeight }: Pick<WindowInput, \"rowHeight\" | \"scrollTop\" | \"viewportHeight\" | \"headerHeight\">,\n): number {\n const rowTop = position * rowHeight;\n const visible = viewportHeight - headerHeight;\n if (rowTop < scrollTop || visible < rowHeight) return rowTop;\n if (rowTop + rowHeight > scrollTop + visible) return rowTop + rowHeight - visible;\n return scrollTop;\n}\n\n/**\n * The position a navigation key moves to from `position`, among `rowCount` rows, `page` rows\n * to a screen; `null` for a key that does not navigate.\n */\nexport function navigate(key: string, position: number, rowCount: number, page: number): number | null {\n const last = rowCount - 1;\n if (last < 0) return null;\n const step = Math.max(1, Math.floor(page));\n const clamp = (p: number) => Math.min(last, Math.max(0, p));\n switch (key) {\n case \"ArrowDown\":\n return clamp(position + 1);\n case \"ArrowUp\":\n return clamp(position - 1);\n case \"PageDown\":\n return clamp(position + step);\n case \"PageUp\":\n return clamp(position - step);\n case \"Home\":\n return 0;\n case \"End\":\n return last;\n default:\n return null;\n }\n}\n","/*\n * A table.\n *\n * `numeric` is the column property that matters here. Quantities are set in mono with\n * tabular figures and aligned right, so a column of scores or measurements can be compared\n * by eye down its decimal point rather than read one row at a time.\n *\n * Sorting is per column and follows the sortable-table pattern of the ARIA Authoring\n * Practices: the header's content is a button, and only the sorted header carries\n * `aria-sort`. The order itself lives in `tableSort.ts`.\n *\n * `windowed` renders only the rows in view, between two spacer rows that keep the scroll\n * height, so a table of thousands of rows scrolls as fast as one of thirty. It stays a real\n * `<table>`: the rows that are rendered carry `aria-rowindex` and the table `aria-rowcount`, so\n * a reader still hears \"row 2,041 of 5,001\". The arithmetic lives in `tableWindow.ts`.\n */\n\nimport { ArrowDown, ArrowUp, ArrowUpDown } from \"lucide-react\";\nimport { type KeyboardEvent, type MouseEvent, type ReactNode, useLayoutEffect, useMemo, useRef, useState } from \"react\";\n\nimport { byDensity, useDensity } from \"./Density\";\nimport { cn, focusRing, focusRingInset } from \"./cn\";\nimport { isSortable, nextSort, type SortDirection, sortOrder, type TableSort } from \"./tableSort\";\nimport { navigate, rowWindow, scrollTopFor } from \"./tableWindow\";\n\n/** One column of a `Table`: its header and how to render a row's cell. */\nexport type Column<Row> = {\n /** Unique among the columns; the React key of its header and cells. */\n key: string;\n /** The `<th>` content. */\n header: ReactNode;\n /** Right-aligned, mono, tabular. Use for anything that is a quantity. */\n numeric?: boolean;\n /** A CSS width for the column, e.g. `\"6rem\"`. */\n width?: string;\n /** The cell's content for a row. */\n cell: (row: Row) => ReactNode;\n /**\n * Whether the header sorts the table. Defaults to `true` when the column has a `sortValue`\n * or a `compare`. Set it `true` with neither for a column the app sorts itself (see the\n * table's `manualSort`), or `false` to keep a column with a `sortValue` unsortable.\n */\n sortable?: boolean | undefined;\n /**\n * The value the column sorts by. Numbers compare by value; strings by a fixed English\n * collation that reads digit runs as numbers (\"frame 9\" before \"frame 10\"), so a server\n * render and the browser agree; numbers come before strings. `null`, `undefined` and `NaN`\n * go last in both directions.\n */\n sortValue?: ((row: Row) => number | string | null | undefined) | undefined;\n /**\n * A full ascending comparison, for an order one value cannot express (two fields, a rank):\n * negative when `a` comes first. Takes precedence over `sortValue`. Reversed for\n * `\"descending\"`; rows that compare equal keep their order in `rows` either way.\n */\n compare?: ((a: Row, b: Row) => number) | undefined;\n /**\n * The direction of the first click on the header. Defaults to `\"ascending\"`; `\"descending\"`\n * suits a score, where the best comes first.\n */\n firstSort?: SortDirection | undefined;\n};\n\n/**\n * Windowed rendering for a long table: only the rows in view, plus `overscan` on each side,\n * are in the DOM. The rows scroll under a sticky header drawn on `surface`, the panel colour.\n */\nexport interface TableWindowing {\n /**\n * Every body row's height in px. Rows are drawn exactly this tall and cells keep to one line\n * (longer content is cut with an ellipsis), so it must be at least the density's own row\n * height: 36 px comfortable, 22 px compact.\n */\n rowHeight: number;\n /**\n * The scroll area's height in px, header included. Leave it out to fill the height of the\n * container, which must then have one (a flex child with `min-h-0`, a fixed-height panel).\n */\n height?: number | undefined;\n /** Rows rendered beyond each edge of the view, so a fast scroll shows no gap. Defaults to 8. */\n overscan?: number | undefined;\n}\n\n/** Rows per screen before a windowed table has measured itself (a server render, the first paint). */\nconst INITIAL_ROWS = 20;\n\n/** The header icon for a sort state: the direction when sorted, both arrows when only sortable. */\nconst SORT_ICONS = { ascending: ArrowUp, descending: ArrowDown, none: ArrowUpDown } as const;\n\n/**\n * A table of rows, with numeric columns set in mono and right-aligned.\n *\n * With `onRowClick` the rows are keyboard-reachable (Tab, then Enter or Space) while keeping\n * their table semantics. The active row carries `aria-current` and `data-state=\"active\"`;\n * padding follows the density in force. With no rows it renders the `empty` message instead.\n *\n * A column with a `sortValue` or a `compare` gets a header button: each click moves its sort\n * from the column's `firstSort` direction to the other one, then back to the rows' own order.\n * The sorted header carries `aria-sort` and `data-sort` (`\"ascending\"` or `\"descending\"`);\n * other sortable headers carry `data-sort=\"none\"`. The sort is kept by the table (from\n * `defaultSort`) or controlled (`sort` with `onSortChange`); with `manualSort` the headers\n * show it and the rows render as given. Row callbacks always receive a row's index in `rows`,\n * whatever the order on screen.\n *\n * Activatable rows also move focus with the arrow keys, Page Up / Page Down and Home / End.\n * With `windowed` the table scrolls itself (vertically, under a sticky header), renders only the\n * rows in view, and uses a fixed layout: give columns a `width` where the content needs it.\n */\nexport function Table<Row>({\n columns,\n rows,\n rowKey,\n empty = \"Nothing here.\",\n caption,\n className,\n onRowClick,\n isRowActive,\n onRowHover,\n sort: controlledSort,\n defaultSort = null,\n onSortChange,\n manualSort = false,\n windowed,\n}: {\n /** The columns, in order. */\n columns: Column<Row>[];\n /** The rows, in their own order: the order shown while the table is not sorted. */\n rows: Row[];\n /** A stable key per row. `index` is the row's position in `rows`, whatever the sort. */\n rowKey: (row: Row, index: number) => string | number;\n /** Shown instead of the table when there are no rows. Defaults to \"Nothing here.\". */\n empty?: ReactNode;\n /** Names the table for assistive technology (a visually hidden `<caption>`). */\n caption?: string | undefined;\n /** Merged with the scroll wrapper's (or the empty message's) own classes through `cn`. */\n className?: string | undefined;\n /** Makes rows activatable. Keyboard-reachable, so a table used as a list of\n * destinations is not a mouse-only control. The event is passed so a caller can read\n * modifiers — a list kept in step with a canvas needs shift-range and meta-toggle to mean\n * the same thing in both places. `index` is the row's position in `rows`, whatever the\n * sort. */\n onRowClick?: (row: Row, index: number, event: MouseEvent<HTMLTableRowElement> | KeyboardEvent<HTMLTableRowElement>) => void;\n /** Marks the row that is current — the selected match, the open frame. Rendered as\n * `aria-current`, so it is announced rather than only tinted. `index` is the row's\n * position in `rows`, whatever the sort. */\n isRowActive?: (row: Row, index: number) => boolean;\n /** Pointer enter/leave, for tables kept in step with a canvas overlay.\n * `null` on leave. `index` is the row's position in `rows`, whatever the sort. */\n onRowHover?: (row: Row | null, index: number | null) => void;\n /**\n * The sort, controlled: a column `key` and a direction, or `null` for the rows' own order.\n * Leave it undefined and the table keeps the sort itself, starting from `defaultSort`.\n */\n sort?: TableSort | null | undefined;\n /** The sort the table starts with when `sort` is not given. Defaults to `null`, the rows' own order. */\n defaultSort?: TableSort | null | undefined;\n /**\n * Called on a header click with the new sort: the column's `firstSort` direction, then the\n * other, then `null`. A click on another column starts that column at its `firstSort`.\n */\n onSortChange?: ((sort: TableSort | null) => void) | undefined;\n /**\n * The app orders the rows (on a server, say): the headers show the sort and report clicks,\n * and the rows render exactly as given. Mark the columns it can sort `sortable: true`.\n */\n manualSort?: boolean | undefined;\n /**\n * Render only the rows in view, for a long table: every row `rowHeight` tall, in a scroll\n * area of `height` (or the container's height) under a sticky header. Sorting, row\n * callbacks and keyboard navigation work as without it.\n */\n windowed?: TableWindowing | undefined;\n}) {\n const density = useDensity();\n const [ownSort, setOwnSort] = useState<TableSort | null>(defaultSort);\n const controlled = controlledSort !== undefined;\n const sort = controlled ? controlledSort : ownSort;\n // Indices into `rows`, so a row keeps its own index (and key) for the callbacks below.\n const order = useMemo(\n () => (manualSort ? rows.map((_, index) => index) : sortOrder(rows, columns, sort)),\n [rows, columns, sort, manualSort],\n );\n\n const changeSort = (column: Column<Row>) => {\n const next = nextSort(sort, column);\n if (!controlled) setOwnSort(next);\n onSortChange?.(next);\n };\n\n const rowHeight = windowed?.rowHeight ?? 0;\n const scrollRef = useRef<HTMLDivElement>(null);\n const headRef = useRef<HTMLTableSectionElement>(null);\n const bodyRef = useRef<HTMLTableSectionElement>(null);\n // The row to focus once it is rendered: a key press may scroll to a row that is not in the DOM yet.\n const pendingFocus = useRef<number | null>(null);\n const [scrollTop, setScrollTop] = useState(0);\n const [measured, setMeasured] = useState<{ viewport: number; header: number } | null>(null);\n const viewportHeight = measured?.viewport ?? windowed?.height ?? INITIAL_ROWS * rowHeight;\n const headerHeight = measured?.header ?? 0;\n const range = windowed\n ? rowWindow({ rowCount: order.length, rowHeight, scrollTop, viewportHeight, headerHeight, overscan: windowed.overscan ?? 8 })\n : { start: 0, end: order.length, before: 0, after: 0 };\n\n const isWindowed = windowed !== undefined;\n useLayoutEffect(() => {\n const scroller = scrollRef.current;\n if (!isWindowed || !scroller || typeof ResizeObserver === \"undefined\") return;\n const measure = () => {\n const next = { viewport: scroller.clientHeight, header: headRef.current?.offsetHeight ?? 0 };\n setMeasured((last) => (last?.viewport === next.viewport && last.header === next.header ? last : next));\n };\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(scroller);\n return () => observer.disconnect();\n }, [isWindowed]);\n\n useLayoutEffect(() => {\n const target = pendingFocus.current;\n if (target === null) return;\n const row = bodyRef.current?.querySelector<HTMLElement>(`tr[data-position=\"${target}\"]`);\n if (!row) return;\n pendingFocus.current = null;\n row.focus({ preventScroll: isWindowed });\n });\n\n /** Moves focus from the row at `position` for a navigation key; `true` when the key was one. */\n const moveFocus = (key: string, position: number): boolean => {\n const page = windowed ? Math.floor((viewportHeight - headerHeight) / rowHeight) : 10;\n const target = navigate(key, position, order.length, page);\n if (target === null) return false;\n pendingFocus.current = target;\n const scroller = scrollRef.current;\n if (windowed && scroller) {\n const top = scrollTopFor(target, { rowHeight, scrollTop: scroller.scrollTop, viewportHeight, headerHeight });\n scroller.scrollTop = top;\n // Render the target's window now rather than on the scroll event, so it can take focus.\n setScrollTop(scroller.scrollTop);\n }\n const row = bodyRef.current?.querySelector<HTMLElement>(`tr[data-position=\"${target}\"]`);\n if (row && !windowed) {\n pendingFocus.current = null;\n row.focus();\n }\n return true;\n };\n\n if (rows.length === 0) {\n return <p className={cn(\"text-center text-fg-muted\", byDensity(density, \"py-6 text-sm\", \"py-3 text-xs\"), className)}>{empty}</p>;\n }\n\n const spacer = (height: number) => (\n // Stands in for the rows outside the window. Hidden: it is no row of the table.\n <tr aria-hidden=\"true\" style={{ height }}>\n <td colSpan={columns.length} className=\"p-0\" />\n </tr>\n );\n\n // A scroll area must be reachable from the keyboard: through its rows or header buttons when\n // it has them, else as a tab stop of its own.\n const scrollStop = windowed !== undefined && !onRowClick && !columns.some(isSortable);\n\n return (\n <div\n ref={scrollRef}\n className={cn(\"w-full\", windowed ? scrollStop && focusRingInset : \"overflow-x-auto\", className)}\n // What the window arithmetic relies on is inline rather than a class, so it holds under\n // any stylesheet: the scroll area, the fixed layout, the sticky header, the row height.\n style={windowed ? { overflow: \"auto\", height: windowed.height ?? \"100%\" } : undefined}\n onScroll={windowed ? (event) => setScrollTop(event.currentTarget.scrollTop) : undefined}\n {...(scrollStop ? { tabIndex: 0, role: \"region\", \"aria-label\": caption ?? \"Table\" } : {})}\n >\n <table\n className={cn(\"w-full text-left\", byDensity(density, \"text-sm\", \"text-[11px]\"))}\n style={windowed ? { tableLayout: \"fixed\" } : undefined}\n aria-rowcount={windowed ? order.length + 1 : undefined}\n >\n {caption && <caption className=\"sr-only\">{caption}</caption>}\n <thead ref={headRef}>\n <tr className=\"border-b border-line\" aria-rowindex={windowed ? 1 : undefined}>\n {columns.map((column) => {\n const sortable = isSortable(column);\n const direction = sortable && sort?.key === column.key ? sort.direction : undefined;\n const SortIcon = SORT_ICONS[direction ?? \"none\"];\n return (\n <th\n key={column.key}\n scope=\"col\"\n style={\n windowed\n ? { position: \"sticky\", top: 0, zIndex: 1, ...(column.width ? { width: column.width } : {}) }\n : column.width\n ? { width: column.width }\n : undefined\n }\n // Only the sorted header: `aria-sort=\"none\"` on every other one would be\n // announced on each, and tells a reader nothing.\n aria-sort={direction}\n data-sort={sortable ? (direction ?? \"none\") : undefined}\n className={cn(\n // `last:pr-0`, as on the body cells: the last column's header ends where\n // its values do, so a numeric header sits flush with its numbers.\n \"font-medium text-fg-muted last:pr-0\",\n byDensity(density, \"pb-2 pr-3 text-xs\", \"pb-1 pr-2 text-[10px]\"),\n column.numeric && \"text-right\",\n // A sticky cell leaves its row's border behind: it draws its own rule.\n windowed && \"bg-surface shadow-[inset_0_-1px_0_var(--line)]\",\n )}\n >\n {sortable ? (\n <button\n type=\"button\"\n onClick={() => changeSort(column)}\n className={cn(\n // Block-level `flex`, not `inline-flex`: an inline box would sit on the\n // cell's line box and make a sortable header row 2px taller than a plain\n // one. The padding, cancelled by the margin, gives the focus ring room\n // without moving the label off the column's edge.\n \"-mx-1 flex w-fit max-w-full cursor-pointer items-center gap-1 rounded-control px-1\",\n \"font-medium transition-colors hover:text-fg\",\n focusRing,\n // The arrow sits on the inner side of a right-aligned header, so the\n // label stays flush with the numbers under it.\n column.numeric ? \"ml-auto flex-row-reverse text-right\" : \"text-left\",\n direction && \"text-fg\",\n )}\n >\n {column.header}\n <SortIcon\n aria-hidden\n className={cn(\"shrink-0\", byDensity(density, \"size-3\", \"size-2.5\"), !direction && \"text-fg-subtle\")}\n />\n </button>\n ) : (\n column.header\n )}\n </th>\n );\n })}\n </tr>\n </thead>\n <tbody ref={bodyRef}>\n {range.before > 0 && spacer(range.before)}\n {order.slice(range.start, range.end).map((index, offset) => {\n const row = rows[index]!;\n const active = isRowActive?.(row, index) ?? false;\n const position = range.start + offset;\n return (\n <tr\n key={rowKey(row, index)}\n data-position={position}\n aria-rowindex={windowed ? position + 2 : undefined}\n style={windowed ? { height: rowHeight } : undefined}\n data-state={active ? \"active\" : undefined}\n className={cn(\n // A windowed row's rule is a shadow on its cells: a collapsed border would add\n // half a pixel to every row, and the arithmetic counts whole rows.\n !windowed && \"border-b border-line/60 last:border-0\",\n onRowClick && \"cursor-pointer hover:bg-raised\",\n active && \"bg-signal/10\",\n )}\n tabIndex={onRowClick ? 0 : undefined}\n // Deliberately *not* `role=\"button\"`. Overriding a `<tr>`'s implicit `row`\n // makes its `<td>`s' implicit `cell` invalid too — the cells have no row to\n // belong to — so a screen reader stops announcing the table as a table and a\n // reader loses the column each value came from, which on a table of\n // quantities is the whole content. `tabIndex` plus the Enter/Space handler\n // below is what makes the row reachable; the role stays what the element is.\n aria-current={active ? true : undefined}\n onClick={onRowClick ? (event) => onRowClick(row, index, event) : undefined}\n onKeyDown={\n onRowClick\n ? (event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n onRowClick(row, index, event);\n // Navigation keys belong to a control inside the row while it has focus.\n } else if (event.target === event.currentTarget && moveFocus(event.key, position)) event.preventDefault();\n }\n : undefined\n }\n onPointerEnter={onRowHover ? () => onRowHover(row, index) : undefined}\n onPointerLeave={onRowHover ? () => onRowHover(null, null) : undefined}\n >\n {columns.map((column) => (\n <td\n key={column.key}\n className={cn(\n \"align-middle text-fg last:pr-0\",\n byDensity(density, \"py-2 pr-3\", \"py-0.5 pr-2\"),\n column.numeric && \"text-right font-mono tabular-nums\",\n windowed && \"shadow-[inset_0_-1px_0_color-mix(in_oklab,var(--line)_60%,transparent)]\",\n )}\n // One line per row keeps every row `rowHeight` tall.\n style={windowed ? { whiteSpace: \"nowrap\", overflow: \"hidden\", textOverflow: \"ellipsis\" } : undefined}\n >\n {column.cell(row)}\n </td>\n ))}\n </tr>\n );\n })}\n {range.after > 0 && spacer(range.after)}\n </tbody>\n </table>\n </div>\n );\n}\n","/*\n * Multi-line text entry.\n *\n * The auto-grow is measured, not CSS: `field-sizing: content` would need no script, but it\n * is not in every engine the packages run in (the desktop shells embed WebKit), and it\n * cannot stop at a row count. The textarea renders `minRows` tall on the server, so the\n * first paint already has the right floor; after mount its height follows the content\n * between the two bounds.\n */\n\nimport { type ChangeEvent, type ComponentProps, type Ref, useCallback, useLayoutEffect, useRef } from \"react\";\n\nimport { useFieldDescription } from \"./Field\";\nimport { controlClasses } from \"./Input\";\nimport { cn } from \"./cn\";\n\n/**\n * Props of `Textarea`: every `<textarea>` prop, `ref` included, plus an invalid state, the\n * mono face, and growing with the content between `minRows` and `maxRows`.\n */\nexport type TextareaProps = ComponentProps<\"textarea\"> & {\n /** Called with the new text on every edit, alongside the native `onChange`. */\n onValueChange?: ((value: string) => void) | undefined;\n /**\n * Marks the value as wrong: `aria-invalid`, `data-invalid` and a border in the defect\n * colour. Inside a `Field` with an `error` it is on without this. Say what is wrong in\n * text too (the field's `error`): the border alone is a colour.\n */\n invalid?: boolean | undefined;\n /** Sets the text in IBM Plex Mono, for code, JSON, lists, or strings whose characters all matter. */\n mono?: boolean | undefined;\n /**\n * Grows with the content from this many lines. Set it or `maxRows` to turn growing on;\n * `rows` is then ignored.\n */\n minRows?: number | undefined;\n /** Stops growing at this many lines and scrolls beyond them. Set it or `minRows` to turn growing on. */\n maxRows?: number | undefined;\n};\n\n/** Sets a ref of either kind. */\nfunction assign<T>(ref: Ref<T> | undefined, value: T | null): void {\n if (typeof ref === \"function\") ref(value);\n else if (ref) ref.current = value;\n}\n\n/**\n * Fits a textarea's height to its content between `minRows` and `maxRows` lines, and scrolls\n * only past the cap.\n */\nfunction fit(node: HTMLTextAreaElement, minRows: number, maxRows: number | undefined): void {\n const style = getComputedStyle(node);\n // `normal` is no length: take the usual 1.2 of the font size.\n const line = Number.parseFloat(style.lineHeight) || Number.parseFloat(style.fontSize) * 1.2 || 20;\n const border = Number.parseFloat(style.borderTopWidth) + Number.parseFloat(style.borderBottomWidth);\n const chrome = Number.parseFloat(style.paddingTop) + Number.parseFloat(style.paddingBottom) + border;\n const min = minRows * line + chrome;\n const max = maxRows === undefined ? Number.POSITIVE_INFINITY : Math.max(min, maxRows * line + chrome);\n // Measure the content at the smallest height, so the box can shrink as well as grow.\n node.style.height = `${min}px`;\n const content = node.scrollHeight + border;\n node.style.height = `${Math.min(Math.max(content, min), max)}px`;\n node.style.overflowY = content > max + 0.5 ? \"auto\" : \"hidden\";\n}\n\n/**\n * Multi-line text, on the shared control palette. Takes every `<textarea>` prop, `ref`\n * included; inside a `Field` it is described by the field's description and error, and\n * shown invalid while the field has an error.\n *\n * With `minRows` or `maxRows` it grows with its content between the two (and scrolls past\n * `maxRows`); without them it is `rows` tall and the reader can resize it. State is exposed\n * as `data-invalid`.\n */\nexport function Textarea({\n className,\n \"aria-describedby\": describedBy,\n invalid,\n mono = false,\n minRows,\n maxRows,\n rows,\n value,\n onChange,\n onValueChange,\n ref,\n ...rest\n}: TextareaProps) {\n const field = useFieldDescription(describedBy);\n const isInvalid = invalid === true || field[\"aria-invalid\"] === true;\n const grows = minRows !== undefined || maxRows !== undefined;\n const floor = Math.max(1, Math.floor(minRows ?? 1));\n const cap = maxRows === undefined ? undefined : Math.max(floor, Math.floor(maxRows));\n\n const node = useRef<HTMLTextAreaElement | null>(null);\n const setNode = useCallback(\n (element: HTMLTextAreaElement | null) => {\n node.current = element;\n assign(ref, element);\n },\n [ref],\n );\n\n // On mount, and when a controlled value changes from outside (typing refits in `change`).\n useLayoutEffect(() => {\n if (grows && node.current) fit(node.current, floor, cap);\n }, [grows, floor, cap, mono, value]);\n\n // A change of width rewraps the text.\n useLayoutEffect(() => {\n const element = node.current;\n if (!grows || !element || typeof ResizeObserver === \"undefined\") return undefined;\n let width = element.clientWidth;\n const observer = new ResizeObserver(() => {\n if (element.clientWidth === width) return;\n width = element.clientWidth;\n fit(element, floor, cap);\n });\n observer.observe(element);\n return () => observer.disconnect();\n }, [grows, floor, cap]);\n\n const change = (event: ChangeEvent<HTMLTextAreaElement>) => {\n onChange?.(event);\n onValueChange?.(event.currentTarget.value);\n if (grows) fit(event.currentTarget, floor, cap);\n };\n\n return (\n <textarea\n {...field}\n aria-invalid={isInvalid || undefined}\n data-invalid={isInvalid ? \"\" : undefined}\n {...rest}\n {...(value === undefined ? {} : { value })}\n ref={setNode}\n rows={grows ? floor : rows}\n onChange={change}\n className={cn(\n controlClasses,\n \"py-1.5\",\n grows && \"resize-none\",\n mono && \"font-mono\",\n \"data-invalid:border-defect data-invalid:hover:border-defect\",\n className,\n )}\n />\n );\n}\n","/*\n * The two binary controls, and the difference between them.\n *\n * A `Switch` changes how the thing behaves from now on -- \"allow downloads\", \"show the\n * mask\", \"lock the caliper axis\" -- and reads as a setting. A `Checkbox` asserts something\n * about a state you are about to commit -- \"I have read the warnings\", \"keep this channel\"\n * -- and reads as a statement.\n */\n\nimport * as RadixCheckbox from \"@radix-ui/react-checkbox\";\nimport * as RadixSwitch from \"@radix-ui/react-switch\";\nimport { Check, Minus } from \"lucide-react\";\nimport { useId, type MouseEvent, type ReactNode } from \"react\";\n\nimport { useFieldDescription } from \"./Field\";\nimport { cn, focusRing } from \"./cn\";\n\n/**\n * A setting: changes how the thing behaves from now on, and reads as one (\"show the mask\").\n * Always labelled; the label and the description sit beside the switch.\n *\n * Controlled. Radix exposes `data-state` (`checked`/`unchecked`) and `data-disabled`.\n */\nexport function Switch({\n checked,\n onCheckedChange,\n label,\n description,\n disabled = false,\n className,\n}: {\n /** Whether it is on. */\n checked: boolean;\n /** Called with the new state. */\n onCheckedChange: (checked: boolean) => void;\n /** What the setting is. */\n label: ReactNode;\n /** What turning it on does. */\n description?: ReactNode;\n /** Blocks the switch. */\n disabled?: boolean | undefined;\n /** Merged with the row's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const descriptionId = useId();\n return (\n <label className={cn(\"flex min-w-0 items-start gap-2.5\", className)}>\n <RadixSwitch.Root\n checked={checked}\n onCheckedChange={onCheckedChange}\n disabled={disabled}\n aria-describedby={description ? descriptionId : undefined}\n className={cn(\n \"mt-0.5 h-4 w-7 shrink-0 rounded-full border border-line-strong bg-raised transition-colors\",\n \"data-[state=checked]:border-signal data-[state=checked]:bg-signal\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n focusRing,\n )}\n >\n <RadixSwitch.Thumb\n className={cn(\n \"block size-3 rounded-full bg-fg-subtle transition-transform will-change-transform\",\n \"translate-x-0.5 data-[state=checked]:translate-x-3.5 data-[state=checked]:bg-signal-fg\",\n )}\n />\n </RadixSwitch.Root>\n <span className=\"flex min-w-0 flex-col gap-0.5\">\n <span className=\"text-sm leading-tight text-fg\">{label}</span>\n {description && (\n <span id={descriptionId} className=\"text-xs leading-snug text-fg-muted\">\n {description}\n </span>\n )}\n </span>\n </label>\n );\n}\n\n/**\n * A statement about a state you are about to commit (\"keep this channel\"). With a `label`\n * it renders its own row; without one it is a bare box (give it `aria-label`, e.g. in a\n * table cell).\n *\n * Controlled. Radix exposes `data-state` (`checked`/`unchecked`/`indeterminate`) and\n * `data-disabled`; inside a `Field` it is described by the field's description and error.\n */\nexport function Checkbox({\n checked,\n onCheckedChange,\n onClick,\n label,\n description,\n disabled = false,\n \"aria-label\": ariaLabel,\n className,\n}: {\n /** `\"indeterminate\"` for a partial selection over a set. */\n checked: boolean | \"indeterminate\";\n /** Called with the new state (never `\"indeterminate\"`: a click resolves it). */\n onCheckedChange: (checked: boolean) => void;\n /**\n * For a checkbox whose meaning depends on the modifier keys — extending a range with\n * shift, toggling one out of a selection with the platform key. `onCheckedChange` gets a\n * boolean and cannot carry them. Keyboard activation synthesises a click with its\n * modifiers intact, so this sees both input paths.\n */\n onClick?: ((event: MouseEvent<HTMLButtonElement>) => void) | undefined;\n /** What checking it asserts. Omit for a bare box. */\n label?: ReactNode;\n /** More about the statement, under the label. */\n description?: ReactNode;\n /** Blocks the box. */\n disabled?: boolean | undefined;\n /** For a checkbox in a table cell, where the row is the label. */\n \"aria-label\"?: string | undefined;\n /** Merged with the row's (or, without a label, the box's) own classes through `cn`. */\n className?: string | undefined;\n}) {\n const descriptionId = useId();\n const described = useFieldDescription(description ? descriptionId : undefined);\n const box = (\n <RadixCheckbox.Root\n checked={checked}\n onCheckedChange={(next) => onCheckedChange(next === true)}\n onClick={onClick}\n disabled={disabled}\n aria-label={ariaLabel}\n {...described}\n className={cn(\n \"flex size-4 shrink-0 items-center justify-center rounded-[0.25rem] border border-line-strong bg-raised transition-colors\",\n \"data-[state=checked]:border-signal data-[state=checked]:bg-signal\",\n \"data-[state=indeterminate]:border-signal data-[state=indeterminate]:bg-signal\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n focusRing,\n label === undefined && className,\n )}\n >\n <RadixCheckbox.Indicator className=\"text-signal-fg\">\n {checked === \"indeterminate\" ? <Minus className=\"size-3\" strokeWidth={3} /> : <Check className=\"size-3\" strokeWidth={3} />}\n </RadixCheckbox.Indicator>\n </RadixCheckbox.Root>\n );\n\n if (label === undefined) return box;\n\n return (\n <label className={cn(\"flex min-w-0 items-start gap-2.5\", className)}>\n <span className=\"mt-0.5\">{box}</span>\n <span className=\"flex min-w-0 flex-col gap-0.5\">\n <span className=\"text-sm leading-tight text-fg\">{label}</span>\n {description && (\n <span id={descriptionId} className=\"text-xs leading-snug text-fg-muted\">\n {description}\n </span>\n )}\n </span>\n </label>\n );\n}\n","/*\n * A dense on/off control for a thing that is drawn on screen.\n *\n * `Switch` is a settings row — a stacked label, a description, generous padding — and\n * three of them side by side over an image canvas is a panel, not a toolbar. This is the\n * same binary decision in the space a toolbar has: one line, one target, and an optional\n * swatch carrying the colour the layer is actually drawn in — an overlay's caliper boxes,\n * a chart's series — so the legend and the control are the same object rather than two\n * things to reconcile.\n *\n * Hand-built on a native `button` with `role=\"switch\"`: this is not one of the four Radix\n * primitives the rest of the toggle vocabulary is built on, and a chip does not need one.\n */\n\nimport type { ReactNode } from \"react\";\n\nimport type { ButtonSize } from \"./Button\";\nimport { cn, focusRing } from \"./cn\";\n\n/**\n * The chip's box, by size. `sm` is the toolbar size and the default. `md` sets the height\n * explicitly (`h-8`, border included) rather than leaving it to padding plus line-height.\n */\nconst SIZE_CLASSES: Record<ButtonSize, string> = {\n sm: \"px-2 py-1 text-xs\",\n md: \"h-8 px-3 text-sm\",\n};\n\n/** The swatch dot, by size: a little larger beside `text-sm` so it keeps its weight against the label. */\nconst SWATCH_SIZE_CLASSES: Record<ButtonSize, string> = {\n sm: \"size-2\",\n md: \"size-2.5\",\n};\n\n/**\n * A dense on/off chip for a layer drawn on screen, with an optional swatch in the layer's\n * colour: a toolbar-sized `Switch` (`role=\"switch\"`, `aria-checked`).\n *\n * Controlled. State is exposed as `data-state` (`checked`/`unchecked`).\n *\n * Toolbar-sized by default. In a row with a comfortable-density `Input`, `Select` or\n * `Button`, pass `size=\"md\"` and the chip's outer height matches theirs.\n */\nexport function ToggleChip({\n checked,\n onCheckedChange,\n children,\n swatch,\n disabled = false,\n title,\n size = \"sm\",\n className,\n}: {\n /** Whether the layer is shown. */\n checked: boolean;\n /** Called with the new state. */\n onCheckedChange: (checked: boolean) => void;\n /** The layer's name. */\n children: ReactNode;\n /** A CSS colour: the colour this layer is drawn in, shown as a dot. */\n swatch?: string;\n /** Blocks the chip. */\n disabled?: boolean | undefined;\n /** Why the control is disabled, or what the layer is. Never the only explanation. */\n title?: string | undefined;\n /**\n * The chip's height: `sm` (the default) is the toolbar size, about 26px with `text-xs`\n * text; `md` is exactly 32px (`h-8`) outside, border included, with `text-sm` text and a\n * slightly larger swatch, the height of a comfortable-density `Input` or `Button`. Not\n * derived from the density in force, so a consumer that never passes it renders as it\n * always did.\n */\n size?: ButtonSize | undefined;\n /** Merged with the chip's own classes through `cn`. */\n className?: string | undefined;\n}) {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n data-state={checked ? \"checked\" : \"unchecked\"}\n disabled={disabled}\n title={title}\n onClick={() => onCheckedChange(!checked)}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-control border\",\n SIZE_CLASSES[size],\n \"whitespace-nowrap transition-colors\",\n checked ? \"border-line-strong bg-raised text-fg\" : \"border-line bg-transparent text-fg-muted hover:text-fg\",\n \"disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:text-fg-muted\",\n focusRing,\n className,\n )}\n >\n {swatch !== undefined && (\n <span\n aria-hidden\n className={cn(\n SWATCH_SIZE_CLASSES[size],\n \"shrink-0 rounded-full border transition-opacity\",\n checked ? \"opacity-100\" : \"opacity-30\",\n )}\n style={{ backgroundColor: swatch, borderColor: swatch }}\n />\n )}\n {children}\n </button>\n );\n}\n","/*\n * Notifications, as a store any module can write to.\n *\n * A toast is raised from wherever the news is — a file loader, a worker's error handler, a\n * bake that finished — which is rarely a component with a context in reach. So `toast()` is\n * a plain function over a module-level store, and `<Toaster />`, mounted once, is its only\n * reader. The store holds no timers and touches no DOM: auto-dismissal belongs to the\n * `Toaster`, which is what knows whether a reader is hovering it.\n */\n\n/** A toast's kind: a fact (`info`), a completed action (`success`), a caveat (`warn`), a failure (`error`). */\nexport type ToastTone = \"info\" | \"success\" | \"warn\" | \"error\";\n\n/** What `toast()` takes. */\nexport interface ToastOptions {\n /** One line: what happened. */\n title: string;\n /** More detail, and what to do about it. */\n description?: string | undefined;\n /** Defaults to `info`. */\n tone?: ToastTone | undefined;\n /**\n * How long it stays, in milliseconds, while not hovered or focused. Defaults to 5000\n * (8000 for `error`); `Infinity` keeps it until dismissed.\n */\n duration?: number | undefined;\n /** Replace the toast with this id instead of adding one — for progress that updates in place. */\n id?: string | undefined;\n}\n\n/** A toast in the store. */\nexport interface ToastRecord {\n /** Identifies the toast to `dismiss` and to `id` replacement. */\n id: string;\n /** One line: what happened. */\n title: string;\n /** More detail, if any. */\n description: string | undefined;\n /** Its kind. */\n tone: ToastTone;\n /** Milliseconds before it dismisses itself (`Infinity`: never). */\n duration: number;\n}\n\n/**\n * A toast store: what `toast()` writes to and `<Toaster />` reads. Every member may be called\n * detached.\n */\nexport interface ToastStore {\n /** Add a toast (or replace the one with `options.id`), returning its id. */\n readonly toast: (options: ToastOptions) => string;\n /** Remove one toast, or every toast when `id` is omitted. */\n readonly dismiss: (id?: string) => void;\n /** The toasts, oldest first. The same array until the store changes. */\n readonly getSnapshot: () => readonly ToastRecord[];\n /** Be told when the toasts change; returns the unsubscribe function. */\n readonly subscribe: (listener: () => void) => () => void;\n}\n\nconst DEFAULT_DURATION = 5000;\nconst ERROR_DURATION = 8000;\n\n/**\n * A new, empty toast store. The package keeps one (behind `toast()` and the default\n * `<Toaster />`); create another only to isolate a region or a test.\n *\n * @returns The store.\n */\nexport function createToastStore(): ToastStore {\n let toasts: readonly ToastRecord[] = [];\n let sequence = 0;\n const listeners = new Set<() => void>();\n const commit = (next: readonly ToastRecord[]) => {\n toasts = next;\n for (const listener of [...listeners]) listener();\n };\n\n return {\n toast: (options) => {\n const tone = options.tone ?? \"info\";\n sequence += 1;\n const record: ToastRecord = {\n id: options.id ?? `toast-${sequence}`,\n title: options.title,\n description: options.description,\n tone,\n duration: options.duration ?? (tone === \"error\" ? ERROR_DURATION : DEFAULT_DURATION),\n };\n const existing = toasts.findIndex((t) => t.id === record.id);\n commit(existing === -1 ? [...toasts, record] : toasts.map((t, i) => (i === existing ? record : t)));\n return record.id;\n },\n dismiss: (id) => {\n if (id === undefined) {\n if (toasts.length > 0) commit([]);\n return;\n }\n if (toasts.some((t) => t.id === id)) commit(toasts.filter((t) => t.id !== id));\n },\n getSnapshot: () => toasts,\n subscribe: (listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\n/** The store behind `toast()` and a `<Toaster />` given no `store`. */\nexport const defaultToastStore: ToastStore = createToastStore();\n\n/**\n * Raise a notification, from anywhere — shown by the mounted `<Toaster />`.\n *\n * `toast({ title: \"Scenario loaded\", tone: \"success\" })`. Pass `id` to update a toast in\n * place (a progress message that becomes a result). `toast.dismiss(id)` removes one early,\n * `toast.dismiss()` all of them.\n *\n * @param options - The message, its tone and its duration.\n * @returns The toast's id.\n */\nexport const toast: ((options: ToastOptions) => string) & {\n /** Remove one toast, or every toast when `id` is omitted. */\n dismiss: (id?: string) => void;\n} = Object.assign((options: ToastOptions) => defaultToastStore.toast(options), {\n dismiss: (id?: string) => defaultToastStore.dismiss(id),\n});\n","/*\n * Where `toast()` shows up.\n *\n * Toasts are for news that does not need an answer — a file loaded, a bake finished, a\n * worker failed in the background. Anything that must be acknowledged is a `Dialog`, and\n * anything tied to one control is that control's `Field` error. So a toast never carries an\n * action beyond dismissing it, it goes away by itself, and it holds still while someone is\n * reading it (pointer over the stack, or focus inside it).\n *\n * The tones reuse the `Callout` vocabulary — the accent for a fact, the verdict colours for\n * success, caveat and failure — on an opaque `overlay` surface, since a toast floats over\n * whatever the viewport is drawing.\n */\n\nimport { CircleCheck, CircleX, Info, TriangleAlert, X } from \"lucide-react\";\nimport { useEffect, useRef, useState, useSyncExternalStore } from \"react\";\nimport type { FocusEvent } from \"react\";\n\nimport { cn, focusRing } from \"./cn\";\nimport { defaultToastStore, type ToastRecord, type ToastStore, type ToastTone } from \"./toastStore\";\n\nconst TONE: Record<ToastTone, { border: string; icon: string; Icon: typeof Info }> = {\n info: { border: \"border-signal/30\", icon: \"text-signal\", Icon: Info },\n success: { border: \"border-normal/30\", icon: \"text-normal\", Icon: CircleCheck },\n warn: { border: \"border-warn/30\", icon: \"text-warn\", Icon: TriangleAlert },\n error: { border: \"border-defect/30\", icon: \"text-defect\", Icon: CircleX },\n};\n\nconst NO_TOASTS: readonly ToastRecord[] = [];\n\n/** Props of `Toaster`. */\nexport interface ToasterProps {\n /** The store to show. Defaults to the one behind `toast()`. */\n store?: ToastStore | undefined;\n /** How many toasts are shown at once; older ones wait. Defaults to 5. */\n limit?: number | undefined;\n /** Merged with the stack's own classes through `cn` (it is fixed to the bottom-right corner). */\n className?: string | undefined;\n}\n\n/**\n * The stack of notifications raised with `toast()`. Mount it once, near the root.\n *\n * Fixed to the bottom-right corner, newest at the bottom. Each toast dismisses itself after\n * its `duration`; the countdown pauses while the pointer is over the stack or focus is inside\n * it. A `section` landmark (\"Notifications\") holding a polite live region, so new toasts are\n * announced; an `error` toast is a `role=\"alert\"`, announced at once. Each toast carries\n * `data-tone` and a \"Dismiss\" button.\n */\nexport function Toaster({ store = defaultToastStore, limit = 5, className }: ToasterProps) {\n const toasts = useSyncExternalStore(store.subscribe, store.getSnapshot, () => NO_TOASTS);\n const [hovered, setHovered] = useState(false);\n const [focused, setFocused] = useState(false);\n const shown = toasts.slice(-Math.max(1, limit));\n\n const onBlur = (event: FocusEvent<HTMLElement>) => {\n if (!event.currentTarget.contains(event.relatedTarget)) setFocused(false);\n };\n\n return (\n <section\n aria-label=\"Notifications\"\n onPointerEnter={() => setHovered(true)}\n onPointerLeave={() => setHovered(false)}\n onFocus={() => setFocused(true)}\n onBlur={onBlur}\n className={cn(\"pointer-events-none fixed right-4 bottom-4 z-50 w-80 max-w-[calc(100vw-2rem)]\", className)}\n >\n <ol aria-live=\"polite\" className=\"flex flex-col gap-2\">\n {shown.map((record) => (\n <ToastItem key={record.id} record={record} paused={hovered || focused} onDismiss={() => store.dismiss(record.id)} />\n ))}\n </ol>\n </section>\n );\n}\n\nfunction ToastItem({ record, paused, onDismiss }: { record: ToastRecord; paused: boolean; onDismiss: () => void }) {\n const { border, icon, Icon } = TONE[record.tone];\n // What is left of the countdown, carried across pauses; restarted when the toast is\n // replaced in place (a new record under the same id).\n const remainingRef = useRef({ record, ms: record.duration });\n const dismissRef = useRef(onDismiss);\n useEffect(() => {\n dismissRef.current = onDismiss;\n });\n\n useEffect(() => {\n if (remainingRef.current.record !== record) remainingRef.current = { record, ms: record.duration };\n if (paused || !Number.isFinite(remainingRef.current.ms)) return;\n const started = Date.now();\n const timer = setTimeout(() => dismissRef.current(), remainingRef.current.ms);\n return () => {\n clearTimeout(timer);\n remainingRef.current.ms = Math.max(0, remainingRef.current.ms - (Date.now() - started));\n };\n }, [paused, record]);\n\n return (\n // The `alert` role sits on the toast inside the list item, not on the `<li>`: an item with\n // another role is no longer a list item, and the list would be malformed.\n <li className=\"pointer-events-auto\">\n <div\n role={record.tone === \"error\" ? \"alert\" : undefined}\n data-tone={record.tone}\n className={cn(\n \"flex items-start gap-2.5 rounded-panel border bg-overlay px-3 py-2.5 text-sm leading-snug shadow-lg shadow-black/25\",\n \"transition-[opacity,translate] duration-200 starting:translate-y-2 starting:opacity-0\",\n border,\n )}\n >\n <Icon className={cn(\"mt-0.5 size-4 shrink-0\", icon)} aria-hidden />\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <p className=\"font-medium text-fg\">{record.title}</p>\n {record.description && <p className=\"text-fg-muted\">{record.description}</p>}\n </div>\n <button\n type=\"button\"\n aria-label=\"Dismiss\"\n title=\"Dismiss\"\n onClick={onDismiss}\n className={cn(\n \"-mr-1 grid size-6 shrink-0 place-items-center rounded-control text-fg-subtle transition-colors hover:bg-raised hover:text-fg\",\n focusRing,\n )}\n >\n <X className=\"size-3.5\" aria-hidden />\n </button>\n </div>\n </li>\n );\n}\n","/*\n * A tab strip.\n *\n * The count is set in mono beside the label rather than in parentheses inside it: it is a\n * quantity -- how many overrides sit behind an options tab, how many samples a filter chip\n * covers -- and mono makes it read as one at a glance.\n */\n\nimport type { KeyboardEvent } from \"react\";\n\nimport { cn, focusRing } from \"./ui/cn\";\n\n/** One tab of a `Tabs` strip. */\nexport interface TabItem<Id extends string> {\n /** Identifies the tab to `active` and `onSelect`. Unique within the strip. */\n id: Id;\n /** The tab's text, and its accessible name. */\n label: string;\n /** Shown after the label, for the chips that carry a row or override count. */\n count?: number;\n /** Shown but not selectable, and skipped by the arrow keys. */\n disabled?: boolean;\n /** Why this tab has nothing to show, for a reader wondering what they missed. */\n title?: string;\n}\n\nconst NEXT_KEYS: Record<string, (index: number, length: number) => number> = {\n ArrowRight: (index, length) => (index + 1) % length,\n ArrowLeft: (index, length) => (index - 1 + length) % length,\n Home: () => 0,\n End: (_, length) => length - 1,\n};\n\n/**\n * A tab strip (`role=\"tablist\"`): the caller renders the panel for the `active` tab.\n *\n * Keyboard: one tab stop for the whole strip (the active tab); ←/→ move to the previous or\n * next enabled tab and select it, Home/End to the first and last. Each tab carries\n * `data-state` (`active`/`inactive`). Pass `idPrefix` to wire the tabs to the panel: the tab\n * for `id` gets the element id `<idPrefix>-tab-<id>` and the active tab gets\n * `aria-controls=\"<idPrefix>-panel-<id>\"`, so render the panel as a `role=\"tabpanel\"` element\n * with the id `<idPrefix>-panel-<active>` and `aria-labelledby=\"<idPrefix>-tab-<active>\"`.\n */\nexport function Tabs<Id extends string>({\n items,\n active,\n onSelect,\n label,\n idPrefix,\n className,\n}: {\n /** The tabs, in order. */\n items: TabItem<Id>[];\n /** The selected tab's id. */\n active: Id;\n /** Called with a tab's id when it is clicked or reached with the arrow keys. */\n onSelect: (id: Id) => void;\n /** Names the tab list. */\n label: string;\n /** Prefix for the tab and panel element ids; enables `aria-controls` (see above). */\n idPrefix?: string | undefined;\n /** Merged with the strip's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const enabled = items.filter((item) => !item.disabled);\n // The one tab stop: the active tab, or the first enabled one when the active tab is\n // disabled or not in the list.\n const focusable = enabled.some((item) => item.id === active) ? active : enabled[0]?.id;\n\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n const move = NEXT_KEYS[event.key];\n const target = event.target as HTMLElement;\n const current = enabled.findIndex((item) => item.id === target.dataset[\"tabId\"]);\n if (move === undefined || current === -1) return;\n event.preventDefault();\n const next = enabled[move(current, enabled.length)];\n if (next === undefined) return;\n const tabs = event.currentTarget.querySelectorAll<HTMLElement>('[role=\"tab\"]');\n for (const tab of tabs) if (tab.dataset[\"tabId\"] === next.id) tab.focus();\n if (next.id !== active) onSelect(next.id);\n };\n\n return (\n <div\n role=\"tablist\"\n aria-label={label}\n aria-orientation=\"horizontal\"\n onKeyDown={onKeyDown}\n className={cn(\"inline-flex max-w-full flex-wrap items-center gap-0.5 rounded-control border border-line bg-raised p-0.5\", className)}\n >\n {items.map((item) => {\n const selected = item.id === active;\n return (\n <button\n key={item.id}\n type=\"button\"\n role=\"tab\"\n id={idPrefix === undefined ? undefined : `${idPrefix}-tab-${item.id}`}\n aria-selected={selected}\n aria-controls={idPrefix !== undefined && selected ? `${idPrefix}-panel-${item.id}` : undefined}\n tabIndex={item.id === focusable ? 0 : -1}\n data-tab-id={item.id}\n data-state={selected ? \"active\" : \"inactive\"}\n disabled={item.disabled}\n title={item.title}\n onClick={() => onSelect(item.id)}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-[0.3rem] px-2.5 py-1 text-xs font-medium transition-colors\",\n \"disabled:cursor-not-allowed disabled:opacity-40\",\n selected ? \"bg-surface text-fg shadow-sm\" : \"text-fg-muted hover:text-fg\",\n focusRing,\n )}\n >\n {item.label}\n {item.count !== undefined && item.count > 0 && (\n <span\n className={cn(\n \"rounded-full px-1.5 font-mono text-[10px] tabular-nums\",\n selected ? \"bg-signal/15 text-signal\" : \"bg-line text-fg-muted\",\n )}\n >\n {item.count}\n </span>\n )}\n </button>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["TONE_CLASSES","count","SIZE_CLASSES"],"sources":["../src/theme.ts","../src/tone.ts","../src/components/ui/cn.ts","../src/components/ui/Tooltip.tsx","../src/components/ThemeToggle.tsx","../src/components/ui/Badge.tsx","../src/components/ui/Density.tsx","../src/components/ui/Breakdown.tsx","../src/components/ui/Button.tsx","../src/components/ui/Dialog.tsx","../src/components/ui/Disclosure.tsx","../src/components/ui/Kbd.tsx","../src/components/ui/DropdownMenu.tsx","../src/components/ui/Feedback.tsx","../src/components/ui/Field.tsx","../src/components/ui/highlightSpans.ts","../src/components/ui/HighlightedText.tsx","../src/components/ui/numberText.ts","../src/components/ui/Input.tsx","../src/components/ui/listboxModel.ts","../src/components/ui/Listbox.tsx","../src/components/ui/Panel.tsx","../src/components/ui/Progress.tsx","../src/components/ui/Popover.tsx","../src/components/ui/VectorInput.tsx","../src/components/ui/PoseInput.tsx","../src/components/ui/SegmentedControl.tsx","../src/components/ui/Select.tsx","../src/components/ui/Slider.tsx","../src/components/ui/tableSort.ts","../src/components/ui/tableWindow.ts","../src/components/ui/Table.tsx","../src/components/ui/Textarea.tsx","../src/components/ui/Toggle.tsx","../src/components/ui/ToggleChip.tsx","../src/components/ui/toastStore.ts","../src/components/ui/Toaster.tsx","../src/components/Tabs.tsx"],"sourcesContent":["/*\n * Which of the two palettes is on screen.\n *\n * Judging an anomaly map, or a measurement overlay drawn over a low-contrast part, against\n * a bright room and against a dark one are different tasks, and a researcher switches\n * between them within a session -- so dark mode is a real, storable choice here rather than\n * only `prefers-color-scheme`.\n *\n * Three states, not two. \"system\" is a real choice and has to survive a reload as itself,\n * otherwise the first time the OS flips at sunset the app disagrees with everything else on\n * the machine and there is no way back to following it.\n *\n * The class is applied to <html> rather than <body> so the page background painted before\n * React mounts is already correct. `applyTheme` (below, as `paint`) has to be duplicated as\n * an inline script in the consumer's `index.html` for exactly that reason -- see the\n * package README for the exact snippet, and keep it in agreement with this module if either\n * changes.\n */\n\n/**\n * A stored theme preference: a palette, or `system` to follow the OS\n * (`prefers-color-scheme`) and keep following it.\n */\nexport type ThemeChoice = \"light\" | \"dark\" | \"system\";\n\n/**\n * The `localStorage` key a consumer's theme choice is kept under.\n *\n * Every function here takes it as an optional last argument and defaults to this constant,\n * so a single app needs to pass nothing, while two apps sharing a browser profile -- or one\n * app that wants a namespaced key -- can each choose their own without stepping on the\n * other's stored preference.\n */\nexport const DEFAULT_THEME_STORAGE_KEY = \"vitavision-theme\";\n\n// Every DOM access here is inside a function, and guarded: this module is imported on the\n// server too.\nconst query = () =>\n typeof window !== \"undefined\" && typeof window.matchMedia === \"function\" ? window.matchMedia(\"(prefers-color-scheme: dark)\") : null;\n\n/**\n * The stored preference.\n *\n * @param storageKey - The `localStorage` key. Defaults to `DEFAULT_THEME_STORAGE_KEY`.\n * @returns The stored choice, or `system` when nothing valid is stored or storage cannot be\n * read (private browsing, the server).\n */\nexport function readThemeChoice(storageKey: string = DEFAULT_THEME_STORAGE_KEY): ThemeChoice {\n try {\n const stored = window.localStorage.getItem(storageKey);\n if (stored === \"light\" || stored === \"dark\" || stored === \"system\") return stored;\n } catch {\n // Private browsing, or a WebView with storage disabled. Following the OS is a fine\n // answer to \"I could not read your preference\", and better than refusing to render.\n }\n return \"system\";\n}\n\n/**\n * What `choice` actually resolves to right now.\n *\n * @param choice - The preference.\n * @returns `choice` itself for `light`/`dark`; for `system`, the OS palette (`light` where\n * it cannot be queried).\n */\nexport function resolveTheme(choice: ThemeChoice): \"light\" | \"dark\" {\n if (choice !== \"system\") return choice;\n return query()?.matches ? \"dark\" : \"light\";\n}\n\nfunction paint(choice: ThemeChoice): void {\n if (typeof document === \"undefined\") return;\n document.documentElement.classList.toggle(\"dark\", resolveTheme(choice) === \"dark\");\n}\n\n/**\n * Store a preference and paint it (the `dark` class on `<html>`).\n *\n * @param choice - The new preference.\n * @param storageKey - The `localStorage` key. Defaults to `DEFAULT_THEME_STORAGE_KEY`.\n */\nexport function setThemeChoice(choice: ThemeChoice, storageKey: string = DEFAULT_THEME_STORAGE_KEY): void {\n try {\n window.localStorage.setItem(storageKey, choice);\n } catch {\n // Not worth failing the click over; the choice still applies for this session.\n }\n paint(choice);\n}\n\n/**\n * Paint the stored choice and keep following the OS while the choice is \"system\".\n *\n * @param storageKey - The `localStorage` key. Defaults to `DEFAULT_THEME_STORAGE_KEY`.\n * @returns An unsubscribe, so a test can tear the listener down; a long-lived app never does.\n */\nexport function initTheme(storageKey: string = DEFAULT_THEME_STORAGE_KEY): () => void {\n paint(readThemeChoice(storageKey));\n\n const media = query();\n if (media === null) return () => {};\n\n const onSystemChange = () => {\n if (readThemeChoice(storageKey) === \"system\") paint(\"system\");\n };\n media.addEventListener(\"change\", onSystemChange);\n return () => media.removeEventListener(\"change\", onSystemChange);\n}\n","/*\n * The verdict vocabulary, for the components that paint outside of `className`.\n *\n * Every Tailwind-driven primitive under `components/ui` reaches the palette through a\n * class name, which only works where Tailwind's own build sees the class. `MeasureOverlay`\n * and `LineProfile` paint through raw SVG `stroke`/`fill` attributes instead — a caliper\n * box or an edge mark is drawn from data the consumer supplies at runtime, not from a\n * fixed set of class names a bundler can scan for. So these two read the same five tokens\n * `styles.css` defines (`--signal`, `--normal`, `--defect`, `--warn`, `--fg-subtle`)\n * directly as CSS custom properties, which resolves correctly in both themes without\n * needing Tailwind involved in the overlay's own render path at all.\n */\n\n/**\n * A paint for SVG overlays and charts: the accent (`signal`), a verdict colour, or `muted`.\n *\n * Named `MeasureTone` rather than `Tone` — `components/ui/Badge.tsx` already owns that name\n * for the verdict-badge vocabulary (`neutral`/`normal`/`defect`/`unlabeled`/`warning`/\n * `info`), and both are re-exported from the package root, where two types of the same name\n * would collide.\n */\nexport type MeasureTone = \"signal\" | \"normal\" | \"defect\" | \"warn\" | \"muted\";\n\nconst TONE_VAR: Record<MeasureTone, string> = {\n signal: \"var(--signal)\",\n normal: \"var(--normal)\",\n defect: \"var(--defect)\",\n warn: \"var(--warn)\",\n muted: \"var(--fg-subtle)\",\n};\n\n/**\n * The CSS paint value for a tone — usable directly as a `stroke`, `fill` or `color`. It is a\n * `var(--token)` reference, so it follows the theme without a re-render.\n *\n * @param tone - The tone; `undefined` falls back.\n * @param fallback - The tone used when `tone` is `undefined`. Defaults to `signal`.\n * @returns A `var(--…)` CSS value.\n */\nexport function toneColor(tone: MeasureTone | undefined, fallback: MeasureTone = \"signal\"): string {\n return TONE_VAR[tone ?? fallback];\n}\n","/**\n * Class composition, and the shared focus treatment.\n *\n * A naive primitive *appends* the caller's `className` to its own base string, so\n * `<Button className=\"px-6\">` produces `px-3 … px-6` and wins or loses on source order\n * rather than on intent. `twMerge` resolves that by conflict group: the caller's `px-6`\n * replaces the base `px-3` and leaves everything else alone.\n */\n\nimport { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Compose class names: `clsx` for the conditionals, then `tailwind-merge` so a later class\n * replaces an earlier one of the same conflict group (the caller's `px-6` beats the base\n * `px-3`). Every primitive merges its caller's `className` through this.\n *\n * @param inputs - Class values: strings, arrays, and `{ class: condition }` records.\n * @returns One class string with conflicts resolved.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n\n/**\n * One focus treatment for every interactive element in the design system.\n *\n * `outline` rather than `ring` because an outline follows the element's own border-radius\n * and needs no offset *colour* -- a ring offset has to know which surface it sits on, and\n * these primitives are used on `ground`, `surface` and `raised` alike.\n *\n * `focus-visible` rather than `focus`, so a mouse click does not leave a ring behind.\n */\nexport const focusRing = \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-signal\";\n\n/**\n * The focus treatment for controls whose own box is the hit target, where an offset ring\n * would clip: the same outline as {@link focusRing}, drawn with no offset.\n */\nexport const focusRingInset = \"focus-visible:outline-2 focus-visible:outline-offset-0 focus-visible:outline-signal\";\n","/*\n * A short explanation, on demand.\n *\n * Replaces the native `title` attribute, which appears after an unpredictable delay, in the\n * OS palette, at a size nobody chose, and never at all for a keyboard user.\n *\n * A tooltip is for text that helps but is not needed to operate the control. Anything a\n * reader must have in order to answer the question belongs in the field's description,\n * where it is always visible.\n */\n\nimport * as RadixTooltip from \"@radix-ui/react-tooltip\";\nimport { HelpCircle, type LucideIcon } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\n\nimport { cn, focusRing } from \"./cn\";\n\n/**\n * Shares one open delay (200 ms) across the tooltips below it. Mount it once near the root:\n * `Tooltip` and `InfoHint` need it above them.\n */\nexport function TooltipProvider({\n children,\n}: {\n /** The app, or the region that uses tooltips. */\n children: ReactNode;\n}) {\n return <RadixTooltip.Provider delayDuration={200}>{children}</RadixTooltip.Provider>;\n}\n\n/**\n * A short explanation on hover or focus of its child, for text that helps but is not needed\n * to operate the control (that belongs in a field's description).\n *\n * The child must be one focusable element that accepts a ref (Radix `asChild`). Radix\n * exposes `data-state` (`closed`/`delayed-open`/`instant-open`) on it.\n */\nexport function Tooltip({\n content,\n children,\n}: {\n /** The explanation. */\n content: ReactNode;\n /** The one element it explains. */\n children: ReactNode;\n}) {\n return (\n <RadixTooltip.Root>\n <RadixTooltip.Trigger asChild>{children}</RadixTooltip.Trigger>\n <RadixTooltip.Portal>\n <RadixTooltip.Content\n sideOffset={6}\n collisionPadding={8}\n className={cn(\n \"z-50 max-w-xs rounded-control border border-line bg-overlay px-2.5 py-1.5\",\n \"text-xs leading-snug text-fg shadow-lg shadow-black/25\",\n )}\n >\n {content}\n <RadixTooltip.Arrow className=\"fill-overlay\" />\n </RadixTooltip.Content>\n </RadixTooltip.Portal>\n </RadixTooltip.Root>\n );\n}\n\n/**\n * The standard affordance: a quiet mark that yields the explanation on hover or focus.\n *\n * `icon` exists because the mark answers two different questions. The default `?` offers\n * help with a control; `Info` offers *facts about the thing on screen* -- where a dataset\n * came from, which method produced a measurement -- which the tone demotes here rather\n * than spending a permanent line on. Reading them as the same affordance would be a small\n * lie.\n */\nexport function InfoHint({\n children,\n label = \"More information\",\n icon: Icon = HelpCircle,\n}: {\n /** The explanation shown on hover or focus. */\n children: ReactNode;\n /** The button's accessible name. Defaults to \"More information\". */\n label?: string | undefined;\n /** The mark: `HelpCircle` (the default) for help, `Info` for facts about the thing. */\n icon?: LucideIcon | undefined;\n}) {\n return (\n <Tooltip content={children}>\n <button type=\"button\" aria-label={label} className={cn(\"rounded-full text-fg-subtle transition-colors hover:text-fg\", focusRing)}>\n <Icon className=\"size-3.5\" />\n </button>\n </Tooltip>\n );\n}\n","/*\n * The palette switch.\n *\n * Three states, not two, and cycled by one button rather than spread across a\n * segmented control: this sits in a header next to the thing the app is\n * actually about, and a control that costs one glyph is the right size for a\n * preference that is set once a session. \"System\" is a real state — an app that\n * silently stops following the OS the first time you touch the switch has taken\n * something away.\n *\n * One shared control rather than a copy per app: two copies of a control that\n * writes to `localStorage` is two chances to disagree about the key, the cycle\n * order, or which icon means which state.\n *\n * `storageKey` is per app on purpose — `theme.ts` takes the key as an argument\n * for the same reason. Two apps sharing a browser profile each keep their own\n * preference, and an app that already had one under its own key does not lose\n * it by adopting this component.\n */\n\nimport { Moon, Sun, SunMoon } from \"lucide-react\";\nimport { useState, useSyncExternalStore } from \"react\";\n\nimport { DEFAULT_THEME_STORAGE_KEY, readThemeChoice, setThemeChoice, type ThemeChoice } from \"../theme\";\nimport { Tooltip } from \"./ui/Tooltip\";\nimport { cn, focusRing } from \"./ui/cn\";\n\nconst ORDER: ThemeChoice[] = [\"system\", \"light\", \"dark\"];\nconst ICON = { system: SunMoon, light: Sun, dark: Moon };\nconst LABEL: Record<ThemeChoice, string> = {\n system: \"Theme: following the system\",\n light: \"Theme: light\",\n dark: \"Theme: dark\",\n};\n\n/** Another tab (or window) changing the stored choice re-renders the toggle. */\nfunction subscribe(onChange: () => void): () => void {\n window.addEventListener(\"storage\", onChange);\n return () => window.removeEventListener(\"storage\", onChange);\n}\n\n/** What the server, and a hydrating client's first render, show: no stored choice read yet. */\nconst serverChoice = (): ThemeChoice => \"system\";\n\n/**\n * The palette switch: one button cycling system → light → dark, stored in `localStorage`\n * under `storageKey` and painted as the `dark` class on `<html>`.\n *\n * Its `aria-label` (repeated in a tooltip) names the current state; it needs a\n * `TooltipProvider` above it. The stored choice is read as an external store, so the server\n * render and the hydrating first client render agree (\"system\") and the stored choice\n * follows without a mismatch. The state is exposed as `data-theme-choice`.\n */\nexport function ThemeToggle({\n storageKey = DEFAULT_THEME_STORAGE_KEY,\n className,\n}: {\n /** The `localStorage` key; per app, so two apps sharing a profile keep their own choice. */\n storageKey?: string | undefined;\n /** Merged with the button's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const stored = useSyncExternalStore(subscribe, () => readThemeChoice(storageKey), serverChoice);\n // What this toggle last set, so it keeps cycling even where storage is unavailable and the\n // stored value cannot follow.\n const [chosen, setChosen] = useState<{ key: string; choice: ThemeChoice } | null>(null);\n const choice = chosen?.key === storageKey ? chosen.choice : stored;\n\n const advance = () => {\n const next = ORDER[(ORDER.indexOf(choice) + 1) % ORDER.length]!;\n setChosen({ key: storageKey, choice: next });\n setThemeChoice(next, storageKey);\n };\n\n const Icon = ICON[choice];\n\n return (\n <Tooltip content={LABEL[choice]}>\n <button\n type=\"button\"\n onClick={advance}\n aria-label={LABEL[choice]}\n data-theme-choice={choice}\n className={cn(\"rounded-control p-1.5 text-fg-muted transition-colors hover:bg-raised hover:text-fg\", focusRing, className)}\n >\n <Icon className=\"size-4\" />\n </button>\n </Tooltip>\n );\n}\n","/*\n * Status colour, in one place.\n *\n * The tone vocabulary is deliberately small and deliberately *about verdicts*. The chrome\n * carries no saturation, so a colour on screen means something: green is a normal sample\n * (or an in-tolerance measurement), red is a defect (or an out-of-tolerance one), amber is\n * a caveat. `info` is the one tone that borrows the interaction accent, and it is reserved\n * for statements of fact about a method -- \"produces anomaly maps\", \"caliper axis fixed\" --\n * rather than for anything a reader must act on.\n */\n\nimport type { ReactNode } from \"react\";\n\nimport { cn } from \"./cn\";\n\n/**\n * The badge and status-dot vocabulary: the verdicts (`normal`, `defect`, `warning`),\n * `unlabeled` for a sample with no verdict yet, `info` for a statement of fact about a\n * method, and `neutral` for everything else.\n */\nexport type Tone = \"neutral\" | \"normal\" | \"defect\" | \"unlabeled\" | \"warning\" | \"info\";\n\nconst TONE_CLASSES: Record<Tone, string> = {\n neutral: \"bg-raised text-fg-muted ring-line\",\n normal: \"bg-normal/12 text-normal ring-normal/25\",\n defect: \"bg-defect/12 text-defect ring-defect/25\",\n unlabeled: \"bg-raised text-fg-subtle ring-line\",\n warning: \"bg-warn/12 text-warn ring-warn/25\",\n info: \"bg-signal/12 text-signal ring-signal/25\",\n};\n\nconst DOT_CLASSES: Record<Tone, string> = {\n neutral: \"bg-fg-subtle\",\n normal: \"bg-normal\",\n defect: \"bg-defect\",\n unlabeled: \"bg-line-strong\",\n warning: \"bg-warn\",\n info: \"bg-signal\",\n};\n\n/**\n * A short label in a tone: a verdict, a status, a count.\n *\n * The tone is exposed as `data-tone`, so a stylesheet or a test can read it without parsing\n * class names.\n */\nexport function Badge({\n tone = \"neutral\",\n className,\n children,\n}: {\n /** Which colour, by meaning. Defaults to `neutral`. */\n tone?: Tone | undefined;\n /** Merged with the badge's own classes through `cn`. */\n className?: string | undefined;\n /** The label. */\n children: ReactNode;\n}) {\n return (\n <span\n data-tone={tone}\n className={cn(\n \"inline-flex items-center gap-1 rounded-control px-1.5 py-0.5 text-xs font-medium ring-1 ring-inset\",\n TONE_CLASSES[tone],\n className,\n )}\n >\n {children}\n </span>\n );\n}\n\n/**\n * A status as a dot and a word.\n *\n * The mapping from a domain status to a tone belongs to each caller -- only it knows\n * whether \"running\" is good news -- but the *rendering* of the answer does not.\n */\nexport function StatusDot({\n tone,\n className,\n children,\n}: {\n /** The status, as a tone; also exposed as `data-tone`. */\n tone: Tone;\n /** Merged with the row's own classes through `cn`. */\n className?: string | undefined;\n /** The word beside the dot. The dot alone is decoration: say the status in words. */\n children?: ReactNode;\n}) {\n return (\n <span data-tone={tone} className={cn(\"inline-flex items-center gap-1.5 text-xs text-fg-muted\", className)}>\n <span className={cn(\"size-1.5 shrink-0 rounded-full\", DOT_CLASSES[tone])} aria-hidden />\n {children}\n </span>\n );\n}\n\n/** Counts as a compact `12 normal · 9 defect` run of badges, skipping the zeroes. */\nexport function CountRun({\n counts,\n className,\n}: {\n /** `[name, count, tone]` triples, in display order. Zero counts are dropped. */\n counts: [string, number, Tone][];\n /** Merged with the run's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const shown = counts.filter(([, value]) => value > 0);\n if (shown.length === 0) {\n return <span className={cn(\"text-xs text-fg-subtle\", className)}>empty</span>;\n }\n return (\n <span className={cn(\"flex flex-wrap items-center gap-1.5\", className)}>\n {shown.map(([name, value, tone]) => (\n <Badge key={name} tone={tone}>\n <span className=\"font-mono\">{value}</span> {name}\n </Badge>\n ))}\n </span>\n );\n}\n","/*\n * How much room the chrome takes, as one decision per region rather than per component.\n *\n * The default spacing is written for a page: a form you read through once, where generous\n * padding is what makes it legible. An inspector column beside a canvas is the opposite\n * problem — it is a permanent surface that accumulates tools, and there the same padding is\n * simply less of the instrument on screen. Nesting compounds it: a panel body's `p-4`\n * inside a section's `gap-3` inside a column's own padding is three margins deep before any\n * control appears.\n *\n * `compact` is not \"the same design, smaller\". It drops the leading and the padding a\n * reading surface needs and keeps the type sizes and hit targets a control needs, so a\n * dense column stays clickable. Below this there is no honest saving left — the next step\n * down is a control too small to hit.\n *\n * The default is `comfortable`, so a consumer that never mentions density gets exactly what\n * it had.\n */\n\nimport { createContext, use } from \"react\";\nimport type { ReactNode } from \"react\";\n\n/**\n * How much room the chrome takes: `comfortable` (the default, for a page you read through)\n * or `compact` (for a permanent tool surface such as an inspector column).\n */\nexport type Density = \"comfortable\" | \"compact\";\n\nconst DensityContext = createContext<Density>(\"comfortable\");\n\n/**\n * The density in force here. Every primitive that has two spacings reads this.\n *\n * @returns The value of the nearest `DensityProvider`, or `comfortable` outside one.\n */\nexport function useDensity(): Density {\n return use(DensityContext);\n}\n\n/**\n * Pick one of two values by the density in force — the primitives' own idiom.\n *\n * @param density - The density in force, from `useDensity()`.\n * @param comfortable - The value for `comfortable`.\n * @param compact - The value for `compact`.\n * @returns `compact` when the density is compact, else `comfortable`.\n */\nexport function byDensity<T>(density: Density, comfortable: T, compact: T): T {\n return density === \"compact\" ? compact : comfortable;\n}\n\n/**\n * Sets the density for a region: every primitive below it reads the value through\n * `useDensity`. Nest providers to switch a sub-region back.\n */\nexport function DensityProvider({\n value,\n children,\n}: {\n /** The density for everything below. */\n value: Density;\n /** The region. */\n children: ReactNode;\n}) {\n return <DensityContext value={value}>{children}</DensityContext>;\n}\n","/*\n * Counts by category, as one thin stacked bar.\n *\n * The segments may use the verdict colours because each one counts things the app has already\n * judged: strings it passed, strings it flagged, strings that failed a check. That is the only\n * licence. A share of a continuous quantity, or one series beside another, is not a verdict and\n * belongs on a chart.\n *\n * It is a `role=\"img\"`: the bar is one picture whose meaning is its name. `progressbar` and\n * `meter` carry a single value, and a `list` of segments would make a reader step through\n * parts of a glyph that is meant to be taken in at once. The name is the counts in words, so\n * colour is never the only cue.\n *\n * Widths are flex grow factors rather than percentages, so the one-pixel gaps that keep two\n * neighbouring colours apart come out of the bar and not off its end. The layout is inline\n * rather than classes, so the shares and the 2 px floor hold under any stylesheet.\n */\n\nimport type { ComponentProps } from \"react\";\n\nimport { byDensity, useDensity } from \"./Density\";\nimport { cn } from \"./cn\";\n\n/** Counts written the same on the server and in every browser: `2,556`, `0.5`. */\nconst count = new Intl.NumberFormat(\"en-US\", { maximumFractionDigits: 2 });\n\n/**\n * The colour of a `Breakdown` segment: a verdict (`normal`, `warn`, `defect`) for things the app\n * has judged, or `neutral` for a category that is no verdict (skipped, not applicable).\n */\nexport type BreakdownTone = \"normal\" | \"warn\" | \"defect\" | \"neutral\";\n\n/** One category of a `Breakdown`: how many, in which colour, and what a reader calls them. */\nexport interface BreakdownSegment {\n /** How many. Negative and non-finite values count as 0, and a segment of 0 is left out. */\n value: number;\n /** The colour, by meaning. */\n tone: BreakdownTone;\n /** What the count is, as it reads after the number: `\"done\"` gives \"412 done\". Unique among the segments. */\n label: string;\n}\n\n/** Options of `breakdownText`, the same as the `Breakdown` props of the same names. */\nexport interface BreakdownTextOptions {\n /** The whole. Defaults to the sum of the segments; a total below the sum counts as the sum. */\n total?: number | undefined;\n /** What the rest of the total is, as it reads after the number: `\"to do\"` gives \"131 to do\". */\n remainderLabel?: string | undefined;\n}\n\nconst TONE_CLASSES: Record<BreakdownTone, string> = {\n normal: \"bg-normal\",\n warn: \"bg-warn\",\n defect: \"bg-defect\",\n neutral: \"bg-fg-subtle\",\n};\n\n/** A count as drawn: 0 for anything that is not a finite, positive number. */\nconst amount = (value: number) => (Number.isFinite(value) && value > 0 ? value : 0);\n\n/** The segments that are drawn, the whole, and the rest of it. */\nfunction measure(segments: readonly BreakdownSegment[], total: number | undefined) {\n const shown = segments.filter((segment) => amount(segment.value) > 0);\n const sum = shown.reduce((acc, segment) => acc + amount(segment.value), 0);\n const whole = total !== undefined && Number.isFinite(total) && total > sum ? total : sum;\n return { shown, sum, whole, rest: whole - sum };\n}\n\n/**\n * The counts of a breakdown in words: `\"412 done, 20 to check, 3 problems, 131 to do of 566\"`.\n *\n * It is the default accessible name of `Breakdown`, and the text for a tooltip or a label printed\n * beside the bar. Segments of 0 are left out; the rest of the total is named only with a\n * `remainderLabel`. With nothing to count it reads `\"0 of 566\"`.\n *\n * @param segments - The categories, in order.\n * @param options - The `total` and the `remainderLabel`.\n * @returns The text.\n */\nexport function breakdownText(segments: readonly BreakdownSegment[], options: BreakdownTextOptions = {}): string {\n const { shown, whole, rest } = measure(segments, options.total);\n const parts = shown.map((segment) => `${count.format(amount(segment.value))} ${segment.label}`);\n if (options.remainderLabel !== undefined && rest > 0) parts.push(`${count.format(rest)} ${options.remainderLabel}`);\n return `${parts.length > 0 ? parts.join(\", \") : \"0\"} of ${count.format(whole)}`;\n}\n\n/** Props of `Breakdown`: the segments and their total, plus the `<div>` props. */\nexport type BreakdownProps = Omit<ComponentProps<\"div\">, \"children\" | \"role\"> & {\n /** The categories, drawn left to right in this order, each as wide as its share of the total. */\n segments: readonly BreakdownSegment[];\n /**\n * The whole. Defaults to the sum of the segments; the rest is drawn as empty track. A total\n * below the sum counts as the sum.\n */\n total?: number | undefined;\n /** What the empty track counts, for the accessible name: `\"to do\"` gives \"131 to do\". */\n remainderLabel?: string | undefined;\n};\n\n/**\n * A thin bar of counts by category: 412 done, 20 to check and 3 problems out of 566, with the\n * rest as empty track. Segments are drawn in the order given, each as wide as its share of the\n * total (a non-zero count is never narrower than 2 px); segments of 0 are left out.\n *\n * Its colours are verdicts, so use it only for things the app has judged — never for a share of\n * a continuous quantity or for series side by side (use `Progress` or a chart).\n *\n * A `role=\"img\"` whose name lists every non-zero count, as `breakdownText` writes it: \"412 done,\n * 20 to check, 3 problems, 131 to do of 566\". `aria-label` or `aria-labelledby` replaces it. The track\n * matches `Progress` and follows the density in force. State is exposed as `data-state`\n * (`\"empty\"` with nothing counted, `\"partial\"`, or `\"complete\"` when the segments fill the\n * total), and each segment's tone as its `data-tone`. Width changes ease over 300 ms, and not at\n * all with reduced motion.\n */\nexport function Breakdown({ segments, total, remainderLabel, className, \"aria-label\": ariaLabel, ...rest }: BreakdownProps) {\n const density = useDensity();\n const { shown, sum, rest: remainder } = measure(segments, total);\n const name = ariaLabel ?? (rest[\"aria-labelledby\"] ? undefined : breakdownText(segments, { total, remainderLabel }));\n return (\n <div\n role=\"img\"\n aria-label={name}\n data-state={sum === 0 ? \"empty\" : remainder > 0 ? \"partial\" : \"complete\"}\n {...rest}\n className={cn(\"w-full overflow-hidden rounded-full bg-line\", byDensity(density, \"h-1.5\", \"h-1\"), className)}\n style={{ display: \"flex\", gap: 1, ...rest.style }}\n >\n {shown.map((segment) => (\n <div\n key={segment.label}\n data-tone={segment.tone}\n className={cn(\"h-full transition-[flex-grow] duration-300\", TONE_CLASSES[segment.tone])}\n style={{ flex: `${amount(segment.value)} 0 0`, minWidth: 2 }}\n />\n ))}\n {remainder > 0 && <div className=\"h-full transition-[flex-grow] duration-300\" style={{ flex: `${remainder} 0 0` }} />}\n </div>\n );\n}\n","import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport { Loader2 } from \"lucide-react\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { byDensity, useDensity } from \"./Density\";\nimport { cn, focusRing } from \"./cn\";\n\n/**\n * A button's look, by what the action is: `primary` for the one action a screen is for,\n * `secondary` for the rest, `ghost` for toolbar actions, `danger` for an action that\n * destroys something.\n */\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"ghost\" | \"danger\";\n\n/**\n * A control's height and padding, shared by `Button`, `SegmentedControl` and `ToggleChip`. For a\n * button, `md` is the comfortable density's size and `sm` the compact one's.\n */\nexport type ButtonSize = \"sm\" | \"md\";\n\nconst VARIANT_CLASSES: Record<ButtonVariant, string> = {\n primary: \"bg-signal text-signal-fg hover:bg-signal-strong\",\n secondary: \"bg-surface text-fg ring-1 ring-inset ring-line-strong hover:bg-raised\",\n ghost: \"text-fg-muted hover:bg-raised hover:text-fg\",\n // Soft rather than a solid red block. The verdict red is also the colour of a defect\n // badge, and a filled button of it reads as an alarm on a screen that is mostly grey.\n // Genuinely destructive actions are behind a confirmation dialog regardless. The hover\n // tint stops at /15 so the red label keeps 4.5:1 on it.\n danger: \"bg-defect/10 text-defect ring-1 ring-inset ring-defect/30 hover:bg-defect/15\",\n};\n\nconst SIZE_CLASSES: Record<ButtonSize, string> = {\n sm: \"h-7 gap-1.5 px-2.5 text-xs\",\n md: \"h-8 gap-2 px-3 text-sm\",\n};\n\n/**\n * The classes that make a control look like a button, for the one element that has to be\n * something else. Prefer `Button` and `ButtonLink`; this exists so a third shape — a\n * `<label>` wrapping a file input, say — does not have to copy the variant table.\n *\n * @param options - The variant (default `secondary`), the size (default `md`) and the\n * caller's own classes, merged last.\n * @returns The class string.\n */\nexport function buttonClasses({\n variant = \"secondary\",\n size = \"md\",\n className,\n}: {\n variant?: ButtonVariant | undefined;\n size?: ButtonSize | undefined;\n className?: string | undefined;\n} = {}): string {\n return cn(\n \"inline-flex shrink-0 items-center justify-center rounded-control font-medium transition-colors\",\n \"disabled:cursor-not-allowed disabled:opacity-45\",\n SIZE_CLASSES[size],\n VARIANT_CLASSES[variant],\n focusRing,\n className,\n );\n}\n\nfunction ButtonIcon({ icon }: { icon: ReactNode }) {\n return (\n <span className=\"shrink-0 [&>svg]:size-3.5\" aria-hidden>\n {icon}\n </span>\n );\n}\n\n/**\n * An action. `asChild` renders the button's look onto the single child element instead of a\n * `<button>` — how a router's link becomes a button-shaped navigation without this package\n * knowing about routers: `<Button asChild><Link to=\"/runs\">Runs</Link></Button>`.\n * For a plain URL, `ButtonLink` is the shorter spelling.\n *\n * Takes every `<button>` prop, `ref` included. State is exposed as `data-variant` and, while\n * `loading`, `data-loading` (plus `aria-busy`).\n */\nexport function Button({\n variant = \"secondary\",\n size,\n type = \"button\",\n loading = false,\n icon,\n asChild = false,\n className,\n children,\n disabled,\n ...rest\n}: ComponentProps<\"button\"> & {\n /** What kind of action this is. Defaults to `secondary`. */\n variant?: ButtonVariant | undefined;\n /** Defaults to the density in force — `md` comfortable, `sm` compact. */\n size?: ButtonSize | undefined;\n /** Swaps the icon for a spinner and blocks the click, without the label changing. */\n loading?: boolean | undefined;\n /** A leading icon, decorative (`aria-hidden`): the label carries the meaning. */\n icon?: ReactNode;\n /**\n * Render onto the one child element (Radix `Slot`) rather than a `<button>`. The child\n * keeps its own semantics — a link stays a link — so `type`, `disabled` and `loading` do\n * not apply.\n */\n asChild?: boolean | undefined;\n}) {\n const density = useDensity();\n const resolved = size ?? byDensity(density, \"md\", \"sm\");\n\n if (asChild) {\n return (\n <Slot {...rest} data-variant={variant} className={buttonClasses({ variant, size: resolved, className })}>\n {icon && <ButtonIcon icon={icon} />}\n <Slottable>{children}</Slottable>\n </Slot>\n );\n }\n\n return (\n <button\n type={type}\n disabled={disabled || loading}\n aria-busy={loading || undefined}\n data-variant={variant}\n data-loading={loading ? \"\" : undefined}\n {...rest}\n className={buttonClasses({ variant, size: resolved, className })}\n >\n {loading ? <Loader2 className=\"size-3.5 shrink-0 animate-spin\" aria-hidden /> : icon && <ButtonIcon icon={icon} />}\n {children}\n </button>\n );\n}\n\n/**\n * A navigation that looks like a button: one `<a>`, styled by the same variants and density.\n *\n * The alternative every consumer reached for was `<Link><Button/></Link>` — a button inside\n * an anchor, which is invalid HTML, gives a keyboard two tab stops for one action, and makes\n * a screen reader announce a button that navigates. There is no `disabled` or `loading`:\n * a link that cannot be followed should not be rendered as one, and a navigation has no\n * pending state of its own.\n *\n * It renders a plain `<a href>`, and takes every `<a>` prop, `ref` included. For a router's\n * link, pass it as the only child with `asChild` —\n * `<ButtonLink asChild><Link to=\"/runs\">Runs</Link></ButtonLink>` — so client-side\n * navigation keeps working and this package still never imports a router.\n */\nexport function ButtonLink({\n variant = \"secondary\",\n size,\n icon,\n asChild = false,\n className,\n children,\n ...rest\n}: ComponentProps<\"a\"> & {\n /** What kind of action this is. Defaults to `secondary`. */\n variant?: ButtonVariant | undefined;\n /** Defaults to the density in force — `md` comfortable, `sm` compact. */\n size?: ButtonSize | undefined;\n /** A leading icon, decorative (`aria-hidden`): the label carries the meaning. */\n icon?: ReactNode;\n /** Render onto the one child element — a router's `<Link>` — instead of an `<a>`. */\n asChild?: boolean | undefined;\n}) {\n const density = useDensity();\n const resolved = size ?? byDensity(density, \"md\", \"sm\");\n const Comp = asChild ? Slot : \"a\";\n\n return (\n <Comp {...rest} data-variant={variant} className={buttonClasses({ variant, size: resolved, className })}>\n {icon && <ButtonIcon icon={icon} />}\n <Slottable>{children}</Slottable>\n </Comp>\n );\n}\n","/*\n * Confirmation for the things that cannot be undone, and a shell for the things that need\n * a form.\n *\n * `Dialog` is a modal with no verdict of its own: a title, a body the caller fills, and a\n * footer it chooses. `ConfirmDialog` is one arrangement of it -- the destructive-action\n * confirmation every app in this family needs at least once (deleting a dataset, discarding\n * an in-progress measurement session) -- rather than a second copy of the overlay, the\n * centring and the width cap.\n */\n\nimport * as RadixDialog from \"@radix-ui/react-dialog\";\nimport { useCallback, useState, type ReactNode } from \"react\";\n\nimport { Button } from \"./Button\";\nimport { cn, focusRingInset } from \"./cn\";\n\n/**\n * Whether the body overflows its height cap, kept current as the dialog or its content\n * resizes. A scrolling body has to be reachable by keyboard (so it can be scrolled without\n * a pointer); a body that fits must not add a tab stop.\n */\nfunction useOverflowing(): [(node: HTMLDivElement | null) => (() => void) | undefined, boolean] {\n const [overflowing, setOverflowing] = useState(false);\n const ref = useCallback((node: HTMLDivElement | null) => {\n if (node === null) return undefined;\n const measure = () => setOverflowing(node.scrollHeight > node.clientHeight + 1);\n measure();\n if (typeof ResizeObserver === \"undefined\") return undefined;\n const observer = new ResizeObserver(measure);\n observer.observe(node);\n // The body is capped, so once it hits the cap only the content keeps changing size.\n if (node.firstElementChild) observer.observe(node.firstElementChild);\n return () => observer.disconnect();\n }, []);\n return [ref, overflowing];\n}\n\n/**\n * A modal: a title, a body the caller fills, and a footer it chooses.\n *\n * Controlled — the caller owns `open`. The body scrolls under a height cap so the footer\n * stays on screen; while it overflows it is a keyboard tab stop (with the focus ring) and\n * carries `data-overflowing`, so it can be scrolled without a pointer.\n */\nexport function Dialog({\n open,\n onOpenChange,\n title,\n description,\n footer,\n className,\n children,\n}: {\n /** Whether the dialog is shown. */\n open: boolean;\n /** Called with `false` on Escape, an overlay click or a `DialogClose`. */\n onOpenChange: (open: boolean) => void;\n /** Names the dialog (`aria-labelledby`). */\n title: string;\n /** Optional: what the reader needs before they can answer, not decoration. */\n description?: ReactNode;\n /** The action row, right-aligned below the body. */\n footer?: ReactNode;\n /** Merged with the dialog panel's own classes through `cn`. */\n className?: string | undefined;\n /** The body. */\n children?: ReactNode;\n}) {\n const [bodyRef, overflowing] = useOverflowing();\n\n return (\n <RadixDialog.Root open={open} onOpenChange={onOpenChange}>\n <RadixDialog.Portal>\n <RadixDialog.Overlay className=\"fixed inset-0 z-50 bg-black/50 backdrop-blur-[1px]\" />\n <RadixDialog.Content\n className={cn(\n \"fixed top-1/2 left-1/2 z-50 w-[min(28rem,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2\",\n // A ceiling and a column, so a dialog that lists things keeps its footer on\n // screen and scrolls its body instead of growing past the viewport. Every\n // short dialog is far below the cap and is unaffected. This is a scroller\n // inside an overlay, not a second page scroller.\n \"flex max-h-[calc(100vh-4rem)] flex-col\",\n \"rounded-panel border border-line bg-overlay p-5 shadow-xl shadow-black/40\",\n \"focus:outline-none\",\n className,\n )}\n >\n <RadixDialog.Title className=\"text-sm font-semibold tracking-tight text-fg\">{title}</RadixDialog.Title>\n {/* Radix warns when a dialog has no description; render an empty one rather than\n inventing prose for a form whose fields already carry their own labels. */}\n <RadixDialog.Description className={cn(\"mt-2 text-sm leading-relaxed text-fg-muted\", description === undefined && \"sr-only\")}>\n {description ?? title}\n </RadixDialog.Description>\n <div\n ref={bodyRef}\n tabIndex={overflowing ? 0 : undefined}\n data-overflowing={overflowing ? \"\" : undefined}\n className={cn(\"min-h-0 overflow-y-auto\", focusRingInset)}\n >\n <div>{children}</div>\n </div>\n {footer && <div className=\"mt-5 flex shrink-0 justify-end gap-2\">{footer}</div>}\n </RadixDialog.Content>\n </RadixDialog.Portal>\n </RadixDialog.Root>\n );\n}\n\n/**\n * The destructive-action confirmation: a `Dialog` whose footer is Cancel and one confirm\n * button. Say in `description` what will actually happen.\n */\nexport function ConfirmDialog({\n open,\n onOpenChange,\n title,\n description,\n confirmLabel,\n onConfirm,\n loading = false,\n disabled = false,\n destructive = false,\n className,\n}: {\n /** Whether the dialog is shown. */\n open: boolean;\n /** Called with `false` on Cancel, Escape or an overlay click. */\n onOpenChange: (open: boolean) => void;\n /** The question, e.g. \"Delete dataset?\". */\n title: string;\n /** Say what will actually happen, naming the thing by the name the reader gave it. */\n description: ReactNode;\n /** The confirm button's label: the verb, e.g. \"Delete\". */\n confirmLabel: string;\n /** Called on confirm. The dialog stays open; close it (or set `loading`) yourself. */\n onConfirm: () => void;\n /** Shows the confirm button's spinner and blocks it while the action runs. */\n loading?: boolean;\n /** Blocks the confirm button. */\n disabled?: boolean;\n /** Renders the confirm button as `danger` rather than `primary`. */\n destructive?: boolean;\n /** Merged with the dialog panel's own classes through `cn`. */\n className?: string | undefined;\n}) {\n return (\n <Dialog\n open={open}\n onOpenChange={onOpenChange}\n title={title}\n description={description}\n className={className}\n footer={\n <>\n <RadixDialog.Close asChild>\n <Button variant=\"ghost\">Cancel</Button>\n </RadixDialog.Close>\n <Button variant={destructive ? \"danger\" : \"primary\"} loading={loading} disabled={disabled} onClick={onConfirm}>\n {confirmLabel}\n </Button>\n </>\n }\n />\n );\n}\n\n/**\n * The dialog's own dismiss, for a footer the caller builds: wrap a button in it with\n * `asChild` — `<DialogClose asChild><Button>Done</Button></DialogClose>`.\n */\nexport const DialogClose = RadixDialog.Close;\n","/*\n * Content folded away, with a caret we draw ourselves.\n *\n * The base layer (`styles.css`) removes the native `<details>` marker; this supplies one\n * that rotates with the element's own open state, so no JavaScript is involved in the\n * animation.\n *\n * `<details>` rather than a headless package on purpose: the element already has the\n * semantics, the keyboard behaviour and the find-in-page integration.\n */\n\nimport { ChevronRight } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\n\nimport { cn } from \"./cn\";\n\n/**\n * Content folded away behind a summary line, on a native `<details>`, with a caret that\n * rotates with the element's own `open` state (no JavaScript involved).\n *\n * Uncontrolled: `defaultOpen` sets the initial state and the element owns it after that.\n */\nexport function Disclosure({\n summary,\n count,\n defaultOpen = false,\n className,\n children,\n}: {\n /** The always-visible line that toggles the content. */\n summary: ReactNode;\n /** How many things are inside, where the number is the reason to open it. */\n count?: number;\n /** Whether it starts open. */\n defaultOpen?: boolean;\n /** Merged with the `<details>` element's own classes through `cn`. */\n className?: string | undefined;\n /** The folded content. */\n children: ReactNode;\n}) {\n return (\n <details open={defaultOpen} className={cn(\"group\", className)}>\n <summary\n className={cn(\n \"flex cursor-pointer items-center gap-1.5 rounded-control py-1 text-xs font-medium text-fg-muted\",\n \"transition-colors hover:text-fg\",\n \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-signal\",\n )}\n >\n <ChevronRight className=\"size-3.5 shrink-0 transition-transform group-open:rotate-90\" aria-hidden />\n {summary}\n {count !== undefined && <span className=\"font-mono text-fg-subtle tabular-nums\">{count}</span>}\n </summary>\n <div className=\"pt-3\">{children}</div>\n </details>\n );\n}\n","/*\n * A key, as the keyboard shows it: the shortcut next to a command, the key a hint refers to.\n */\n\nimport type { ComponentProps } from \"react\";\n\nimport { cn } from \"./cn\";\n\n/**\n * A keyboard key or chord (`F`, `⌘K`, `[`), set in mono on a raised key cap.\n *\n * Renders `<kbd>`, which screen readers announce as text. Write the key as the user's keyboard\n * labels it; for a chord, one `Kbd` with the whole chord reads better than several. Takes\n * every `<kbd>` prop, `ref` included.\n */\nexport function Kbd({ className, ...rest }: ComponentProps<\"kbd\">) {\n return (\n <kbd\n {...rest}\n className={cn(\n \"inline-flex min-w-[1.5em] items-center justify-center rounded-control border border-line-strong\",\n \"bg-raised px-1 font-mono text-[10px] leading-4 text-fg-muted\",\n className,\n )}\n />\n );\n}\n","/*\n * A menu of commands or toggles behind a trigger: a layers menu, an overflow menu.\n *\n * Use it for things a person *does* or *switches*. To pick one value from a set use `Select`,\n * or a `Listbox` in a `Popover` when the options need more than a label.\n */\n\nimport * as RadixMenu from \"@radix-ui/react-dropdown-menu\";\nimport { Check } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\n\nimport { Kbd } from \"./Kbd\";\nimport { cn } from \"./cn\";\n\n/** Props of `DropdownMenu`. */\nexport interface DropdownMenuProps {\n /** The element that opens it: one focusable element that accepts a ref (Radix `asChild`). */\n trigger: ReactNode;\n /** `MenuItem`, `MenuCheckboxItem`, `MenuLabel` and `MenuSeparator` elements. */\n children: ReactNode;\n /** Controlled open state. */\n open?: boolean | undefined;\n /** Called when it opens or closes. */\n onOpenChange?: ((open: boolean) => void) | undefined;\n /** Its alignment along the trigger. Defaults to `start`. */\n align?: \"start\" | \"center\" | \"end\" | undefined;\n /** The trigger's side it opens on. Defaults to `bottom`. */\n side?: \"top\" | \"right\" | \"bottom\" | \"left\" | undefined;\n /** Merged with the menu's own classes through `cn`. */\n className?: string | undefined;\n}\n\n/**\n * A menu (`role=\"menu\"`) that opens from `trigger`, with arrow-key navigation, typeahead,\n * Escape or a press outside to close, and focus returned to the trigger. It is non-modal: the\n * page behind stays readable and operable. Toggling a `MenuCheckboxItem` keeps the menu open,\n * so several layers can be switched in one visit.\n */\nexport function DropdownMenu({ trigger, children, open, onOpenChange, align = \"start\", side = \"bottom\", className }: DropdownMenuProps) {\n return (\n <RadixMenu.Root\n // Non-modal: a modal menu hides the rest of the page from assistive technology while\n // its trigger stays focusable under it, and a layers menu is used next to the canvas\n // it controls.\n modal={false}\n {...(open === undefined ? {} : { open })}\n {...(onOpenChange === undefined ? {} : { onOpenChange })}\n >\n <RadixMenu.Trigger asChild>{trigger}</RadixMenu.Trigger>\n <RadixMenu.Portal>\n <RadixMenu.Content\n side={side}\n align={align}\n sideOffset={6}\n collisionPadding={8}\n className={cn(\n \"z-50 min-w-44 rounded-panel border border-line bg-overlay p-1 text-sm text-fg shadow-lg shadow-black/25\",\n className,\n )}\n >\n {children}\n </RadixMenu.Content>\n </RadixMenu.Portal>\n </RadixMenu.Root>\n );\n}\n\nconst itemClasses = cn(\n \"relative flex h-7 cursor-default items-center gap-2 rounded-control pr-2 pl-7 text-sm outline-none select-none\",\n \"data-[highlighted]:bg-raised data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n);\n\n/** Props of `MenuItem`. */\nexport interface MenuItemProps {\n /** The item's text. */\n children: ReactNode;\n /** Runs the command. */\n onSelect?: (() => void) | undefined;\n /** An icon before the text, in the indicator column. */\n icon?: ReactNode;\n /** `defect` for a destructive command. */\n tone?: \"defect\" | undefined;\n /** A shortcut shown at the end of the row, as a `Kbd`. Display only: binding it is the app's. */\n shortcut?: string | undefined;\n /** Blocks the item. */\n disabled?: boolean | undefined;\n}\n\n/**\n * A command. `onSelect` runs when it is clicked or chosen with Enter, and the menu then\n * closes. `tone=\"defect\"` marks a destructive command.\n */\nexport function MenuItem({ children, onSelect, shortcut, disabled, icon, tone }: MenuItemProps) {\n return (\n <RadixMenu.Item\n {...(disabled === undefined ? {} : { disabled })}\n onSelect={() => onSelect?.()}\n className={cn(itemClasses, tone === \"defect\" && \"text-defect\")}\n >\n {icon && (\n <span aria-hidden className=\"absolute left-2 flex size-3.5 items-center [&>svg]:size-3.5\">\n {icon}\n </span>\n )}\n <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n {shortcut && <Kbd>{shortcut}</Kbd>}\n </RadixMenu.Item>\n );\n}\n\n/** Props of `MenuCheckboxItem`. */\nexport interface MenuCheckboxItemProps {\n /** The item's text. */\n children: ReactNode;\n /** Whether it is on. */\n checked: boolean;\n /** Called with the new state. */\n onCheckedChange: (checked: boolean) => void;\n /** A shortcut shown at the end of the row, as a `Kbd`. Display only: binding it is the app's. */\n shortcut?: string | undefined;\n /** Blocks the item. */\n disabled?: boolean | undefined;\n}\n\n/**\n * A toggle (`role=\"menuitemcheckbox\"`): a layer on or off. A check marks it on. Choosing it\n * leaves the menu open.\n */\nexport function MenuCheckboxItem({ children, checked, onCheckedChange, shortcut, disabled }: MenuCheckboxItemProps) {\n return (\n <RadixMenu.CheckboxItem\n checked={checked}\n onCheckedChange={onCheckedChange}\n {...(disabled === undefined ? {} : { disabled })}\n // A toggle in a menu of toggles: keep the menu open for the next one.\n onSelect={(event) => event.preventDefault()}\n className={itemClasses}\n >\n <RadixMenu.ItemIndicator className=\"absolute left-2 flex items-center text-signal\">\n <Check className=\"size-3.5\" aria-hidden />\n </RadixMenu.ItemIndicator>\n <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n {shortcut && <Kbd>{shortcut}</Kbd>}\n </RadixMenu.CheckboxItem>\n );\n}\n\n/** A heading over a group of items, in the eyebrow role. Not focusable. */\nexport function MenuLabel({\n children,\n}: {\n /** The heading. */\n children: ReactNode;\n}) {\n return (\n <RadixMenu.Label className=\"px-2 pt-1.5 pb-1 text-[11px] font-semibold tracking-wider text-fg-muted uppercase\">\n {children}\n </RadixMenu.Label>\n );\n}\n\n/** A divider between groups of items. */\nexport function MenuSeparator() {\n return <RadixMenu.Separator className=\"my-1 h-px bg-line\" />;\n}\n","/*\n * What the screen says when it has something other than data to show.\n *\n * An error explains what happened and what to do about it; it does not apologise and it is\n * never vague. An empty state is an invitation to act, so it carries the action rather than\n * describing it. And loading is a shape, not the word \"Loading…\" -- a skeleton or a\n * progress bar keeps a slow request from looking identical to a broken one.\n */\n\nimport { AlertTriangle, CheckCircle2, Info, XCircle } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\n\nimport { cn } from \"./cn\";\n\n/** A callout's kind: a fact (`info`), a caveat (`warning`), a failure (`error`) or `success`. */\nexport type CalloutTone = \"info\" | \"warning\" | \"error\" | \"success\";\n\nconst CALLOUT: Record<CalloutTone, { classes: string; Icon: typeof Info }> = {\n info: { classes: \"border-signal/30 bg-signal/8 text-fg\", Icon: Info },\n warning: { classes: \"border-warn/30 bg-warn/8 text-fg\", Icon: AlertTriangle },\n error: { classes: \"border-defect/30 bg-defect/8 text-fg\", Icon: XCircle },\n success: { classes: \"border-normal/30 bg-normal/8 text-fg\", Icon: CheckCircle2 },\n};\n\nconst ICON_TONE: Record<CalloutTone, string> = {\n info: \"text-signal\",\n warning: \"text-warn\",\n error: \"text-defect\",\n success: \"text-normal\",\n};\n\n/**\n * A boxed message with a tone icon, an optional title and optional actions.\n *\n * An `error` callout is a `role=\"alert\"` (announced at once); the others are\n * `role=\"status\"`. The tone is exposed as `data-tone`.\n */\nexport function Callout({\n tone = \"info\",\n title,\n actions,\n className,\n children,\n}: {\n /** Defaults to `info`. */\n tone?: CalloutTone | undefined;\n /** A one-line summary above the message. */\n title?: ReactNode;\n /** Buttons for what to do about it, below the message. */\n actions?: ReactNode;\n /** Merged with the callout's own classes through `cn`. */\n className?: string | undefined;\n /** The message: what happened and what to do about it. */\n children: ReactNode;\n}) {\n const { classes, Icon } = CALLOUT[tone];\n return (\n <div\n role={tone === \"error\" ? \"alert\" : \"status\"}\n data-tone={tone}\n className={cn(\"flex items-start gap-2.5 rounded-control border px-3 py-2.5 text-sm leading-snug\", classes, className)}\n >\n <Icon className={cn(\"mt-0.5 size-4 shrink-0\", ICON_TONE[tone])} aria-hidden />\n <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n {title && <p className=\"font-medium\">{title}</p>}\n <div className=\"min-w-0 text-fg-muted\">{children}</div>\n {actions && <div className=\"mt-1 flex flex-wrap items-center gap-2\">{actions}</div>}\n </div>\n </div>\n );\n}\n\n/** The common error shape: an `error` `Callout` with only a message. */\nexport function ErrorBox({\n className,\n children,\n}: {\n /** Merged with the callout's own classes through `cn`. */\n className?: string | undefined;\n /** What went wrong and what to do about it. */\n children: ReactNode;\n}) {\n return (\n <Callout tone=\"error\" className={className}>\n {children}\n </Callout>\n );\n}\n\n/** An empty state: a sentence saying why there is nothing here, and the action that fixes it. */\nexport function Empty({\n action,\n className,\n children,\n}: {\n /** The action — usually a primary `Button` — rather than a description of it. */\n action?: ReactNode;\n /** Merged with the empty state's own classes through `cn`. */\n className?: string | undefined;\n /** Why there is nothing to show. */\n children: ReactNode;\n}) {\n return (\n <div className={cn(\"flex flex-col items-center gap-3 py-10 text-center\", className)}>\n <p className=\"text-sm text-fg-muted\">{children}</p>\n {action}\n </div>\n );\n}\n\n/** One placeholder bar for content on its way (`aria-hidden`; size it with `className`). */\nexport function Skeleton({\n className,\n}: {\n /** Sets the bar's size; merged with its own classes through `cn`. */\n className?: string | undefined;\n}) {\n return <div aria-hidden className={cn(\"animate-pulse rounded-control bg-line\", className)} />;\n}\n\n/** A stand-in for a panel's worth of content, so the layout does not jump when it lands. */\nexport function SkeletonRows({\n rows = 3,\n className,\n}: {\n /** How many bars. Defaults to 3; the last is shorter. */\n rows?: number | undefined;\n /** Merged with the column's own classes through `cn`. */\n className?: string | undefined;\n}) {\n return (\n <div role=\"status\" aria-label=\"Loading\" className={cn(\"flex flex-col gap-2\", className)}>\n {Array.from({ length: rows }, (_, index) => (\n <Skeleton key={index} className={cn(\"h-4\", index === rows - 1 ? \"w-2/3\" : \"w-full\")} />\n ))}\n </div>\n );\n}\n\n/**\n * A known fraction done, as a bar and (with `label`) a percentage line.\n *\n * A `role=\"progressbar\"` with `aria-valuenow` in percent. Its accessible name is\n * `aria-label` if given, else `label`, else \"Progress\" — pass one that names the job when\n * a screen shows more than one bar.\n *\n * @deprecated Use `Progress`: a value out of a `max` with a \"412 of 2,556\" value text, an\n * indeterminate state, and a track that follows the density.\n */\nexport function ProgressBar({\n fraction,\n label,\n \"aria-label\": ariaLabel,\n className,\n}: {\n /** Done, from 0 to 1. Clamped. */\n fraction: number;\n /** What is progressing, printed after the percentage: \"42 of 100 images\". */\n label?: string | undefined;\n /** The bar's accessible name. Defaults to `label`, then to \"Progress\". */\n \"aria-label\"?: string | undefined;\n /** Merged with the wrapper's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const percent = Math.round(Math.min(Math.max(fraction, 0), 1) * 100);\n return (\n <div className={cn(\"flex flex-col gap-1.5\", className)}>\n <div\n role=\"progressbar\"\n aria-label={ariaLabel ?? label ?? \"Progress\"}\n aria-valuenow={percent}\n aria-valuemin={0}\n aria-valuemax={100}\n data-complete={percent === 100 ? \"\" : undefined}\n className=\"h-1.5 overflow-hidden rounded-full bg-line\"\n >\n <div className=\"h-full rounded-full bg-signal transition-[width] duration-300\" style={{ width: `${percent}%` }} />\n </div>\n {label && (\n <span className=\"font-mono text-xs text-fg-muted tabular-nums\">\n {percent}% · {label}\n </span>\n )}\n </div>\n );\n}\n","/*\n * A labelled control.\n *\n * Two things a naive label cannot express, both of which the schema form needs. A required\n * field's marker is a separate span rather than text concatenated onto the label string, so\n * it can be styled, read out as a marker, or dropped. And a field's description (and its\n * error) is wired to the control through `aria-describedby`, so a screen reader connects\n * the two.\n *\n * `as=\"group\"` exists because a `<label>` cannot label a set of radios or a segmented\n * control; that case needs a labelled group instead, and getting it wrong is the difference\n * between a control that announces itself and one that announces nothing.\n */\n\nimport { createContext, use, useId, useMemo, type ReactNode } from \"react\";\n\nimport { byDensity, useDensity } from \"./Density\";\nimport { cn } from \"./cn\";\n\n/** What a `Field` tells the control inside it: the ids that describe it, and whether it is invalid. */\ninterface FieldControl {\n describedBy: string | undefined;\n invalid: boolean;\n}\n\nconst FieldContext = createContext<FieldControl>({ describedBy: undefined, invalid: false });\n\n/**\n * The `aria-describedby` and `aria-invalid` a control inside a `Field` should carry, merged\n * with the control's own `aria-describedby`. Used by this package's controls; not exported\n * from the package.\n */\nexport function useFieldDescription(own?: string): {\n \"aria-describedby\": string | undefined;\n \"aria-invalid\": true | undefined;\n} {\n const { describedBy, invalid } = use(FieldContext);\n const ids = [own, describedBy].filter(Boolean).join(\" \");\n return { \"aria-describedby\": ids === \"\" ? undefined : ids, \"aria-invalid\": invalid || undefined };\n}\n\n/**\n * A label, the control, and optionally a description, an error and a compact annotation.\n *\n * The label text and the annotation name the control (the `<label>` wraps them and the\n * control); the description and the error are outside the label and wired into the\n * control's `aria-describedby` instead (the error also sets `aria-invalid`): directly for\n * this package's controls — `Input`, `NumberInput`, `Textarea`, `Select`, `Slider`,\n * `SegmentedControl`, `Checkbox` — and, with `as=\"group\"`, on the group itself. A control of your own inside a `label` field does not\n * pick them up; give it `aria-describedby` yourself. State is exposed as `data-invalid` and\n * `data-required`.\n */\nexport function Field({\n label,\n description,\n error,\n required = false,\n annotation,\n as = \"label\",\n className,\n children,\n}: {\n /** The field's name, shown above the control. */\n label: string;\n /** What the value means or does. Always visible, and read with the control. */\n description?: ReactNode;\n /** What is wrong with the current value. Shown in the verdict red, announced as an alert. */\n error?: ReactNode;\n /** Marks the field required (a `*` after the label). */\n required?: boolean | undefined;\n /** A compact fact about the accepted values -- a range, a unit -- kept out of the prose. */\n annotation?: ReactNode;\n /**\n * `label` (the default) wraps the control in a `<label>`; `group` renders a\n * `role=\"group\"` named by `label`, for a set of radios or a segmented control.\n */\n as?: \"label\" | \"group\";\n /** Merged with the field's own classes through `cn`. */\n className?: string | undefined;\n /** The control. */\n children: ReactNode;\n}) {\n const descriptionId = useId();\n const errorId = useId();\n const density = useDensity();\n\n const hasError = error !== undefined && error !== null && error !== false;\n const describedBy = [description ? descriptionId : undefined, hasError ? errorId : undefined].filter(Boolean).join(\" \") || undefined;\n\n // A group carries the description itself; its controls would only repeat it.\n const control = useMemo(\n () => ({ describedBy: as === \"label\" ? describedBy : undefined, invalid: hasError }),\n [as, describedBy, hasError],\n );\n\n const gap = byDensity(density, \"gap-1.5\", \"gap-1\");\n\n const labelled = (\n <>\n <span className=\"flex items-baseline gap-2\">\n <span className={cn(\"font-medium text-fg\", byDensity(density, \"text-xs\", \"text-[11px]\"))}>\n {label}\n {required && (\n <span className=\"ml-0.5 text-defect\" title=\"Required\">\n *\n </span>\n )}\n </span>\n {annotation && <span className=\"ml-auto font-mono text-[11px] text-fg-subtle\">{annotation}</span>}\n </span>\n <FieldContext value={control}>{children}</FieldContext>\n </>\n );\n\n return (\n <div\n className={cn(\"flex min-w-0 flex-col\", gap, className)}\n data-invalid={hasError ? \"\" : undefined}\n data-required={required ? \"\" : undefined}\n {...(as === \"group\" ? { role: \"group\", \"aria-label\": label, \"aria-describedby\": describedBy } : {})}\n >\n {/* The description and the error sit outside the <label>: inside it they would be read\n as part of the control's name as well as its description. */}\n {as === \"label\" ? <label className={cn(\"flex min-w-0 flex-col\", gap)}>{labelled}</label> : labelled}\n\n {description && (\n <span id={descriptionId} className=\"text-xs leading-snug text-fg-muted\">\n {description}\n </span>\n )}\n {hasError && (\n <span id={errorId} role=\"alert\" className=\"text-xs leading-snug text-defect\">\n {error}\n </span>\n )}\n </div>\n );\n}\n","/*\n * The model behind `HighlightedText`: spans over a string, cut into the runs that render\n * alike, and spans found by regular expressions. Pure, so it is tested without a DOM.\n *\n * Offsets are UTF-16 code-unit indices, as `String.prototype.slice` and `RegExp` use them.\n */\n\n/** What a highlighted span is. Each kind has its own shape on screen, not only its own colour. */\nexport type HighlightKind = \"placeholder\" | \"tag\" | \"protected-term\" | \"mismatch\";\n\n/** A highlighted range of the text: `[start, end)` in UTF-16 code units. */\nexport interface HighlightSpan {\n /** The offset of the span's first code unit. */\n start: number;\n /** The offset just past the span's last code unit. */\n end: number;\n /** What the span is; it decides the span's shape and its default label. */\n kind: HighlightKind;\n /**\n * What a screen reader hears after the span, instead of the kind's default label: say\n * *why* it is marked (\"missing from the translation\").\n */\n label?: string | undefined;\n}\n\n/** A run of the text covered by one set of spans; two spans side by side are two runs. */\nexport interface TextSegment {\n /** The run's characters. */\n text: string;\n /** Where the run starts in the whole text. */\n start: number;\n /** Where it ends. */\n end: number;\n /** The spans covering the run, in their order in the input. Empty for plain text. */\n spans: HighlightSpan[];\n}\n\n/** The kinds in the order their styles stack: a later one wins where two would clash. */\nexport const HIGHLIGHT_KINDS: readonly HighlightKind[] = [\"tag\", \"protected-term\", \"placeholder\", \"mismatch\"];\n\n/**\n * Cuts `text` into runs that are each covered by one fixed set of spans. Spans are clamped\n * to the text; empty, inverted and non-finite ones are dropped. Overlapping spans are kept:\n * the overlap is a run covered by both.\n *\n * @param text - The string, shown as text.\n * @param spans - The highlights, in any order.\n * @returns The runs, in order, together covering the whole text.\n */\nexport function segmentText(text: string, spans: readonly HighlightSpan[]): TextSegment[] {\n const length = text.length;\n const clamp = (offset: number) => Math.floor(Math.max(0, Math.min(length, offset)));\n const valid = spans\n .map((span) => ({ span, start: clamp(span.start), end: clamp(span.end) }))\n .filter(({ start, end }) => Number.isFinite(start) && Number.isFinite(end) && end > start);\n const cuts = new Set<number>([0, length]);\n for (const { start, end } of valid) {\n cuts.add(start);\n cuts.add(end);\n }\n const points = [...cuts].sort((a, b) => a - b);\n const segments: TextSegment[] = [];\n for (let i = 0; i + 1 < points.length; i++) {\n const start = points[i]!;\n const end = points[i + 1]!;\n const covering = valid.filter((v) => v.start <= start && v.end >= end).map((v) => v.span);\n segments.push({ text: text.slice(start, end), start, end, spans: covering });\n }\n return segments;\n}\n\n/** A pattern whose matches become spans of one kind. */\nexport interface HighlightRule {\n /** Matched against the whole text, every match (the `g` flag is added if missing). */\n pattern: RegExp;\n /** The kind every match gets. */\n kind: HighlightKind;\n /** The spans' `label`, if they need one. */\n label?: string | undefined;\n}\n\n/**\n * Spans for every match of every rule: `%1`, `<b>`, a glossary term. Empty matches are\n * skipped. The result is sorted by start, then longest first.\n *\n * @param text - The string to search.\n * @param rules - Each pattern and the kind its matches get.\n * @returns The spans, ready for `HighlightedText`.\n *\n * @example\n * ```ts\n * findSpans(\"Open %1 in <b>ParamsManager</b>\", [\n * { pattern: /%\\d+/, kind: \"placeholder\" },\n * { pattern: /<\\/?[a-z][^>]*>/i, kind: \"tag\" },\n * { pattern: /\\bParamsManager\\b/, kind: \"protected-term\" },\n * ]);\n * ```\n */\nexport function findSpans(text: string, rules: readonly HighlightRule[]): HighlightSpan[] {\n const spans: HighlightSpan[] = [];\n for (const rule of rules) {\n const flags = rule.pattern.flags.includes(\"g\") ? rule.pattern.flags : `${rule.pattern.flags}g`;\n for (const match of text.matchAll(new RegExp(rule.pattern.source, flags))) {\n if (match[0] === \"\") continue;\n const start = match.index;\n spans.push({ start, end: start + match[0].length, kind: rule.kind, ...(rule.label === undefined ? {} : { label: rule.label }) });\n }\n }\n return spans.sort((a, b) => a.start - b.start || b.end - a.end);\n}\n","/*\n * A string with marked spans: the placeholders, tags and protected terms of a UI string, and\n * where a translation does not match its source.\n *\n * The string is only ever text. It is cut into runs and each run is a React text child, so\n * `<b>` in a string is shown as the four characters `<b>`, never parsed. There is no\n * `dangerouslySetInnerHTML` here, nor anywhere in the package.\n *\n * Each kind differs in shape, not only in colour (a box, a mono face, an underline style),\n * and a screen reader hears the kind after the span. That label is selection-proof: a reader\n * copying the string gets the string.\n */\n\nimport type { ComponentProps } from \"react\";\n\nimport { cn } from \"./cn\";\nimport { HIGHLIGHT_KINDS, segmentText, type HighlightKind, type HighlightSpan } from \"./highlightSpans\";\n\n/** What a screen reader hears after a span of each kind, by default. */\nconst LABELS: Record<HighlightKind, string> = {\n placeholder: \"placeholder\",\n tag: \"markup\",\n \"protected-term\": \"protected term\",\n mismatch: \"mismatch\",\n};\n\n/** The look of each kind; listed in `HIGHLIGHT_KINDS` order, so a later kind wins a clash. */\nconst STYLES: Record<HighlightKind, string> = {\n // Code-like, in a quiet box.\n tag: \"rounded-sm bg-raised px-px font-mono text-fg-muted\",\n // Read as a name: a dotted underline, the face unchanged.\n \"protected-term\": \"underline decoration-fg-subtle decoration-dotted decoration-1 underline-offset-[3px]\",\n // A slot the code fills: mono, outlined.\n placeholder: \"rounded-sm bg-raised px-px font-mono text-fg ring-1 ring-line-strong ring-inset\",\n // A verdict the app made: a wavy underline and a tint, in the defect colour.\n mismatch: \"bg-defect/12 text-fg underline decoration-defect decoration-wavy decoration-1 underline-offset-[3px]\",\n};\n\n/** Props of `HighlightedText`: the text, its spans, and every `<span>` prop but `children`. */\nexport type HighlightedTextProps = Omit<ComponentProps<\"span\">, \"children\"> & {\n /** The string. Shown as text, whitespace and line breaks kept; markup in it is not interpreted. */\n text: string;\n /** The ranges to mark, in UTF-16 offsets, in any order; overlaps are allowed. `findSpans` builds them from patterns. */\n spans?: readonly HighlightSpan[] | undefined;\n /**\n * What a screen reader hears after a span of each kind: \"placeholder\", \"markup\",\n * \"protected term\" and \"mismatch\" by default. `null` silences a kind. A span's own `label` wins.\n */\n labels?: Partial<Record<HighlightKind, string | null>> | undefined;\n};\n\n/**\n * A string with highlighted spans: placeholders (`%1`, `{count}`), markup tags (`<b>`),\n * protected terms (`ParamsManager`) and mismatches, each drawn in its own shape. The caller\n * supplies the spans, or finds them with `findSpans`.\n *\n * The text is rendered as text, never as HTML. Each span is a `<mark>` with `data-kind` (the\n * kinds covering it, space-separated), followed by its label for screen readers (\"%1,\n * placeholder\"), which a copy of the text leaves out.\n */\nexport function HighlightedText({ text, spans = [], labels, className, ...rest }: HighlightedTextProps) {\n const segments = segmentText(text, spans);\n return (\n <span {...rest} className={cn(\"break-words whitespace-pre-wrap\", className)}>\n {segments.map((segment) => {\n if (segment.spans.length === 0) return segment.text;\n const kinds = HIGHLIGHT_KINDS.filter((kind) => segment.spans.some((span) => span.kind === kind));\n const said = segment.spans\n .map((span) => span.label ?? (labels?.[span.kind] === undefined ? LABELS[span.kind] : labels[span.kind]))\n .filter((label): label is string => typeof label === \"string\" && label !== \"\");\n return (\n <mark\n key={segment.start}\n data-kind={kinds.join(\" \")}\n className={cn(\"bg-transparent text-inherit\", ...kinds.map((kind) => STYLES[kind]))}\n >\n {segment.text}\n {said.length > 0 && <span className=\"sr-only select-none\">{`, ${[...new Set(said)].join(\", \")}`}</span>}\n </mark>\n );\n })}\n </span>\n );\n}\n","/*\n * Numbers as a field shows them and as a person types them. Used by `NumberInput`'s\n * number-valued mode, `VectorInput` and `PoseInput`, and exported for an app's own fields.\n *\n * A field bound straight to a number re-formats under the cursor: typing `0.` into a field\n * showing `0.000` becomes `0` before the next digit arrives. So the multi-number fields keep\n * the typed text while they have focus and show the formatted value otherwise; these are the\n * two conversions between the two.\n */\n\n/**\n * A number at a fixed number of decimals, without a negative zero (`-0.000` reads as a sign\n * error in a coordinate readout).\n *\n * @param value - The number. Non-finite values print as `NaN`, `Infinity`, `-Infinity`.\n * @param precision - Decimals, 0–20.\n * @returns The text.\n */\nexport function formatNumber(value: number, precision: number): string {\n if (!Number.isFinite(value)) return String(value);\n const text = value.toFixed(precision);\n return /^-0(?:\\.0*)?$/.test(text) ? text.slice(1) : text;\n}\n\n/**\n * The number a field's text means, if it means one.\n *\n * @param text - What is in the field.\n * @returns The number, or `null` for empty, partial (`-`, `1e`) or non-numeric text.\n */\nexport function parseNumber(text: string): number | null {\n const trimmed = text.trim();\n if (trimmed === \"\") return null;\n const value = Number(trimmed);\n return Number.isFinite(value) ? value : null;\n}\n","/*\n * Text entry.\n *\n * `controlClasses` is exported so a native control that isn't one of the wrapped ones below\n * can still pick up the shared palette and focus treatment.\n */\n\nimport { useId, useState, type ComponentProps, type KeyboardEvent } from \"react\";\n\nimport { byDensity, useDensity } from \"./Density\";\nimport { useFieldDescription } from \"./Field\";\nimport { cn, focusRingInset } from \"./cn\";\nimport { formatNumber, parseNumber } from \"./numberText\";\n\n/**\n * The shared look of a text-like control — border, palette, placeholder, hover, disabled and\n * focus — for a native control that is not one of `Input`, `NumberInput` or `Textarea`.\n * Add the height with `useControlHeight()`.\n */\nexport const controlClasses = cn(\n \"w-full rounded-control border border-line-strong bg-raised px-2.5 text-sm text-fg\",\n \"placeholder:text-fg-subtle\",\n \"transition-colors hover:border-fg-subtle\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n focusRingInset,\n);\n\n/**\n * The former name of `controlClasses`, kept as an alias for existing consumers.\n *\n * @deprecated Use `Input`, `NumberInput`, `Textarea` or `Select`, or `controlClasses`.\n */\nexport const inputClasses = controlClasses;\n\n/**\n * The control height in force — exported so an app's own native control can match.\n *\n * @returns A Tailwind height class: `h-8` comfortable, `h-7` compact.\n */\nexport function useControlHeight(): string {\n return byDensity(useDensity(), \"h-8\", \"h-7\");\n}\n\n/**\n * A single-line text input. Takes every `<input>` prop, `ref` included; inside a `Field` it\n * is described by the field's description and error.\n */\nexport function Input({ className, \"aria-describedby\": describedBy, ...rest }: ComponentProps<\"input\">) {\n return <input {...useFieldDescription(describedBy)} {...rest} className={cn(controlClasses, useControlHeight(), className)} />;\n}\n\n/**\n * Props of `NumberInput`: every `<input>` prop, `ref` included, with numeric bounds, a `unit`,\n * and an optional number-valued API (`onValueChange`, `precision`).\n */\nexport type NumberInputProps = Omit<ComponentProps<\"input\">, \"min\" | \"max\" | \"value\"> & {\n /**\n * The value. As text, it is the native input's `value`. With `onValueChange` it is a\n * number, and `null` shows an empty field.\n */\n value?: string | number | readonly string[] | null | undefined;\n /**\n * Makes the field number-valued.\n *\n * - **While focused** it keeps the text as typed. A keystroke is reported only when the\n * text is a finite number inside `[min, max]` and differs from `value`. Empty, partial\n * (`-`, `1e`) and out-of-range text is never reported, so clearing a field to retype it\n * does not pass through 0, and typing `5` on the way to `50` does not report a value under\n * `min`.\n * - **Enter** shows the value at `precision`. **Escape** restores the value the field had\n * on focus.\n * - **On leaving**, the field shows `value` again.\n */\n onValueChange?: ((value: number) => void) | undefined;\n /**\n * Called when a number-valued field is committed empty (left, or Enter pressed, with no\n * text), for a quantity that may be unset. Without it, emptying the field and leaving it\n * restores `value`.\n */\n onClear?: (() => void) | undefined;\n /**\n * Decimals shown when a number-valued field is not being edited. Without it the number is\n * shown as JavaScript prints it.\n */\n precision?: number | undefined;\n /** The smallest accepted value. */\n min?: number | undefined;\n /** The largest accepted value. */\n max?: number | undefined;\n /**\n * The quantity's unit (`mm`, `m`, `°`, `px`), written inside the field after the number, in\n * mono and muted. It is not part of the value. It is announced as the field's description\n * (`aria-describedby`): after the caller's own `aria-describedby`, before a surrounding\n * `Field`'s description and error.\n */\n unit?: string | undefined;\n};\n\n/**\n * A number, with the schema's own bounds attached and, optionally, its unit written in the\n * field.\n *\n * `font-mono` because these are read as quantities and compared down a column. Takes every\n * `<input>` prop, `ref` included (`type` is always `number`); `className`, `style` and `ref`\n * always go to the `<input>`.\n *\n * With a `unit`, the input sits in a `relative` full-width wrapper carrying `data-unit`, keeps\n * room on its right for the unit, and is described by it; size such a field through its\n * container. Without one — or with `\"\"` — the markup is exactly the plain input.\n *\n * With `onValueChange` the field is number-valued: `value` is a number (`null` shows empty), the\n * typed text survives while the field has focus, and only finite numbers inside `[min, max]`\n * are reported (see `onValueChange`; `onClear` reports an emptied field). Without it the field\n * is the native text-valued input.\n */\nexport function NumberInput({ onValueChange, onClear, precision, ...props }: NumberInputProps) {\n if (onValueChange) {\n return <ValueNumberInput {...props} onValueChange={onValueChange} onClear={onClear} precision={precision} />;\n }\n const { unit, ...rest } = props;\n // A `null` text value is an empty field, not a switch to uncontrolled.\n const text = (rest.value === null ? { ...rest, value: \"\" } : rest) as TextNumberInputProps;\n return unit === undefined || unit === \"\" ? <PlainNumberInput {...text} /> : <UnitNumberInput unit={unit} {...text} />;\n}\n\ntype TextNumberInputProps = Omit<NumberInputProps, \"unit\" | \"value\" | \"onValueChange\" | \"onClear\" | \"precision\"> & {\n value?: string | number | readonly string[] | undefined;\n};\n\n/** The number-valued field: formatted at rest, the typed text while focused. */\nfunction ValueNumberInput({\n value,\n onValueChange,\n onClear,\n precision,\n step,\n min,\n max,\n onFocus,\n onBlur,\n onChange,\n onKeyDown,\n ...rest\n}: Omit<NumberInputProps, \"onValueChange\"> & { onValueChange: (value: number) => void }) {\n const numeric = typeof value === \"number\" ? value : null;\n const show = (v: number | null) => (v === null ? \"\" : precision === undefined ? String(v) : formatNumber(v, precision));\n // Non-null while the field is being edited: the text as typed, and the value on focus.\n const [draft, setDraft] = useState<{ text: string; initial: number | null } | null>(null);\n\n /** The number `text` commits, or `null` while it is empty, partial or out of range. */\n const parse = (text: string) => {\n const parsed = parseNumber(text);\n if (parsed === null) return null;\n if ((min !== undefined && parsed < min) || (max !== undefined && parsed > max)) return null;\n return parsed;\n };\n\n /** An emptied field, committed: cleared if the caller allows it and it was not already empty. */\n const commitEmpty = (text: string) => {\n if (text.trim() === \"\" && numeric !== null) onClear?.();\n };\n\n const keyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n if (draft && event.key === \"Enter\") {\n commitEmpty(draft.text);\n setDraft({ ...draft, text: draft.text.trim() === \"\" && onClear ? \"\" : show(numeric) });\n } else if (draft && event.key === \"Escape\") {\n event.preventDefault();\n if (draft.initial !== numeric) {\n if (draft.initial === null) onClear?.();\n else onValueChange(draft.initial);\n }\n setDraft({ ...draft, text: show(draft.initial) });\n }\n onKeyDown?.(event);\n };\n\n return (\n <NumberInput\n {...rest}\n value={draft?.text ?? show(numeric)}\n step={step ?? \"any\"}\n min={min}\n max={max}\n onFocus={(event) => {\n setDraft({ text: show(numeric), initial: numeric });\n onFocus?.(event);\n }}\n onBlur={(event) => {\n if (draft) commitEmpty(draft.text);\n setDraft(null);\n onBlur?.(event);\n }}\n onKeyDown={keyDown}\n onChange={(event) => {\n const text = event.currentTarget.value;\n setDraft((current) => ({ text, initial: current?.initial ?? numeric }));\n const parsed = parse(text);\n if (parsed !== null && parsed !== numeric) onValueChange(parsed);\n onChange?.(event);\n }}\n />\n );\n}\n\nfunction PlainNumberInput({ className, \"aria-describedby\": describedBy, ...rest }: TextNumberInputProps) {\n return (\n <input\n {...useFieldDescription(describedBy)}\n {...rest}\n type=\"number\"\n inputMode=\"decimal\"\n className={cn(controlClasses, useControlHeight(), \"font-mono tabular-nums\", className)}\n />\n );\n}\n\n/*\n * A component of its own so that only a field with a unit calls `useId`: a plain\n * `NumberInput` stays byte-identical to the one before `unit` existed, down to the ids React\n * hands the elements rendered after it.\n */\nfunction UnitNumberInput({ unit, style, \"aria-describedby\": describedBy, ...rest }: TextNumberInputProps & { unit: string }) {\n const unitId = useId();\n return (\n <span data-unit={unit} className=\"relative flex w-full min-w-0 items-center\">\n <PlainNumberInput\n {...rest}\n aria-describedby={describedBy ? `${describedBy} ${unitId}` : unitId}\n // Room for the unit: its width in the unit's own characters, plus the field's padding.\n style={{ paddingInlineEnd: `calc(${unit.length}ch + 1rem)`, ...style }}\n />\n {/* `aria-hidden` keeps the unit out of the field's *name* when a `<label>` wraps it;\n `aria-describedby` still reads it, as the description. */}\n <span\n id={unitId}\n aria-hidden\n className=\"pointer-events-none absolute inset-y-0 right-2.5 flex items-center font-mono text-xs text-fg-subtle select-none\"\n >\n {unit}\n </span>\n </span>\n );\n}\n","/*\n * Keyboard movement in a single-select listbox, as a pure function so it is tested without a\n * DOM. `Listbox` calls it on every key press.\n */\n\n/** The keys that move the active option. */\nexport type ListboxKey = \"ArrowDown\" | \"ArrowUp\" | \"Home\" | \"End\" | \"PageDown\" | \"PageUp\";\n\n/**\n * The option a key moves the active one to, skipping disabled options.\n *\n * Arrows move by one and stop at the ends (no wrap: a long list that wraps loses its place).\n * Home and End go to the first and last enabled option. Page keys move by `page` options.\n * From no active option (`-1`), Down and Page Down go to the first enabled option, and Up and\n * Page Up to the last.\n *\n * @param disabled - Per option, whether it is disabled.\n * @param from - The active index, or -1.\n * @param key - The key pressed.\n * @param page - Options per page, for Page Up/Down. Defaults to 10.\n * @returns The new active index; `from` when there is nowhere to go; -1 when every option is\n * disabled.\n */\nexport function moveActive(disabled: readonly boolean[], from: number, key: ListboxKey, page = 10): number {\n const enabled = disabled.flatMap((off, index) => (off ? [] : [index]));\n if (enabled.length === 0) return -1;\n const first = enabled[0]!;\n const last = enabled[enabled.length - 1]!;\n if (key === \"Home\") return first;\n if (key === \"End\") return last;\n if (from < 0) return key === \"ArrowDown\" || key === \"PageDown\" ? first : last;\n\n const forward = key === \"ArrowDown\" || key === \"PageDown\";\n const steps = key === \"PageDown\" || key === \"PageUp\" ? Math.max(1, page) : 1;\n let target = from;\n let moved = 0;\n for (let index = from + (forward ? 1 : -1); index >= 0 && index < disabled.length; index += forward ? 1 : -1) {\n if (disabled[index]) continue;\n target = index;\n moved += 1;\n if (moved === steps) break;\n }\n return target;\n}\n\n/**\n * The first enabled option at or after `index`, or before it when none is after. Used to place\n * the active option when the list opens or changes.\n *\n * @param disabled - Per option, whether it is disabled.\n * @param index - The preferred index.\n * @returns An enabled index, or -1 when every option is disabled.\n */\nexport function nearestEnabled(disabled: readonly boolean[], index: number): number {\n for (let i = Math.max(0, index); i < disabled.length; i += 1) if (!disabled[i]) return i;\n for (let i = Math.min(index, disabled.length) - 1; i >= 0; i -= 1) if (!disabled[i]) return i;\n return -1;\n}\n","/*\n * Pick one of a list of options that need more than a label: a frame with its thumbnail and\n * size, a model with its point counts.\n *\n * `Select` is the compact choice of a value by name. A `Listbox` shows its options in place —\n * on its own in a panel, or inside a `Popover` behind a trigger — and each option can render\n * whatever identifies it.\n */\n\nimport { useId, useRef, useState, type KeyboardEvent, type ReactNode } from \"react\";\n\nimport { cn, focusRing } from \"./cn\";\nimport { moveActive, nearestEnabled, type ListboxKey } from \"./listboxModel\";\n\n/** One option of a `Listbox`. */\nexport interface ListboxOption {\n /** Its value, unique in the list. */\n value: string;\n /** Its name: the default rendering, and what a screen reader announces. */\n label: string;\n /** A second line under the label, in the default rendering. */\n description?: string | undefined;\n /** Shown but not choosable. */\n disabled?: boolean | undefined;\n}\n\n/** What `renderOption` is told about an option. */\nexport interface ListboxOptionState {\n /** It is the value. */\n selected: boolean;\n /** It has the keyboard cursor or the pointer. */\n active: boolean;\n}\n\n/** Props of `Listbox`. */\nexport interface ListboxProps {\n /** The options, in order. */\n options: readonly ListboxOption[];\n /** The selected value, or `null` for none. */\n value: string | null;\n /** Called with the value of the option chosen by click, Enter or Space. */\n onValueChange?: ((value: string) => void) | undefined;\n /**\n * Renders an option's content (the row keeps its own padding, states and accessibility).\n * Defaults to the label, with the description under it.\n */\n renderOption?: ((option: ListboxOption, state: ListboxOptionState) => ReactNode) | undefined;\n /** Names the list. */\n \"aria-label\": string;\n /** Focus the list when it mounts, e.g. when it opens in a `Popover`. */\n autoFocus?: boolean | undefined;\n /** Merged with the list's own classes through `cn`. */\n className?: string | undefined;\n}\n\nconst KEYS = new Set<string>([\"ArrowDown\", \"ArrowUp\", \"Home\", \"End\", \"PageDown\", \"PageUp\"]);\n\n/**\n * A single-select list (`role=\"listbox\"`) of options that each render their own content.\n *\n * The list is one tab stop. The arrow keys, Home, End and Page keys move the active option\n * (`aria-activedescendant`), skipping disabled ones, and Enter or Space choose it. A click\n * chooses the option under the pointer. Controlled: the caller keeps `value`. Each option\n * carries `aria-selected`, `data-selected` and `data-active`; the list scrolls the active\n * option into view.\n */\nexport function Listbox({\n options,\n value,\n onValueChange,\n renderOption = defaultRender,\n \"aria-label\": ariaLabel,\n autoFocus,\n className,\n}: ListboxProps) {\n const id = useId();\n const listRef = useRef<HTMLDivElement>(null);\n const disabled = options.map((option) => option.disabled === true);\n const selectedIndex = options.findIndex((option) => option.value === value);\n const [active, setActive] = useState(() => nearestEnabled(disabled, Math.max(0, selectedIndex)));\n // Options can change under the list; keep the cursor on an enabled option that exists.\n const current = active >= 0 && active < options.length && !disabled[active] ? active : nearestEnabled(disabled, active);\n\n const optionId = (index: number) => `${id}-option-${index}`;\n\n const activate = (index: number) => {\n setActive(index);\n listRef.current?.querySelector(`#${CSS.escape(optionId(index))}`)?.scrollIntoView({ block: \"nearest\" });\n };\n\n const choose = (index: number) => {\n const option = options[index];\n if (!option || option.disabled) return;\n onValueChange?.(option.value);\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (KEYS.has(event.key)) {\n event.preventDefault();\n const next = moveActive(disabled, current, event.key as ListboxKey);\n if (next >= 0) activate(next);\n } else if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n choose(current);\n }\n };\n\n return (\n <div\n ref={listRef}\n role=\"listbox\"\n aria-label={ariaLabel}\n tabIndex={0}\n autoFocus={autoFocus}\n aria-activedescendant={current >= 0 ? optionId(current) : undefined}\n onKeyDown={onKeyDown}\n className={cn(\"flex flex-col gap-px rounded-control p-0.5 outline-none\", focusRing, className)}\n >\n {options.map((option, index) => {\n const selected = index === selectedIndex;\n const isActive = index === current;\n return (\n <div\n key={option.value}\n id={optionId(index)}\n role=\"option\"\n aria-selected={selected}\n aria-disabled={option.disabled ? true : undefined}\n data-selected={selected ? \"\" : undefined}\n data-active={isActive ? \"\" : undefined}\n onPointerMove={() => {\n if (!option.disabled && !isActive) setActive(index);\n }}\n onClick={() => choose(index)}\n className={cn(\n \"flex min-w-0 cursor-default items-center gap-2 rounded-control px-2 py-1 text-sm text-fg\",\n // Selected is a bar on the leading edge plus the accent text; active (keyboard\n // cursor or pointer) is the raised fill. The two can coincide and each stays\n // visible, and neither puts muted text on a tint it fails contrast on.\n \"data-[selected]:text-signal data-[selected]:shadow-[inset_2px_0_0_var(--color-signal)]\",\n \"data-[active]:bg-raised\",\n option.disabled && \"opacity-50\",\n )}\n >\n {renderOption(option, { selected, active: isActive })}\n </div>\n );\n })}\n </div>\n );\n}\n\nfunction defaultRender(option: ListboxOption) {\n return (\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"truncate\">{option.label}</span>\n {option.description && <span className=\"truncate text-xs text-fg-muted\">{option.description}</span>}\n </span>\n );\n}\n","/*\n * The page furniture: what holds a screen together above the level of a single control.\n *\n * One heading size and one gap rule, defined once so every route in every consuming app\n * agrees with the others by construction rather than by luck.\n */\n\nimport { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport { isValidElement, type ReactElement, type ReactNode } from \"react\";\n\nimport { byDensity, useDensity } from \"./Density\";\nimport { cn } from \"./cn\";\n\n/**\n * A bordered surface with an optional header (a title and actions) above a padded body.\n * The header and the padding follow the density in force.\n */\nexport function Panel({\n title,\n actions,\n className,\n bodyClassName,\n children,\n}: {\n /** The header's heading (an `<h2>`). */\n title?: ReactNode;\n /** Controls at the header's right end. */\n actions?: ReactNode;\n /** Merged with the panel's own classes through `cn`. */\n className?: string | undefined;\n /** Merged with the body's own classes (its padding) through `cn`. */\n bodyClassName?: string | undefined;\n /** The body. */\n children: ReactNode;\n}) {\n const density = useDensity();\n\n return (\n <section className={cn(\"rounded-panel border border-line bg-surface\", className)}>\n {(title || actions) && (\n <header\n className={cn(\n \"flex items-center justify-between gap-4 border-b border-line\",\n byDensity(density, \"min-h-11 px-4 py-2.5\", \"min-h-8 px-2.5 py-1\"),\n )}\n >\n <h2\n className={cn(\n \"truncate font-semibold text-fg\",\n byDensity(density, \"text-sm tracking-tight\", \"text-[11px] tracking-wider uppercase text-fg-muted\"),\n )}\n >\n {title}\n </h2>\n {actions && <div className=\"flex shrink-0 items-center gap-1.5\">{actions}</div>}\n </header>\n )}\n <div className={cn(byDensity(density, \"p-4\", \"p-2.5\"), bodyClassName)}>{children}</div>\n </section>\n );\n}\n\n/**\n * A numbered step inside a form.\n *\n * The number is not decoration: it marks a genuine ordering, where each choice narrows the\n * next. Do not reach for this where the parts are merely adjacent.\n */\nexport function Section({\n step,\n title,\n hint,\n actions,\n className,\n children,\n}: {\n /** The step's number, shown zero-padded before the title (decorative, `aria-hidden`). */\n step?: number | undefined;\n /** The step's heading (an `<h3>`). */\n title: string;\n /** A short line after the title. */\n hint?: ReactNode;\n /** Controls at the heading row's right end. */\n actions?: ReactNode;\n /** Merged with the section's own classes through `cn`. */\n className?: string | undefined;\n /** The step's content. */\n children: ReactNode;\n}) {\n const density = useDensity();\n\n return (\n <section className={cn(\"flex flex-col\", byDensity(density, \"gap-3\", \"gap-1.5\"), className)}>\n <div className={cn(\"flex items-baseline\", byDensity(density, \"gap-2.5\", \"gap-2\"))}>\n {step !== undefined && (\n <span className=\"font-mono text-xs text-fg-subtle tabular-nums\" aria-hidden>\n {String(step).padStart(2, \"0\")}\n </span>\n )}\n <h3 className=\"text-sm font-semibold tracking-tight text-fg\">{title}</h3>\n {hint && <p className=\"text-xs text-fg-muted\">{hint}</p>}\n {actions && <div className=\"ml-auto flex items-center gap-2\">{actions}</div>}\n </div>\n {children}\n </section>\n );\n}\n\n/**\n * Where `PageHeader`'s back link goes: `{ href, label }` for a plain URL, or the app's own\n * link element (a router's `<Link>`).\n */\nexport type BackLink =\n | { href: string; label: string }\n /** A link element — typically a router's `<Link to=\"…\">Label</Link>` — styled and prefixed with an arrow. */\n | ReactElement;\n\nconst BACK_CLASSES = \"w-fit text-xs text-fg-muted transition-colors hover:text-signal\";\n\n/**\n * The top of a screen: an optional back link, the `<h1>` title with actions beside it, and\n * a line of meta facts below. Links go through `back`, never a router import.\n */\nexport function PageHeader({\n title,\n meta,\n actions,\n back,\n className,\n}: {\n /** The screen's heading (the `<h1>`). */\n title: ReactNode;\n /** Small facts under the title — an id, a date, a status badge. */\n meta?: ReactNode;\n /** The screen's actions, beside the title. */\n actions?: ReactNode;\n /**\n * The way up. `{ href, label }` renders a plain `<a>`; for client-side navigation pass the\n * router's own link — `back={<Link to=\"/experiments\">Experiments</Link>}` — which is\n * rendered with the back-link style and arrow, so this package never imports a router.\n */\n back?: BackLink | undefined;\n /** Merged with the header's own classes through `cn`. */\n className?: string | undefined;\n}) {\n return (\n <header className={cn(\"flex flex-col gap-1.5\", className)}>\n {back &&\n (isHrefLink(back) ? (\n <a href={back.href} className={BACK_CLASSES}>\n ← {back.label}\n </a>\n ) : (\n <Slot className={BACK_CLASSES}>\n {\"← \"}\n <Slottable>{back}</Slottable>\n </Slot>\n ))}\n <div className=\"flex flex-wrap items-center justify-between gap-x-4 gap-y-2\">\n <h1 className=\"text-xl font-semibold tracking-tight text-fg\">{title}</h1>\n {actions && <div className=\"flex shrink-0 items-center gap-2\">{actions}</div>}\n </div>\n {meta && <div className=\"flex flex-wrap items-center gap-2 text-xs text-fg-muted\">{meta}</div>}\n </header>\n );\n}\n\nfunction isHrefLink(back: BackLink): back is { href: string; label: string } {\n return !isValidElement(back);\n}\n\n/** One fact on a `ReadoutStrip`: a value, optionally labelled and optionally a link. */\nexport type ReadoutItem = {\n /** A quiet prefix naming what the value is, where the value alone is ambiguous. */\n label?: string | undefined;\n /** The fact. An item whose value is `null` or `undefined` is skipped. */\n value: ReactNode;\n /** Makes the value a plain `<a href>`. */\n href?: string | undefined;\n /**\n * Makes the value a link through the app's own element — a router's `<Link to=\"…\" />`,\n * given without children: the value becomes its content. Takes precedence over `href`.\n */\n link?: ReactElement | undefined;\n};\n\nconst READOUT_LINK = \"text-fg transition-colors hover:text-signal\";\n\n/**\n * The instrument's display line: the facts about what is on screen, in one fixed slot.\n *\n * Stated in the same place on every screen, in mono, the way a piece of measuring equipment\n * has one readout rather than a label per panel. Rendered as a list because it is one.\n */\nexport function ReadoutStrip({\n items,\n className,\n}: {\n /** The facts, in order. Items with no value are skipped. */\n items: ReadoutItem[];\n /** Merged with the list's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const shown = items.filter((item) => item.value !== null && item.value !== undefined);\n if (shown.length === 0) return null;\n\n return (\n <ol className={cn(\"flex flex-wrap items-center gap-x-2 gap-y-1 font-mono text-xs text-fg-muted\", className)}>\n {shown.map((item, index) => (\n // The items are positional facts with no identity of their own (a label is optional\n // and values repeat), so their place in the strip is the key.\n // oxlint-disable-next-line react/no-array-index-key -- positional list, see above\n <li key={index} className=\"flex items-center gap-2\">\n {index > 0 && (\n <span className=\"text-fg-subtle\" aria-hidden>\n ·\n </span>\n )}\n {item.label && <span className=\"text-fg-subtle\">{item.label}</span>}\n {item.link ? (\n <Slot className={READOUT_LINK}>\n <Slottable>{item.link}</Slottable>\n {item.value}\n </Slot>\n ) : item.href ? (\n <a href={item.href} className={READOUT_LINK}>\n {item.value}\n </a>\n ) : (\n <span className=\"text-fg\">{item.value}</span>\n )}\n </li>\n ))}\n </ol>\n );\n}\n","/*\n * How far a long operation has got.\n *\n * The bar is in `signal`, not a verdict colour: progress is not a pass or a fail, and a bar\n * that turned green at 100 % would claim a verdict nobody made. The value text is \"412 of\n * 2,556\" rather than a bare percentage, because the count is what a reader checks against\n * the work (\"2,556 strings\"), and a screen reader says it as written.\n */\n\nimport type { ComponentProps } from \"react\";\n\nimport { byDensity, useDensity } from \"./Density\";\nimport { cn } from \"./cn\";\n\n/** Counts written the same on the server and in every browser: `2,556`, `0.5`. */\nconst count = new Intl.NumberFormat(\"en-US\", { maximumFractionDigits: 2 });\n\n/**\n * The accessible value text of a progress: `\"412 of 2,556\"`, or `null` while indeterminate.\n *\n * @param value - Done so far; `null` or `undefined` for an unknown amount.\n * @param max - The whole. Defaults to 100.\n * @returns The text, with the value clamped to `[0, max]`.\n */\nexport function progressText(value: number | null | undefined, max = 100): string | null {\n if (value === null || value === undefined || !Number.isFinite(value)) return null;\n const whole = max > 0 ? max : 100;\n return `${count.format(Math.min(Math.max(value, 0), whole))} of ${count.format(whole)}`;\n}\n\n/** Props of `Progress`: a value out of a `max`, or none for an unknown amount, plus the `<div>` props. */\nexport type ProgressProps = Omit<ComponentProps<\"div\">, \"children\" | \"role\"> & {\n /** Done so far, from 0 to `max`; clamped. `null` or left out: indeterminate, the amount unknown. */\n value?: number | null | undefined;\n /** The whole. Defaults to 100; a value ≤ 0 counts as 100. */\n max?: number | undefined;\n /**\n * What a screen reader says for the value. Defaults to `\"412 of 2,556\"`; give your own when\n * the unit matters (\"412 of 2,556 strings\").\n */\n valueText?: string | undefined;\n};\n\n/**\n * A progress bar: determinate when it has a `value` (a `max`, 100 by default), indeterminate\n * without one. Name it with `aria-label` or `aria-labelledby`.\n *\n * A `progressbar` with `aria-valuenow`, `aria-valuemax` and `aria-valuetext` (\"412 of 2,556\",\n * or your `valueText`); indeterminate, it has none of them, as the ARIA pattern asks.\n * The track follows the density in force. State is exposed as `data-state`\n * (`\"indeterminate\"`, `\"loading\"` or `\"complete\"`). With reduced motion the indeterminate bar\n * holds still.\n */\nexport function Progress({ value, max = 100, valueText, className, ...rest }: ProgressProps) {\n const density = useDensity();\n const whole = max > 0 ? max : 100;\n const text = progressText(value, whole);\n const done = text === null ? null : Math.min(Math.max(value as number, 0), whole);\n const state = done === null ? \"indeterminate\" : done >= whole ? \"complete\" : \"loading\";\n return (\n <div\n role=\"progressbar\"\n aria-valuemin={done === null ? undefined : 0}\n aria-valuemax={done === null ? undefined : whole}\n aria-valuenow={done ?? undefined}\n aria-valuetext={done === null ? undefined : (valueText ?? text ?? undefined)}\n data-state={state}\n {...rest}\n className={cn(\"relative w-full overflow-hidden rounded-full bg-line\", byDensity(density, \"h-1.5\", \"h-1\"), className)}\n >\n {done === null ? (\n // A third of the track sweeping across it. Its resting place (reduced motion, a\n // screenshot) is the left third, still visibly \"in progress\".\n <div className=\"absolute inset-y-0 left-0 w-1/3 animate-progress-sweep rounded-full bg-signal\" />\n ) : (\n <div className=\"h-full rounded-full bg-signal transition-[width] duration-300\" style={{ width: `${(done / whole) * 100}%` }} />\n )}\n </div>\n );\n}\n","/*\n * Floating content anchored to a trigger: a picker, a small form, a list to choose from.\n *\n * A popover is for content a person works in. A *menu* of commands is `DropdownMenu`, and a\n * short explanation is `Tooltip`. It closes on Escape, on a press outside, or when the caller\n * sets `open` to false.\n */\n\nimport * as RadixPopover from \"@radix-ui/react-popover\";\nimport type { ReactNode } from \"react\";\n\nimport { cn } from \"./cn\";\n\n/** Props of `Popover`. */\nexport interface PopoverProps {\n /**\n * The element that opens it: one focusable element that accepts a ref (Radix `asChild`).\n * It carries `aria-expanded` and `data-state` (`open` / `closed`).\n */\n trigger: ReactNode;\n /** The content. */\n children: ReactNode;\n /** Controlled open state. */\n open?: boolean | undefined;\n /** Initial open state when uncontrolled. */\n defaultOpen?: boolean | undefined;\n /** Called when it opens or closes. */\n onOpenChange?: ((open: boolean) => void) | undefined;\n /** The trigger's side it opens on. Defaults to `bottom`. */\n side?: \"top\" | \"right\" | \"bottom\" | \"left\" | undefined;\n /** Its alignment along that side. Defaults to `start`. */\n align?: \"start\" | \"center\" | \"end\" | undefined;\n /** Names the content (`aria-label`), when nothing inside it does. */\n \"aria-label\"?: string | undefined;\n /** Merged with the content's own classes through `cn`. */\n className?: string | undefined;\n}\n\n/**\n * Floating content anchored to `trigger`, on the `overlay` surface with a shadow (floating\n * layers are the only ones that cast one).\n *\n * Controlled (`open` / `onOpenChange`) or not (`defaultOpen`). Focus moves into the content\n * when it opens and back to the trigger when it closes. Escape and a press outside close it.\n * The content carries `data-state` and `data-side`.\n */\nexport function Popover({\n trigger,\n children,\n open,\n defaultOpen,\n onOpenChange,\n side = \"bottom\",\n align = \"start\",\n \"aria-label\": ariaLabel,\n className,\n}: PopoverProps) {\n return (\n <RadixPopover.Root\n {...(open === undefined ? {} : { open })}\n {...(defaultOpen === undefined ? {} : { defaultOpen })}\n {...(onOpenChange === undefined ? {} : { onOpenChange })}\n >\n <RadixPopover.Trigger asChild>{trigger}</RadixPopover.Trigger>\n <RadixPopover.Portal>\n <RadixPopover.Content\n side={side}\n align={align}\n sideOffset={6}\n collisionPadding={8}\n aria-label={ariaLabel}\n className={cn(\n \"z-50 max-h-[var(--radix-popover-content-available-height)] overflow-y-auto\",\n \"rounded-panel border border-line bg-overlay p-1 text-sm text-fg shadow-lg shadow-black/25\",\n className,\n )}\n >\n {children}\n </RadixPopover.Content>\n </RadixPopover.Portal>\n </RadixPopover.Root>\n );\n}\n\n/**\n * Closes the surrounding `Popover` when its child is activated. Wraps one element (Radix\n * `asChild`).\n */\nexport function PopoverClose({\n children,\n}: {\n /** The one element that closes the popover. */\n children: ReactNode;\n}) {\n return <RadixPopover.Close asChild>{children}</RadixPopover.Close>;\n}\n","/*\n * A small vector — a position, a set of angles, a scale — edited as one row.\n *\n * Three separate `Field`s for x, y and z spend three labels and three rows on what a reader\n * thinks of as one quantity. This is one row: a quiet axis letter before each number, the\n * unit once at the end, and one name for the whole group.\n *\n * Each field keeps the text being typed while it has focus and shows the formatted value\n * otherwise (see `numberText.ts`), so `precision` never fights the cursor. Every valid\n * keystroke is reported, so whatever the vector drives follows the typing.\n */\n\nimport { useId, useState } from \"react\";\nimport type { KeyboardEvent } from \"react\";\n\nimport { NumberInput } from \"./Input\";\nimport { ReadoutStrip } from \"./Panel\";\nimport { cn } from \"./cn\";\nimport { formatNumber, parseNumber } from \"./numberText\";\n\nconst DEFAULT_LABELS = [\"x\", \"y\", \"z\", \"w\"];\n\n/** Props of `VectorInput`. */\nexport interface VectorInputProps {\n /** The components. Its length is the number of fields. */\n value: readonly number[];\n /** Called with the whole vector (a new array) whenever one component changes to a valid number. */\n onValueChange?: ((value: number[]) => void) | undefined;\n /** Each component's label, shown before its field and naming it. Defaults to x, y, z, w. */\n labels?: readonly string[] | undefined;\n /** The unit of every component, shown once after the row (`m`, `mm`, `°`). */\n unit?: string | undefined;\n /** The arrow-key step. Defaults to `any` (the browser then steps by 1). */\n step?: number | undefined;\n /** Decimals shown when a field is not being edited. Defaults to 3. */\n precision?: number | undefined;\n /** The smallest accepted component. */\n min?: number | undefined;\n /** The largest accepted component. */\n max?: number | undefined;\n /** Show the values as a compact mono readout instead of fields. */\n readOnly?: boolean | undefined;\n /** Blocks every field. */\n disabled?: boolean | undefined;\n /** Names the group (`role=\"group\"`). */\n \"aria-label\": string;\n /** Merged with the row's own classes through `cn`. */\n className?: string | undefined;\n}\n\n/**\n * A row of number fields editing one small vector, with the axis labels before the fields\n * and the unit once after them.\n *\n * Controlled. A `role=\"group\"` named by `aria-label`. Each field is named by the group and its\n * label (\"Translation x\"), so two vectors on one screen stay apart, and is described by the unit. While a field has focus it shows what is typed; on blur or Enter it\n * shows the value at `precision`; Escape restores the value it had on focus. With `readOnly`\n * it renders a `ReadoutStrip` (`x 0.100 m · y …`) instead. The row carries `data-readonly`\n * and `data-disabled`.\n */\nexport function VectorInput({\n value,\n onValueChange,\n labels = DEFAULT_LABELS,\n unit,\n step,\n precision = 3,\n min,\n max,\n readOnly = false,\n disabled = false,\n \"aria-label\": ariaLabel,\n className,\n}: VectorInputProps) {\n const unitId = useId();\n const labelOf = (index: number) => labels[index] ?? String(index);\n\n if (readOnly) {\n return (\n <div role=\"group\" aria-label={ariaLabel} data-readonly=\"\" className={cn(\"min-w-0\", className)}>\n <ReadoutStrip\n items={value.map((component, index) => ({\n label: labelOf(index),\n value: unit ? `${formatNumber(component, precision)} ${unit}` : formatNumber(component, precision),\n }))}\n />\n </div>\n );\n }\n\n const change = (index: number, component: number) => {\n if (component === value[index]) return;\n onValueChange?.(value.map((current, i) => (i === index ? component : current)));\n };\n\n return (\n <div\n role=\"group\"\n aria-label={ariaLabel}\n data-disabled={disabled ? \"\" : undefined}\n className={cn(\"flex min-w-0 items-center gap-1.5\", className)}\n >\n {value.map((component, index) => (\n // The components are positional: index is their identity.\n // oxlint-disable-next-line react/no-array-index-key -- see above\n <label key={index} className=\"flex min-w-0 flex-1 items-center gap-1\">\n <span className=\"shrink-0 font-mono text-[11px] text-fg-subtle\">{labelOf(index)}</span>\n <ComponentField\n name={`${ariaLabel} ${labelOf(index)}`}\n value={component}\n precision={precision}\n step={step}\n min={min}\n max={max}\n disabled={disabled}\n describedBy={unit ? unitId : undefined}\n onCommit={(next) => change(index, next)}\n />\n </label>\n ))}\n {unit && (\n <span id={unitId} className=\"shrink-0 font-mono text-xs text-fg-subtle\">\n {unit}\n </span>\n )}\n </div>\n );\n}\n\n/** One component: formatted at rest, the typed text while focused. */\nfunction ComponentField({\n name,\n value,\n precision,\n step,\n min,\n max,\n disabled,\n describedBy,\n onCommit,\n}: {\n name: string;\n value: number;\n precision: number;\n step: number | undefined;\n min: number | undefined;\n max: number | undefined;\n disabled: boolean;\n describedBy: string | undefined;\n onCommit: (value: number) => void;\n}) {\n // Non-null while the field is being edited: the text as typed, and the value on focus.\n const [draft, setDraft] = useState<{ text: string; initial: number } | null>(null);\n const formatted = formatNumber(value, precision);\n\n const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n if (event.key === \"Enter\") {\n setDraft((current) => current && { ...current, text: formatted });\n } else if (event.key === \"Escape\" && draft) {\n event.preventDefault();\n onCommit(draft.initial);\n setDraft({ ...draft, text: formatNumber(draft.initial, precision) });\n }\n };\n\n return (\n <NumberInput\n value={draft?.text ?? formatted}\n step={step ?? \"any\"}\n min={min}\n max={max}\n disabled={disabled}\n aria-label={name}\n aria-describedby={describedBy}\n onFocus={() => setDraft({ text: formatted, initial: value })}\n onBlur={() => setDraft(null)}\n onKeyDown={onKeyDown}\n onChange={(event) => {\n const text = event.currentTarget.value;\n setDraft((current) => ({ text, initial: current?.initial ?? value }));\n const parsed = parseNumber(text);\n if (parsed !== null) onCommit(parsed);\n }}\n className=\"min-w-0 px-1.5\"\n />\n );\n}\n","/*\n * A rigid transform, edited the way people think about one.\n *\n * Poses travel as `{ rotation: [qx, qy, qz, qw], translation: [tx, ty, tz] }` — the SE(3) wire\n * form — because a quaternion is the honest representation. Nobody edits one by hand,\n * though: a rotation is read and typed as roll, pitch and yaw in degrees. This component\n * shows that view and writes back the wire form.\n *\n * It owns no rotation mathematics. Which Euler convention \"roll, pitch, yaw\" means — the\n * axis order, intrinsic or extrinsic — is a decision of the 3D package that defines the\n * frames, so the conversion is passed in as a `RotationView`. This package stays pure UI.\n */\n\nimport { useState } from \"react\";\n\nimport { byDensity, useDensity } from \"./Density\";\nimport { ReadoutStrip } from \"./Panel\";\nimport { cn } from \"./cn\";\nimport { formatNumber } from \"./numberText\";\nimport { VectorInput } from \"./VectorInput\";\n\n/** Three numbers: a translation `[tx, ty, tz]`, or Euler angles. */\nexport type Vec3 = readonly [number, number, number];\n\n/** A unit quaternion in the wire order `[qx, qy, qz, qw]` (scalar last). */\nexport type Quaternion = readonly [number, number, number, number];\n\n/**\n * An SE(3) pose in the wire form (`a_se3_b` as nalgebra's `Isometry3` serialises it):\n * translation in metres, rotation as a unit quaternion `[qx, qy, qz, qw]`.\n */\nexport interface PoseValue {\n /** The rotation, `[qx, qy, qz, qw]`. */\n rotation: Quaternion;\n /** The translation `[tx, ty, tz]`, in metres. */\n translation: Vec3;\n}\n\n/**\n * How a rotation is shown as three angles: the conversion both ways, and the angles' names.\n * Supplied by the package that owns the frame conventions; `PoseInput` only calls it.\n */\nexport interface RotationView {\n /** The quaternion as three angles, in radians, in the order of `labels`. */\n toEuler: (rotation: Quaternion) => Vec3;\n /** Three angles in radians, in the order of `labels`, as a unit quaternion `[qx, qy, qz, qw]`. */\n fromEuler: (angles: Vec3) => Quaternion;\n /** The angles' names, shown before each field. Defaults to roll, pitch, yaw. */\n labels?: readonly [string, string, string] | undefined;\n}\n\n/** Props of `PoseInput`. */\nexport interface PoseInputProps {\n /** The pose, in the wire form. */\n value: PoseValue;\n /** Called with a new pose (new arrays) whenever a component changes. */\n onValueChange?: ((value: PoseValue) => void) | undefined;\n /** The quaternion ↔ angles conversion and the angles' names. Required: see `RotationView`. */\n rotationView: RotationView;\n /** Show and edit the translation in metres (the default) or millimetres. The value is always metres. */\n translationUnit?: \"m\" | \"mm\" | undefined;\n /** Decimals of the translation. Defaults to 4 for m, 1 for mm. */\n translationPrecision?: number | undefined;\n /** Decimals of the angles, in degrees. Defaults to 2. */\n rotationPrecision?: number | undefined;\n /** Show a compact mono readout instead of fields. */\n readOnly?: boolean | undefined;\n /** Blocks every field. */\n disabled?: boolean | undefined;\n /** Names the pose (`role=\"group\"`). Defaults to \"Pose\". */\n \"aria-label\"?: string | undefined;\n /** Merged with the component's own classes through `cn`. */\n className?: string | undefined;\n}\n\nconst DEGREES = 180 / Math.PI;\nconst DEFAULT_ANGLE_LABELS = [\"roll\", \"pitch\", \"yaw\"] as const;\n\nconst sameQuaternion = (a: Quaternion, b: Quaternion) => a.every((component, index) => component === b[index]);\n\n/**\n * An SE(3) pose shown and edited as a translation (m or mm) and three angles in degrees,\n * reading and writing the wire form `{ rotation: [qx, qy, qz, qw], translation: [tx, ty, tz] }`.\n *\n * The angles come from `rotationView.toEuler` and go back through `rotationView.fromEuler`.\n * The angles as typed are kept while the pose they produced is the one shown, so a yaw of\n * 190° is not redisplayed as −170° and a gimbal-locked pitch does not scramble the other two\n * angles mid-edit.\n *\n * A `role=\"group\"` named by `aria-label`, holding two `VectorInput` groups (\"Translation\",\n * \"Rotation\"). With `readOnly` it renders a `ReadoutStrip` instead\n * (`t 0.1000 0.2000 0.3000 m · rpy 0.00 90.00 0.00 °`). The root carries `data-readonly`.\n */\nexport function PoseInput({\n value,\n onValueChange,\n rotationView,\n translationUnit = \"m\",\n translationPrecision,\n rotationPrecision = 2,\n readOnly = false,\n disabled = false,\n \"aria-label\": ariaLabel = \"Pose\",\n className,\n}: PoseInputProps) {\n const density = useDensity();\n // The angles last typed, and the quaternion they became.\n const [typed, setTyped] = useState<{ rotation: Quaternion; angles: Vec3 } | null>(null);\n\n const scale = translationUnit === \"mm\" ? 1000 : 1;\n const tPrecision = translationPrecision ?? (translationUnit === \"mm\" ? 1 : 4);\n const translation = value.translation.map((component) => component * scale);\n const radians = typed !== null && sameQuaternion(typed.rotation, value.rotation) ? typed.angles : rotationView.toEuler(value.rotation);\n const degrees = radians.map((angle) => angle * DEGREES);\n const angleLabels = rotationView.labels ?? DEFAULT_ANGLE_LABELS;\n\n if (readOnly) {\n const join = (values: readonly number[], precision: number) => values.map((component) => formatNumber(component, precision)).join(\" \");\n return (\n <div role=\"group\" aria-label={ariaLabel} data-readonly=\"\" className={cn(\"min-w-0\", className)}>\n <ReadoutStrip\n items={[\n { label: \"t\", value: `${join(translation, tPrecision)} ${translationUnit}` },\n { label: \"rpy\", value: `${join(degrees, rotationPrecision)} °` },\n ]}\n />\n </div>\n );\n }\n\n const onTranslation = (next: number[]) => {\n const [x = 0, y = 0, z = 0] = next.map((component) => component / scale);\n onValueChange?.({ rotation: value.rotation, translation: [x, y, z] });\n };\n\n const onRotation = (next: number[]) => {\n const [a = 0, b = 0, c = 0] = next.map((angle) => angle / DEGREES);\n const angles: Vec3 = [a, b, c];\n const rotation = rotationView.fromEuler(angles);\n setTyped({ rotation, angles });\n onValueChange?.({ rotation, translation: value.translation });\n };\n\n const rowLabel = cn(\"text-fg-muted\", byDensity(density, \"text-xs\", \"text-[11px]\"));\n\n return (\n <div\n role=\"group\"\n aria-label={ariaLabel}\n className={cn(\n \"grid min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center gap-x-2\",\n byDensity(density, \"gap-y-2\", \"gap-y-1\"),\n className,\n )}\n >\n <span aria-hidden className={rowLabel}>\n Translation\n </span>\n <VectorInput\n aria-label=\"Translation\"\n value={translation}\n onValueChange={onTranslation}\n unit={translationUnit}\n precision={tPrecision}\n step={translationUnit === \"mm\" ? 1 : 0.001}\n disabled={disabled}\n />\n <span aria-hidden className={rowLabel}>\n Rotation\n </span>\n <VectorInput\n aria-label=\"Rotation\"\n value={degrees}\n onValueChange={onRotation}\n labels={angleLabels}\n unit=\"°\"\n precision={rotationPrecision}\n step={1}\n disabled={disabled}\n />\n </div>\n );\n}\n","/*\n * A choice among two or three, shown as two or three things.\n *\n * The control for a closed set the app can enumerate up front -- `Literal[\"small\", \"medium\"]`, an axis choice -- shown all at once, no popup, no typing, and no way to enter\n * a value the backend or algorithm would reject.\n *\n * Built on native radios rather than a headless package: a radio group in one `name` gets\n * roving focus and arrow-key navigation from the platform, which is the entire reason to\n * reach for a library here.\n *\n * **`\"\"` means unset**, and this control keeps that true without showing it. The segment\n * highlighted is always the *effective* value, so the reader sees what will happen;\n * choosing the segment that matches the default stores `\"\"` rather than the value, so the\n * default is still sent by nobody and defined only in one place. See `api/schemaForm.ts`\n * for why that rule is worth this much care.\n */\n\nimport { useId } from \"react\";\n\nimport type { ButtonSize } from \"./Button\";\nimport { useFieldDescription } from \"./Field\";\nimport { cn } from \"./cn\";\n\n/**\n * The segment's own box, by size. `sm` is the toolbar size and the default. `md` sets the\n * height explicitly rather than leaving it to padding plus line-height: the strip around it\n * is `h-8` with a 1px border and `p-0.5`, which leaves 26px of content box, and `h-full`\n * fills exactly that whatever the font's line height turns out to be.\n */\nconst SEGMENT_SIZE_CLASSES: Record<ButtonSize, string> = {\n sm: \"px-2.5 py-1 text-xs\",\n md: \"flex h-full items-center px-3.5 text-sm\",\n};\n\n/**\n * A choice among two or three, shown as a strip of segments (native radios in one group, so\n * arrow keys move between them).\n *\n * **`\"\"` means unset.** The highlighted segment is always the *effective* value — `value`,\n * or `defaultValue` while `value` is `\"\"` — and choosing the segment that matches\n * `defaultValue` reports `\"\"`, so the default stays defined in one place. Each segment\n * carries `data-state=\"checked\"` or `\"unchecked\"`; the group carries `data-disabled`.\n *\n * Toolbar-sized by default. In a row with a comfortable-density `Input`, `Select` or\n * `Button`, pass `size=\"md\"` and the strip's outer height matches theirs.\n */\nexport function SegmentedControl({\n value,\n defaultValue,\n options,\n onValueChange,\n disabled = false,\n \"aria-label\": ariaLabel,\n size = \"sm\",\n className,\n}: {\n /** `\"\"` when the field is untouched. */\n value: string;\n /** What applies when nothing is sent. Highlighted while `value` is `\"\"`. */\n defaultValue?: string | undefined;\n /** The segments, in order. */\n options: { value: string; label: string }[];\n /** Called with the chosen value, or `\"\"` when the default's segment is chosen. */\n onValueChange: (value: string) => void;\n /** Blocks every segment. */\n disabled?: boolean | undefined;\n /** Names the radio group. */\n \"aria-label\"?: string | undefined;\n /**\n * The strip's height: `sm` (the default) is the toolbar size, about 30px with `text-xs`\n * labels; `md` is exactly 32px (`h-8`) outside, border included, with `text-sm` labels, the\n * height of a comfortable-density `Input` or `Button`. Not derived from the density in\n * force, so a consumer that never passes it renders as it always did.\n */\n size?: ButtonSize | undefined;\n /** Merged with the strip's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const nameId = useId();\n const described = useFieldDescription();\n const effective = value === \"\" ? defaultValue : value;\n\n return (\n <div\n role=\"radiogroup\"\n aria-label={ariaLabel}\n aria-describedby={described[\"aria-describedby\"]}\n data-disabled={disabled ? \"\" : undefined}\n className={cn(\n // `shrink-0` because this is a control, not filler: in a crowded toolbar a flex item at\n // its default `1 1 auto` gives up width first, and the segment labels are what pay for\n // it. Let the row scroll instead of squeezing the choice.\n \"inline-flex w-fit max-w-full shrink-0 items-center gap-0.5 rounded-control border border-line-strong bg-raised p-0.5\",\n size === \"md\" && \"h-8\",\n disabled && \"opacity-50\",\n className,\n )}\n >\n {options.map((option) => {\n const checked = option.value === effective;\n return (\n <label\n key={option.value}\n data-state={checked ? \"checked\" : \"unchecked\"}\n className={cn(\n // `whitespace-nowrap` or a two-word label's min-content width is its longest\n // *word*, and \"Side by side\" is free to stack onto three lines inside a\n // fixed-height strip that then clips it.\n \"relative cursor-pointer whitespace-nowrap rounded-[0.3rem]\",\n SEGMENT_SIZE_CLASSES[size],\n \"font-medium transition-colors\",\n \"has-focus-visible:outline-2 has-focus-visible:outline-offset-1 has-focus-visible:outline-signal\",\n checked ? \"bg-surface text-fg shadow-sm\" : \"text-fg-muted hover:text-fg\",\n disabled && \"cursor-not-allowed\",\n )}\n >\n <input\n type=\"radio\"\n name={nameId}\n value={option.value}\n checked={checked}\n disabled={disabled}\n onChange={() =>\n // Selecting the default returns the field to unset: same effective value,\n // nothing sent, and the default keeps living in exactly one place.\n onValueChange(option.value === defaultValue ? \"\" : option.value)\n }\n className=\"absolute inset-0 cursor-pointer opacity-0\"\n />\n {option.label}\n </label>\n );\n })}\n </div>\n );\n}\n","/*\n * A picker with a list you can actually see.\n *\n * A native `<select>` cannot style its option list on macOS, so the popup arrives in the OS\n * palette in the middle of an otherwise deliberate instrument panel, and it cannot render a\n * two-line option or a disabled one with a reason.\n *\n * The one contract worth reading before changing this: **`\"\"` means unset.** Radix reserves\n * the empty string internally -- an `Item` may not carry it -- so an explicit \"use the\n * default\" entry travels under a sentinel and is mapped back at the boundary. Callers only\n * ever see `\"\"`.\n */\n\nimport * as RadixSelect from \"@radix-ui/react-select\";\nimport { Check, ChevronDown } from \"lucide-react\";\nimport { useState } from \"react\";\n\nimport { useFieldDescription } from \"./Field\";\nimport { useControlHeight } from \"./Input\";\nimport { cn, focusRingInset } from \"./cn\";\n\nconst UNSET_SENTINEL = \"__unset__\";\n\n/** One entry of a `Select`'s list. */\nexport type SelectOption = {\n /** What `onValueChange` reports. Must not be `\"\"`, which means unset. */\n value: string;\n /** What the list and the trigger show. */\n label: string;\n /** Shown quietly after the label -- a strategy, a count, a reason it is unavailable. */\n note?: string;\n /** Shown but not choosable; say why in `note`. */\n disabled?: boolean;\n};\n\n/**\n * A picker whose option list is styled with the app, with room for a quiet note per option.\n *\n * Controlled, and **`\"\"` means unset**: the trigger then shows `placeholder`, and the\n * `unsetLabel` entry (when given) reports `\"\"`. The trigger carries Radix's `data-state`\n * (`open`/`closed`), `data-placeholder` while unset and `data-disabled`; inside a `Field` it\n * is described by the field's description and error.\n */\nexport function Select({\n value,\n onValueChange,\n options,\n placeholder = \"Choose…\",\n unsetLabel,\n disabled = false,\n \"aria-label\": ariaLabel,\n \"aria-describedby\": ownDescribedBy,\n className,\n}: {\n /** The chosen option's value, or `\"\"` for unset. */\n value: string;\n /** Called with the chosen value, or `\"\"` for the unset entry. */\n onValueChange: (value: string) => void;\n /** The list, in order. */\n options: SelectOption[];\n /** Shown on the trigger while the value is unset. Defaults to \"Choose…\". */\n placeholder?: string | undefined;\n /** When given, adds a leading entry that returns the field to unset. */\n unsetLabel?: string | undefined;\n /** Blocks the trigger. */\n disabled?: boolean | undefined;\n /** Names the trigger, where no `<label>` does. */\n \"aria-label\"?: string | undefined;\n /** Ids of elements describing the trigger, merged with a surrounding `Field`'s. */\n \"aria-describedby\"?: string | undefined;\n /** Merged with the trigger's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const [open, setOpen] = useState(false);\n const described = useFieldDescription(ownDescribedBy);\n // The trigger is a control like any input: 32 px comfortable, 28 px compact.\n const height = useControlHeight();\n\n return (\n <RadixSelect.Root\n // Controlled for its whole lifetime: Radix treats `value=\"\"` as \"no selection\" and shows\n // the placeholder, which is exactly this component's unset.\n value={value}\n onValueChange={(next) => onValueChange(next === UNSET_SENTINEL ? \"\" : next)}\n open={open}\n onOpenChange={setOpen}\n disabled={disabled}\n >\n <RadixSelect.Trigger\n aria-label={ariaLabel}\n {...described}\n // While the list is open Radix hides everything outside it from assistive technology\n // (`aria-hidden`), this trigger included; a hidden element must not stay in the tab\n // order. Focus returns to it on close all the same.\n tabIndex={open ? -1 : undefined}\n className={cn(\n \"flex w-full items-center justify-between gap-2 rounded-control border border-line-strong bg-raised px-2.5 text-sm text-fg\",\n height,\n \"transition-colors hover:border-fg-subtle\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n \"data-[placeholder]:text-fg-subtle\",\n focusRingInset,\n className,\n )}\n >\n <RadixSelect.Value placeholder={placeholder} />\n <RadixSelect.Icon className=\"shrink-0 text-fg-subtle\">\n <ChevronDown className=\"size-3.5\" />\n </RadixSelect.Icon>\n </RadixSelect.Trigger>\n\n <RadixSelect.Portal>\n <RadixSelect.Content\n position=\"popper\"\n sideOffset={4}\n className={cn(\n \"z-50 max-h-72 min-w-(--radix-select-trigger-width) overflow-hidden\",\n \"rounded-panel border border-line bg-overlay shadow-lg shadow-black/25\",\n )}\n >\n <RadixSelect.Viewport className=\"p-1\">\n {unsetLabel !== undefined && <SelectItem value={UNSET_SENTINEL} label={unsetLabel} muted />}\n {options.map((option) => (\n <SelectItem key={option.value} value={option.value} label={option.label} note={option.note} disabled={option.disabled} />\n ))}\n </RadixSelect.Viewport>\n </RadixSelect.Content>\n </RadixSelect.Portal>\n </RadixSelect.Root>\n );\n}\n\nfunction SelectItem({\n value,\n label,\n note,\n disabled,\n muted = false,\n}: {\n value: string;\n label: string;\n note?: string | undefined;\n disabled?: boolean | undefined;\n muted?: boolean;\n}) {\n return (\n <RadixSelect.Item\n value={value}\n disabled={disabled ?? false}\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-control py-1.5 pr-2 pl-7 text-sm outline-none select-none\",\n \"data-highlighted:bg-raised data-highlighted:text-fg\",\n \"data-disabled:pointer-events-none data-disabled:opacity-45\",\n muted ? \"text-fg-muted\" : \"text-fg\",\n )}\n >\n <RadixSelect.ItemIndicator className=\"absolute left-2 text-signal\">\n <Check className=\"size-3.5\" />\n </RadixSelect.ItemIndicator>\n <RadixSelect.ItemText>{label}</RadixSelect.ItemText>\n {note && <span className=\"ml-auto font-mono text-xs text-fg-subtle\">{note}</span>}\n </RadixSelect.Item>\n );\n}\n","/*\n * A continuous value, with its number always on screen.\n *\n * A slider that drives what you are looking at -- a results threshold, an overlay opacity,\n * a caliper search range -- is a measurement instrument: the number matters as much as the\n * position, so it is rendered beside the track rather than left to a separate line of prose.\n */\n\nimport * as RadixSlider from \"@radix-ui/react-slider\";\nimport type { ReactNode } from \"react\";\n\nimport { useFieldDescription } from \"./Field\";\nimport { cn, focusRing } from \"./cn\";\n\n/**\n * A continuous value, with its number rendered beside the track.\n *\n * Controlled. The name (`aria-label`) and a surrounding `Field`'s description go on the\n * thumb — the element with `role=\"slider\"` that a keyboard and a screen reader operate.\n * Radix exposes `data-disabled` and `data-orientation` on the parts.\n */\nexport function Slider({\n value,\n onValueChange,\n onValueCommit,\n min = 0,\n max = 1,\n step = 0.01,\n readout,\n disabled = false,\n \"aria-label\": ariaLabel,\n \"aria-describedby\": ownDescribedBy,\n className,\n}: {\n /** The current value. */\n value: number;\n /** Called on every move. */\n onValueChange: (value: number) => void;\n /** Called once when a drag or a key press ends — for updates too expensive to run per move. */\n onValueCommit?: ((value: number) => void) | undefined;\n /** Defaults to 0. */\n min?: number | undefined;\n /** Defaults to 1. */\n max?: number | undefined;\n /** Defaults to 0.01. */\n step?: number | undefined;\n /** Rendered to the right of the track. Give it a fixed-width mono span for stability. */\n readout?: ReactNode;\n /** Blocks the thumb and dims the track. */\n disabled?: boolean | undefined;\n /** Names the slider (set on the thumb). */\n \"aria-label\"?: string | undefined;\n /** Ids of elements describing the slider, merged with a surrounding `Field`'s. */\n \"aria-describedby\"?: string | undefined;\n /** Merged with the row's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const described = useFieldDescription(ownDescribedBy);\n\n return (\n // Grows by default: a slider that does not fill the space it is given collapses to a\n // few pixels of track, which is unusable and reads as a rendering fault.\n <div className={cn(\"flex min-w-0 flex-1 items-center gap-3\", className)}>\n <RadixSlider.Root\n value={[value]}\n onValueChange={([next]) => next !== undefined && onValueChange(next)}\n onValueCommit={([next]) => next !== undefined && onValueCommit?.(next)}\n min={min}\n max={max}\n step={step}\n disabled={disabled}\n className={cn(\"relative flex h-5 min-w-0 flex-1 touch-none items-center select-none\", \"data-disabled:opacity-50\")}\n >\n <RadixSlider.Track className=\"relative h-1 grow overflow-hidden rounded-full bg-line\">\n <RadixSlider.Range className=\"absolute h-full bg-signal\" />\n </RadixSlider.Track>\n <RadixSlider.Thumb\n aria-label={ariaLabel}\n {...described}\n className={cn(\n \"block size-3.5 rounded-full border-2 border-signal bg-surface transition-shadow\",\n \"hover:shadow-[0_0_0_4px] hover:shadow-signal/20\",\n \"disabled:cursor-not-allowed\",\n focusRing,\n )}\n />\n </RadixSlider.Root>\n {readout && <span className=\"shrink-0 font-mono text-xs text-fg tabular-nums\">{readout}</span>}\n </div>\n );\n}\n","/*\n * The order of a sorted `Table`, as pure functions so it is tested without a DOM, and so an app\n * can reproduce the table's order (next/previous navigation, an export, a server that sorts the\n * same way) with `sortRows`.\n *\n * The order is deterministic across machines: strings go through one collator with a fixed\n * locale, so a server render and the browser that hydrates it put the rows in the same order.\n * A `Collator` is not DOM, so building it once at module scope keeps the package SSR-safe.\n */\n\nimport type { Column } from \"./Table\";\n\n/** Which way a column is sorted. Named as `aria-sort` names them: `\"ascending\"` puts the smallest first. */\nexport type SortDirection = \"ascending\" | \"descending\";\n\n/** The column a table is sorted by, and which way. Where a sort is expected, `null` means the rows' own order. */\nexport interface TableSort {\n /** The sorted column's `key`. */\n key: string;\n /** Which way. */\n direction: SortDirection;\n}\n\n/** The column fields that decide the order. */\ntype SortColumn<Row> = Pick<Column<Row>, \"key\" | \"sortable\" | \"sortValue\" | \"compare\">;\n\n/** Strings by a fixed English collation that reads digit runs as numbers: \"frame 9\" before \"frame 10\". */\nconst collator = new Intl.Collator(\"en\", { numeric: true });\n\n/**\n * Whether a column's header sorts the table: `sortable` if given, otherwise whether the column\n * says how to order (`sortValue` or `compare`).\n */\nexport function isSortable<Row>(column: Pick<Column<Row>, \"sortable\" | \"sortValue\" | \"compare\">): boolean {\n return column.sortable ?? (column.sortValue !== undefined || column.compare !== undefined);\n}\n\n/** The sort after a click on `column`'s header: its first direction, then the other, then none. */\nexport function nextSort<Row>(current: TableSort | null, column: Pick<Column<Row>, \"key\" | \"firstSort\">): TableSort | null {\n const first = column.firstSort ?? \"ascending\";\n if (current?.key !== column.key) return { key: column.key, direction: first };\n if (current.direction !== first) return null;\n return { key: column.key, direction: first === \"ascending\" ? \"descending\" : \"ascending\" };\n}\n\n/** A value with no place in the order: `null`, `undefined` or `NaN`. */\nfunction missing(value: number | string | null | undefined): boolean {\n return value === null || value === undefined || Number.isNaN(value);\n}\n\n/**\n * Two present values in ascending order: numbers by value (compared, not subtracted, so two\n * infinities tie), strings by the collator, and every number before every string.\n */\nfunction compareValues(a: number | string, b: number | string): number {\n if (typeof a === \"number\" && typeof b === \"number\") return a < b ? -1 : a > b ? 1 : 0;\n if (typeof a === \"string\" && typeof b === \"string\") return collator.compare(a, b);\n return typeof a === \"number\" ? -1 : 1;\n}\n\n/**\n * The display order of `rows` under `sort`, as indices into `rows`. `Table` renders through it\n * so each row keeps its original index for the row callbacks.\n */\nexport function sortOrder<Row>(rows: readonly Row[], columns: readonly SortColumn<Row>[], sort: TableSort | null | undefined): number[] {\n const order = rows.map((_, index) => index);\n if (!sort) return order;\n const column = columns.find((candidate) => candidate.key === sort.key);\n if (!column || !isSortable(column)) return order;\n const sign = sort.direction === \"descending\" ? -1 : 1;\n\n // Every comparison falls back to the original index, so equal rows keep their order in both\n // directions: descending is not ascending reversed, it is ascending with each run of ties kept.\n const { compare, sortValue } = column;\n if (compare) return order.sort((a, b) => sign * compare(rows[a]!, rows[b]!) || a - b);\n if (!sortValue) return order;\n\n // One call per row, not two per comparison: a `sortValue` may parse or look something up.\n const values = rows.map((row) => sortValue(row));\n return order.sort((a, b) => {\n const left = values[a];\n const right = values[b];\n const leftMissing = missing(left);\n const rightMissing = missing(right);\n // Missing values go last whichever way the column is sorted: a reader sorting by score in\n // either direction wants the rows that have one.\n if (leftMissing || rightMissing) return leftMissing === rightMissing ? a - b : leftMissing ? 1 : -1;\n return sign * compareValues(left!, right!) || a - b;\n });\n}\n\n/**\n * The rows in the order a `Table` with these columns shows them under `sort`.\n *\n * The sort is stable in both directions: rows that compare equal keep their order in `rows`.\n * With the column's `compare`, that function decides (reversed for `\"descending\"`). With its\n * `sortValue`, numbers compare by value and strings by a fixed English collation that reads\n * digit runs as numbers (\"frame 9\" before \"frame 10\"), the same on a server as in any\n * browser; numbers come before strings; and `null`, `undefined` and `NaN` go last in both\n * directions. A `null` sort, a `key` that names no column, and a column that is not sortable or\n * has neither function leave the order as given.\n *\n * @param rows - The rows, in their own order. Not modified.\n * @param columns - The table's columns; only `key`, `sortable`, `sortValue` and `compare` are read.\n * @param sort - The column and direction, or `null` for the rows' own order.\n * @returns A new array of the same rows, sorted.\n */\nexport function sortRows<Row>(\n rows: readonly Row[],\n columns: readonly Pick<Column<Row>, \"key\" | \"sortable\" | \"sortValue\" | \"compare\">[],\n sort: TableSort | null | undefined,\n): Row[] {\n return sortOrder(rows, columns, sort).map((index) => rows[index]!);\n}\n","/*\n * The arithmetic of a windowed table: which rows to render for a scroll position, and where\n * to scroll to bring a row into view. Pure, so it is tested on its own (`tableWindow.test.ts`,\n * `tableWindow.property.test.ts`) rather than through layout.\n *\n * Every row is `rowHeight` tall. The body starts below a sticky header `headerHeight` tall, and\n * the scroll container shows `viewportHeight` of the whole (header included).\n */\n\n/** The rows to render, as a half-open range of positions, and the space around them. */\nexport interface RowWindow {\n /** The first rendered position. */\n start: number;\n /** One past the last rendered position. */\n end: number;\n /** The height, in px, of the rows before `start`. */\n before: number;\n /** The height, in px, of the rows from `end` on. */\n after: number;\n}\n\n/** What the window depends on. Lengths in px. */\nexport interface WindowInput {\n rowCount: number;\n rowHeight: number;\n /** The scroll container's `scrollTop`. */\n scrollTop: number;\n /** The scroll container's visible height, header included. */\n viewportHeight: number;\n /** The sticky header's height. */\n headerHeight: number;\n /** Rows rendered beyond each edge of the visible ones. */\n overscan: number;\n}\n\n/**\n * The rows that intersect the visible part of the body, plus `overscan` on each side.\n * Degenerate input (no rows, a row height ≤ 0, NaN) gives an empty window.\n */\nexport function rowWindow({ rowCount, rowHeight, scrollTop, viewportHeight, headerHeight, overscan }: WindowInput): RowWindow {\n const count = Number.isFinite(rowCount) ? Math.max(0, Math.floor(rowCount)) : 0;\n if (count === 0 || !(rowHeight > 0)) return { start: 0, end: 0, before: 0, after: 0 };\n const extra = Math.max(0, Math.floor(overscan) || 0);\n // In the body's own coordinates the visible part is [scrollTop, scrollTop + viewport − header]:\n // the sticky header covers the top of the container.\n const top = Math.max(0, scrollTop || 0);\n const bottom = Math.max(top, top + Math.max(0, viewportHeight || 0) - Math.max(0, headerHeight || 0));\n const first = Math.min(count, Math.floor(top / rowHeight));\n const last = Math.min(count, Math.max(first + 1, Math.ceil(bottom / rowHeight)));\n const start = Math.max(0, first - extra);\n const end = Math.min(count, last + extra);\n return { start, end, before: start * rowHeight, after: (count - end) * rowHeight };\n}\n\n/**\n * The `scrollTop` that shows the row at `position` in full below the sticky header, moving\n * as little as possible: unchanged when it is already in view, else aligned to the nearer\n * edge. A viewport shorter than one row aligns the row's top.\n */\nexport function scrollTopFor(\n position: number,\n { rowHeight, scrollTop, viewportHeight, headerHeight }: Pick<WindowInput, \"rowHeight\" | \"scrollTop\" | \"viewportHeight\" | \"headerHeight\">,\n): number {\n const rowTop = position * rowHeight;\n const visible = viewportHeight - headerHeight;\n if (rowTop < scrollTop || visible < rowHeight) return rowTop;\n if (rowTop + rowHeight > scrollTop + visible) return rowTop + rowHeight - visible;\n return scrollTop;\n}\n\n/**\n * The position a navigation key moves to from `position`, among `rowCount` rows, `page` rows\n * to a screen; `null` for a key that does not navigate.\n */\nexport function navigate(key: string, position: number, rowCount: number, page: number): number | null {\n const last = rowCount - 1;\n if (last < 0) return null;\n const step = Math.max(1, Math.floor(page));\n const clamp = (p: number) => Math.min(last, Math.max(0, p));\n switch (key) {\n case \"ArrowDown\":\n return clamp(position + 1);\n case \"ArrowUp\":\n return clamp(position - 1);\n case \"PageDown\":\n return clamp(position + step);\n case \"PageUp\":\n return clamp(position - step);\n case \"Home\":\n return 0;\n case \"End\":\n return last;\n default:\n return null;\n }\n}\n","/*\n * A table.\n *\n * `numeric` is the column property that matters here. Quantities are set in mono with\n * tabular figures and aligned right, so a column of scores or measurements can be compared\n * by eye down its decimal point rather than read one row at a time.\n *\n * Sorting is per column and follows the sortable-table pattern of the ARIA Authoring\n * Practices: the header's content is a button, and only the sorted header carries\n * `aria-sort`. The order itself lives in `tableSort.ts`.\n *\n * `windowed` renders only the rows in view, between two spacer rows that keep the scroll\n * height, so a table of thousands of rows scrolls as fast as one of thirty. It stays a real\n * `<table>`: the rows that are rendered carry `aria-rowindex` and the table `aria-rowcount`, so\n * a reader still hears \"row 2,041 of 5,001\". The arithmetic lives in `tableWindow.ts`.\n */\n\nimport { ArrowDown, ArrowUp, ArrowUpDown } from \"lucide-react\";\nimport { type KeyboardEvent, type MouseEvent, type ReactNode, useLayoutEffect, useMemo, useRef, useState } from \"react\";\n\nimport { byDensity, useDensity } from \"./Density\";\nimport { cn, focusRing, focusRingInset } from \"./cn\";\nimport { isSortable, nextSort, type SortDirection, sortOrder, type TableSort } from \"./tableSort\";\nimport { navigate, rowWindow, scrollTopFor } from \"./tableWindow\";\n\n/** One column of a `Table`: its header and how to render a row's cell. */\nexport type Column<Row> = {\n /** Unique among the columns; the React key of its header and cells. */\n key: string;\n /** The `<th>` content. */\n header: ReactNode;\n /** Right-aligned, mono, tabular. Use for anything that is a quantity. */\n numeric?: boolean;\n /** A CSS width for the column, e.g. `\"6rem\"`. */\n width?: string;\n /** The cell's content for a row. */\n cell: (row: Row) => ReactNode;\n /**\n * Whether the header sorts the table. Defaults to `true` when the column has a `sortValue`\n * or a `compare`. Set it `true` with neither for a column the app sorts itself (see the\n * table's `manualSort`), or `false` to keep a column with a `sortValue` unsortable.\n */\n sortable?: boolean | undefined;\n /**\n * The value the column sorts by. Numbers compare by value; strings by a fixed English\n * collation that reads digit runs as numbers (\"frame 9\" before \"frame 10\"), so a server\n * render and the browser agree; numbers come before strings. `null`, `undefined` and `NaN`\n * go last in both directions.\n */\n sortValue?: ((row: Row) => number | string | null | undefined) | undefined;\n /**\n * A full ascending comparison, for an order one value cannot express (two fields, a rank):\n * negative when `a` comes first. Takes precedence over `sortValue`. Reversed for\n * `\"descending\"`; rows that compare equal keep their order in `rows` either way.\n */\n compare?: ((a: Row, b: Row) => number) | undefined;\n /**\n * The direction of the first click on the header. Defaults to `\"ascending\"`; `\"descending\"`\n * suits a score, where the best comes first.\n */\n firstSort?: SortDirection | undefined;\n};\n\n/**\n * Windowed rendering for a long table: only the rows in view, plus `overscan` on each side,\n * are in the DOM. The rows scroll under a sticky header drawn on `surface`, the panel colour.\n */\nexport interface TableWindowing {\n /**\n * Every body row's height in px. Rows are drawn exactly this tall and cells keep to one line\n * (longer content is cut with an ellipsis), so it must be at least the density's own row\n * height: 36 px comfortable, 22 px compact.\n */\n rowHeight: number;\n /**\n * The scroll area's height in px, header included. Leave it out to fill the height of the\n * container, which must then have one (a flex child with `min-h-0`, a fixed-height panel).\n */\n height?: number | undefined;\n /** Rows rendered beyond each edge of the view, so a fast scroll shows no gap. Defaults to 8. */\n overscan?: number | undefined;\n}\n\n/** Rows per screen before a windowed table has measured itself (a server render, the first paint). */\nconst INITIAL_ROWS = 20;\n\n/** The header icon for a sort state: the direction when sorted, both arrows when only sortable. */\nconst SORT_ICONS = { ascending: ArrowUp, descending: ArrowDown, none: ArrowUpDown } as const;\n\n/**\n * A table of rows, with numeric columns set in mono and right-aligned.\n *\n * With `onRowClick` the rows are keyboard-reachable (Tab, then Enter or Space) while keeping\n * their table semantics. The active row carries `aria-current` and `data-state=\"active\"`;\n * padding follows the density in force. With no rows it renders the `empty` message instead.\n *\n * A column with a `sortValue` or a `compare` gets a header button: each click moves its sort\n * from the column's `firstSort` direction to the other one, then back to the rows' own order.\n * The sorted header carries `aria-sort` and `data-sort` (`\"ascending\"` or `\"descending\"`);\n * other sortable headers carry `data-sort=\"none\"`. The sort is kept by the table (from\n * `defaultSort`) or controlled (`sort` with `onSortChange`); with `manualSort` the headers\n * show it and the rows render as given. Row callbacks always receive a row's index in `rows`,\n * whatever the order on screen.\n *\n * Activatable rows also move focus with the arrow keys, Page Up / Page Down and Home / End;\n * with `selectionFollowsFocus` each of those moves also activates the row it lands on.\n * With `windowed` the table scrolls itself (vertically, under a sticky header), renders only the\n * rows in view, and uses a fixed layout: give columns a `width` where the content needs it.\n */\nexport function Table<Row>({\n columns,\n rows,\n rowKey,\n empty = \"Nothing here.\",\n caption,\n className,\n onRowClick,\n isRowActive,\n onRowHover,\n sort: controlledSort,\n defaultSort = null,\n onSortChange,\n manualSort = false,\n windowed,\n selectionFollowsFocus = false,\n}: {\n /** The columns, in order. */\n columns: Column<Row>[];\n /** The rows, in their own order: the order shown while the table is not sorted. */\n rows: Row[];\n /** A stable key per row. `index` is the row's position in `rows`, whatever the sort. */\n rowKey: (row: Row, index: number) => string | number;\n /** Shown instead of the table when there are no rows. Defaults to \"Nothing here.\". */\n empty?: ReactNode;\n /** Names the table for assistive technology (a visually hidden `<caption>`). */\n caption?: string | undefined;\n /** Merged with the scroll wrapper's (or the empty message's) own classes through `cn`. */\n className?: string | undefined;\n /** Makes rows activatable. Keyboard-reachable, so a table used as a list of\n * destinations is not a mouse-only control. The event is passed so a caller can read\n * modifiers — a list kept in step with a canvas needs shift-range and meta-toggle to mean\n * the same thing in both places. `index` is the row's position in `rows`, whatever the\n * sort. */\n onRowClick?: (row: Row, index: number, event: MouseEvent<HTMLTableRowElement> | KeyboardEvent<HTMLTableRowElement>) => void;\n /** Marks the row that is current — the selected match, the open frame. Rendered as\n * `aria-current`, so it is announced rather than only tinted. `index` is the row's\n * position in `rows`, whatever the sort. */\n isRowActive?: (row: Row, index: number) => boolean;\n /** Pointer enter/leave, for tables kept in step with a canvas overlay.\n * `null` on leave. `index` is the row's position in `rows`, whatever the sort. */\n onRowHover?: (row: Row | null, index: number | null) => void;\n /**\n * The sort, controlled: a column `key` and a direction, or `null` for the rows' own order.\n * Leave it undefined and the table keeps the sort itself, starting from `defaultSort`.\n */\n sort?: TableSort | null | undefined;\n /** The sort the table starts with when `sort` is not given. Defaults to `null`, the rows' own order. */\n defaultSort?: TableSort | null | undefined;\n /**\n * Called on a header click with the new sort: the column's `firstSort` direction, then the\n * other, then `null`. A click on another column starts that column at its `firstSort`.\n */\n onSortChange?: ((sort: TableSort | null) => void) | undefined;\n /**\n * The app orders the rows (on a server, say): the headers show the sort and report clicks,\n * and the rows render exactly as given. Mark the columns it can sort `sortable: true`.\n */\n manualSort?: boolean | undefined;\n /**\n * Render only the rows in view, for a long table: every row `rowHeight` tall, in a scroll\n * area of `height` (or the container's height) under a sticky header. Sorting, row\n * callbacks and keyboard navigation work as without it.\n */\n windowed?: TableWindowing | undefined;\n /**\n * Activate a row as soon as the keyboard moves to it: each arrow key, Page Up / Page Down,\n * Home or End that lands on another row also calls `onRowClick` for that row, with the key\n * press as the event — selection follows focus, as in a list where the selected item shows\n * its detail beside the list. Clicks, Enter and Space work as without it. Leave it off when\n * activating a row is costly or navigates away: then a reader can move through the rows and\n * choose one. Needs `onRowClick`.\n */\n selectionFollowsFocus?: boolean | undefined;\n}) {\n const density = useDensity();\n const [ownSort, setOwnSort] = useState<TableSort | null>(defaultSort);\n const controlled = controlledSort !== undefined;\n const sort = controlled ? controlledSort : ownSort;\n // Indices into `rows`, so a row keeps its own index (and key) for the callbacks below.\n const order = useMemo(\n () => (manualSort ? rows.map((_, index) => index) : sortOrder(rows, columns, sort)),\n [rows, columns, sort, manualSort],\n );\n\n const changeSort = (column: Column<Row>) => {\n const next = nextSort(sort, column);\n if (!controlled) setOwnSort(next);\n onSortChange?.(next);\n };\n\n const rowHeight = windowed?.rowHeight ?? 0;\n const scrollRef = useRef<HTMLDivElement>(null);\n const headRef = useRef<HTMLTableSectionElement>(null);\n const bodyRef = useRef<HTMLTableSectionElement>(null);\n // The row to focus once it is rendered: a key press may scroll to a row that is not in the DOM yet.\n const pendingFocus = useRef<number | null>(null);\n const [scrollTop, setScrollTop] = useState(0);\n const [measured, setMeasured] = useState<{ viewport: number; header: number } | null>(null);\n const viewportHeight = measured?.viewport ?? windowed?.height ?? INITIAL_ROWS * rowHeight;\n const headerHeight = measured?.header ?? 0;\n const range = windowed\n ? rowWindow({ rowCount: order.length, rowHeight, scrollTop, viewportHeight, headerHeight, overscan: windowed.overscan ?? 8 })\n : { start: 0, end: order.length, before: 0, after: 0 };\n\n const isWindowed = windowed !== undefined;\n // With no rows the scroll area is replaced by the `empty` message: measure the new one when\n // the rows come back, rather than keep observing the one that went away.\n const hasRows = rows.length > 0;\n useLayoutEffect(() => {\n const scroller = scrollRef.current;\n if (!isWindowed || !hasRows || !scroller || typeof ResizeObserver === \"undefined\") return;\n const measure = () => {\n // A detached element measures 0, which would shrink the window to a row or two.\n if (!scroller.isConnected) return;\n const next = { viewport: scroller.clientHeight, header: headRef.current?.offsetHeight ?? 0 };\n setMeasured((last) => (last?.viewport === next.viewport && last.header === next.header ? last : next));\n };\n measure();\n // A new scroll area starts at the top, whatever the last one was scrolled to.\n setScrollTop(scroller.scrollTop);\n const observer = new ResizeObserver(measure);\n observer.observe(scroller);\n return () => observer.disconnect();\n }, [isWindowed, hasRows]);\n\n useLayoutEffect(() => {\n const target = pendingFocus.current;\n if (target === null) return;\n const row = bodyRef.current?.querySelector<HTMLElement>(`tr[data-position=\"${target}\"]`);\n if (!row) return;\n pendingFocus.current = null;\n row.focus({ preventScroll: isWindowed });\n });\n\n /**\n * Moves focus from the row at `position` for a navigation key, activating the new row when\n * selection follows focus; `true` when the key was one.\n */\n const moveFocus = (event: KeyboardEvent<HTMLTableRowElement>, position: number): boolean => {\n const page = windowed ? Math.floor((viewportHeight - headerHeight) / rowHeight) : 10;\n const target = navigate(event.key, position, order.length, page);\n if (target === null) return false;\n if (selectionFollowsFocus && onRowClick && target !== position) {\n const index = order[target]!;\n onRowClick(rows[index]!, index, event);\n }\n pendingFocus.current = target;\n const scroller = scrollRef.current;\n if (windowed && scroller) {\n const top = scrollTopFor(target, { rowHeight, scrollTop: scroller.scrollTop, viewportHeight, headerHeight });\n scroller.scrollTop = top;\n // Render the target's window now rather than on the scroll event, so it can take focus.\n setScrollTop(scroller.scrollTop);\n }\n // A row already in the DOM takes focus now: when nothing scrolled, nothing re-renders, and\n // the pending focus would wait for a render that never comes.\n const row = bodyRef.current?.querySelector<HTMLElement>(`tr[data-position=\"${target}\"]`);\n if (row) {\n pendingFocus.current = null;\n row.focus({ preventScroll: windowed !== undefined });\n }\n return true;\n };\n\n if (rows.length === 0) {\n return <p className={cn(\"text-center text-fg-muted\", byDensity(density, \"py-6 text-sm\", \"py-3 text-xs\"), className)}>{empty}</p>;\n }\n\n const spacer = (height: number) => (\n // Stands in for the rows outside the window. Hidden: it is no row of the table.\n <tr aria-hidden=\"true\" style={{ height }}>\n <td colSpan={columns.length} className=\"p-0\" />\n </tr>\n );\n\n // A scroll area must be reachable from the keyboard: through its rows or header buttons when\n // it has them, else as a tab stop of its own.\n const scrollStop = windowed !== undefined && !onRowClick && !columns.some(isSortable);\n\n return (\n <div\n ref={scrollRef}\n className={cn(\"w-full\", windowed ? scrollStop && focusRingInset : \"overflow-x-auto\", className)}\n // What the window arithmetic relies on is inline rather than a class, so it holds under\n // any stylesheet: the scroll area, the fixed layout, the sticky header, the row height.\n style={windowed ? { overflow: \"auto\", height: windowed.height ?? \"100%\" } : undefined}\n onScroll={windowed ? (event) => setScrollTop(event.currentTarget.scrollTop) : undefined}\n {...(scrollStop ? { tabIndex: 0, role: \"region\", \"aria-label\": caption ?? \"Table\" } : {})}\n >\n <table\n className={cn(\"w-full text-left\", byDensity(density, \"text-sm\", \"text-[11px]\"))}\n style={windowed ? { tableLayout: \"fixed\" } : undefined}\n aria-rowcount={windowed ? order.length + 1 : undefined}\n >\n {caption && <caption className=\"sr-only\">{caption}</caption>}\n <thead ref={headRef}>\n <tr className=\"border-b border-line\" aria-rowindex={windowed ? 1 : undefined}>\n {columns.map((column) => {\n const sortable = isSortable(column);\n const direction = sortable && sort?.key === column.key ? sort.direction : undefined;\n const SortIcon = SORT_ICONS[direction ?? \"none\"];\n return (\n <th\n key={column.key}\n scope=\"col\"\n style={\n windowed\n ? { position: \"sticky\", top: 0, zIndex: 1, ...(column.width ? { width: column.width } : {}) }\n : column.width\n ? { width: column.width }\n : undefined\n }\n // Only the sorted header: `aria-sort=\"none\"` on every other one would be\n // announced on each, and tells a reader nothing.\n aria-sort={direction}\n data-sort={sortable ? (direction ?? \"none\") : undefined}\n className={cn(\n // `last:pr-0`, as on the body cells: the last column's header ends where\n // its values do, so a numeric header sits flush with its numbers.\n \"font-medium text-fg-muted last:pr-0\",\n byDensity(density, \"pb-2 pr-3 text-xs\", \"pb-1 pr-2 text-[10px]\"),\n column.numeric && \"text-right\",\n // A sticky cell leaves its row's border behind: it draws its own rule.\n windowed && \"bg-surface shadow-[inset_0_-1px_0_var(--line)]\",\n )}\n >\n {sortable ? (\n <button\n type=\"button\"\n onClick={() => changeSort(column)}\n className={cn(\n // Block-level `flex`, not `inline-flex`: an inline box would sit on the\n // cell's line box and make a sortable header row 2px taller than a plain\n // one. The padding, cancelled by the margin, gives the focus ring room\n // without moving the label off the column's edge.\n \"-mx-1 flex w-fit max-w-full cursor-pointer items-center gap-1 rounded-control px-1\",\n \"font-medium transition-colors hover:text-fg\",\n focusRing,\n // The arrow sits on the inner side of a right-aligned header, so the\n // label stays flush with the numbers under it.\n column.numeric ? \"ml-auto flex-row-reverse text-right\" : \"text-left\",\n direction && \"text-fg\",\n )}\n >\n {column.header}\n <SortIcon\n aria-hidden\n className={cn(\"shrink-0\", byDensity(density, \"size-3\", \"size-2.5\"), !direction && \"text-fg-subtle\")}\n />\n </button>\n ) : (\n column.header\n )}\n </th>\n );\n })}\n </tr>\n </thead>\n <tbody ref={bodyRef}>\n {range.before > 0 && spacer(range.before)}\n {order.slice(range.start, range.end).map((index, offset) => {\n const row = rows[index]!;\n const active = isRowActive?.(row, index) ?? false;\n const position = range.start + offset;\n return (\n <tr\n key={rowKey(row, index)}\n data-position={position}\n aria-rowindex={windowed ? position + 2 : undefined}\n style={windowed ? { height: rowHeight } : undefined}\n data-state={active ? \"active\" : undefined}\n className={cn(\n // A windowed row's rule is a shadow on its cells: a collapsed border would add\n // half a pixel to every row, and the arithmetic counts whole rows.\n !windowed && \"border-b border-line/60 last:border-0\",\n onRowClick && \"cursor-pointer hover:bg-raised\",\n active && \"bg-signal/10\",\n )}\n tabIndex={onRowClick ? 0 : undefined}\n // Deliberately *not* `role=\"button\"`. Overriding a `<tr>`'s implicit `row`\n // makes its `<td>`s' implicit `cell` invalid too — the cells have no row to\n // belong to — so a screen reader stops announcing the table as a table and a\n // reader loses the column each value came from, which on a table of\n // quantities is the whole content. `tabIndex` plus the Enter/Space handler\n // below is what makes the row reachable; the role stays what the element is.\n aria-current={active ? true : undefined}\n onClick={onRowClick ? (event) => onRowClick(row, index, event) : undefined}\n onKeyDown={\n onRowClick\n ? (event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n onRowClick(row, index, event);\n // Navigation keys belong to a control inside the row while it has focus.\n } else if (event.target === event.currentTarget && moveFocus(event, position)) event.preventDefault();\n }\n : undefined\n }\n onPointerEnter={onRowHover ? () => onRowHover(row, index) : undefined}\n onPointerLeave={onRowHover ? () => onRowHover(null, null) : undefined}\n >\n {columns.map((column) => (\n <td\n key={column.key}\n className={cn(\n \"align-middle text-fg last:pr-0\",\n byDensity(density, \"py-2 pr-3\", \"py-0.5 pr-2\"),\n column.numeric && \"text-right font-mono tabular-nums\",\n windowed && \"shadow-[inset_0_-1px_0_color-mix(in_oklab,var(--line)_60%,transparent)]\",\n )}\n // One line per row keeps every row `rowHeight` tall.\n style={windowed ? { whiteSpace: \"nowrap\", overflow: \"hidden\", textOverflow: \"ellipsis\" } : undefined}\n >\n {column.cell(row)}\n </td>\n ))}\n </tr>\n );\n })}\n {range.after > 0 && spacer(range.after)}\n </tbody>\n </table>\n </div>\n );\n}\n","/*\n * Multi-line text entry.\n *\n * The auto-grow is measured, not CSS: `field-sizing: content` would need no script, but it\n * is not in every engine the packages run in (the desktop shells embed WebKit), and it\n * cannot stop at a row count. The textarea renders `minRows` tall on the server, so the\n * first paint already has the right floor; after mount its height follows the content\n * between the two bounds.\n */\n\nimport { type ChangeEvent, type ComponentProps, type Ref, useCallback, useLayoutEffect, useRef } from \"react\";\n\nimport { useFieldDescription } from \"./Field\";\nimport { controlClasses } from \"./Input\";\nimport { cn } from \"./cn\";\n\n/**\n * Props of `Textarea`: every `<textarea>` prop, `ref` included, plus an invalid state, the\n * mono face, and growing with the content between `minRows` and `maxRows`.\n */\nexport type TextareaProps = ComponentProps<\"textarea\"> & {\n /** Called with the new text on every edit, alongside the native `onChange`. */\n onValueChange?: ((value: string) => void) | undefined;\n /**\n * Marks the value as wrong: `aria-invalid`, `data-invalid` and a border in the defect\n * colour. Inside a `Field` with an `error` it is on without this. Say what is wrong in\n * text too (the field's `error`): the border alone is a colour.\n */\n invalid?: boolean | undefined;\n /** Sets the text in IBM Plex Mono, for code, JSON, lists, or strings whose characters all matter. */\n mono?: boolean | undefined;\n /**\n * Grows with the content from this many lines. Set it or `maxRows` to turn growing on;\n * `rows` is then ignored.\n */\n minRows?: number | undefined;\n /** Stops growing at this many lines and scrolls beyond them. Set it or `minRows` to turn growing on. */\n maxRows?: number | undefined;\n};\n\n/** Sets a ref of either kind. */\nfunction assign<T>(ref: Ref<T> | undefined, value: T | null): void {\n if (typeof ref === \"function\") ref(value);\n else if (ref) ref.current = value;\n}\n\n/**\n * Fits a textarea's height to its content between `minRows` and `maxRows` lines, and scrolls\n * only past the cap.\n */\nfunction fit(node: HTMLTextAreaElement, minRows: number, maxRows: number | undefined): void {\n const style = getComputedStyle(node);\n // `normal` is no length: take the usual 1.2 of the font size.\n const line = Number.parseFloat(style.lineHeight) || Number.parseFloat(style.fontSize) * 1.2 || 20;\n const border = Number.parseFloat(style.borderTopWidth) + Number.parseFloat(style.borderBottomWidth);\n const chrome = Number.parseFloat(style.paddingTop) + Number.parseFloat(style.paddingBottom) + border;\n const min = minRows * line + chrome;\n const max = maxRows === undefined ? Number.POSITIVE_INFINITY : Math.max(min, maxRows * line + chrome);\n // Measure the content at the smallest height, so the box can shrink as well as grow.\n node.style.height = `${min}px`;\n const content = node.scrollHeight + border;\n node.style.height = `${Math.min(Math.max(content, min), max)}px`;\n node.style.overflowY = content > max + 0.5 ? \"auto\" : \"hidden\";\n}\n\n/**\n * Multi-line text, on the shared control palette. Takes every `<textarea>` prop, `ref`\n * included; inside a `Field` it is described by the field's description and error, and\n * shown invalid while the field has an error.\n *\n * With `minRows` or `maxRows` it grows with its content between the two (and scrolls past\n * `maxRows`); without them it is `rows` tall and the reader can resize it. State is exposed\n * as `data-invalid`.\n */\nexport function Textarea({\n className,\n \"aria-describedby\": describedBy,\n invalid,\n mono = false,\n minRows,\n maxRows,\n rows,\n value,\n onChange,\n onValueChange,\n ref,\n ...rest\n}: TextareaProps) {\n const field = useFieldDescription(describedBy);\n const isInvalid = invalid === true || field[\"aria-invalid\"] === true;\n const grows = minRows !== undefined || maxRows !== undefined;\n const floor = Math.max(1, Math.floor(minRows ?? 1));\n const cap = maxRows === undefined ? undefined : Math.max(floor, Math.floor(maxRows));\n\n const node = useRef<HTMLTextAreaElement | null>(null);\n const setNode = useCallback(\n (element: HTMLTextAreaElement | null) => {\n node.current = element;\n assign(ref, element);\n },\n [ref],\n );\n\n // On mount, and when a controlled value changes from outside (typing refits in `change`).\n useLayoutEffect(() => {\n if (grows && node.current) fit(node.current, floor, cap);\n }, [grows, floor, cap, mono, value]);\n\n // A change of width rewraps the text.\n useLayoutEffect(() => {\n const element = node.current;\n if (!grows || !element || typeof ResizeObserver === \"undefined\") return undefined;\n let width = element.clientWidth;\n const observer = new ResizeObserver(() => {\n if (element.clientWidth === width) return;\n width = element.clientWidth;\n fit(element, floor, cap);\n });\n observer.observe(element);\n return () => observer.disconnect();\n }, [grows, floor, cap]);\n\n const change = (event: ChangeEvent<HTMLTextAreaElement>) => {\n onChange?.(event);\n onValueChange?.(event.currentTarget.value);\n if (grows) fit(event.currentTarget, floor, cap);\n };\n\n return (\n <textarea\n {...field}\n aria-invalid={isInvalid || undefined}\n data-invalid={isInvalid ? \"\" : undefined}\n {...rest}\n {...(value === undefined ? {} : { value })}\n ref={setNode}\n rows={grows ? floor : rows}\n onChange={change}\n className={cn(\n controlClasses,\n \"py-1.5\",\n grows && \"resize-none\",\n mono && \"font-mono\",\n \"data-invalid:border-defect data-invalid:hover:border-defect\",\n className,\n )}\n />\n );\n}\n","/*\n * The two binary controls, and the difference between them.\n *\n * A `Switch` changes how the thing behaves from now on -- \"allow downloads\", \"show the\n * mask\", \"lock the caliper axis\" -- and reads as a setting. A `Checkbox` asserts something\n * about a state you are about to commit -- \"I have read the warnings\", \"keep this channel\"\n * -- and reads as a statement.\n */\n\nimport * as RadixCheckbox from \"@radix-ui/react-checkbox\";\nimport * as RadixSwitch from \"@radix-ui/react-switch\";\nimport { Check, Minus } from \"lucide-react\";\nimport { useId, type MouseEvent, type ReactNode } from \"react\";\n\nimport { useFieldDescription } from \"./Field\";\nimport { cn, focusRing } from \"./cn\";\n\n/**\n * A setting: changes how the thing behaves from now on, and reads as one (\"show the mask\").\n * Always labelled; the label and the description sit beside the switch.\n *\n * Controlled. Radix exposes `data-state` (`checked`/`unchecked`) and `data-disabled`.\n */\nexport function Switch({\n checked,\n onCheckedChange,\n label,\n description,\n disabled = false,\n className,\n}: {\n /** Whether it is on. */\n checked: boolean;\n /** Called with the new state. */\n onCheckedChange: (checked: boolean) => void;\n /** What the setting is. */\n label: ReactNode;\n /** What turning it on does. */\n description?: ReactNode;\n /** Blocks the switch. */\n disabled?: boolean | undefined;\n /** Merged with the row's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const descriptionId = useId();\n return (\n <label className={cn(\"flex min-w-0 items-start gap-2.5\", className)}>\n <RadixSwitch.Root\n checked={checked}\n onCheckedChange={onCheckedChange}\n disabled={disabled}\n aria-describedby={description ? descriptionId : undefined}\n className={cn(\n \"mt-0.5 h-4 w-7 shrink-0 rounded-full border border-line-strong bg-raised transition-colors\",\n \"data-[state=checked]:border-signal data-[state=checked]:bg-signal\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n focusRing,\n )}\n >\n <RadixSwitch.Thumb\n className={cn(\n \"block size-3 rounded-full bg-fg-subtle transition-transform will-change-transform\",\n \"translate-x-0.5 data-[state=checked]:translate-x-3.5 data-[state=checked]:bg-signal-fg\",\n )}\n />\n </RadixSwitch.Root>\n <span className=\"flex min-w-0 flex-col gap-0.5\">\n <span className=\"text-sm leading-tight text-fg\">{label}</span>\n {description && (\n <span id={descriptionId} className=\"text-xs leading-snug text-fg-muted\">\n {description}\n </span>\n )}\n </span>\n </label>\n );\n}\n\n/**\n * A statement about a state you are about to commit (\"keep this channel\"). With a `label`\n * it renders its own row; without one it is a bare box (give it `aria-label`, e.g. in a\n * table cell).\n *\n * Controlled. Radix exposes `data-state` (`checked`/`unchecked`/`indeterminate`) and\n * `data-disabled`; inside a `Field` it is described by the field's description and error.\n */\nexport function Checkbox({\n checked,\n onCheckedChange,\n onClick,\n label,\n description,\n disabled = false,\n \"aria-label\": ariaLabel,\n className,\n}: {\n /** `\"indeterminate\"` for a partial selection over a set. */\n checked: boolean | \"indeterminate\";\n /** Called with the new state (never `\"indeterminate\"`: a click resolves it). */\n onCheckedChange: (checked: boolean) => void;\n /**\n * For a checkbox whose meaning depends on the modifier keys — extending a range with\n * shift, toggling one out of a selection with the platform key. `onCheckedChange` gets a\n * boolean and cannot carry them. Keyboard activation synthesises a click with its\n * modifiers intact, so this sees both input paths.\n */\n onClick?: ((event: MouseEvent<HTMLButtonElement>) => void) | undefined;\n /** What checking it asserts. Omit for a bare box. */\n label?: ReactNode;\n /** More about the statement, under the label. */\n description?: ReactNode;\n /** Blocks the box. */\n disabled?: boolean | undefined;\n /** For a checkbox in a table cell, where the row is the label. */\n \"aria-label\"?: string | undefined;\n /** Merged with the row's (or, without a label, the box's) own classes through `cn`. */\n className?: string | undefined;\n}) {\n const descriptionId = useId();\n const described = useFieldDescription(description ? descriptionId : undefined);\n const box = (\n <RadixCheckbox.Root\n checked={checked}\n onCheckedChange={(next) => onCheckedChange(next === true)}\n onClick={onClick}\n disabled={disabled}\n aria-label={ariaLabel}\n {...described}\n className={cn(\n \"flex size-4 shrink-0 items-center justify-center rounded-[0.25rem] border border-line-strong bg-raised transition-colors\",\n \"data-[state=checked]:border-signal data-[state=checked]:bg-signal\",\n \"data-[state=indeterminate]:border-signal data-[state=indeterminate]:bg-signal\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n focusRing,\n label === undefined && className,\n )}\n >\n <RadixCheckbox.Indicator className=\"text-signal-fg\">\n {checked === \"indeterminate\" ? <Minus className=\"size-3\" strokeWidth={3} /> : <Check className=\"size-3\" strokeWidth={3} />}\n </RadixCheckbox.Indicator>\n </RadixCheckbox.Root>\n );\n\n if (label === undefined) return box;\n\n return (\n <label className={cn(\"flex min-w-0 items-start gap-2.5\", className)}>\n <span className=\"mt-0.5\">{box}</span>\n <span className=\"flex min-w-0 flex-col gap-0.5\">\n <span className=\"text-sm leading-tight text-fg\">{label}</span>\n {description && (\n <span id={descriptionId} className=\"text-xs leading-snug text-fg-muted\">\n {description}\n </span>\n )}\n </span>\n </label>\n );\n}\n","/*\n * A dense on/off control for a thing that is drawn on screen.\n *\n * `Switch` is a settings row — a stacked label, a description, generous padding — and\n * three of them side by side over an image canvas is a panel, not a toolbar. This is the\n * same binary decision in the space a toolbar has: one line, one target, and an optional\n * swatch carrying the colour the layer is actually drawn in — an overlay's caliper boxes,\n * a chart's series — so the legend and the control are the same object rather than two\n * things to reconcile.\n *\n * Hand-built on a native `button` with `role=\"switch\"`: this is not one of the four Radix\n * primitives the rest of the toggle vocabulary is built on, and a chip does not need one.\n */\n\nimport type { ReactNode } from \"react\";\n\nimport type { ButtonSize } from \"./Button\";\nimport { cn, focusRing } from \"./cn\";\n\n/**\n * The chip's box, by size. `sm` is the toolbar size and the default. `md` sets the height\n * explicitly (`h-8`, border included) rather than leaving it to padding plus line-height.\n */\nconst SIZE_CLASSES: Record<ButtonSize, string> = {\n sm: \"px-2 py-1 text-xs\",\n md: \"h-8 px-3 text-sm\",\n};\n\n/** The swatch dot, by size: a little larger beside `text-sm` so it keeps its weight against the label. */\nconst SWATCH_SIZE_CLASSES: Record<ButtonSize, string> = {\n sm: \"size-2\",\n md: \"size-2.5\",\n};\n\n/**\n * A dense on/off chip for a layer drawn on screen, with an optional swatch in the layer's\n * colour: a toolbar-sized `Switch` (`role=\"switch\"`, `aria-checked`).\n *\n * Controlled. State is exposed as `data-state` (`checked`/`unchecked`).\n *\n * Toolbar-sized by default. In a row with a comfortable-density `Input`, `Select` or\n * `Button`, pass `size=\"md\"` and the chip's outer height matches theirs.\n */\nexport function ToggleChip({\n checked,\n onCheckedChange,\n children,\n swatch,\n disabled = false,\n title,\n size = \"sm\",\n className,\n}: {\n /** Whether the layer is shown. */\n checked: boolean;\n /** Called with the new state. */\n onCheckedChange: (checked: boolean) => void;\n /** The layer's name. */\n children: ReactNode;\n /** A CSS colour: the colour this layer is drawn in, shown as a dot. */\n swatch?: string;\n /** Blocks the chip. */\n disabled?: boolean | undefined;\n /** Why the control is disabled, or what the layer is. Never the only explanation. */\n title?: string | undefined;\n /**\n * The chip's height: `sm` (the default) is the toolbar size, about 26px with `text-xs`\n * text; `md` is exactly 32px (`h-8`) outside, border included, with `text-sm` text and a\n * slightly larger swatch, the height of a comfortable-density `Input` or `Button`. Not\n * derived from the density in force, so a consumer that never passes it renders as it\n * always did.\n */\n size?: ButtonSize | undefined;\n /** Merged with the chip's own classes through `cn`. */\n className?: string | undefined;\n}) {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n data-state={checked ? \"checked\" : \"unchecked\"}\n disabled={disabled}\n title={title}\n onClick={() => onCheckedChange(!checked)}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-control border\",\n SIZE_CLASSES[size],\n \"whitespace-nowrap transition-colors\",\n checked ? \"border-line-strong bg-raised text-fg\" : \"border-line bg-transparent text-fg-muted hover:text-fg\",\n \"disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:text-fg-muted\",\n focusRing,\n className,\n )}\n >\n {swatch !== undefined && (\n <span\n aria-hidden\n className={cn(\n SWATCH_SIZE_CLASSES[size],\n \"shrink-0 rounded-full border transition-opacity\",\n checked ? \"opacity-100\" : \"opacity-30\",\n )}\n style={{ backgroundColor: swatch, borderColor: swatch }}\n />\n )}\n {children}\n </button>\n );\n}\n","/*\n * Notifications, as a store any module can write to.\n *\n * A toast is raised from wherever the news is — a file loader, a worker's error handler, a\n * bake that finished — which is rarely a component with a context in reach. So `toast()` is\n * a plain function over a module-level store, and `<Toaster />`, mounted once, is its only\n * reader. The store holds no timers and touches no DOM: auto-dismissal belongs to the\n * `Toaster`, which is what knows whether a reader is hovering it.\n */\n\n/** A toast's kind: a fact (`info`), a completed action (`success`), a caveat (`warn`), a failure (`error`). */\nexport type ToastTone = \"info\" | \"success\" | \"warn\" | \"error\";\n\n/** What `toast()` takes. */\nexport interface ToastOptions {\n /** One line: what happened. */\n title: string;\n /** More detail, and what to do about it. */\n description?: string | undefined;\n /** Defaults to `info`. */\n tone?: ToastTone | undefined;\n /**\n * How long it stays, in milliseconds, while not hovered or focused. Defaults to 5000\n * (8000 for `error`); `Infinity` keeps it until dismissed.\n */\n duration?: number | undefined;\n /** Replace the toast with this id instead of adding one — for progress that updates in place. */\n id?: string | undefined;\n}\n\n/** A toast in the store. */\nexport interface ToastRecord {\n /** Identifies the toast to `dismiss` and to `id` replacement. */\n id: string;\n /** One line: what happened. */\n title: string;\n /** More detail, if any. */\n description: string | undefined;\n /** Its kind. */\n tone: ToastTone;\n /** Milliseconds before it dismisses itself (`Infinity`: never). */\n duration: number;\n}\n\n/**\n * A toast store: what `toast()` writes to and `<Toaster />` reads. Every member may be called\n * detached.\n */\nexport interface ToastStore {\n /** Add a toast (or replace the one with `options.id`), returning its id. */\n readonly toast: (options: ToastOptions) => string;\n /** Remove one toast, or every toast when `id` is omitted. */\n readonly dismiss: (id?: string) => void;\n /** The toasts, oldest first. The same array until the store changes. */\n readonly getSnapshot: () => readonly ToastRecord[];\n /** Be told when the toasts change; returns the unsubscribe function. */\n readonly subscribe: (listener: () => void) => () => void;\n}\n\nconst DEFAULT_DURATION = 5000;\nconst ERROR_DURATION = 8000;\n\n/**\n * A new, empty toast store. The package keeps one (behind `toast()` and the default\n * `<Toaster />`); create another only to isolate a region or a test.\n *\n * @returns The store.\n */\nexport function createToastStore(): ToastStore {\n let toasts: readonly ToastRecord[] = [];\n let sequence = 0;\n const listeners = new Set<() => void>();\n const commit = (next: readonly ToastRecord[]) => {\n toasts = next;\n for (const listener of [...listeners]) listener();\n };\n\n return {\n toast: (options) => {\n const tone = options.tone ?? \"info\";\n sequence += 1;\n const record: ToastRecord = {\n id: options.id ?? `toast-${sequence}`,\n title: options.title,\n description: options.description,\n tone,\n duration: options.duration ?? (tone === \"error\" ? ERROR_DURATION : DEFAULT_DURATION),\n };\n const existing = toasts.findIndex((t) => t.id === record.id);\n commit(existing === -1 ? [...toasts, record] : toasts.map((t, i) => (i === existing ? record : t)));\n return record.id;\n },\n dismiss: (id) => {\n if (id === undefined) {\n if (toasts.length > 0) commit([]);\n return;\n }\n if (toasts.some((t) => t.id === id)) commit(toasts.filter((t) => t.id !== id));\n },\n getSnapshot: () => toasts,\n subscribe: (listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\n/** The store behind `toast()` and a `<Toaster />` given no `store`. */\nexport const defaultToastStore: ToastStore = createToastStore();\n\n/**\n * Raise a notification, from anywhere — shown by the mounted `<Toaster />`.\n *\n * `toast({ title: \"Scenario loaded\", tone: \"success\" })`. Pass `id` to update a toast in\n * place (a progress message that becomes a result). `toast.dismiss(id)` removes one early,\n * `toast.dismiss()` all of them.\n *\n * @param options - The message, its tone and its duration.\n * @returns The toast's id.\n */\nexport const toast: ((options: ToastOptions) => string) & {\n /** Remove one toast, or every toast when `id` is omitted. */\n dismiss: (id?: string) => void;\n} = Object.assign((options: ToastOptions) => defaultToastStore.toast(options), {\n dismiss: (id?: string) => defaultToastStore.dismiss(id),\n});\n","/*\n * Where `toast()` shows up.\n *\n * Toasts are for news that does not need an answer — a file loaded, a bake finished, a\n * worker failed in the background. Anything that must be acknowledged is a `Dialog`, and\n * anything tied to one control is that control's `Field` error. So a toast never carries an\n * action beyond dismissing it, it goes away by itself, and it holds still while someone is\n * reading it (pointer over the stack, or focus inside it).\n *\n * The tones reuse the `Callout` vocabulary — the accent for a fact, the verdict colours for\n * success, caveat and failure — on an opaque `overlay` surface, since a toast floats over\n * whatever the viewport is drawing.\n */\n\nimport { CircleCheck, CircleX, Info, TriangleAlert, X } from \"lucide-react\";\nimport { useEffect, useRef, useState, useSyncExternalStore } from \"react\";\nimport type { FocusEvent } from \"react\";\n\nimport { cn, focusRing } from \"./cn\";\nimport { defaultToastStore, type ToastRecord, type ToastStore, type ToastTone } from \"./toastStore\";\n\nconst TONE: Record<ToastTone, { border: string; icon: string; Icon: typeof Info }> = {\n info: { border: \"border-signal/30\", icon: \"text-signal\", Icon: Info },\n success: { border: \"border-normal/30\", icon: \"text-normal\", Icon: CircleCheck },\n warn: { border: \"border-warn/30\", icon: \"text-warn\", Icon: TriangleAlert },\n error: { border: \"border-defect/30\", icon: \"text-defect\", Icon: CircleX },\n};\n\nconst NO_TOASTS: readonly ToastRecord[] = [];\n\n/** Props of `Toaster`. */\nexport interface ToasterProps {\n /** The store to show. Defaults to the one behind `toast()`. */\n store?: ToastStore | undefined;\n /** How many toasts are shown at once; older ones wait. Defaults to 5. */\n limit?: number | undefined;\n /** Merged with the stack's own classes through `cn` (it is fixed to the bottom-right corner). */\n className?: string | undefined;\n}\n\n/**\n * The stack of notifications raised with `toast()`. Mount it once, near the root.\n *\n * Fixed to the bottom-right corner, newest at the bottom. Each toast dismisses itself after\n * its `duration`; the countdown pauses while the pointer is over the stack or focus is inside\n * it. A `section` landmark (\"Notifications\") holding a polite live region, so new toasts are\n * announced; an `error` toast is a `role=\"alert\"`, announced at once. Each toast carries\n * `data-tone` and a \"Dismiss\" button.\n */\nexport function Toaster({ store = defaultToastStore, limit = 5, className }: ToasterProps) {\n const toasts = useSyncExternalStore(store.subscribe, store.getSnapshot, () => NO_TOASTS);\n const [hovered, setHovered] = useState(false);\n const [focused, setFocused] = useState(false);\n const shown = toasts.slice(-Math.max(1, limit));\n\n const onBlur = (event: FocusEvent<HTMLElement>) => {\n if (!event.currentTarget.contains(event.relatedTarget)) setFocused(false);\n };\n\n return (\n <section\n aria-label=\"Notifications\"\n onPointerEnter={() => setHovered(true)}\n onPointerLeave={() => setHovered(false)}\n onFocus={() => setFocused(true)}\n onBlur={onBlur}\n className={cn(\"pointer-events-none fixed right-4 bottom-4 z-50 w-80 max-w-[calc(100vw-2rem)]\", className)}\n >\n <ol aria-live=\"polite\" className=\"flex flex-col gap-2\">\n {shown.map((record) => (\n <ToastItem key={record.id} record={record} paused={hovered || focused} onDismiss={() => store.dismiss(record.id)} />\n ))}\n </ol>\n </section>\n );\n}\n\nfunction ToastItem({ record, paused, onDismiss }: { record: ToastRecord; paused: boolean; onDismiss: () => void }) {\n const { border, icon, Icon } = TONE[record.tone];\n // What is left of the countdown, carried across pauses; restarted when the toast is\n // replaced in place (a new record under the same id).\n const remainingRef = useRef({ record, ms: record.duration });\n const dismissRef = useRef(onDismiss);\n useEffect(() => {\n dismissRef.current = onDismiss;\n });\n\n useEffect(() => {\n if (remainingRef.current.record !== record) remainingRef.current = { record, ms: record.duration };\n if (paused || !Number.isFinite(remainingRef.current.ms)) return;\n const started = Date.now();\n const timer = setTimeout(() => dismissRef.current(), remainingRef.current.ms);\n return () => {\n clearTimeout(timer);\n remainingRef.current.ms = Math.max(0, remainingRef.current.ms - (Date.now() - started));\n };\n }, [paused, record]);\n\n return (\n // The `alert` role sits on the toast inside the list item, not on the `<li>`: an item with\n // another role is no longer a list item, and the list would be malformed.\n <li className=\"pointer-events-auto\">\n <div\n role={record.tone === \"error\" ? \"alert\" : undefined}\n data-tone={record.tone}\n className={cn(\n \"flex items-start gap-2.5 rounded-panel border bg-overlay px-3 py-2.5 text-sm leading-snug shadow-lg shadow-black/25\",\n \"transition-[opacity,translate] duration-200 starting:translate-y-2 starting:opacity-0\",\n border,\n )}\n >\n <Icon className={cn(\"mt-0.5 size-4 shrink-0\", icon)} aria-hidden />\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <p className=\"font-medium text-fg\">{record.title}</p>\n {record.description && <p className=\"text-fg-muted\">{record.description}</p>}\n </div>\n <button\n type=\"button\"\n aria-label=\"Dismiss\"\n title=\"Dismiss\"\n onClick={onDismiss}\n className={cn(\n \"-mr-1 grid size-6 shrink-0 place-items-center rounded-control text-fg-subtle transition-colors hover:bg-raised hover:text-fg\",\n focusRing,\n )}\n >\n <X className=\"size-3.5\" aria-hidden />\n </button>\n </div>\n </li>\n );\n}\n","/*\n * A tab strip.\n *\n * The count is set in mono beside the label rather than in parentheses inside it: it is a\n * quantity -- how many overrides sit behind an options tab, how many samples a filter chip\n * covers -- and mono makes it read as one at a glance.\n */\n\nimport type { KeyboardEvent } from \"react\";\n\nimport { cn, focusRing } from \"./ui/cn\";\n\n/** One tab of a `Tabs` strip. */\nexport interface TabItem<Id extends string> {\n /** Identifies the tab to `active` and `onSelect`. Unique within the strip. */\n id: Id;\n /** The tab's text, and its accessible name. */\n label: string;\n /** Shown after the label, for the chips that carry a row or override count. */\n count?: number;\n /** Shown but not selectable, and skipped by the arrow keys. */\n disabled?: boolean;\n /** Why this tab has nothing to show, for a reader wondering what they missed. */\n title?: string;\n}\n\nconst NEXT_KEYS: Record<string, (index: number, length: number) => number> = {\n ArrowRight: (index, length) => (index + 1) % length,\n ArrowLeft: (index, length) => (index - 1 + length) % length,\n Home: () => 0,\n End: (_, length) => length - 1,\n};\n\n/**\n * A tab strip (`role=\"tablist\"`): the caller renders the panel for the `active` tab.\n *\n * Keyboard: one tab stop for the whole strip (the active tab); ←/→ move to the previous or\n * next enabled tab and select it, Home/End to the first and last. Each tab carries\n * `data-state` (`active`/`inactive`). Pass `idPrefix` to wire the tabs to the panel: the tab\n * for `id` gets the element id `<idPrefix>-tab-<id>` and the active tab gets\n * `aria-controls=\"<idPrefix>-panel-<id>\"`, so render the panel as a `role=\"tabpanel\"` element\n * with the id `<idPrefix>-panel-<active>` and `aria-labelledby=\"<idPrefix>-tab-<active>\"`.\n */\nexport function Tabs<Id extends string>({\n items,\n active,\n onSelect,\n label,\n idPrefix,\n className,\n}: {\n /** The tabs, in order. */\n items: TabItem<Id>[];\n /** The selected tab's id. */\n active: Id;\n /** Called with a tab's id when it is clicked or reached with the arrow keys. */\n onSelect: (id: Id) => void;\n /** Names the tab list. */\n label: string;\n /** Prefix for the tab and panel element ids; enables `aria-controls` (see above). */\n idPrefix?: string | undefined;\n /** Merged with the strip's own classes through `cn`. */\n className?: string | undefined;\n}) {\n const enabled = items.filter((item) => !item.disabled);\n // The one tab stop: the active tab, or the first enabled one when the active tab is\n // disabled or not in the list.\n const focusable = enabled.some((item) => item.id === active) ? active : enabled[0]?.id;\n\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n const move = NEXT_KEYS[event.key];\n const target = event.target as HTMLElement;\n const current = enabled.findIndex((item) => item.id === target.dataset[\"tabId\"]);\n if (move === undefined || current === -1) return;\n event.preventDefault();\n const next = enabled[move(current, enabled.length)];\n if (next === undefined) return;\n const tabs = event.currentTarget.querySelectorAll<HTMLElement>('[role=\"tab\"]');\n for (const tab of tabs) if (tab.dataset[\"tabId\"] === next.id) tab.focus();\n if (next.id !== active) onSelect(next.id);\n };\n\n return (\n <div\n role=\"tablist\"\n aria-label={label}\n aria-orientation=\"horizontal\"\n onKeyDown={onKeyDown}\n className={cn(\"inline-flex max-w-full flex-wrap items-center gap-0.5 rounded-control border border-line bg-raised p-0.5\", className)}\n >\n {items.map((item) => {\n const selected = item.id === active;\n return (\n <button\n key={item.id}\n type=\"button\"\n role=\"tab\"\n id={idPrefix === undefined ? undefined : `${idPrefix}-tab-${item.id}`}\n aria-selected={selected}\n aria-controls={idPrefix !== undefined && selected ? `${idPrefix}-panel-${item.id}` : undefined}\n tabIndex={item.id === focusable ? 0 : -1}\n data-tab-id={item.id}\n data-state={selected ? \"active\" : \"inactive\"}\n disabled={item.disabled}\n title={item.title}\n onClick={() => onSelect(item.id)}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-[0.3rem] px-2.5 py-1 text-xs font-medium transition-colors\",\n \"disabled:cursor-not-allowed disabled:opacity-40\",\n selected ? \"bg-surface text-fg shadow-sm\" : \"text-fg-muted hover:text-fg\",\n focusRing,\n )}\n >\n {item.label}\n {item.count !== undefined && item.count > 0 && (\n <span\n className={cn(\n \"rounded-full px-1.5 font-mono text-[10px] tabular-nums\",\n selected ? \"bg-signal/15 text-signal\" : \"bg-line text-fg-muted\",\n )}\n >\n {item.count}\n </span>\n )}\n </button>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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"}