@fasl-work/caos-app-shell 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/README.md CHANGED
@@ -87,6 +87,14 @@ const config: ShellConfig = {
87
87
  `fitLabel`, `niceTicks`) and declares `data-chart` ([docs/05](docs/05_drawings.md)).
88
88
  - **Views in the URL:** `CaseWorkbench` and `SurfacePage` hold the open view in `?view=` (and the case selector the
89
89
  case in `?case=`), so a view can be shared and survives a reload.
90
+ - **Focus mode for the App (0.14.0):** `<CaseWorkbench focus />` adds a Focus button to the rail head that opens the
91
+ selected case's open group in the full viewport (`FocusShell`: the stage is the view, the rail stays), keeps `?focus=1`
92
+ beside `?case=` and `?view=`, and returns with Escape or the exit button to the same case, group and sub-view; a product with
93
+ a simulation must offer it (ADR-0070, `caos-shell-gate --require-focus`). See [docs/04](docs/04_views.md).
94
+ - **Small cards and animation (0.14.0):** `FlipCard` and `CardDeck` for SMALL cards the reader scans by title (a flip card
95
+ shows its title and turns to its content; a deck is a row up to four cards and a carousel above), used by the executive
96
+ view for its bearers, roles, approaches and 4 C's; an executive illustration may be an animated SVG with a
97
+ `prefers-reduced-motion` block ([docs/04](docs/04_views.md), [docs/06](docs/06_executive-view.md)).
90
98
  - **Icons:** lucide icons are sized by where they sit (`svg.lucide`); pass no `size` ([docs/06](docs/06_icons-and-numbers.md)).
91
99
  - **Numbers:** `formatNumber(value, lang, opts)` or `useFormat()`; never `toFixed` in a view. A magnitude below 1e-4
92
100
  is written in scientific notation (a p-value of 1e-200 is `1E-200`, not two hundred zeros); give `digits`, not
@@ -130,7 +138,7 @@ const config: ShellConfig = {
130
138
  `ChipGroup`, `Stage`, `useStageSize`, `makeStateKey`, `isStale`, `useWorkbenchState`,
131
139
  `WorkbenchStateContext`, `pick`; since 0.8.0 `SurfacePage`, `useUrlView`, `viewParamOf`, `replaceQueryParam`,
132
140
  `BREAKPOINTS`, `BarChart`, `textWidth`, `widestLabel`, `fitLabel`, `niceTicks`, `niceStep`, `fontFamily`,
133
- `formatTicks`, `NBSP`, `SCIENTIFIC_BELOW`, the type `ShellColorToken`; since 0.9.0 `ViewsRow`. Separate entries:
141
+ `formatTicks`, `NBSP`, `SCIENTIFIC_BELOW`, the type `ShellColorToken`; since 0.9.0 `ViewsRow`. Since 0.14.0 `FlipCard`, `CardDeck` (with `FLIP_LIMITS`, `FLIP_TEXT`, `DECK_TEXT`, `perPageFor`, `deckWindow`, `deckPosition`), the `focus` prop of `CaseWorkbench` (`CaseFocusConfig`, `FOCUS_TEXT`, `FOCUS_PARAM`) and `removeQueryParam`. Separate entries:
134
142
  `/chart` (`UPlotChart` with `x.log`, `parity`, `onPick`; `drawMarks`, `tickSteps`, `repeatedLabels`, `seriesStyle`,
135
143
  `sortOrder`, `parityRange`), `/keys` (the storage keys, no React), `/styles.css`, `/chart.css`,
136
144
  `/reserved-classes.json`. Bin: `caos-shell-gate`.
@@ -1,72 +1,74 @@
1
- #!/usr/bin/env node
2
- // caos-shell-gate: the measured web baseline of a CAOS product (ADR-0071, ADR-0017, ADR-0016, ADR-0078).
3
- // The checks and the traps each one embeds are listed in gate/run.mjs; the self-test (npm run test:gate in the
4
- // shell repository) plants one defect per check and requires each to fail.
5
- //
6
- // Usage:
7
- // npx caos-shell-gate --serve dist --expect-brand "Contraste" a Pages-faithful server over the build
8
- // npx caos-shell-gate --url https://contraste.fasl-work.com --expect-brand "Contraste" the deployed origin
9
- // Options:
10
- // --base-path /Repo/ the site's base path with --serve (a project page); default /
11
- // --routes /,/methodology the routes; default: read from the header navigation
12
- // --workbench /app the routes that carry the case workbench; default /app
13
- // --sizes 390x844,...,2560x1440 --themes light,dark --langs en,es
14
- // --case-sample id1,id2 cases walked outside the primary mode; default the first and the last
15
- // --single-case the workbench has one case and no case control
16
- // --idle-ms 3000 --settle-ms 15000 --text-floor 400 --instrument-min 0.5 --stage-fill-min 0.3 --stage-floor 140
17
- // --wide-font-sizes 390x844,1280x800 the sizes of the wide-font pass (G10); --no-wide-font skips it
18
- // --out gate-output report (gate-report.json), screenshots (shots/) and the capture index (index.html)
19
- // Needs `playwright` in the consuming project (optional peer dependency). Exit 1 on any failure, 2 on misuse.
20
-
21
- import { runGate } from '../gate/run.mjs';
22
-
23
- const FLAGS = new Set(['single-case', 'no-wide-font']);
24
- const args = {};
25
- const argv = process.argv.slice(2);
26
- for (let i = 0; i < argv.length; i += 1) {
27
- const a = argv[i];
28
- if (!a.startsWith('--')) continue;
29
- const key = a.slice(2);
30
- if (FLAGS.has(key)) args[key] = true;
31
- else args[key] = argv[i + 1];
32
- if (!FLAGS.has(key)) i += 1;
33
- }
34
- const num = (k) => (args[k] === undefined ? undefined : Number(args[k]));
35
- const options = {
36
- url: args.url,
37
- serve: args.serve,
38
- basePath: args['base-path'],
39
- expectBrand: args['expect-brand'],
40
- routes: args.routes,
41
- workbench: args.workbench,
42
- sizes: args.sizes,
43
- themes: args.themes,
44
- langs: args.langs,
45
- caseSample: args['case-sample'],
46
- singleCase: args['single-case'] === true,
47
- idleMs: num('idle-ms'),
48
- settleMs: num('settle-ms'),
49
- textFloor: num('text-floor'),
50
- instrumentMin: num('instrument-min'),
51
- stageFillMin: num('stage-fill-min'),
52
- stageFloor: num('stage-floor'),
53
- wideFontSizes: args['wide-font-sizes'],
54
- wideFont: args['no-wide-font'] === true ? false : undefined,
55
- out: args.out,
56
- };
57
- for (const k of Object.keys(options)) if (options[k] === undefined) delete options[k];
58
-
59
- let report;
60
- try {
61
- report = await runGate(options);
62
- } catch (e) {
63
- console.error(e.message);
64
- process.exit(2);
65
- }
66
- const shown = report.failures.slice(0, 80);
67
- for (const f of shown) console.log(`FAIL [${f.check}] ${f.route} ${f.mode}${f.trail ? ` (${f.trail})` : ''}: ${f.message}`);
68
- if (report.failures.length > shown.length) console.log(`... and ${report.failures.length - shown.length} more in ${options.out ?? 'gate-output'}/gate-report.json`);
69
- for (const w of report.waivers ?? []) console.log(`WAIVER ${w.route} (${w.lang}) ${w.floor}: ${w.reason}${w.valid ? '' : ' [refused]'}`);
70
- const counts = Object.entries(report.checks).map(([k, n]) => `${k} ${n}`).join(', ');
71
- console.log(report.ok ? `caos-shell-gate: OK, ${report.states} measured states` : `caos-shell-gate: ${report.failures.length} failure(s) in ${report.states} measured states (${counts})`);
72
- process.exit(report.ok ? 0 : 1);
1
+ #!/usr/bin/env node
2
+ // caos-shell-gate: the measured web baseline of a CAOS product (ADR-0071, ADR-0017, ADR-0016, ADR-0078).
3
+ // The checks and the traps each one embeds are listed in gate/run.mjs; the self-test (npm run test:gate in the
4
+ // shell repository) plants one defect per check and requires each to fail.
5
+ //
6
+ // Usage:
7
+ // npx caos-shell-gate --serve dist --expect-brand "Contraste" a Pages-faithful server over the build
8
+ // npx caos-shell-gate --url https://contraste.fasl-work.com --expect-brand "Contraste" the deployed origin
9
+ // Options:
10
+ // --base-path /Repo/ the site's base path with --serve (a project page); default /
11
+ // --routes /,/methodology the routes; default: read from the header navigation
12
+ // --workbench /app the routes that carry the case workbench; default /app
13
+ // --sizes 390x844,...,2560x1440 --themes light,dark --langs en,es
14
+ // --case-sample id1,id2 cases walked outside the primary mode; default the first and the last
15
+ // --single-case the workbench has one case and no case control
16
+ // --idle-ms 3000 --settle-ms 15000 --text-floor 400 --instrument-min 0.5 --stage-fill-min 0.3 --stage-floor 140
17
+ // --wide-font-sizes 390x844,1280x800 the sizes of the wide-font pass (G10); --no-wide-font skips it
18
+ // --require-focus fail an App without a Focus control (G18; ADR-0070 requires one in a product with simulations)
19
+ // --out gate-output report (gate-report.json), screenshots (shots/) and the capture index (index.html)
20
+ // Needs `playwright` in the consuming project (optional peer dependency). Exit 1 on any failure, 2 on misuse.
21
+
22
+ import { runGate } from '../gate/run.mjs';
23
+
24
+ const FLAGS = new Set(['single-case', 'no-wide-font', 'require-focus']);
25
+ const args = {};
26
+ const argv = process.argv.slice(2);
27
+ for (let i = 0; i < argv.length; i += 1) {
28
+ const a = argv[i];
29
+ if (!a.startsWith('--')) continue;
30
+ const key = a.slice(2);
31
+ if (FLAGS.has(key)) args[key] = true;
32
+ else args[key] = argv[i + 1];
33
+ if (!FLAGS.has(key)) i += 1;
34
+ }
35
+ const num = (k) => (args[k] === undefined ? undefined : Number(args[k]));
36
+ const options = {
37
+ url: args.url,
38
+ serve: args.serve,
39
+ basePath: args['base-path'],
40
+ expectBrand: args['expect-brand'],
41
+ routes: args.routes,
42
+ workbench: args.workbench,
43
+ sizes: args.sizes,
44
+ themes: args.themes,
45
+ langs: args.langs,
46
+ caseSample: args['case-sample'],
47
+ singleCase: args['single-case'] === true,
48
+ requireFocus: args['require-focus'] === true,
49
+ idleMs: num('idle-ms'),
50
+ settleMs: num('settle-ms'),
51
+ textFloor: num('text-floor'),
52
+ instrumentMin: num('instrument-min'),
53
+ stageFillMin: num('stage-fill-min'),
54
+ stageFloor: num('stage-floor'),
55
+ wideFontSizes: args['wide-font-sizes'],
56
+ wideFont: args['no-wide-font'] === true ? false : undefined,
57
+ out: args.out,
58
+ };
59
+ for (const k of Object.keys(options)) if (options[k] === undefined) delete options[k];
60
+
61
+ let report;
62
+ try {
63
+ report = await runGate(options);
64
+ } catch (e) {
65
+ console.error(e.message);
66
+ process.exit(2);
67
+ }
68
+ const shown = report.failures.slice(0, 80);
69
+ for (const f of shown) console.log(`FAIL [${f.check}] ${f.route} ${f.mode}${f.trail ? ` (${f.trail})` : ''}: ${f.message}`);
70
+ if (report.failures.length > shown.length) console.log(`... and ${report.failures.length - shown.length} more in ${options.out ?? 'gate-output'}/gate-report.json`);
71
+ for (const w of report.waivers ?? []) console.log(`WAIVER ${w.route} (${w.lang}) ${w.floor}: ${w.reason}${w.valid ? '' : ' [refused]'}`);
72
+ const counts = Object.entries(report.checks).map(([k, n]) => `${k} ${n}`).join(', ');
73
+ console.log(report.ok ? `caos-shell-gate: OK, ${report.states} measured states` : `caos-shell-gate: ${report.failures.length} failure(s) in ${report.states} measured states (${counts})`);
74
+ process.exit(report.ok ? 0 : 1);
package/dist/chart.js CHANGED
@@ -1,5 +1,5 @@
1
- import { useShellLang, useThemeStore, useStageSize, pick, resolveToken, NBSP, formatNumber, formatTicks } from './chunk-3QYQCWSZ.js';
2
- import './chunk-YVVDBSAC.js';
1
+ import { useShellLang, useThemeStore, useStageSize, pick, resolveToken, NBSP, formatNumber, formatTicks } from './chunk-YECZXGVG.js';
2
+ import './chunk-33WTUQ7Q.js';
3
3
  import { useRef, useCallback, useState, useMemo, useEffect } from 'react';
4
4
  import uPlot from 'uplot';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,4 +1,4 @@
1
- import { LANG_STORAGE_KEY, THEME_STORAGE_KEY } from './chunk-YVVDBSAC.js';
1
+ import { LANG_STORAGE_KEY, THEME_STORAGE_KEY } from './chunk-33WTUQ7Q.js';
2
2
  import { create } from 'zustand';
3
3
  import { useMemo, useState, useRef, useCallback, useEffect } from 'react';
4
4
  import { jsx } from 'react/jsx-runtime';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as APP_ROUTE, a as AppShell, b as ArchTab, c as ArchitectureConfig, d as ArchitectureModal, L as LANDING_ROUTE, e as LANG_STORAGE_KEY, S as STANDARD_ROUTES, f as ShellConfig, g as ShellRoute, T as THEME_BOOT_SCRIPT, h as THEME_STORAGE_KEY, v as validateArchitectureConfig } from './keys-CTQZafOf.js';
1
+ export { A as APP_ROUTE, a as AppShell, b as ArchTab, c as ArchitectureConfig, d as ArchitectureModal, L as LANDING_ROUTE, e as LANG_STORAGE_KEY, S as STANDARD_ROUTES, f as ShellConfig, g as ShellRoute, T as THEME_BOOT_SCRIPT, h as THEME_STORAGE_KEY, v as validateArchitectureConfig } from './keys-DDQGijwQ.js';
2
2
  import * as react from 'react';
3
3
  import { ReactNode, Component, ErrorInfo } from 'react';
4
4
  import * as zustand from 'zustand';
@@ -63,9 +63,14 @@ interface FocusShellProps {
63
63
  onExit: () => void;
64
64
  exitLabel: string;
65
65
  stageLabel: string;
66
+ /**
67
+ * True (default): the frame is the page's `main`. False: the frame sits inside a page that already has its `main` (the App's
68
+ * Focus mode does), so it renders a `div` and the page holds one `main`.
69
+ */
70
+ landmark?: boolean;
66
71
  }
67
72
  /** Full-viewport, scenario-scoped analysis frame. The product supplies only its instrument and controls. */
68
- declare function FocusShell({ stage, rail, title, description, hud, onExit, exitLabel, stageLabel }: FocusShellProps): react.JSX.Element;
73
+ declare function FocusShell({ stage, rail, title, description, hud, onExit, exitLabel, stageLabel, landmark }: FocusShellProps): react.JSX.Element;
69
74
 
70
75
  /** Light/dark icon toggle. Shows the icon of the theme you would switch TO. */
71
76
  declare function ThemeToggle(): react.JSX.Element;
@@ -406,9 +411,10 @@ declare function ReferenceList({ ids, heading }: {
406
411
 
407
412
  /**
408
413
  * A documentation route (Introduction, Methodology, Implementation, Experiments, Benchmark): the page head with
409
- * its title and lede, then sections. `wide` for a route with a sub-tab rail (ADR-0071 rule 2, amended
410
- * 2026-09-04); a single prose column keeps the reading width and is centred (ADR-0017 §1). Text takes the full width
411
- * of the column, and the references of its sections are listed once at the end of the page (ADR-0017 §4).
414
+ * its title and lede, then sections. Every documentation route takes the full width of the page, the Introduction
415
+ * included (Felipe, 2026-10-13: the Introduction did not use the full width while Methodology, Implementation,
416
+ * Experiments and Benchmark did; 0.13.1). `wide` is kept for compatibility and changes nothing. Text takes the full
417
+ * width of the column, and the references of its sections are listed once at the end of the page (ADR-0017 section 4).
412
418
  */
413
419
  /**
414
420
  * The areas a section may declare, per documentation route (ADR-0017 section 2.1). The gate's table (`gate/areas.mjs`)
@@ -424,6 +430,7 @@ declare const DOC_AREAS: {
424
430
  interface DocPageProps {
425
431
  title: BiText;
426
432
  lede: ReactNode;
433
+ /** Kept for compatibility (0.13.1): every documentation route is full width, so this changes nothing. */
427
434
  wide?: boolean;
428
435
  /**
429
436
  * The required areas this page cannot carry, each with the reason (ADR-0017 section 2.1): rendered as
@@ -436,7 +443,7 @@ interface DocPageProps {
436
443
  }>;
437
444
  children: ReactNode;
438
445
  }
439
- declare function DocPage({ title, lede, wide, floorWaivers, children }: DocPageProps): react.JSX.Element;
446
+ declare function DocPage({ title, lede, floorWaivers, children }: DocPageProps): react.JSX.Element;
440
447
  interface DocSectionProps {
441
448
  title: BiText;
442
449
  id?: string;
@@ -663,6 +670,129 @@ interface QuestionProps {
663
670
  */
664
671
  declare function Question({ question, answer }: QuestionProps): react.JSX.Element;
665
672
 
673
+ /**
674
+ * The limits of a SMALL card (rule of use, CAOS_MANAGE plans/app-shell/0-14-0-plan.md decision 3): a title of about eight
675
+ * words and a back of about forty. They are counted in the interface language: Spanish runs about a quarter longer than
676
+ * English for the same text, so its limits are a quarter higher. `gate/areas.mjs` FLIP_LIMITS holds the same values and
677
+ * `test/flipCard.test.tsx` keeps the two equal.
678
+ */
679
+ declare const FLIP_LIMITS: {
680
+ readonly title: {
681
+ readonly en: 8;
682
+ readonly es: 10;
683
+ };
684
+ readonly back: {
685
+ readonly en: 40;
686
+ readonly es: 50;
687
+ };
688
+ };
689
+ /** The words the card itself writes (the product writes none of them). */
690
+ declare const FLIP_TEXT: {
691
+ readonly show: {
692
+ readonly en: "Show details";
693
+ readonly es: "Mostrar detalles";
694
+ };
695
+ readonly hide: {
696
+ readonly en: "Hide details";
697
+ readonly es: "Ocultar detalles";
698
+ };
699
+ };
700
+ interface FlipCardProps {
701
+ /** The face: a short title (a node, so a card may carry a source mark). */
702
+ title: ReactNode;
703
+ /** The title as plain text, repeated on the back beside the return control; omit it and the back shows none. */
704
+ label?: string;
705
+ /** The back: the content shown after the card turns. It stays in the markup while the face shows. */
706
+ children: ReactNode;
707
+ /** Base of the ids (default: generated). */
708
+ id?: string;
709
+ /** Start turned (default: face up). */
710
+ defaultOpen?: boolean;
711
+ /** Controlled turn, when the product holds it itself. */
712
+ open?: boolean;
713
+ onOpenChange?: (open: boolean) => void;
714
+ className?: string;
715
+ /** Forces the language of the card's own words (tests; the card otherwise follows the language toggle). */
716
+ lang?: Lang;
717
+ }
718
+ /**
719
+ * A small card whose face shows its title and whose back shows its content after a click or a key press (Enter or Space),
720
+ * and back again (shell 0.14.0). Use it only for a small card the reader scans by title first and opens when it matters
721
+ * (a title of about eight words, a back of about forty; the gate fails a card over the limit): a long card or an ordered
722
+ * sequence stays plain.
723
+ *
724
+ * - A real `button` carries `aria-expanded` and `aria-controls`; each face has one, so focus moves to the face that shows
725
+ * and never rests on a hidden one. Escape closes an open card.
726
+ * - The hidden face is out of the accessibility tree and the tab order (`aria-hidden` and `inert` at once, `visibility: hidden`
727
+ * after the turn) while its content stays in the markup (search, copy and the gate read it).
728
+ * - `prefers-reduced-motion: reduce` swaps the faces without the turn; colours are theme tokens only.
729
+ * - The face states that it turns: a small icon and a visually hidden label in both languages.
730
+ */
731
+ declare function FlipCard({ title, label, children, id, defaultOpen, open: controlled, onOpenChange, className, lang: forced }: FlipCardProps): react.JSX.Element;
732
+
733
+ /** The words the deck itself writes (the product writes none of them). */
734
+ declare const DECK_TEXT: {
735
+ readonly previous: {
736
+ readonly en: "Previous";
737
+ readonly es: "Anterior";
738
+ };
739
+ readonly next: {
740
+ readonly en: "Next";
741
+ readonly es: "Siguiente";
742
+ };
743
+ readonly of: {
744
+ readonly en: "of";
745
+ readonly es: "de";
746
+ };
747
+ readonly to: {
748
+ readonly en: "to";
749
+ readonly es: "a";
750
+ };
751
+ };
752
+ /** How many cards of at least `minCard` px fit a row of `width` px with `gap` px between them: at least one. */
753
+ declare function perPageFor(width: number, minCard: number, gap: number): number;
754
+ /** The window `[start, end)` of `n` cards that `per` at a time show from `start`: never empty, never past the end, pulled back so it is full. */
755
+ declare function deckWindow(n: number, per: number, start: number): {
756
+ start: number;
757
+ end: number;
758
+ };
759
+ /** "2 of 5" for one card, "1 to 3 of 5" for several (1-based). */
760
+ declare function deckPosition(start: number, shown: number, n: number, lang: Lang): string;
761
+ interface DeckCard {
762
+ /** Stable key (default: the index). */
763
+ id?: string | number;
764
+ /** The title of the card (a flip card's face). */
765
+ title: ReactNode;
766
+ /** The title as plain text, for the back of a flip card. */
767
+ label?: string;
768
+ /** The content: a plain card's body, a flip card's back. */
769
+ body: ReactNode;
770
+ /** Attributes written on the card's list item (for example `data-exec-item`). */
771
+ attrs?: Record<string, string | number | undefined>;
772
+ }
773
+ interface CardDeckProps {
774
+ cards: DeckCard[];
775
+ /** Render each card as a `FlipCard` (a small card the reader scans by title; see FlipCard for the rule of use). */
776
+ flip?: boolean;
777
+ /** A list longer than this is a carousel; up to it, a row (default 4). */
778
+ carouselAbove?: number;
779
+ /** The accessible name of the deck. */
780
+ ariaLabel: string;
781
+ /** Extra classes of the list that holds the cards. */
782
+ className?: string;
783
+ /** Forces the language of the deck's own words (tests). */
784
+ lang?: Lang;
785
+ }
786
+ /**
787
+ * A list of cards, plain or as flip cards, shown as a row up to `carouselAbove` cards (default 4) and as a CAROUSEL above
788
+ * it (shell 0.14.0): one row of as many cards as fit (at least one), previous and next buttons, a swipe on touch, the arrow
789
+ * keys, Home and End while it has focus, and a position ("2 of 5"). No autoplay. Every card is reachable by keyboard: the
790
+ * cards the window hides are removed from sight and from the tab order but stay in the markup. The carousel pages through
791
+ * its cards instead of scrolling a strip, so neither the page nor its panel ever scrolls sideways (ADR-0071). Reduced
792
+ * motion removes the slide. When every card fits, the controls are not drawn.
793
+ */
794
+ declare function CardDeck({ cards, flip, carouselAbove, ariaLabel, className, lang: forced }: CardDeckProps): react.JSX.Element;
795
+
666
796
  interface SurfacePageProps {
667
797
  /** The route's heading; omit it when the header's active route already names the surface. */
668
798
  title?: BiText;
@@ -695,6 +825,8 @@ declare function SurfacePage({ title, lede, actions, tabs, value, onChange, deep
695
825
  /** Writes one query parameter with replaceState, keeping the router's history state (React Router keeps its entry
696
826
  * key and index there; replacing it with null broke the index the back button relies on). */
697
827
  declare function replaceQueryParam(param: string, value: string): void;
828
+ /** Removes one query parameter with replaceState (the counterpart of `replaceQueryParam`). */
829
+ declare function removeQueryParam(param: string): void;
698
830
  /**
699
831
  * The open view of a tab row, held in the URL (`?view=` by default) so a reader can share it and a reload keeps it.
700
832
  * Returns the id to show and the selector to call. Controlled when `value` is given: the product holds the state and
@@ -964,6 +1096,44 @@ interface VariantBarProps {
964
1096
  * one-line note of what it shows. Selecting a regime must change every view of the case. */
965
1097
  declare function VariantBar({ variants, activeId, onSelect, title, lane }: VariantBarProps): react.JSX.Element;
966
1098
 
1099
+ /** The words Focus mode writes (the product writes none of them). */
1100
+ declare const FOCUS_TEXT: {
1101
+ readonly enter: {
1102
+ readonly en: "Focus";
1103
+ readonly es: "Enfocar";
1104
+ };
1105
+ readonly enterLabel: {
1106
+ readonly en: "Open this case in focus mode";
1107
+ readonly es: "Abrir este caso en modo enfoque";
1108
+ };
1109
+ readonly exit: {
1110
+ readonly en: "Back to the App";
1111
+ readonly es: "Volver a la App";
1112
+ };
1113
+ readonly stage: {
1114
+ readonly en: "Focus stage";
1115
+ readonly es: "Escenario de enfoque";
1116
+ };
1117
+ };
1118
+ /** The query parameter that holds Focus mode (`?focus=1`), beside `?case=` and `?view=`. */
1119
+ declare const FOCUS_PARAM = "focus";
1120
+ /** What a product may say about the Focus mode of its App (`CaseWorkbench.focus`; `true` means all defaults). */
1121
+ interface CaseFocusConfig {
1122
+ /** The stage's heading (default: the name of the selected case). */
1123
+ title?: BiText;
1124
+ /** One line under it (default: the name of the open group). */
1125
+ description?: BiText;
1126
+ /** Readouts laid over the stage (ADR-0070): at most four on a phone. */
1127
+ hud?: Array<{
1128
+ label: BiText;
1129
+ value: string;
1130
+ }>;
1131
+ /** Called when Focus opens (`true`) and closes (`false`), for a product that records it. */
1132
+ onChange?: (on: boolean) => void;
1133
+ /** The query parameter (default `focus`). */
1134
+ param?: string;
1135
+ }
1136
+
967
1137
  /** One group of views of the selected case, named for the question the user is asking (ADR-0071 rule 5). */
968
1138
  interface WorkbenchGroup {
969
1139
  id: string;
@@ -1014,6 +1184,13 @@ interface CaseWorkbenchProps {
1014
1184
  * keeps it out of the URL. Ignored when `group` is controlled. */
1015
1185
  deepLinkView?: boolean | string;
1016
1186
  ariaLabel?: BiText;
1187
+ /**
1188
+ * Focus mode for the App (ADR-0070, shell 0.14.0): a Focus button in the rail head opens the selected case's open group in
1189
+ * the full viewport through `FocusShell` (the stage is the view, the rail stays), holds `?focus=1` beside `?case=` and
1190
+ * `?view=` in the URL, and returns with Escape or the exit button to the same case, group and sub-view. `true` takes all
1191
+ * the defaults. Omit it and the App is exactly what it was.
1192
+ */
1193
+ focus?: boolean | CaseFocusConfig;
1017
1194
  }
1018
1195
  declare const MAX_WORKBENCH_GROUPS = 6;
1019
1196
  /**
@@ -1102,4 +1279,4 @@ interface ViewsRowProps {
1102
1279
  */
1103
1280
  declare function ViewsRow({ children, shares }: ViewsRowProps): react.JSX.Element;
1104
1281
 
1105
- export { BREAKPOINTS, BarChart, type BarChartProps, type BarDatum, 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, DOC_AREAS, DocPage, type DocPageProps, DocReferences, DocRefsScope, DocSection, type DocSectionProps, EXECUTIVE_SCHEMA_VERSION, Equation, type ExecutiveData, type ExecutiveProblem, ExecutiveView, type ExecutiveViewProps, FOUR_CS, Figure, type FittedLabel, FocusShell, type FocusShellProps, FormatOptions, Gauge, type GaugeProps, type GaugeZone, Glossary, type GlossaryEntry, InlineMath, Knob, type KnobProps, Landing, type Lane, LaneBadge, Lang, LanguageToggle, MAX_PEER_TABS, MAX_WORKBENCH_GROUPS, PanelBoundary, type PausedVizController, PlotCard, type PlotCardProps, type Provenance, Question, type QuestionProps, type RailSection, Readout, type ReadoutItem, type ReadoutProps, ReferenceList, Refs, Stage, type StageSize, Stat, type StatProps, type SubTabDef, SubTabs, type SubTabsProps, SurfacePage, type SurfacePageProps, 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, ViewsRow, type ViewsRowProps, 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, firstAppearance, fitLabel, fontFamily, groupByCategory, isStale, makeStateKey, niceStep, niceTicks, readCaseParam, readTheme, replaceQueryParam, sourcesPresent, textWidth, usePausedViz, useStageSize, useThemeStore, useUrlView, useWorkbenchState, validateExecutive, viewParamOf, widestLabel, withCaseParam };
1282
+ export { BREAKPOINTS, BarChart, type BarChartProps, type BarDatum, BiText, Callout, CardDeck, type CardDeckProps, type CaseDef, type CaseFocusConfig, type CaseGroup, type CaseKind, CaseSelector, type CaseSelectorProps, type CaseSelectorText, CaseWorkbench, type CaseWorkbenchProps, ChipGroup, type ChipGroupProps, type ChipOption, type Citation, CitationsProvider, Cite, DECK_TEXT, DOC_AREAS, type DeckCard, DocPage, type DocPageProps, DocReferences, DocRefsScope, DocSection, type DocSectionProps, EXECUTIVE_SCHEMA_VERSION, Equation, type ExecutiveData, type ExecutiveProblem, ExecutiveView, type ExecutiveViewProps, FLIP_LIMITS, FLIP_TEXT, FOCUS_PARAM, FOCUS_TEXT, FOUR_CS, Figure, type FittedLabel, FlipCard, type FlipCardProps, FocusShell, type FocusShellProps, FormatOptions, Gauge, type GaugeProps, type GaugeZone, Glossary, type GlossaryEntry, InlineMath, Knob, type KnobProps, Landing, type Lane, LaneBadge, Lang, LanguageToggle, MAX_PEER_TABS, MAX_WORKBENCH_GROUPS, PanelBoundary, type PausedVizController, PlotCard, type PlotCardProps, type Provenance, Question, type QuestionProps, type RailSection, Readout, type ReadoutItem, type ReadoutProps, ReferenceList, Refs, Stage, type StageSize, Stat, type StatProps, type SubTabDef, SubTabs, type SubTabsProps, SurfacePage, type SurfacePageProps, 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, ViewsRow, type ViewsRowProps, 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, deckPosition, deckWindow, firstAppearance, fitLabel, fontFamily, groupByCategory, isStale, makeStateKey, niceStep, niceTicks, perPageFor, readCaseParam, readTheme, removeQueryParam, replaceQueryParam, sourcesPresent, textWidth, usePausedViz, useStageSize, useThemeStore, useUrlView, useWorkbenchState, validateExecutive, viewParamOf, widestLabel, withCaseParam };