@weasel-js/labkit 1.5.2 → 1.6.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.
Files changed (139) hide show
  1. package/README.md +3 -7
  2. package/dist/_dts/{CanvasStackContext-CMMo180l.d.ts → CanvasStackContext-DQgcH1ny.d.ts} +1 -1
  3. package/dist/_dts/LayerList.d-2c9yup5Y.d.ts +83 -0
  4. package/dist/_dts/{PrefsForm.d-DPy9I8It.d.ts → PrefsRow.d-DFpPIeIC.d.ts} +16 -1
  5. package/dist/_dts/{Select.d-ZTiwzYyT.d.ts → Select.d-BchGMC2Y.d.ts} +102 -11
  6. package/dist/_dts/{frac-B4hFJMiN.d.ts → frac-DTRq7Xpb.d.ts} +18 -10
  7. package/dist/_dts/inDialog-D5ZKGjiD.d.ts +23 -0
  8. package/dist/_dts/{index-DlKolEIZ.d.ts → index-B0--8PEM.d.ts} +2 -2
  9. package/dist/_dts/index-DINmCuLA.d.ts +453 -0
  10. package/dist/_dts/number.d-OeQYQoUO.d.ts +28 -0
  11. package/dist/_dts/{types-Db-3NNk1.d.ts → types-B7y8hX1D.d.ts} +2 -2
  12. package/dist/_dts/{usePanZoom-SSTZe410.d.ts → usePanZoom-Bq73pwFe.d.ts} +2 -2
  13. package/dist/_dts/{useTrialState-pnAE-UBD.d.ts → useTrialState-CP4jfuKl.d.ts} +1 -1
  14. package/dist/canvas/index.d.ts +6 -5
  15. package/dist/chrome/index.d.ts +5 -4
  16. package/dist/chrome/index.js +7 -7
  17. package/dist/chunk-2RVCB2PT.js +3 -0
  18. package/dist/chunk-2RVCB2PT.js.map +1 -0
  19. package/dist/{chunk-753Y6HE6.js → chunk-3FI7Y4R4.js} +4 -4
  20. package/dist/chunk-3FI7Y4R4.js.map +1 -0
  21. package/dist/{chunk-REOBGYMC.js → chunk-AWH3SRPW.js} +31 -5
  22. package/dist/chunk-AWH3SRPW.js.map +1 -0
  23. package/dist/{chunk-A2ACAGW6.js → chunk-BVL7Y574.js} +22 -5
  24. package/dist/chunk-BVL7Y574.js.map +1 -0
  25. package/dist/{chunk-CKUKF72R.js → chunk-H5VIV7MQ.js} +26 -22
  26. package/dist/chunk-H5VIV7MQ.js.map +1 -0
  27. package/dist/chunk-HFRE4QQV.js +400 -0
  28. package/dist/chunk-HFRE4QQV.js.map +1 -0
  29. package/dist/{chunk-LRI3MBGR.js → chunk-MOLIUNVH.js} +4 -4
  30. package/dist/{chunk-LRI3MBGR.js.map → chunk-MOLIUNVH.js.map} +1 -1
  31. package/dist/chunk-USO6TERP.js +90 -0
  32. package/dist/chunk-USO6TERP.js.map +1 -0
  33. package/dist/{chunk-EMXAIRUJ.js → chunk-VQ47KWD5.js} +3 -3
  34. package/dist/{chunk-EMXAIRUJ.js.map → chunk-VQ47KWD5.js.map} +1 -1
  35. package/dist/chunk-WUJXJC5O.js +14568 -0
  36. package/dist/chunk-WUJXJC5O.js.map +1 -0
  37. package/dist/{chunk-KJCUERQF.js → chunk-ZORVNOKX.js} +6 -25
  38. package/dist/chunk-ZORVNOKX.js.map +1 -0
  39. package/dist/config/index.d.ts +50 -7
  40. package/dist/config/index.js +5 -5
  41. package/dist/controls/index.d.ts +6 -3
  42. package/dist/controls/index.js +4 -4
  43. package/dist/dragdrop/index.d.ts +4 -3
  44. package/dist/index.d.ts +83 -102
  45. package/dist/index.js +430 -458
  46. package/dist/index.js.map +1 -1
  47. package/dist/layers/index.d.ts +6 -35
  48. package/dist/layers/index.js +2 -3
  49. package/dist/loupe/index.d.ts +6 -5
  50. package/dist/passthrough/weasel-ui.d.ts +407 -204
  51. package/dist/passthrough/weasel-ui.js +1 -1
  52. package/dist/primitives/index.d.ts +2 -13
  53. package/dist/primitives/index.js +4 -4
  54. package/dist/state/index.d.ts +6 -5
  55. package/dist/state/index.js +5 -5
  56. package/dist/styles.css +298 -174
  57. package/dist/undo/index.d.ts +4 -3
  58. package/package.json +9 -13
  59. package/src/annotations/frac.test.ts +7 -0
  60. package/src/annotations/paint.test.ts +21 -0
  61. package/src/annotations/paint.ts +12 -0
  62. package/src/annotations/store.test.ts +31 -0
  63. package/src/annotations/types.ts +3 -2
  64. package/src/canvas/Stage.test.tsx +18 -0
  65. package/src/chrome/regions/SidebarRegion.less +197 -0
  66. package/src/chrome/regions/SidebarRegion.test.tsx +20 -0
  67. package/src/chrome/regions/SidebarRegion.tsx +7 -2
  68. package/src/chrome/types.ts +5 -2
  69. package/src/config/autoConfig.ts +2 -2
  70. package/src/config/builder.ts +20 -1
  71. package/src/config/entry.test.ts +8 -0
  72. package/src/config/index.ts +2 -0
  73. package/src/config/rules.ts +2 -0
  74. package/src/config/sectionTree.test.ts +146 -0
  75. package/src/config/sectionTree.ts +124 -0
  76. package/src/controls/ControlPanel.less +4 -0
  77. package/src/controls/ControlPanel.stories.tsx +55 -2
  78. package/src/controls/ControlPanel.test.tsx +30 -3
  79. package/src/controls/ControlPanel.tsx +116 -337
  80. package/src/controls/inDialog.test.tsx +82 -0
  81. package/src/controls/inDialog.tsx +46 -0
  82. package/src/controls/index.ts +1 -0
  83. package/src/index.ts +11 -26
  84. package/src/instrument/types.ts +5 -2
  85. package/src/lab/Lab.nebula.test.tsx +31 -0
  86. package/src/lab/Lab.tsx +38 -15
  87. package/src/lab/LabBoundary.test.tsx +66 -0
  88. package/src/lab/LabBoundary.tsx +56 -0
  89. package/src/lab/LabHeader.tsx +4 -23
  90. package/src/lab/LabRoot.tsx +2 -3
  91. package/src/lab/LabShell.less +0 -1
  92. package/src/lab/LabShell.stories.tsx +15 -0
  93. package/src/lab/LabShell.test.tsx +18 -0
  94. package/src/lab/LabShell.tsx +14 -1
  95. package/src/lab/LabSwitcher.less +1 -1
  96. package/src/lab/LabSwitcher.test.tsx +41 -0
  97. package/src/lab/LabSwitcher.tsx +30 -8
  98. package/src/lab/index.ts +1 -0
  99. package/src/layers/AGENTS.md +18 -74
  100. package/src/layers/index.ts +4 -2
  101. package/src/passthrough/weasel-ui.ts +25 -10
  102. package/src/primitives/index.ts +0 -2
  103. package/src/specimen/Specimen.tsx +42 -56
  104. package/src/state/types.ts +2 -1
  105. package/src/styles.less +4 -2
  106. package/src/theme/Interstellar.stories.tsx +8 -9
  107. package/src/theme/base.less +3 -20
  108. package/src/theme/interstellar.test.ts +6 -3
  109. package/src/theme/interstellar.theme.json +0 -56
  110. package/src/theme/interstellar.ts +2 -2
  111. package/src/trial/Trial.canvas.test.tsx +1 -1
  112. package/src/trial/Trial.tsx +30 -8
  113. package/dist/_dts/index-8LiKe3A-.d.ts +0 -410
  114. package/dist/chunk-6BUQ77ZV.js +0 -18
  115. package/dist/chunk-6BUQ77ZV.js.map +0 -1
  116. package/dist/chunk-753Y6HE6.js.map +0 -1
  117. package/dist/chunk-A2ACAGW6.js.map +0 -1
  118. package/dist/chunk-CKUKF72R.js.map +0 -1
  119. package/dist/chunk-D35PFTS7.js +0 -170
  120. package/dist/chunk-D35PFTS7.js.map +0 -1
  121. package/dist/chunk-FKLT3ITC.js +0 -590
  122. package/dist/chunk-FKLT3ITC.js.map +0 -1
  123. package/dist/chunk-KJCUERQF.js.map +0 -1
  124. package/dist/chunk-LASCBWW7.js +0 -13456
  125. package/dist/chunk-LASCBWW7.js.map +0 -1
  126. package/dist/chunk-REOBGYMC.js.map +0 -1
  127. package/dist/ui/layers/index.d.ts +0 -66
  128. package/dist/ui/layers/index.js +0 -3
  129. package/dist/ui/layers/index.js.map +0 -1
  130. package/src/lab/useSystemMode.ts +0 -30
  131. package/src/layers/LayerList.less +0 -93
  132. package/src/layers/LayerList.stories.tsx +0 -66
  133. package/src/layers/LayerList.test.tsx +0 -232
  134. package/src/layers/LayerList.tsx +0 -268
  135. package/src/primitives/Sidebar.less +0 -112
  136. package/src/primitives/Sidebar.stories.tsx +0 -38
  137. package/src/primitives/Sidebar.test.tsx +0 -74
  138. package/src/primitives/Sidebar.tsx +0 -35
  139. package/src/ui/layers/index.ts +0 -5
package/README.md CHANGED
@@ -30,9 +30,6 @@ the `@weasel-js/ui` components it passes through:
30
30
  import '@weasel-js/labkit/styles.css';
31
31
  ```
32
32
 
33
- Annotations are on the `pre` tag until the next stable release:
34
- `npm i @weasel-js/labkit@pre`.
35
-
36
33
  ## A lab
37
34
 
38
35
  ```tsx
@@ -250,8 +247,8 @@ it. `SidebarSection.undockable` and `undockAs` control the offer;
250
247
 
251
248
  ## Theming
252
249
 
253
- labkit ships one theme, `interstellar` — a cosmic dark and a warm parchment
254
- light — as a value, not a stylesheet. `<Lab>` and `<LabShell>` apply it for you;
250
+ labkit ships one theme, `interstellar` — a cosmic dark over weasel's own light
251
+ mode — as a value, not a stylesheet. `<Lab>` and `<LabShell>` apply it for you;
255
252
  `mode` is `"auto"` (follow the OS), `"light"` or `"dark"`. Only `styles.css`
256
253
  needs importing, and the token values arrive through the provider:
257
254
 
@@ -316,14 +313,13 @@ directly. Several expose more than the root does, `/state` most of all.
316
313
  | `/chrome` | Regions, contribution types, built-in contributions |
317
314
  | `/controls` | `<ControlPanel>` and the config field types |
318
315
  | `/canvas` | `<CanvasStack>`, coordinate helpers, `usePanZoom`, `useOrbit` |
319
- | `/layers` | `<LayerList>` |
316
+ | `/layers` | `<LayerList>` from `@weasel-js/ui`, and the layers capability types |
320
317
  | `/loupe` | Magnifier components and `useLoupe` |
321
318
  | `/surface` | Tiled surface hooks for your own renderer |
322
319
  | `/job` | `useJob` and the job capability types |
323
320
  | `/state` | The lab store, storage adapters, `useTrialState`, serialization helpers |
324
321
  | `/undo` | Undo stack and event bus |
325
322
  | `/dragdrop` | `<Palette>`, `<DragGhost>`, `useDragDrop` |
326
- | `/ui/layers` | `<LayerStack>`, the expandable layer-card list |
327
323
  | `/weasel-ui`, `/weasel-canvas` | Passthroughs to `@weasel-js/ui` and `@weasel-js/core` |
328
324
 
329
325
  ## Development
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { RefObject } from 'react';
3
- import { k as ViewTransform, W as WorldFrame } from './frac-B4hFJMiN.js';
3
+ import { m as ViewTransform, W as WorldFrame } from './frac-DTRq7Xpb.js';
4
4
 
5
5
  /** One layer of a canvas stack: its id, whether it is currently shown, and how
6
6
  * it paints itself. */
@@ -0,0 +1,83 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { S as StanceProps } from './PrefsRow.d-DFpPIeIC.js';
4
+
5
+ /** One layer. With a body (see `renderBody`) it draws as a card that folds its
6
+ * body away; without one, as a one-line row. Either can hold child layers. */
7
+ interface LayerListItem {
8
+ id: string;
9
+ /** The layer's name. Also what its controls are labeled by. */
10
+ label: string;
11
+ /** Shown in the header in place of `label` — a select that switches the
12
+ * layer's kind, say. */
13
+ title?: ReactNode;
14
+ /** Fixed-size content before the label: a color swatch, a kind icon. */
15
+ leading?: ReactNode;
16
+ /** Drawn in the drag handle in place of the grip — an ordinal. */
17
+ badge?: ReactNode;
18
+ /** Which of its list this layer is: an index into the theme's tone list, or
19
+ * a color. A card draws its edge and handle in it and recolors its controls. */
20
+ tone?: StanceProps['tone'];
21
+ /** Cannot be dragged, hidden or removed, walls off drops on either side, and
22
+ * selects alone rather than joining a multi-selection. */
23
+ locked?: boolean;
24
+ /** Shown or not, when the list has `onVisibilityChange`. Default true. */
25
+ visible?: boolean;
26
+ children?: LayerListItem[];
27
+ /** Folds the body and children away until the user opens them.
28
+ * `collapsedIds` overrides it. */
29
+ defaultCollapsed?: boolean;
30
+ }
31
+ /**
32
+ * A reorder: `ids`, siblings under `parentId` (`null` at the top level), now
33
+ * sit as one block in list order starting at `index` of the parent's children.
34
+ * A drag never moves a layer to another parent.
35
+ */
36
+ interface LayerMove {
37
+ ids: string[];
38
+ parentId: string | null;
39
+ index: number;
40
+ }
41
+ /** Props for `<LayerList>`. */
42
+ interface LayerListProps {
43
+ items: LayerListItem[];
44
+ /** Omit for a list the user cannot reorder. */
45
+ onReorder?: (move: LayerMove) => void;
46
+ /** Giving both makes rows selectable: a click selects one, shift-click adds
47
+ * or removes one, and dragging a selected row drags its selected siblings. */
48
+ selectedIds?: readonly string[];
49
+ onSelect?: (ids: string[]) => void;
50
+ /** Adds a visibility checkbox to every unlocked layer. */
51
+ onVisibilityChange?: (id: string, visible: boolean) => void;
52
+ /** Adds a ✕ to every unlocked layer. */
53
+ onRemove?: (id: string) => void;
54
+ /** A layer's controls. Returning `null` leaves that layer a one-line row. */
55
+ renderBody?: (item: LayerListItem) => ReactNode;
56
+ /** Ids whose body and children are folded away. Giving it makes folding
57
+ * controlled; `onCollapsedChange` fires either way. */
58
+ collapsedIds?: readonly string[];
59
+ onCollapsedChange?: (ids: string[]) => void;
60
+ /** Heading above the list. */
61
+ title?: string;
62
+ /** Names the list for assistive tech when it has no `title`. */
63
+ label?: string;
64
+ /** Kinds the user can add, as buttons beside the title. Shown only with `onAdd`. */
65
+ addKinds?: string[];
66
+ onAdd?: (kind: string) => void;
67
+ /** Shown in place of the list when `items` is empty. */
68
+ empty?: ReactNode;
69
+ /** Appended to the root's class list — the module's own names are hashed. */
70
+ className?: string;
71
+ }
72
+ /** Returns `items` with `move` applied, for a consumer that holds the tree as state. */
73
+ declare function moveLayers(items: LayerListItem[], move: LayerMove): LayerListItem[];
74
+ /**
75
+ * An ordered, drag-reorderable list of layers — a document's objects, a lab's
76
+ * canvases, a fill's effects. Each layer is a one-line row or, with a body, a
77
+ * card whose controls fold away; either can nest child layers. Selection,
78
+ * visibility, removal and an add palette are each on when their handler is given.
79
+ */
80
+ declare function LayerList(props: LayerListProps): react_jsx_runtime.JSX.Element;
81
+
82
+ export { LayerList as L, moveLayers as m };
83
+ export type { LayerListItem as a, LayerListProps as b, LayerMove as c };
@@ -1,6 +1,21 @@
1
+ import { Stance } from '@weasel-js/theme';
1
2
  import { ReactNode } from 'react';
2
3
  import { ToolPrefLeaf } from '@weasel-js/core';
3
4
 
5
+ /**
6
+ * What a surface holds, and which of its peers it is. Every surface that takes
7
+ * these takes both, and the theme decides how each looks.
8
+ */
9
+ interface StanceProps {
10
+ /** The class of content the surface holds. Omitted, it draws as it always has. */
11
+ stance?: Stance;
12
+ /**
13
+ * Which of its peers this is: an index into the theme's tone list, wrapping,
14
+ * or a color given directly. Independent of `stance`, and allowed without one.
15
+ */
16
+ tone?: number | string;
17
+ }
18
+
4
19
  /** What a {@link PrefRenderer} is given for the leaf it is rendering. */
5
20
  interface PrefRenderContext {
6
21
  /** Dotted path of the leaf within the schema root. */
@@ -21,4 +36,4 @@ interface PrefRenderContext {
21
36
  */
22
37
  type PrefRenderer = (ctx: PrefRenderContext) => ReactNode;
23
38
 
24
- export type { PrefRenderer as P, PrefRenderContext as a };
39
+ export type { PrefRenderer as P, StanceProps as S, PrefRenderContext as a };
@@ -1,6 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ReactNode, ButtonHTMLAttributes, ReactElement } from 'react';
4
+ import { S as StanceProps } from './PrefsRow.d-DFpPIeIC.js';
4
5
  import { TextFieldProps, ValidationResult, SelectProps as SelectProps$1, ListBoxItemProps } from 'react-aria-components';
5
6
 
6
7
  declare const ICON_PATHS: {
@@ -42,6 +43,7 @@ declare const ICON_PATHS: {
42
43
  readonly warning: "<path d=\"M10 3.4 17.4 16.2H2.6z\"/><path d=\"M10 8.4v3.4\"/><path d=\"M10 14h0\"/>";
43
44
  readonly error: "<circle cx=\"10\" cy=\"10\" r=\"7\"/><path d=\"M7.6 7.6 12.4 12.4M12.4 7.6 7.6 12.4\"/>";
44
45
  readonly busy: "<path d=\"M10 3.2A6.8 6.8 0 1 1 3.2 10\"/>";
46
+ readonly check: "<path d=\"M4.35 10.56 8.03 14.24 15.66 5.77\"/>";
45
47
  readonly modeLight: "<circle cx=\"10\" cy=\"10\" r=\"3.5\"/><path d=\"M15.3 10L17.7 10M13.75 6.25L15.44 4.56M10 4.7L10 2.3M6.25 6.25L4.56 4.56M4.7 10L2.3 10M6.25 13.75L4.56 15.44M10 15.3L10 17.7M13.75 13.75L15.44 15.44\"/>";
46
48
  readonly modeDark: "<path d=\"M8.19 3.55A6.7 6.7 0 1 0 16.45 11.81A5.9 5.9 0 0 1 8.19 3.55Z\"/>";
47
49
  readonly modeAuto: "<path d=\"M10 2.6Q10 10 17.4 10Q10 10 10 17.4Q10 10 2.6 10Q10 10 10 2.6Z\"/>";
@@ -51,6 +53,7 @@ declare const ICON_PATHS: {
51
53
  readonly search: "<circle cx=\"9\" cy=\"9\" r=\"5.4\"/><path d=\"M12.82 12.82 16.9 16.9\"/>";
52
54
  readonly loupe: "<circle cx=\"9\" cy=\"9\" r=\"5.6\"/><path d=\"M12.96 12.96 17 17\"/><rect x=\"6\" y=\"6\" width=\"6\" height=\"6\" stroke-width=\"1\"/><path d=\"M9 6v6M6 9h6\" stroke-width=\"1\"/>";
53
55
  readonly layers: "<path d=\"M10 2.8 17.2 6.6 10 10.4 2.8 6.6z\"/><path d=\"M2.8 10 10 13.8 17.2 10\" stroke-width=\"1\"/><path d=\"M2.8 13.4 10 17.2 17.2 13.4\" stroke-width=\"1\"/>";
56
+ readonly page: "<path d=\"M4.6 2.8H11.6L15.4 6.6V17.2H4.6z\"/><path d=\"M11.6 2.8V6.6H15.4\"/>";
54
57
  readonly link: "<path d=\"M8.6 11.4a3.4 3.4 0 0 1 0-4.8l2.2-2.2a3.4 3.4 0 0 1 4.8 4.8l-1.1 1.1\"/><path d=\"M11.4 8.6a3.4 3.4 0 0 1 0 4.8l-2.2 2.2a3.4 3.4 0 0 1-4.8-4.8l1.1-1.1\"/>";
55
58
  readonly collapse: "<path d=\"M14.16 4.05 10 7.8 5.84 4.05M5.84 15.95 10 12.2 14.16 15.95\"/>";
56
59
  readonly expand: "<path d=\"M5.84 8.15 10 4.4 14.16 8.15M14.16 11.85 10 15.6 5.84 11.85\"/>";
@@ -238,13 +241,31 @@ declare function Icon({ name, className, size, label, filled, fillOpacity, }: Ic
238
241
  name: IconName;
239
242
  }): react_jsx_runtime.JSX.Element;
240
243
 
241
- /** Visual weight of a button. Defaults to `secondary`. */
242
- type ButtonVariant = 'primary' | 'secondary' | 'ghost';
244
+ /** The tooltip fields a segmented bar's item carries. */
245
+ interface SegmentTooltipFields {
246
+ /** Tooltip content. Overrides the name-plus-`shortcut` default. */
247
+ tooltip?: ReactNode;
248
+ /** Shortcut hint shown in the tooltip as `Name (⌘Z)`, the way `ToolButton`
249
+ * and `ActionBar` show theirs. The name is `ariaLabel`, else a string
250
+ * `label`. */
251
+ shortcut?: string;
252
+ }
253
+
254
+ /** Visual weight of a button. Defaults to `secondary`. `link` drops the
255
+ * control box entirely and reads as a hyperlink in running text -- for an
256
+ * in-page navigation that is still a button, not an `<a href>`. */
257
+ type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'link';
258
+ /** Text color of a `link` button — the same set as `Code`'s statuses. */
259
+ type ButtonStatus = 'neutral' | 'muted' | 'accent' | 'success' | 'warn' | 'danger';
243
260
  /** Button height and type scale. */
244
261
  type ButtonSize = 'sm' | 'md';
245
262
  type ButtonBase = {
246
263
  variant?: ButtonVariant;
247
264
  size?: ButtonSize;
265
+ /** Text color of the `link` variant, which is `accent` unless told
266
+ * otherwise. The boxed variants carry their weight in their fill and
267
+ * ignore it. A peer `tone`, or a stance's accent, paints over it. */
268
+ status?: ButtonStatus;
248
269
  /** A button that stays down: renders `aria-pressed` and holds the active
249
270
  * treatment while it is on. For a control that reports a state rather than
250
271
  * firing an action -- a panel toggle, a mode switch. Left off, the button
@@ -260,7 +281,7 @@ type ButtonBase = {
260
281
  style?: ButtonHTMLAttributes<HTMLButtonElement>['style'];
261
282
  children?: ReactNode;
262
283
  onClick?: ButtonHTMLAttributes<HTMLButtonElement>['onClick'];
263
- };
284
+ } & StanceProps & SegmentTooltipFields;
264
285
  type ButtonRegular = ButtonBase & {
265
286
  iconOnly?: false;
266
287
  ariaLabel?: string;
@@ -279,6 +300,10 @@ type ButtonProps = ButtonRegular | ButtonIconOnly;
279
300
  * button `aria-busy`, and leaves the label in place; it does not disable the
280
301
  * button, so pass `disabled` too if the click should be blocked.
281
302
  *
303
+ * `shortcut` adds a kit tooltip reading `Name (⌘S)`, named by `ariaLabel` or
304
+ * string children, the way `ToolButton` and `ButtonBar` items show theirs;
305
+ * `tooltip` replaces that text with any content.
306
+ *
282
307
  * `ref` forwards to the underlying `<button>`.
283
308
  */
284
309
  declare const Button: react.ForwardRefExoticComponent<ButtonProps & react.RefAttributes<HTMLButtonElement>>;
@@ -289,7 +314,7 @@ type ToggleBarItem<V extends string | number = string> = {
289
314
  label?: ReactNode;
290
315
  ariaLabel?: string;
291
316
  disabled?: boolean;
292
- };
317
+ } & SegmentTooltipFields;
293
318
  /** Segment height and type scale for a {@link ToggleBar}. */
294
319
  type ToggleBarSize = 'sm' | 'md';
295
320
  /** Visual treatment of a {@link ToggleBar}. */
@@ -362,9 +387,48 @@ interface OverlayPortalProps {
362
387
  /** React Aria props minus the portal key a weasel overlay owns. */
363
388
  type WithoutPortalTarget<T> = Omit<T, 'UNSTABLE_portalContainer'>;
364
389
 
390
+ /**
391
+ * Whether a {@link Field} stacks its label above the control or sits them
392
+ * side by side.
393
+ */
394
+ type FieldOrientation = 'stacked' | 'row';
395
+ /** Props for {@link Field}. */
396
+ type FieldProps = {
397
+ orientation?: FieldOrientation;
398
+ className?: string;
399
+ children?: ReactNode;
400
+ };
401
+ /**
402
+ * Layout primitive that arranges a label, control, hint, and error in the
403
+ * canonical weasel-ui form-row shape. The control itself owns its semantics
404
+ * (RAC `TextField`, `Checkbox`, etc.); this just paints the layout.
405
+ *
406
+ * Most users will instead reach for `Input` / `Checkbox` / `Switch`, which
407
+ * compose Field internally. Use Field directly when you need a custom
408
+ * control to sit in the same visual rhythm.
409
+ */
410
+ declare function Field({ orientation, className, children }: FieldProps): react_jsx_runtime.JSX.Element;
411
+ /**
412
+ * The class names {@link Field} paints its slots with, so a control composing
413
+ * its own layout can match the same form-row rhythm.
414
+ */
415
+ declare const fieldClasses: {
416
+ root: string;
417
+ row: string;
418
+ label: string;
419
+ hint: string;
420
+ error: string;
421
+ };
422
+
365
423
  /** Props for {@link Input}, on top of React Aria's `TextField` props. */
366
424
  type InputProps = Omit<TextFieldProps, 'children' | 'className'> & {
367
425
  label?: ReactNode;
426
+ /**
427
+ * `'stacked'` (the default) puts the label above the field; `'row'` sets it
428
+ * beside the field at its own width, with any description or error on a
429
+ * line below. Same vocabulary as {@link Field}'s `orientation`.
430
+ */
431
+ orientation?: FieldOrientation;
368
432
  description?: ReactNode;
369
433
  errorMessage?: ReactNode | ((v: ValidationResult) => ReactNode);
370
434
  placeholder?: string;
@@ -381,6 +445,12 @@ type InputProps = Omit<TextFieldProps, 'children' | 'className'> & {
381
445
  */
382
446
  declare const Input: react.ForwardRefExoticComponent<Omit<TextFieldProps, "className" | "children"> & {
383
447
  label?: ReactNode;
448
+ /**
449
+ * `'stacked'` (the default) puts the label above the field; `'row'` sets it
450
+ * beside the field at its own width, with any description or error on a
451
+ * line below. Same vocabulary as {@link Field}'s `orientation`.
452
+ */
453
+ orientation?: FieldOrientation;
384
454
  description?: ReactNode;
385
455
  errorMessage?: ReactNode | ((v: ValidationResult) => ReactNode);
386
456
  placeholder?: string;
@@ -402,6 +472,13 @@ type SelectOption = {
402
472
  */
403
473
  textValue?: string;
404
474
  };
475
+ /** A titled group of options in a {@link Select}'s `options` list. */
476
+ type SelectOptionGroup<T extends Key = string> = {
477
+ title: ReactNode;
478
+ options: ReadonlyArray<SelectOption & {
479
+ value: T;
480
+ }>;
481
+ };
405
482
  type Key = string | number;
406
483
  /** How a {@link Select} trigger signals that it opens a list. */
407
484
  type SelectIndicator = 'chevron' | 'underline' | 'none';
@@ -413,13 +490,22 @@ type SelectPopup = 'over' | 'below';
413
490
  */
414
491
  type SelectProps<T extends Key = string> = Omit<SelectProps$1<object>, 'children' | 'className' | 'selectedKey' | 'defaultSelectedKey' | 'onSelectionChange'> & {
415
492
  label?: ReactNode;
493
+ /**
494
+ * `'stacked'` (the default) puts the label above the trigger; `'row'` sets it
495
+ * beside the trigger at its own width, with any description or error on a
496
+ * line below. Same vocabulary as {@link Field}'s `orientation`.
497
+ */
498
+ orientation?: FieldOrientation;
416
499
  description?: ReactNode;
417
500
  errorMessage?: ReactNode | ((v: ValidationResult) => ReactNode);
418
501
  placeholder?: string;
419
- /** Either pass `options` for a quick render, or `children` for full control. */
420
- options?: ReadonlyArray<SelectOption & {
502
+ /**
503
+ * Either pass `options` for a quick render, or `children` for full control.
504
+ * An entry with `title` and `options` of its own is a titled section.
505
+ */
506
+ options?: ReadonlyArray<(SelectOption & {
421
507
  value: T;
422
- }>;
508
+ }) | SelectOptionGroup<T>>;
423
509
  children?: ReactNode;
424
510
  selectedKey?: T | null;
425
511
  defaultSelectedKey?: T;
@@ -453,14 +539,19 @@ type SelectProps<T extends Key = string> = Omit<SelectProps$1<object>, 'children
453
539
  */
454
540
  triggerId?: string;
455
541
  className?: string;
456
- } & OverlayPortalProps;
542
+ } & SegmentTooltipFields & OverlayPortalProps;
457
543
  /**
458
544
  * Form select wrapping React Aria's Select. Pass either `options` for a
459
545
  * quick declarative render or `children` of `<SelectItem>` for control over
460
- * each row.
546
+ * each row; `<SelectSection>` (or an `options` entry with a `title`) groups
547
+ * rows under a heading.
461
548
  *
462
549
  * The selection key type is parameterized so consumers with a string-literal
463
550
  * union for `value` (e.g. `'r' | 'g' | 'b'`) get a typed `onSelectionChange`.
551
+ *
552
+ * `shortcut` puts a kit tooltip on the trigger reading `Name (⌘K)`, named by
553
+ * `aria-label` or a string `label`, as `Button` does; `tooltip` replaces that
554
+ * text.
464
555
  */
465
556
  declare function Select<T extends Key = string>(props: SelectProps<T>): react_jsx_runtime.JSX.Element;
466
557
  /** Props for {@link SelectItem}, on top of React Aria's `ListBoxItem` props. */
@@ -475,5 +566,5 @@ type SelectItemProps = Omit<ListBoxItemProps, 'className' | 'children'> & {
475
566
  */
476
567
  declare function SelectItem({ children, className, textValue, ...rest }: SelectItemProps): react_jsx_runtime.JSX.Element;
477
568
 
478
- export { Button as B, Select as S, ToggleBar as T, Icon as d, Input as e, ICON_PATHS as l, SelectItem as n };
479
- export type { IconProps as I, OverlayPortalProps as O, WithoutPortalTarget as W, ButtonProps as a, ButtonSize as b, ButtonVariant as c, InputProps as f, SelectProps as g, ToggleBarItem as h, ToggleBarProps as i, ToggleBarSize as j, ToggleBarVariant as k, IconName as m, SelectItemProps as o, SelectOption as p };
569
+ export { Button as B, Select as S, ToggleBar as T, Icon as d, Input as e, Field as m, ICON_PATHS as o, SelectItem as q, fieldClasses as t };
570
+ export type { FieldOrientation as F, IconProps as I, OverlayPortalProps as O, WithoutPortalTarget as W, ButtonProps as a, ButtonSize as b, ButtonVariant as c, InputProps as f, SelectProps as g, ToggleBarItem as h, ToggleBarProps as i, ToggleBarSize as j, ToggleBarVariant as k, SegmentTooltipFields as l, FieldProps as n, IconName as p, SelectItemProps as r, SelectOption as s };
@@ -1,5 +1,7 @@
1
1
  import { ComponentType, ReactNode, RefObject } from 'react';
2
- import { R as ResolvedConfig, C as ConfigPath, V as ValueAtPath, a as ConfigField, b as ConfigSchema } from './types-Db-3NNk1.js';
2
+ import { S as StanceProps } from './PrefsRow.d-DFpPIeIC.js';
3
+ import { R as ResolvedConfig, a as ConfigPath, V as ValueAtPath, b as ConfigField, c as ConfigSchema } from './types-B7y8hX1D.js';
4
+ import { ColorModePreference } from '@weasel-js/theme';
3
5
  import { J as JobHandle, a as JobCapability } from './types-DJ79Tg5J.js';
4
6
  import { Scene } from '@weasel-js/core';
5
7
 
@@ -129,7 +131,7 @@ interface SavedSnapshot {
129
131
  savedAt: number;
130
132
  }
131
133
  /** `auto` follows the OS; the other two are an explicit choice. */
132
- type LabMode = 'auto' | 'light' | 'dark';
134
+ type LabMode = ColorModePreference;
133
135
  /** How much room the lab's own chrome takes — the theme's density axis, named
134
136
  * the way the theme names it. A lab's instruments are unaffected: this sizes
135
137
  * the shell around them. */
@@ -282,8 +284,10 @@ interface ToolItem<TCtx = TrialChromeContext> {
282
284
  * never writes the tool slot, and never reads as the current tool. */
283
285
  onActivate?: (ctx: TCtx) => void;
284
286
  }
285
- /** A titled block in the sidebar. */
286
- interface SidebarSection {
287
+ /** A titled block in the sidebar. `stance` and `tone` work as on a
288
+ * `<PropertyPanel>`: what kind of content the section holds, and which of its
289
+ * peers it is. */
290
+ interface SidebarSection extends StanceProps {
287
291
  title: string;
288
292
  /** Starts collapsed, until the trial remembers a fold of its own. */
289
293
  defaultCollapsed?: boolean;
@@ -599,7 +603,7 @@ interface CanvasCapability<TS = unknown, TC = unknown> {
599
603
  /** Declares that an instrument's DOM is content of a fixed size, which the
600
604
  * trial pans and zooms the way it does a canvas's layers. Not combined with
601
605
  * `canvas`: there the DOM is an unzoomed overlay, and `canvas` wins. */
602
- interface StageCapability {
606
+ interface StageCapability<TS = unknown, TC = unknown> {
603
607
  /** The content's own size in CSS pixels, at zoom 1. */
604
608
  size: ViewportSize;
605
609
  /** Where the view starts. Omitted, the content opens centered and shrunk to
@@ -607,6 +611,9 @@ interface StageCapability {
607
611
  initialView?: ViewTransform | ((viewport: ViewportSize) => ViewTransform);
608
612
  minZoom?: number;
609
613
  maxZoom?: number;
614
+ /** Drawn over the content in viewport pixels, outside the camera — a legend,
615
+ * a floating panel, anything that must not zoom with the picture. */
616
+ overlay?: (ctx: RenderContext<TS, TC>) => ReactNode;
610
617
  }
611
618
  /** Declares which of an instrument's layers the trial should offer
612
619
  * show/hide controls for. */
@@ -706,7 +713,7 @@ interface Instrument<TS = unknown, TC = unknown, TItem = unknown> {
706
713
  serialize?: (state: TS) => unknown;
707
714
  deserialize?: (data: unknown, config: TC) => TS;
708
715
  canvas?: CanvasCapability<TS, TC>;
709
- stage?: StageCapability;
716
+ stage?: StageCapability<TS, TC>;
710
717
  layers?: LayerCapability;
711
718
  dragDrop?: DragDropCapability<TS, TC>;
712
719
  undo?: UndoCapability;
@@ -795,8 +802,9 @@ interface FracRect {
795
802
  h: number;
796
803
  }
797
804
  /** What shape a mark is. The kinds map onto weasel's own tools; an arrow is a
798
- * line carrying an end marker, not a separate geometry. */
799
- type AnnotationKind = 'stroke' | 'line' | 'arrow' | 'rect' | 'ellipse' | 'text';
805
+ * line carrying an end marker, not a separate geometry. A point holds one
806
+ * entry in `points` and zero-size bounds at that position. */
807
+ type AnnotationKind = 'stroke' | 'line' | 'arrow' | 'rect' | 'ellipse' | 'text' | 'point';
800
808
  /** One selectable status in an instrument's meaning tier. */
801
809
  interface AnnotationStatus {
802
810
  id: string;
@@ -1028,5 +1036,5 @@ declare function fracContains(box: FracRect, pt: FracPoint, tol?: number): boole
1028
1036
  * answers false for two rects that merely overlap. */
1029
1037
  declare function fracEncloses(outer: FracRect, inner: FracRect): boolean;
1030
1038
 
1031
- export { DEFAULT_FRAME as D, LOUPE_DEFAULTS as a9, fracContains as aA, fracEncloses as aB, fracToWorld as aC, isAuto as aD, resolveLoupe as aE, roundFrac as aF, worldToFrac as aG, annotationsFromJSON as aw, auto as ax, createAnnotationScene as ay, createAnnotationStore as az, applyCamera as b, dockPanel as j, panelKey as p, resolveFrame as r, undockPanel as u };
1032
- export type { AnnotationTargetInfo as $, AnnotationData as A, AnnotationTarget as B, CreateLabStoreOptions as C, AnnotationsApi as E, AnnotationsCapability as F, TrialInfo as G, TrialTool as H, InstrumentSerializers as I, AnnotationKind as J, Instrument as K, LabDocument as L, Migration as M, LabDensity as N, TrialContribution as O, PaletteItem as P, Annotation as Q, AnnotationInit as R, SerializedTrial as S, TrialRecord as T, UndoStack as U, ViewportSize as V, WorldFrame as W, AnnotationPatch as X, AnnotationQuery as Y, AnnotationStatus as Z, AnnotationStoreOptions as _, WorldSpec as a, Auto as a0, CanvasCapability as a1, CanvasLayer as a2, CaptureDeps as a3, ContributionBase as a4, FracPoint as a5, FracRect as a6, HitResult as a7, IconComponent as a8, LoupePoint as aH, LoupeMode as aI, LayerCapability as aa, LayerDescriptor as ab, LoupeCapability as ac, LoupeDeclaration as ad, LoupeRenderArgs as ae, RegionContribution as af, RenderContext as ag, ResolvedLoupe as ah, SerializedAnnotations as ai, SidebarSection as aj, SidebarSlotContext as ak, StageCapability as al, StatusReadout as am, SystemEvent as an, ToolCapability as ao, ToolItem as ap, ToolSlotContext as aq, ToolbarItem as ar, TrialChromeContext as as, TrialRegion as at, UndoCapability as au, ViewportControl as av, StorageAdapter as c, LabStoreState as d, SavedSnapshot as e, StorageChange as f, TrialStateHandle as g, UndockedPanel as h, UndockedPanels as i, ViewTransform as k, Point as l, DragFeedback as m, DragDropCapability as n, LabMode as o, InstrumentList as q, InstrumentHooks as s, WorldRect as t, AnnotationMeaning as v, MarkScene as w, CaptureSource as x, CaptureOptions as y, CaptureResult as z };
1039
+ export { DEFAULT_FRAME as D, fracContains as aA, fracEncloses as aB, fracToWorld as aC, isAuto as aD, resolveLoupe as aE, roundFrac as aF, worldToFrac as aG, LOUPE_DEFAULTS as ab, annotationsFromJSON as aw, auto as ax, createAnnotationScene as ay, createAnnotationStore as az, applyCamera as c, dockPanel as l, panelKey as p, resolveFrame as r, undockPanel as u };
1040
+ export type { AnnotationStatus as $, AnnotationData as A, CaptureOptions as B, CreateLabStoreOptions as C, CaptureResult as E, AnnotationTarget as F, AnnotationsApi as G, AnnotationsCapability as H, InstrumentSerializers as I, TrialInfo as J, TrialTool as K, LayerCapability as L, Migration as M, AnnotationKind as N, Instrument as O, PaletteItem as P, LabDensity as Q, TrialContribution as R, SerializedTrial as S, TrialRecord as T, UndoStack as U, ViewportSize as V, WorldFrame as W, Annotation as X, AnnotationInit as Y, AnnotationPatch as Z, AnnotationQuery as _, LayerDescriptor as a, AnnotationStoreOptions as a0, AnnotationTargetInfo as a1, Auto as a2, CanvasCapability as a3, CanvasLayer as a4, CaptureDeps as a5, ContributionBase as a6, FracPoint as a7, FracRect as a8, HitResult as a9, LoupePoint as aH, LoupeMode as aI, IconComponent as aa, LoupeCapability as ac, LoupeDeclaration as ad, LoupeRenderArgs as ae, RegionContribution as af, RenderContext as ag, ResolvedLoupe as ah, SerializedAnnotations as ai, SidebarSection as aj, SidebarSlotContext as ak, StageCapability as al, StatusReadout as am, SystemEvent as an, ToolCapability as ao, ToolItem as ap, ToolSlotContext as aq, ToolbarItem as ar, TrialChromeContext as as, TrialRegion as at, UndoCapability as au, ViewportControl as av, WorldSpec as b, StorageAdapter as d, LabDocument as e, LabStoreState as f, SavedSnapshot as g, StorageChange as h, TrialStateHandle as i, UndockedPanel as j, UndockedPanels as k, ViewTransform as m, Point as n, DragFeedback as o, DragDropCapability as q, LabMode as s, InstrumentList as t, InstrumentHooks as v, WorldRect as w, AnnotationMeaning as x, MarkScene as y, CaptureSource as z };
@@ -0,0 +1,23 @@
1
+ import { ReactNode } from 'react';
2
+ import { C as ControlRenderer } from './types-B7y8hX1D.js';
3
+
4
+ /** Options for {@link inDialog}. */
5
+ interface InDialogOptions {
6
+ /** What the row's button shows of the value. Defaults to a list joined with
7
+ * commas, or the value itself. */
8
+ summary?: (value: unknown) => ReactNode;
9
+ /** The dialog's heading. Defaults to the row's label. */
10
+ title?: ReactNode;
11
+ }
12
+ /** The one-line summary a dialog row shows when it is given none. */
13
+ declare function summarizeValue(value: unknown): string;
14
+ /**
15
+ * Moves a control into a modal: the row becomes a button showing a summary of
16
+ * the value, and the button opens a dialog whose body is `body`, handed the
17
+ * same context a row renderer gets. For a leaf whose editor will never fit a
18
+ * sidebar row.
19
+ */
20
+ declare function inDialog(body: ControlRenderer, opts?: InDialogOptions): ControlRenderer;
21
+
22
+ export { inDialog as i, summarizeValue as s };
23
+ export type { InDialogOptions as I };
@@ -1,5 +1,5 @@
1
- import { d as ControlRenderer, c as ConfigRule } from './types-Db-3NNk1.js';
2
- import { K as Instrument, as as TrialChromeContext, O as TrialContribution, T as TrialRecord, q as InstrumentList, e as SavedSnapshot, o as LabMode, aq as ToolSlotContext, a4 as ContributionBase, ar as ToolbarItem, ap as ToolItem, aj as SidebarSection, am as StatusReadout, H as TrialTool, af as RegionContribution, ak as SidebarSlotContext } from './frac-B4hFJMiN.js';
1
+ import { C as ControlRenderer, d as ConfigRule } from './types-B7y8hX1D.js';
2
+ import { O as Instrument, as as TrialChromeContext, R as TrialContribution, T as TrialRecord, t as InstrumentList, g as SavedSnapshot, s as LabMode, aq as ToolSlotContext, a6 as ContributionBase, ar as ToolbarItem, ap as ToolItem, aj as SidebarSection, am as StatusReadout, K as TrialTool, af as RegionContribution, ak as SidebarSlotContext } from './frac-DTRq7Xpb.js';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import * as react from 'react';
5
5
  import { ReactNode } from 'react';