@fasl-work/caos-app-shell 0.6.13 → 0.7.1

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.d.ts CHANGED
@@ -1,105 +1,11 @@
1
+ export { A as AppShell, a as ArchTab, b as ArchitectureConfig, c as ArchitectureModal, L as LANG_STORAGE_KEY, S as STANDARD_ROUTES, d as ShellConfig, e as ShellRoute, T as THEME_BOOT_SCRIPT, f as THEME_STORAGE_KEY, v as validateArchitectureConfig } from './keys-CnvqkckF.js';
1
2
  import * as react from 'react';
2
- import { ReactNode } from 'react';
3
+ import { ReactNode, Component, ErrorInfo } from 'react';
3
4
  import * as zustand from 'zustand';
4
-
5
- interface ArchTab {
6
- id: string;
7
- /** tab label. */
8
- en: string;
9
- es: string;
10
- /** the explanation body (paragraphs separated by a blank line). */
11
- body_en: string;
12
- body_es: string;
13
- /**
14
- * an inline '<svg…>…</svg>' string OR a path under the app's public/ (fetched + inlined).
15
- *
16
- * ONE file carries BOTH languages (ADR-0058). Tag each translatable `<text>` twice at the same
17
- * coordinates, `class="… l-en"` and `class="… l-es"`; the modal wrapper sets `data-arch-lang`
18
- * and the shell stylesheet shows exactly one. Two files would be two things to keep in step, and
19
- * the one that is not on screen is the one that goes stale. Anything language-neutral (a number,
20
- * a file name, an identifier) needs no pair.
21
- */
22
- svg: string;
23
- }
24
- interface ArchitectureConfig {
25
- /** modal title override (defaults to "Architecture / How it works"). */
26
- title_en?: string;
27
- title_es?: string;
28
- tabs: ArchTab[];
29
- }
30
- declare function ArchitectureModal({ config, onClose }: {
31
- config: ArchitectureConfig;
32
- onClose: () => void;
33
- }): react.JSX.Element | null;
34
-
35
- interface ShellRoute {
36
- path: string;
37
- en: string;
38
- es: string;
39
- }
40
- interface ShellConfig {
41
- /** Brand: product name + optional lucide icon element (defaults to a generic mark). */
42
- product: {
43
- name: string;
44
- mark?: ReactNode;
45
- };
46
- /** Top-level routes for the header nav. 0–1 entries → nav hidden (e.g. the Faena hub). */
47
- routes?: ShellRoute[];
48
- /** External links. personal/portfolio default to Felipe's canonical URLs (kept identical app-to-app). */
49
- links: {
50
- github: string;
51
- personal?: string;
52
- portfolio?: string;
53
- };
54
- /** Human version string (X.XX.XXX) shown in the footer. */
55
- version: string;
56
- /** In-app Architecture / "How it works" modal (ADR-0058). When present, an ⓘ button appears in the header. */
57
- architecture?: ArchitectureConfig;
58
- /** Footer provenance + honesty (ADR-0016 §2): the real data/engine source with citation +
59
- * license (e.g. "Engine: lingbot-map (arXiv:2604.14141, Apache-2.0)") and the one-line honest
60
- * disclaimer of how the app runs. Both optional, both bilingual. */
61
- footer?: {
62
- /** Explicit product attribution, or false when the product excludes personal attribution. */
63
- attribution?: {
64
- en: string;
65
- es: string;
66
- } | false;
67
- /** The consuming product's license; the shell's MIT license is not inherited by applications. */
68
- license?: {
69
- en: string;
70
- es: string;
71
- };
72
- provenance?: {
73
- en: string;
74
- es: string;
75
- };
76
- disclaimer?: {
77
- en: string;
78
- es: string;
79
- };
80
- };
81
- /**
82
- * Routes whose surface IS the viewport (ADR-0071 rule 1): on these the shell adds
83
- * `.app-shell.fixed`, which sizes the page to 100dvh and lets flex distribute the
84
- * height, so the ONE container that owns long content scrolls and the document
85
- * never does. A doc route is not listed and keeps the document scroll. Matching is
86
- * exact, or by prefix for a non-root path (`/lab` matches `/lab/case-1`).
87
- *
88
- * The stylesheet has carried `.app-shell.fixed` with the note "apps that fill the
89
- * viewport add it" since the containment fix, and no app could: this component
90
- * rendered a fixed class string. Measured on Porvenir before this field existed:
91
- * every tab of the App route scrolled the document by 200 to 770px.
92
- */
93
- fixedRoutes?: string[];
94
- /** Every route fixed: a single-surface app (a hub, a one-page tool). */
95
- fixed?: boolean;
96
- }
97
- /** The shared CAOS/Faena app shell: sticky header (brand + nav + icon-links + lang/theme) + footer.
98
- * Wrap your <Routes> (or single landing) in it, inside a Router. */
99
- declare function AppShell({ config, children }: {
100
- config: ShellConfig;
101
- children: ReactNode;
102
- }): react.JSX.Element;
5
+ import { F as FormatOptions } from './tokens-CSV0MOgR.js';
6
+ export { N as NOT_AVAILABLE, a as SHELL_TOKENS, S as ShellToken, f as formatNumber, r as resolveToken, u as useFormat, b as useThemeTokens } from './tokens-CSV0MOgR.js';
7
+ import { L as Lang, B as BiText } from './text-BlY47nIt.js';
8
+ export { p as pick, u as useLangStore, a as useShellLang } from './text-BlY47nIt.js';
103
9
 
104
10
  /** One authenticated workbench destination rendered by the shared responsive navigation frame. */
105
11
  interface WorkbenchRoute {
@@ -163,15 +69,25 @@ interface ThemeState {
163
69
  }
164
70
  declare const useThemeStore: zustand.UseBoundStore<zustand.StoreApi<ThemeState>>;
165
71
 
166
- type Lang = 'en' | 'es';
167
- interface LangState {
168
- lang: Lang;
169
- setLang: (l: Lang) => void;
170
- toggle: () => void;
72
+ interface Props {
73
+ /** Which view this boundary protects; logged and shown so the failure is located, not guessed. */
74
+ panel: string;
75
+ children: ReactNode;
76
+ }
77
+ interface State {
78
+ error: Error | null;
79
+ }
80
+ /**
81
+ * An error boundary around one view. A failing chart or panel shows an inline message in its own frame instead of
82
+ * blanking the whole page, and the failure is logged with its panel id, which the measured gate fails on (failure
83
+ * class 20 of the 2026-10-04 history: white screens and silent failures).
84
+ */
85
+ declare class PanelBoundary extends Component<Props, State> {
86
+ state: State;
87
+ static getDerivedStateFromError(error: Error): State;
88
+ componentDidCatch(error: Error, info: ErrorInfo): void;
89
+ render(): ReactNode;
171
90
  }
172
- declare const useLangStore: zustand.UseBoundStore<zustand.StoreApi<LangState>>;
173
- /** Current language, for app pages that render language-branched long-form content. */
174
- declare const useShellLang: () => Lang;
175
91
 
176
92
  /** Return `false` from a frame to end the current pass (the animation is complete); return `true`
177
93
  * or nothing to keep going. `dt` = ms since the previous frame, `elapsed` = ms since this pass
@@ -333,25 +249,72 @@ interface TabDef {
333
249
  label: ReactNode;
334
250
  content: ReactNode;
335
251
  }
336
- /** Accessible roving-tabindex tab strip (arrow-key nav, aria wiring). Accent-soft pill for active. */
337
- declare function Tabs({ tabs, initial, ariaLabel }: {
252
+ /** At most about six peers, then group by the question (ADR-0071 rule 5). */
253
+ declare const MAX_PEER_TABS = 6;
254
+ interface TabsProps {
338
255
  tabs: TabDef[];
256
+ /** Uncontrolled: the first selected tab. */
339
257
  initial?: string;
258
+ /** Controlled: the selected tab, owned by the app (for example a view held in the URL). */
259
+ value?: string;
260
+ /** Called on every selection, controlled or not. */
261
+ onChange?: (id: string) => void;
340
262
  ariaLabel?: string;
341
- }): react.JSX.Element;
263
+ }
264
+ /**
265
+ * Accessible roving-tabindex tab strip on ONE row (ADR-0071 rule 4): arrow keys, Home and End; only the active panel
266
+ * is rendered, inside an error boundary; a row wider than its box fades the hidden end. Controlled when `value` is
267
+ * given (known shell defect 6). More than six peers is reported (rule 5).
268
+ */
269
+ declare function Tabs({ tabs, initial, value, onChange, ariaLabel }: TabsProps): react.JSX.Element;
342
270
 
343
271
  interface SubTabDef {
344
272
  id: string;
345
273
  label: ReactNode;
346
274
  content: ReactNode;
347
275
  }
348
- /** Second-level tabs nested inside a Tabs panel. Lighter chips; optional vertical left rail. */
349
- declare function SubTabs({ tabs, initial, ariaLabel, orientation, }: {
276
+ interface SubTabsProps {
350
277
  tabs: SubTabDef[];
351
278
  initial?: string;
279
+ /** Controlled selection (known shell defect 6). */
280
+ value?: string;
281
+ onChange?: (id: string) => void;
352
282
  ariaLabel?: string;
353
283
  orientation?: 'horizontal' | 'vertical';
354
- }): react.JSX.Element;
284
+ }
285
+ /**
286
+ * Second-level tabs nested inside a Tabs panel: lighter chips, or a vertical left rail for deep content. Only the
287
+ * active panel is rendered, inside an error boundary; controlled when `value` is given; more than six peers is
288
+ * reported (ADR-0071 rule 5).
289
+ */
290
+ declare function SubTabs({ tabs, initial, value, onChange, ariaLabel, orientation }: SubTabsProps): react.JSX.Element;
291
+
292
+ interface TabGroupDef {
293
+ id: string;
294
+ /** The question the group answers (ADR-0071 rule 5: group by the user's question). */
295
+ label: ReactNode;
296
+ tabs: SubTabDef[];
297
+ }
298
+ interface TabGroupsProps {
299
+ groups: TabGroupDef[];
300
+ /** Controlled selection as `group` and `tab` ids (for example held in the URL). */
301
+ value?: {
302
+ group: string;
303
+ tab?: string;
304
+ };
305
+ onChange?: (value: {
306
+ group: string;
307
+ tab: string;
308
+ }) => void;
309
+ ariaLabel?: string;
310
+ }
311
+ /**
312
+ * Grouped navigation for more than six views: one row of at most six groups, then the sub-tabs of the open group
313
+ * only, each its own single row (ADR-0071 rules 4 and 5). No hover menus: every view is reached by plain clicks on
314
+ * visible tabs, which a pointer and a keyboard can both operate (rule 9; menus that swallowed the first click were a
315
+ * recorded failure).
316
+ */
317
+ declare function TabGroups({ groups, value, onChange, ariaLabel }: TabGroupsProps): react.JSX.Element;
355
318
 
356
319
  /** A left-bordered note box. `honest` = amber (caveats/limits), `strong` = accent, `note` = neutral. */
357
320
  declare function Callout({ variant, title, children, }: {
@@ -360,7 +323,8 @@ declare function Callout({ variant, title, children, }: {
360
323
  children: ReactNode;
361
324
  }): react.JSX.Element;
362
325
 
363
- /** A display equation rendered with KaTeX, with an optional caption. */
326
+ /** A display equation rendered with KaTeX and its caption. ADR-0017 s2 requires every display equation to carry a
327
+ * bilingual caption that defines its symbols; a missing one is reported on the console, which the gate fails on. */
364
328
  declare function Equation({ tex, caption }: {
365
329
  tex: string;
366
330
  caption?: ReactNode;
@@ -379,8 +343,9 @@ declare function Figure({ caption, children }: {
379
343
 
380
344
  interface Citation {
381
345
  id: string;
382
- /** Short inline label, e.g. "Little 1961". */
383
- label: string;
346
+ /** Short inline label, e.g. "Little 1961"; `{ en, es }` when it has words to translate ("Laplante and
347
+ * Staunton" / "Laplante y Staunton"), known shell defect 9. The bibliographic record stays verbatim. */
348
+ label: BiText;
384
349
  /** Full bibliographic string for the reference list. */
385
350
  citation: string;
386
351
  doi?: string;
@@ -401,10 +366,324 @@ declare function Refs({ ids, label }: {
401
366
  ids: string[];
402
367
  label: string;
403
368
  }): react.JSX.Element;
404
- /** Full ordered bibliography. Omit `ids` to render every provided citation. */
369
+ /** Full ordered bibliography. Omit `ids` to render every provided citation.
370
+ * @deprecated ADR-0017 s4 bans a bottom-of-page bibliography; end each section with `Refs` (or use `DocSection`).
371
+ * Kept so existing products still compile; it reports itself on the console, which the gate fails on. */
405
372
  declare function ReferenceList({ ids, heading }: {
406
373
  ids?: string[];
407
374
  heading?: string;
408
375
  }): react.JSX.Element;
409
376
 
410
- export { AppShell, type ArchTab, type ArchitectureConfig, ArchitectureModal, Callout, type CaseDef, type CaseGroup, type CaseKind, CaseSelector, type CaseSelectorProps, type CaseSelectorText, type Citation, CitationsProvider, Cite, Equation, Figure, FocusShell, type FocusShellProps, InlineMath, type Lang, LanguageToggle, type PausedVizController, ReferenceList, Refs, type ShellConfig, type ShellRoute, type SubTabDef, SubTabs, type TabDef, Tabs, type Theme, ThemeToggle, type UsePausedVizOptions, type VizFrame, type VizLoop, type VizLoopDeps, type VizLoopOptions, type WorkbenchRoute, WorkbenchShell, type WorkbenchShellProps, applyTheme, caseKind, caseTooltip, casesInSource, createVizLoop, groupByCategory, readCaseParam, readTheme, sourcesPresent, useLangStore, usePausedViz, useShellLang, useThemeStore, withCaseParam };
377
+ /**
378
+ * A documentation route (Introduction, Methodology, Implementation, Experiments, Benchmark): the page head with
379
+ * its title and lede, then sections. `wide` for a route with a sub-tab rail (ADR-0071 rule 2, amended
380
+ * 2026-09-04); a single prose column keeps the reading width and is centred (ADR-0017 §1).
381
+ */
382
+ declare function DocPage({ title, lede, wide, children }: {
383
+ title: BiText;
384
+ lede: ReactNode;
385
+ wide?: boolean;
386
+ children: ReactNode;
387
+ }): react.JSX.Element;
388
+ interface DocSectionProps {
389
+ title: BiText;
390
+ id?: string;
391
+ /** Citation ids for this section only, rendered as one inline `Refs` row at its end (ADR-0017 §4). */
392
+ refs?: string[];
393
+ /** Required when a section cites nothing, so the absence is a stated choice the gate can read. */
394
+ noRefsReason?: BiText;
395
+ children: ReactNode;
396
+ }
397
+ /** One section of a documentation route, ending in its own references. */
398
+ declare function DocSection({ title, id, refs, noRefsReason, children }: DocSectionProps): react.JSX.Element;
399
+
400
+ /** One section of the control rail. A rail whose content does not fit is SPLIT into sections, one shown at a
401
+ * time, never scrolled (ADR-0071 rule 6: a panel must show its own controls). */
402
+ interface RailSection {
403
+ id: string;
404
+ label: BiText;
405
+ content: ReactNode;
406
+ }
407
+ interface WorkbenchLayoutProps {
408
+ /** The control rail: case and variant pickers, parameters, the live readout. Either one node or sections. */
409
+ rail: ReactNode | RailSection[];
410
+ /** Shown above the rail (and above its sections): the case and variant pickers, which every section shares. */
411
+ railHead?: ReactNode;
412
+ /** The instrument: the views of the selected case. It takes the rest of the viewport (ADR-0071 rules 2, 8). */
413
+ children: ReactNode;
414
+ /** Accessible name of the rail. */
415
+ railLabel?: BiText;
416
+ className?: string;
417
+ }
418
+ /**
419
+ * The App route of a CAOS product: a sized control rail beside the instrument, on the full viewport.
420
+ *
421
+ * Renders `.page-body.wide.caos-wb`: no reading-width cap (a workbench is not prose, ADR-0071 rule 2),
422
+ * `min-width: 0` down the tree so no row pushes the page wider than the screen (rule 3), a rail with an
423
+ * explicit width that never scrolls (rule 6), and an instrument marked `data-instrument` so the measured gate
424
+ * can check that it covers at least half of the viewport (rule 8). Pair with `ShellConfig.contain`.
425
+ */
426
+ declare function WorkbenchLayout({ rail, railHead, children, railLabel, className }: WorkbenchLayoutProps): react.JSX.Element;
427
+
428
+ /** Where a value on screen came from: observed data, a generator, or a published reference. */
429
+ type Provenance = 'real' | 'synthetic' | 'published';
430
+ /** The selection the instrument shows, and the key that identifies it (S10 of the 2026-10-04 requirements). */
431
+ interface WorkbenchState {
432
+ stateKey: string;
433
+ caseId?: string;
434
+ variantId?: string;
435
+ source?: string;
436
+ }
437
+ declare const WorkbenchStateContext: react.Context<WorkbenchState | null>;
438
+ /** The current workbench selection, or null outside a `CaseWorkbench`. */
439
+ declare function useWorkbenchState(): WorkbenchState | null;
440
+ /**
441
+ * A short, deterministic key for a selection: case, variant, source and every control value. A view that shows data
442
+ * computed for another key is stale, and says so (failure classes 8 and 17: views that silently kept an old result
443
+ * after a control changed). FNV-1a over a canonical serialisation.
444
+ */
445
+ declare function makeStateKey(parts: Record<string, unknown>): string;
446
+ /** True when a view's data key is known and differs from the workbench's current key. */
447
+ declare function isStale(dataKey: string | undefined, current: WorkbenchState | null): boolean;
448
+
449
+ /** Where a value on screen was computed (ADR-0057 lanes, ADR-0069 s5). */
450
+ type Lane = 'live' | 'replay' | 'offline';
451
+ /** Badges that state honestly which lane produced what is shown (recomputed live, replayed from a committed
452
+ * artifact, or available only offline) and, when given, whether it rests on real, synthetic or published data
453
+ * (failure class 17 of the 2026-10-04 history: live versus replay honesty). */
454
+ declare function LaneBadge({ lane, provenance, label }: {
455
+ lane: Lane;
456
+ provenance?: Provenance;
457
+ label?: BiText;
458
+ }): react.JSX.Element;
459
+
460
+ /** One configured regime of a case: a full parameter vector, never a cosmetic label (ADR-0016 §9). */
461
+ interface VariantDef {
462
+ id: string;
463
+ label: BiText;
464
+ /** One line: what this regime shows. */
465
+ note?: BiText;
466
+ /** The lane of this variant's result, when it differs from the bar's. */
467
+ lane?: Lane;
468
+ disabled?: boolean;
469
+ }
470
+ interface VariantBarProps {
471
+ variants: VariantDef[];
472
+ activeId: string;
473
+ onSelect: (id: string) => void;
474
+ title?: BiText;
475
+ lane?: Lane;
476
+ }
477
+ /** The regimes of the selected case on ONE scrollable row (ADR-0071 rule 4), the active regime's lane, and the
478
+ * one-line note of what it shows. Selecting a regime must change every view of the case. */
479
+ declare function VariantBar({ variants, activeId, onSelect, title, lane }: VariantBarProps): react.JSX.Element;
480
+
481
+ /** One group of views of the selected case, named for the question the user is asking (ADR-0071 rule 5). */
482
+ interface WorkbenchGroup {
483
+ id: string;
484
+ label: BiText;
485
+ /** Which lane produced this group's views, and what data they rest on. Each view shows its own badges (PlotCard,
486
+ * Readout); these are written on the panel as `data-lane` and `data-provenance`. */
487
+ lane?: Lane;
488
+ provenance?: Provenance;
489
+ content: ReactNode;
490
+ }
491
+ interface CaseWorkbenchProps {
492
+ /** The selected case (written as `data-case`). */
493
+ caseId: string;
494
+ /** The case picker, shown first in the rail. Omit only for a workbench with a single case. */
495
+ cases?: CaseSelectorProps;
496
+ /** The data source of the case (for example `real` or `synthetic`), written as `data-source`. */
497
+ source?: string;
498
+ /** Every control value of the instrument; with the case and variant it forms the state key (S10). */
499
+ controls?: Record<string, unknown>;
500
+ /** A workbench whose views have no live control declares it, so the reactivity gate does not expect one. */
501
+ replayOnly?: boolean;
502
+ /** The regimes of the case, shown in the rail under the case picker; omit only for a case with no meaningful
503
+ * parametric or temporal family. */
504
+ variants?: VariantBarProps;
505
+ /** The rest of the rail: the case's parameters (Knob, ChipGroup) and its live values (Readout, Gauge, Verdict),
506
+ * as one node or as sections shown one at a time (ADR-0071 rule 6). */
507
+ rail?: ReactNode | RailSection[];
508
+ railLabel?: BiText;
509
+ /** The instrument: the case's own question groups (for example Model, Validation, Impact, Findings). */
510
+ groups: WorkbenchGroup[];
511
+ /** The comparison across the case's variants (one metric per regime; selecting one loads it). */
512
+ compare?: {
513
+ label?: BiText;
514
+ lane?: Lane;
515
+ provenance?: Provenance;
516
+ content: ReactNode;
517
+ };
518
+ /** The deep write-up of the case: problem, variables, equations, assumptions, what each variant shows, how to read
519
+ * the views (ADR-0016 s9.B). Always present. */
520
+ context: {
521
+ label?: BiText;
522
+ content: ReactNode;
523
+ };
524
+ /** Controlled group selection (for example held in the URL). */
525
+ group?: string;
526
+ onGroupChange?: (id: string) => void;
527
+ ariaLabel?: BiText;
528
+ }
529
+ declare const MAX_WORKBENCH_GROUPS = 6;
530
+ /**
531
+ * The App route of a product (ADR-0016 s9, ADR-0071 as amended 2026-10-04): the control rail beside the instrument on
532
+ * the full viewport. The rail holds, in order, the case picker, the variants of the case, and the product's
533
+ * parameters and live values; the instrument holds ONE row of at most six question groups in a fixed order (the
534
+ * instrument groups, the variant comparison, the context write-up) and the open group's views, which take the
535
+ * height that row leaves. Pickers, parameters and numbers sit in the rail so the drawing keeps the instrument
536
+ * (rule 8: at least half the viewport is drawn).
537
+ *
538
+ * It provides the selection's state key to every `PlotCard` and `Readout` in the rail and the instrument, so a view
539
+ * still showing data for an earlier selection says so, and it writes `data-case`, `data-variant`, `data-source` and
540
+ * `data-state-key` on the instrument so the gate can check that the instrument shows what was asked for.
541
+ */
542
+ declare function CaseWorkbench(props: CaseWorkbenchProps): react.JSX.Element;
543
+
544
+ type Tone = 'good' | 'warn' | 'bad' | 'accent' | 'neutral';
545
+ interface ReadoutItem {
546
+ label: BiText;
547
+ /** A number, or `null`/`undefined` when it is not available. NaN is reported and shown as not available. */
548
+ value?: number | null;
549
+ /** A categorical value shown as text instead of a number. */
550
+ text?: BiText;
551
+ /** The unit, bilingual when it is a word (people / personas), a plain string when it is a symbol (d, %). Required
552
+ * unless `unitless` is set (failure class 23: readouts without units). */
553
+ unit?: BiText;
554
+ unitless?: boolean;
555
+ /** What the quantity is, in one line (ADR-0017 s3.8: every variable is self-explanatory). */
556
+ hint?: BiText;
557
+ /** An explicit tone, or a tone derived from `better` and the `good`/`bad` thresholds, never from the sign. */
558
+ tone?: Tone;
559
+ better?: 'higher' | 'lower';
560
+ good?: number;
561
+ bad?: number;
562
+ format?: FormatOptions;
563
+ }
564
+ interface ReadoutProps {
565
+ items: ReadoutItem[];
566
+ title?: BiText;
567
+ /** Which lane computed these values, and what data they rest on (required, failure class 17). */
568
+ lane: Lane;
569
+ provenance: Provenance;
570
+ /** The workbench state key these values were computed for; differing from the current key shows them as stale. */
571
+ dataKey?: string;
572
+ }
573
+ /** The live readout: labelled values with units that update with every control (ADR-0017 s3.3). */
574
+ declare function Readout({ items, title, lane, provenance, dataKey }: ReadoutProps): react.JSX.Element;
575
+ interface GaugeZone {
576
+ from: number;
577
+ to: number;
578
+ tone: Tone;
579
+ label: BiText;
580
+ }
581
+ interface GaugeProps {
582
+ title: BiText;
583
+ value: number | null | undefined;
584
+ min: number;
585
+ max: number;
586
+ zones: GaugeZone[];
587
+ unit?: BiText;
588
+ unitless?: boolean;
589
+ format?: FormatOptions;
590
+ }
591
+ /** A value against labelled zones on a linear scale, the needle at the value (ADR-0017 s3.3). */
592
+ declare function Gauge({ title, value, min, max, zones, unit, unitless, format }: GaugeProps): react.JSX.Element;
593
+ type VerdictProps = {
594
+ title: BiText;
595
+ /** Title and verdict on one row, for a verdict that sits above a drawing and must not take its height. */
596
+ compact?: boolean;
597
+ children?: ReactNode;
598
+ } & ({
599
+ code: string;
600
+ messages: Record<string, {
601
+ tone: Tone;
602
+ text: BiText;
603
+ }>;
604
+ } | {
605
+ tone: Tone;
606
+ verdict: BiText;
607
+ });
608
+ /** The verdict panel: the current diagnosis in one line, coloured by its tone. A status code goes through a
609
+ * `messages` map, and an unmapped code is reported (failure class 23: raw status codes on screen). */
610
+ declare function Verdict(props: VerdictProps): react.JSX.Element;
611
+ interface PlotCardProps {
612
+ title: BiText;
613
+ /** Which lane produced this view and what data it rests on (required, failure class 17). */
614
+ lane: Lane;
615
+ provenance: Provenance;
616
+ /** The workbench state key the data was computed for; a different key shows the view as stale. */
617
+ dataKey?: string;
618
+ actions?: ReactNode;
619
+ note?: BiText;
620
+ /** The view takes the height its panel leaves (a chart or stage that should fill the instrument, ADR-0071 rule 8);
621
+ * views marked `fill` in one panel share that height. A `Stage` or `UPlotChart height="fill"` inside it gets a
622
+ * real size from it instead of a guessed one (failure class 4). */
623
+ fill?: boolean;
624
+ children: ReactNode;
625
+ }
626
+ /** A framed view with a title row, its lane and provenance, the unit every chart or table sits in. Its body is
627
+ * protected by an error boundary, so one failing chart never blanks the page (failure class 20). */
628
+ declare function PlotCard({ title, lane, provenance, dataKey, actions, note, fill, children }: PlotCardProps): react.JSX.Element;
629
+
630
+ interface KnobProps {
631
+ /** Stable id, written as `data-control` so the gate can enumerate and move every control. */
632
+ id: string;
633
+ label: BiText;
634
+ value: number;
635
+ min: number;
636
+ max: number;
637
+ step: number;
638
+ unit?: BiText;
639
+ /** What the parameter is (ADR-0017 s3.8). */
640
+ hint?: BiText;
641
+ format?: FormatOptions;
642
+ disabled?: boolean;
643
+ onChange: (value: number) => void;
644
+ }
645
+ /**
646
+ * A continuous parameter of the case (ADR-0017 s3.2): a labelled slider with its value and unit shown. Registered as
647
+ * a control (`data-control`), so the gate can change it and require that some view's state key changes (failure
648
+ * class 8: controls that change nothing).
649
+ */
650
+ declare function Knob({ id, label, value, min, max, step, unit, hint, format, disabled, onChange }: KnobProps): react.JSX.Element;
651
+ interface ChipOption {
652
+ id: string;
653
+ label: BiText;
654
+ hint?: BiText;
655
+ disabled?: boolean;
656
+ }
657
+ interface ChipGroupProps {
658
+ id: string;
659
+ label: BiText;
660
+ options: ChipOption[];
661
+ value: string;
662
+ onChange: (id: string) => void;
663
+ }
664
+ /** A small one-of-N choice as chips on one row, registered as a control. For a categorised or long list use the
665
+ * `CaseSelector` select mode instead (ADR-0071 rule 7). */
666
+ declare function ChipGroup({ id, label, options, value, onChange }: ChipGroupProps): react.JSX.Element;
667
+
668
+ interface StageSize {
669
+ width: number;
670
+ height: number;
671
+ }
672
+ /**
673
+ * The size of an element, observed through a callback ref and a ResizeObserver, so a canvas is sized from its real
674
+ * box and never from viewport units, and is not built before it has a size (failure class 4: instruments drawn at
675
+ * zero or at a guessed size).
676
+ */
677
+ declare function useStageSize(): [(el: HTMLElement | null) => void, StageSize];
678
+ /**
679
+ * The drawing surface of an instrument. It renders its children only once it has a non-zero size, passes that size
680
+ * down, and declares what it drew (`data-drawn`, `data-width`, `data-height`) so the gate measures the drawing, not
681
+ * the host. A stage that never gets a size is reported.
682
+ */
683
+ declare function Stage({ label, children, className }: {
684
+ label: BiText;
685
+ children: (size: StageSize) => ReactNode;
686
+ className?: string;
687
+ }): react.JSX.Element;
688
+
689
+ export { BiText, Callout, type CaseDef, type CaseGroup, type CaseKind, CaseSelector, type CaseSelectorProps, type CaseSelectorText, CaseWorkbench, type CaseWorkbenchProps, ChipGroup, type ChipGroupProps, type ChipOption, type Citation, CitationsProvider, Cite, DocPage, DocSection, type DocSectionProps, Equation, Figure, FocusShell, type FocusShellProps, FormatOptions, Gauge, type GaugeProps, type GaugeZone, InlineMath, Knob, type KnobProps, type Lane, LaneBadge, Lang, LanguageToggle, MAX_PEER_TABS, MAX_WORKBENCH_GROUPS, PanelBoundary, type PausedVizController, PlotCard, type PlotCardProps, type Provenance, type RailSection, Readout, type ReadoutItem, type ReadoutProps, ReferenceList, Refs, Stage, type StageSize, type SubTabDef, SubTabs, type SubTabsProps, type TabDef, type TabGroupDef, TabGroups, type TabGroupsProps, Tabs, type TabsProps, type Theme, ThemeToggle, type Tone, type UsePausedVizOptions, VariantBar, type VariantBarProps, type VariantDef, Verdict, type VerdictProps, type VizFrame, type VizLoop, type VizLoopDeps, type VizLoopOptions, type WorkbenchGroup, WorkbenchLayout, type WorkbenchLayoutProps, type WorkbenchRoute, WorkbenchShell, type WorkbenchShellProps, type WorkbenchState, WorkbenchStateContext, applyTheme, caseKind, caseTooltip, casesInSource, createVizLoop, groupByCategory, isStale, makeStateKey, readCaseParam, readTheme, sourcesPresent, usePausedViz, useStageSize, useThemeStore, useWorkbenchState, withCaseParam };