@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
@@ -0,0 +1,453 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import { ReactNode } from 'react';
4
+ import { S as StanceProps } from './PrefsRow.d-DFpPIeIC.js';
5
+ import { ToolPrefNumberFormat, FillStyle, PaintKind } from '@weasel-js/core';
6
+ import { U as UnitTable } from './number.d-OeQYQoUO.js';
7
+ import { R as ResolvedConfig, b as ConfigField, C as ControlRenderer } from './types-B7y8hX1D.js';
8
+ import './inDialog-D5ZKGjiD.js';
9
+
10
+ /**
11
+ * How much room a container gives its rows — gaps, padding, and field height,
12
+ * moved together. Set on any container in the family; it reaches every
13
+ * descendant, so an inner group can differ from the panel around it.
14
+ */
15
+ type PropertyDensity = 'tight' | 'normal' | 'roomy';
16
+ /** Where an inline row's label and control sit on the row's cross axis. Set on
17
+ * a container to line a whole column of them up. */
18
+ type PropertyAlign = 'start' | 'center' | 'end' | 'baseline';
19
+ /** Metric props every container in the family takes. Both reach descendants,
20
+ * so the nearest container that states one wins. */
21
+ interface PropertyMetricProps {
22
+ /** Room the rows get. Unset inherits from an outer container, else `normal`. */
23
+ density?: PropertyDensity;
24
+ /**
25
+ * Cross-axis alignment of an inline row's label and control. `baseline` sits
26
+ * the control on the label's first-line baseline, which is what lines a
27
+ * column of swatches up against labels of different heights. Unset keeps each
28
+ * variant's own alignment — a color row centers its label and swatch and
29
+ * sinks the pair to the row's bottom edge, which keeps a paired alpha track
30
+ * level with its neighbour.
31
+ */
32
+ align?: PropertyAlign;
33
+ }
34
+ /** Props for `<PropertyPanel>`. */
35
+ interface PropertyPanelProps extends PropertyMetricProps, StanceProps {
36
+ title?: ReactNode;
37
+ /** Controls on the trailing edge of the title row — a switch, a clear
38
+ * button. They sit outside the heading, so they neither take its type nor
39
+ * join its accessible name. */
40
+ actions?: ReactNode;
41
+ /**
42
+ * Lets text inside the panel be selected and copied. A panel is chrome by
43
+ * default, so a drag across it moves a slider rather than painting a
44
+ * selection; a `debug` panel defaults to selectable, since what it shows —
45
+ * ids, keys, traces — is there to be copied.
46
+ */
47
+ selectable?: boolean;
48
+ children: ReactNode;
49
+ className?: string;
50
+ }
51
+ /**
52
+ * A titled panel holding property rows — the sidebar container the rest of
53
+ * this module's components fill.
54
+ *
55
+ * `stance` says what kind of content it holds and `tone` which of its peers it
56
+ * is; the theme's `--wzl-panel-*` and `--wzl-stance-*` slots decide how each looks. A stanced
57
+ * panel's title takes the row-label recipe rather than the display title.
58
+ */
59
+ declare function PropertyPanel({ title, actions, selectable, children, className, density, align, stance, tone, }: PropertyPanelProps): react_jsx_runtime.JSX.Element;
60
+ /** How a property list packs its rows into two columns. */
61
+ type PropertyListPack = 'auto-color' | 'pairs' | 'one-up';
62
+ /** Props for `<PropertyList>`. */
63
+ interface PropertyListProps extends PropertyMetricProps {
64
+ children: ReactNode;
65
+ className?: string;
66
+ /**
67
+ * How rows pack into the 2-column grid.
68
+ * - `'auto-color'` (default): only color rows pair side-by-side; everything
69
+ * else spans the full width. Right for sparse top-level panels.
70
+ * - `'pairs'`: every row auto-places into the 2-column grid two-per-row.
71
+ * Headers and subpanels still span the full width; wrap any other
72
+ * full-width child in `<PropertySpan>`. Right for dense effect bodies.
73
+ * - `'one-up'`: every row spans the full width, color rows included. Right
74
+ * for a palette — a column of swatches read as a group.
75
+ */
76
+ pack?: PropertyListPack;
77
+ }
78
+ /**
79
+ * Grid container for PropertyRows. Use standalone for chrome-less layouts, or
80
+ * nest inside <PropertyPanel/> for the standard glass card.
81
+ */
82
+ declare const PropertyList: react.ForwardRefExoticComponent<PropertyListProps & react.RefAttributes<HTMLDivElement>>;
83
+ /** Props for `<PropertySpan>`. */
84
+ interface PropertySpanProps {
85
+ children: ReactNode;
86
+ className?: string;
87
+ }
88
+ /**
89
+ * Wrapper that makes an arbitrary child span both columns of a
90
+ * `<PropertyList pack="pairs">`, a `<PropertyGroup pack="pairs">` body, or a
91
+ * `<Subpanel>`. `<PropertyRow span>` covers the row case; this covers
92
+ * everything else a consumer puts in the grid.
93
+ */
94
+ declare function PropertySpan({ children, className }: PropertySpanProps): react_jsx_runtime.JSX.Element;
95
+ /** Which control shape a row holds, which decides its intrinsic layout. */
96
+ type PropertyRowVariant = 'default' | 'color' | 'checkbox';
97
+ /** Whether a row's label sits above its control or beside it. */
98
+ type PropertyRowLayout = 'block' | 'inline';
99
+ /** Props for `<PropertyRow>`. */
100
+ interface PropertyRowProps extends PropertyMetricProps {
101
+ label: ReactNode;
102
+ /** Right-aligned readout shown next to the label (e.g. current value). */
103
+ readout?: ReactNode;
104
+ /**
105
+ * Help text for the row, shown in a tooltip off an ⓘ affordance beside the
106
+ * label. Empty or absent renders no affordance.
107
+ */
108
+ description?: string;
109
+ variant?: PropertyRowVariant;
110
+ /**
111
+ * Label position relative to the control. Unset takes the variant's own
112
+ * orientation: `block` — label above control — for the default variant, and
113
+ * `inline` for the color and checkbox variants, which read as a row.
114
+ */
115
+ layout?: PropertyRowLayout;
116
+ /**
117
+ * Take the full width of the enclosing grid. Only has an effect inside a
118
+ * `<PropertyList pack="pairs">` or a `<Subpanel>`; elsewhere rows are already
119
+ * full width.
120
+ */
121
+ span?: boolean;
122
+ children: ReactNode;
123
+ htmlFor?: string;
124
+ /** The control is a group of several (a segmented toggle), not one element.
125
+ * The row then renders as a `<div>`: a `<label>` hands a click on its text to
126
+ * its first control, which in a group means selecting the first option. Each
127
+ * member has to carry its own name. */
128
+ group?: boolean;
129
+ className?: string;
130
+ /** The row is auto: its value is not pinned and the owner computes it. The
131
+ * control is hidden — its box kept, so the row does not resize — and the
132
+ * row reads out the word instead. */
133
+ auto?: boolean;
134
+ /** Given, the row's label toggles `auto` when it is clicked. Omitted, the row
135
+ * can show an auto state but not change it.
136
+ *
137
+ * The label then names that toggle, so a control passed as `children` has to
138
+ * carry its own accessible name — every `<PropertyField>` does. */
139
+ onAutoChange?: (next: boolean) => void;
140
+ /**
141
+ * What draws the row's control. `bare` (the default) is a native input the
142
+ * row's stylesheet dresses; `framed` is a kit field bringing its own frame,
143
+ * which the row then leaves alone — see {@link PropertyFieldChrome}.
144
+ */
145
+ chrome?: PropertyFieldChrome;
146
+ }
147
+ /** The label-plus-control frame `<PropertyField>` draws its rows in. Use it
148
+ * directly for a control no field kind covers. */
149
+ declare function PropertyRow({ label, readout, description, variant, layout, span, children, htmlFor, group, className, density, align, auto, onAutoChange, chrome, }: PropertyRowProps): react_jsx_runtime.JSX.Element;
150
+
151
+ /**
152
+ * Which controls a field is drawn with.
153
+ *
154
+ * - `'bare'` (the default): native inputs the property row's own stylesheet
155
+ * draws — the compact controls of an inspector sidebar or a lab panel.
156
+ * - `'framed'`: the kit's field components, each bringing its own frame —
157
+ * for a surface whose row does not style its controls (`SelectionPanel`,
158
+ * `ToolOptionsBar`), or that needs what only they do, such as reading a
159
+ * unit typed into a number (`accepts`).
160
+ *
161
+ * The two differ only where the kit still has two widgets for one job; a
162
+ * slider, a switch, a font picker and a paint are the same in both.
163
+ */
164
+ type PropertyFieldChrome = 'bare' | 'framed';
165
+ /** One choice of an enum field. */
166
+ interface PropertyOption<T extends string> {
167
+ value: T;
168
+ /** The option's name. A string also names its segment for a screen reader. */
169
+ label: ReactNode;
170
+ /** What a segment shows in place of `label` when the full one would not fit
171
+ * — a glyph or a letter. `label` stays the accessible name. */
172
+ glyph?: ReactNode;
173
+ /** Shown but not choosable: a value the control reports and cannot author. */
174
+ disabled?: boolean;
175
+ }
176
+ /** Props every kind of field shares. */
177
+ interface FieldBase {
178
+ /** The control's accessible name. A `<PropertyField>` takes it from a string
179
+ * `label` when this is omitted. */
180
+ name?: string;
181
+ /** The sources the field reads disagree — a multi-selection whose nodes
182
+ * differ. The control shows no value, in whatever form it has for that. */
183
+ mixed?: boolean;
184
+ /**
185
+ * The sources agree on holding nothing: whatever is in effect comes from a
186
+ * fallback further down. The control shows no chosen value rather than
187
+ * inventing one, since the next edit would write the invention back.
188
+ */
189
+ unset?: boolean;
190
+ chrome?: PropertyFieldChrome;
191
+ /** Class on the control element itself. */
192
+ className?: string;
193
+ /** Id for the control element, for a `<label htmlFor>` outside it. */
194
+ id?: string;
195
+ }
196
+ /** A boolean field. */
197
+ interface PropertyBooleanFieldProps extends FieldBase {
198
+ kind: 'boolean';
199
+ /** `checkbox` (default), `switch`, or `toggle` — a one-segment pressable bar. */
200
+ control?: 'checkbox' | 'switch' | 'toggle';
201
+ /** Absent reads as off. */
202
+ value: boolean | undefined;
203
+ onChange: (next: boolean) => void;
204
+ /** A `toggle`'s segment content. Defaults to the first letter of `name`. */
205
+ glyph?: ReactNode;
206
+ }
207
+ /** A number field. */
208
+ interface PropertyNumberFieldProps extends FieldBase {
209
+ kind: 'number';
210
+ /** `input` (default) for a typed number; `slider` for a track with an
211
+ * editable readout, where the range matters more than the exact value. */
212
+ control?: 'input' | 'slider';
213
+ /** Absent reads as an empty field. */
214
+ value: number | null | undefined;
215
+ /**
216
+ * The committed value. Given `onInput` as well, it fires once a drag ends,
217
+ * or on blur or Enter in a typed field; on its own it fires on every move
218
+ * and keystroke. A `framed` typed field commits only on blur or Enter,
219
+ * whichever callbacks it has.
220
+ */
221
+ onChange: (next: number) => void;
222
+ /** The live value, fired continuously. Pass it alongside `onChange` when the
223
+ * write is expensive — cheap state here, the costly work there. */
224
+ onInput?: (next: number) => void;
225
+ /** A slider's track runs 0..100 where these are omitted. */
226
+ min?: number;
227
+ max?: number;
228
+ step?: number;
229
+ /**
230
+ * Suffix after the value. A string becomes a dim word unit (`px`); pass JSX
231
+ * like `<sup>°</sup>` for a symbol. Display only — the value stays a number.
232
+ */
233
+ unit?: ReactNode;
234
+ /** Units a person may type into a `framed` field, each mapped to the factor
235
+ * that turns it into the unit shown: `{ mm: 0.1, cm: 1 }`. */
236
+ accepts?: Readonly<UnitTable>;
237
+ /** A slider readout's display. Defaults to the value at `step`'s precision. */
238
+ format?: (value: number) => ReactNode;
239
+ /** A named display for the value: `compact` reads `2.00M`. `format` wins. */
240
+ notation?: ToolPrefNumberFormat;
241
+ placeholder?: string;
242
+ /** A `framed` unitless field's stepper buttons. Default `true`. */
243
+ steppers?: boolean;
244
+ }
245
+ /** A text field. */
246
+ interface PropertyStringFieldProps extends FieldBase {
247
+ kind: 'string';
248
+ control?: 'input' | 'textarea';
249
+ /** Absent reads as an empty field. */
250
+ value: string | null | undefined;
251
+ /**
252
+ * The committed text. On its own it fires on every keystroke. Given
253
+ * `onInput` as well, the field drafts: `onInput` gets each keystroke and
254
+ * this fires once, on blur or Enter, and only if the text changed.
255
+ */
256
+ onChange: (next: string) => void;
257
+ onInput?: (next: string) => void;
258
+ placeholder?: string;
259
+ maxLength?: number;
260
+ }
261
+ /** A choice among fixed options. */
262
+ interface PropertyEnumFieldProps<T extends string = string> extends FieldBase {
263
+ kind: 'enum';
264
+ /**
265
+ * `select` (default) is a dropdown. `radio` and `toggle` show every option
266
+ * at once — a radio group chooses, and a toggle is a row of pressable
267
+ * segments. Drawn `bare`, both are segments.
268
+ */
269
+ control?: 'select' | 'radio' | 'toggle';
270
+ /** Absent, or not one of `options`, chooses nothing. */
271
+ value: T | undefined;
272
+ options: ReadonlyArray<PropertyOption<T>>;
273
+ onChange: (next: T) => void;
274
+ /** Shown while no option is chosen. Defaults to "Choose option…". */
275
+ placeholder?: string;
276
+ }
277
+ /** A single color. */
278
+ interface PropertyColorFieldProps extends FieldBase {
279
+ kind: 'color';
280
+ /** `#rrggbb`, or `#rrggbbaa` when `alpha` is `true`. */
281
+ value: string | undefined;
282
+ /**
283
+ * The committed color. `bare`: given `onInput` as well, it fires once the
284
+ * picker closes; on its own it fires on every move. `framed`: it fires once
285
+ * per gesture, whichever callbacks it has.
286
+ */
287
+ onChange: (next: string) => void;
288
+ onInput?: (next: string) => void;
289
+ /**
290
+ * Offer an opacity track. `true` keeps the alpha in `value` as `#rrggbbaa`;
291
+ * a number is the 0..1 alpha held apart from it, reported through
292
+ * `onAlphaChange` / `onAlphaInput`.
293
+ */
294
+ alpha?: boolean | number;
295
+ onAlphaChange?: (next: number) => void;
296
+ onAlphaInput?: (next: number) => void;
297
+ /** Draw the opacity track inert, for a consumer that drops alpha. */
298
+ alphaDisabled?: boolean;
299
+ }
300
+ /** A whole paint: a solid, a gradient, a pattern, or none. */
301
+ interface PropertyPaintFieldProps extends FieldBase {
302
+ kind: 'paint';
303
+ /** `swatch` (default) opens the editor in a popover, for a narrow slot;
304
+ * `inline` draws the kind bar and its editor in place. */
305
+ control?: 'swatch' | 'inline';
306
+ /** `null` is an explicit "no paint"; `undefined` is nothing to show. */
307
+ value: FillStyle | null | undefined;
308
+ onChange: (next: FillStyle | null) => void;
309
+ onInput?: (next: FillStyle | null) => void;
310
+ /** Offer "None". Default `true`. */
311
+ allowNone?: boolean;
312
+ /** Restrict the kinds offered. Default: every registered kind. */
313
+ kinds?: readonly PaintKind[];
314
+ }
315
+ /** A font family, picked from the live font registry. */
316
+ interface PropertyFontFamilyFieldProps extends FieldBase {
317
+ kind: 'font-family';
318
+ value: string | undefined;
319
+ onChange: (next: string) => void;
320
+ /** The weight and slant the family is drawn at, so the picker names the
321
+ * variant that will actually paint. */
322
+ weight?: number;
323
+ fontStyle?: 'normal' | 'italic';
324
+ }
325
+ /**
326
+ * One field for {@link PropertyControl} and {@link PropertyField}, keyed by
327
+ * `kind`.
328
+ */
329
+ type PropertyControlProps<T extends string = string> = PropertyBooleanFieldProps | PropertyNumberFieldProps | PropertyStringFieldProps | PropertyEnumFieldProps<T> | PropertyColorFieldProps | PropertyPaintFieldProps | PropertyFontFamilyFieldProps;
330
+ /** Row props {@link PropertyField} adds to the field it draws. */
331
+ interface PropertyFieldRowProps extends PropertyMetricProps {
332
+ label: ReactNode;
333
+ /**
334
+ * Text beside the label. On a slider it replaces the editable readout —
335
+ * use it when the row is not showing a number a caller could type back,
336
+ * such as an auto row's `auto`.
337
+ */
338
+ readout?: ReactNode;
339
+ description?: string;
340
+ layout?: PropertyRowLayout;
341
+ /** Take the full width of the enclosing grid — see `<PropertyRow span>`. */
342
+ span?: boolean;
343
+ /** The row is auto — see `<PropertyRow auto>`. */
344
+ auto?: boolean;
345
+ /** Toggles `auto` from the row's label — see `<PropertyRow onAutoChange>`. */
346
+ onAutoChange?: (next: boolean) => void;
347
+ /** Class on the row. `controlClassName` reaches the control. */
348
+ rowClassName?: string;
349
+ controlClassName?: string;
350
+ }
351
+ /** Props for {@link PropertyField}: one field, and the row around it. */
352
+ type PropertyFieldProps<T extends string = string> = PropertyFieldRowProps & DistributiveOmit<PropertyControlProps<T>, 'className' | 'id'>;
353
+ type DistributiveOmit<U, K extends PropertyKey> = U extends unknown ? Omit<U, K> : never;
354
+ /**
355
+ * A labeled settings row for one field, its control chosen by `kind` (and
356
+ * `control`): `<PropertyField kind="number" control="slider" label="Blur" …>`.
357
+ *
358
+ * The row is a {@link PropertyRow}; the control is the one
359
+ * {@link PropertyControl} draws for the same props. Every schema-driven
360
+ * settings surface in the kit — `ControlPanel`, `PrefsForm`, `SelectionPanel`
361
+ * — draws its built-in kinds through that one mapping, so a kind's control
362
+ * is decided once.
363
+ *
364
+ * Callbacks follow the field's shape: a discrete control (a box, a choice)
365
+ * reports each pick; a continuous or typed one reports every change through
366
+ * `onChange` alone, or splits them into live `onInput` and settled
367
+ * `onChange` when given both. `mixed` and `unset` show a field whose sources
368
+ * disagree, or agree on holding nothing.
369
+ */
370
+ declare function PropertyField<T extends string = string>(props: PropertyFieldProps<T>): react_jsx_runtime.JSX.Element;
371
+ /**
372
+ * The control for one field, with no row around it — for a cell that shares
373
+ * a row with others, or a surface that draws its own row. Keyed by `kind`
374
+ * (and `control`), the same mapping {@link PropertyField} draws.
375
+ *
376
+ * A slider brings its readout beside the track. Several elements come back
377
+ * as siblings rather than wrapped, so the surrounding row lays them out.
378
+ */
379
+ declare function PropertyControl<T extends string = string>(typed: PropertyControlProps<T>): react_jsx_runtime.JSX.Element;
380
+
381
+ /** How a panel packs its rows into the two-column property grid.
382
+ * - `'auto'`: narrow controls (numbers, checkboxes, dropdowns, colors) sit
383
+ * two per row; the ones that need the width — text, sliders, segmented
384
+ * toggles — span it.
385
+ * - `'pairs'`: every row pairs. A control that needs the full width says so
386
+ * itself, with `<PropertyRow span>`.
387
+ * - `'one-up'`: one control per row, colors excepted.
388
+ */
389
+ type ControlPack = 'auto' | 'pairs' | 'one-up';
390
+ interface ControlPanelProps<TC extends Record<string, unknown>> extends StanceProps {
391
+ /** The instrument's resolved config schema. */
392
+ schema?: ResolvedConfig;
393
+ /** @deprecated Pass `schema`. A field list is adapted into one internally. */
394
+ fields?: ConfigField[];
395
+ config: TC;
396
+ /** Writes one value. The path is dotted for a leaf inside an `f.group`, and
397
+ * the bare key for one at the root. */
398
+ setConfig: (path: string, value: unknown) => void;
399
+ /**
400
+ * Control overrides and app-defined kinds, PrefsForm-style. Keys are config
401
+ * paths (checked first) or leaf kinds. A renderer returning `null` collapses
402
+ * the row; entries override the built-in rows on collision.
403
+ */
404
+ renderers?: Record<string, ControlRenderer>;
405
+ /** How rows pack into the two-column grid. Defaults to `'pairs'`. */
406
+ pack?: ControlPack;
407
+ /**
408
+ * Where a row's label sits relative to its control. Unset takes each row's
409
+ * own orientation — `block` (label above, which is what leaves a paired row
410
+ * room for its value) for most, `inline` for colors and checkboxes.
411
+ */
412
+ layout?: PropertyRowLayout;
413
+ /** Room the rows get. Passed straight to the property list. */
414
+ density?: PropertyDensity;
415
+ /** Cross-axis alignment of an inline row's label and control. */
416
+ align?: PropertyAlign;
417
+ /**
418
+ * Fold each section away behind a twisty, starting `'open'` or `'closed'`.
419
+ * Unset draws the heading alone, as the panel always has — unless a section
420
+ * in the schema declares how it opens, which makes that section foldable on
421
+ * its own and outranks this for that one section.
422
+ */
423
+ collapse?: 'open' | 'closed';
424
+ /**
425
+ * Which sections are folded, keyed as `onCollapse` reports them. Given, the
426
+ * panel keeps no state of its own: every toggle arrives at `onCollapse`
427
+ * instead, which is where a lab that remembers a trial's sections writes
428
+ * them.
429
+ */
430
+ collapsed?: Readonly<Record<string, boolean>>;
431
+ /** A fold moved. The key is a section's label — prefixed by its group's
432
+ * dotted path when the section sits inside one — or a group's own path. */
433
+ onCollapse?: (key: string, collapsed: boolean) => void;
434
+ /** Dotted paths currently unpinned. A row in this set hides its control and
435
+ * reads out `auto`, and clicking its label writes the sentinel back through
436
+ * `setConfig`. Omitted, the panel keeps the set itself: the labels still
437
+ * toggle and the sentinel never reaches `setConfig`, which would otherwise
438
+ * store it as the row's value. */
439
+ auto?: ReadonlySet<string>;
440
+ /** Draw leaves marked `hidden`. */
441
+ showHidden?: boolean;
442
+ /** Heads the panel. With `title`, `stance` or `tone` given, the rows sit in a
443
+ * `<PropertyPanel>` carrying them; with none, they are the bare list. */
444
+ title?: ReactNode;
445
+ className?: string;
446
+ }
447
+ /** Render an instrument's config schema as a stack of controls, each writing
448
+ * back through `setConfig`. Built on the property rows, so a lab's controls
449
+ * are the same aligned, themed rows the rest of the kit uses. */
450
+ declare function ControlPanel<TC extends Record<string, unknown>>({ schema, fields, config, setConfig, renderers, pack, layout, density, align, collapse, collapsed, onCollapse, auto: given, showHidden, title, stance, tone, className, }: ControlPanelProps<TC>): react_jsx_runtime.JSX.Element;
451
+
452
+ export { ControlPanel as C, PropertyControl as b, PropertyField as d, PropertyList as f, PropertyPanel as i, PropertyRow as k, PropertySpan as o };
453
+ export type { PropertyMetricProps as P, PropertyListPack as a, PropertyControlProps as c, PropertyFieldProps as e, PropertyListProps as g, PropertyOption as h, PropertyPanelProps as j, PropertyRowLayout as l, PropertyRowProps as m, PropertyRowVariant as n, PropertySpanProps as p };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Display-formatter for numbers. Use this anywhere a number is shown to
3
+ * a user. The whole point: negative values get prefixed with the real
4
+ * MINUS SIGN (U+2212) instead of the ASCII HYPHEN-MINUS (U+002D) that
5
+ * `toLocaleString` and template literals produce by default.
6
+ *
7
+ * U+2212 is the same visual width as `+` and reads as a sign rather
8
+ * than a hyphen — columns of signed numbers align cleanly and the
9
+ * glyph doesn't get confused with a bullet or list dash.
10
+ */
11
+ declare const MINUS_SIGN = "\u2212";
12
+ /**
13
+ * Formats a number for display, substituting {@link MINUS_SIGN} for the ASCII
14
+ * hyphen `toLocaleString` emits. Non-finite values stringify as-is.
15
+ */
16
+ declare function formatNumber(value: number, options?: Intl.NumberFormatOptions): string;
17
+ /** One unit's conversion into the shown unit: a bare factor, or a scale that
18
+ * also moves zero. Structurally the kit's `UnitEntry`, kept local so this
19
+ * formatting helper stays free of the scene vocabulary. */
20
+ type UnitTableEntry = number | {
21
+ factor: number;
22
+ offset?: number;
23
+ };
24
+ /** Suffixes a person may type, each mapped to its conversion. */
25
+ type UnitTable = Record<string, UnitTableEntry>;
26
+
27
+ export { MINUS_SIGN as M, formatNumber as f };
28
+ export type { UnitTable as U };
@@ -1,5 +1,5 @@
1
1
  import { ToolPrefNumberFormat, ToolPrefNumberUnit, ToolPrefLeaf, ToolPrefGroup } from '@weasel-js/core';
2
- import { P as PrefRenderer } from './PrefsForm.d-DPy9I8It.js';
2
+ import { P as PrefRenderer } from './PrefsRow.d-DFpPIeIC.js';
3
3
 
4
4
  /**
5
5
  * Renders the control cell for one config leaf. Identical to weasel-ui's
@@ -250,4 +250,4 @@ interface ColorField extends ConfigFieldBase {
250
250
  * panel renders, and what `validateConfigSchema` checks. */
251
251
  type ConfigField = SliderField | CheckboxField | SelectField | NumberField | TextField | ColorField;
252
252
 
253
- export type { Annotations as A, BranchAnnotations as B, ConfigPath as C, EntryValue as E, InferConfig as I, LeafPatch as L, NodeOptions as N, ResolvedConfig as R, SectionSpec as S, TextField as T, ValueAtPath as V, ConfigField as a, ConfigSchema as b, ConfigRule as c, ControlRenderer as d, BranchOptions as e, CheckboxField as f, ColorField as g, ConfigBranch as h, ConfigEntry as i, ConfigFieldBase as j, ConfigFieldType as k, ConfigNode as l, ConfigOf as m, ConfigOption as n, ConfigRuleContext as o, ConfigShape as p, NodeValue as q, NumberField as r, SelectField as s, SelectOption as t, SliderField as u, SectionOption as v };
253
+ export type { Annotations as A, BranchAnnotations as B, ControlRenderer as C, EntryValue as E, InferConfig as I, LeafPatch as L, NodeOptions as N, ResolvedConfig as R, SectionSpec as S, TextField as T, ValueAtPath as V, ConfigPath as a, ConfigField as b, ConfigSchema as c, ConfigRule as d, BranchOptions as e, CheckboxField as f, ColorField as g, ConfigBranch as h, ConfigEntry as i, ConfigFieldBase as j, ConfigFieldType as k, ConfigNode as l, ConfigOf as m, ConfigOption as n, ConfigRuleContext as o, ConfigShape as p, NodeValue as q, NumberField as r, SelectField as s, SelectOption as t, SliderField as u, SectionOption as v };
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, WheelEvent, PointerEvent as PointerEvent$1 } from 'react';
3
- import { k as ViewTransform, a as WorldSpec, V as ViewportSize, l as Point, W as WorldFrame } from './frac-B4hFJMiN.js';
4
- import { C as CanvasLayerDescriptor } from './CanvasStackContext-CMMo180l.js';
3
+ import { m as ViewTransform, b as WorldSpec, V as ViewportSize, n as Point, W as WorldFrame } from './frac-DTRq7Xpb.js';
4
+ import { C as CanvasLayerDescriptor } from './CanvasStackContext-DQgcH1ny.js';
5
5
 
6
6
  /** Props for `<CanvasStack>`. */
7
7
  interface CanvasStackProps {
@@ -1,4 +1,4 @@
1
- import { c as StorageAdapter, d as LabStoreState, T as TrialRecord, e as SavedSnapshot, o as LabMode, h as UndockedPanel, q as InstrumentList, s as InstrumentHooks, C as CreateLabStoreOptions, g as TrialStateHandle } from './frac-B4hFJMiN.js';
1
+ import { d as StorageAdapter, f as LabStoreState, T as TrialRecord, g as SavedSnapshot, s as LabMode, j as UndockedPanel, t as InstrumentList, v as InstrumentHooks, C as CreateLabStoreOptions, i as TrialStateHandle } from './frac-DTRq7Xpb.js';
2
2
  import * as react from 'react';
3
3
  import { ReactNode, ReactElement, Dispatch, SetStateAction } from 'react';
4
4
  import { StoreApi } from 'zustand/vanilla';
@@ -1,11 +1,12 @@
1
1
  import * as react from 'react';
2
- export { C as CanvasStack, a as CanvasStackProps, P as PanZoomHandlers, S as Stage, b as StageProps, U as UsePanZoomOptions, Z as ZoomAtOptions, c as centerOn, f as fitStage, s as screenToWorld, u as usePanZoom, w as worldToScreen, z as zoomAt } from '../_dts/usePanZoom-SSTZe410.js';
3
- export { C as CanvasLayerDescriptor, a as CanvasStackContext, b as CanvasStackContextValue, c as CanvasStackSurface } from '../_dts/CanvasStackContext-CMMo180l.js';
4
- export { D as DEFAULT_FRAME, V as ViewportSize, W as WorldFrame, a as WorldSpec, b as applyCamera, r as resolveFrame } from '../_dts/frac-B4hFJMiN.js';
2
+ export { C as CanvasStack, a as CanvasStackProps, P as PanZoomHandlers, S as Stage, b as StageProps, U as UsePanZoomOptions, Z as ZoomAtOptions, c as centerOn, f as fitStage, s as screenToWorld, u as usePanZoom, w as worldToScreen, z as zoomAt } from '../_dts/usePanZoom-Bq73pwFe.js';
3
+ export { C as CanvasLayerDescriptor, a as CanvasStackContext, b as CanvasStackContextValue, c as CanvasStackSurface } from '../_dts/CanvasStackContext-DQgcH1ny.js';
4
+ export { D as DEFAULT_FRAME, V as ViewportSize, W as WorldFrame, b as WorldSpec, c as applyCamera, r as resolveFrame } from '../_dts/frac-DTRq7Xpb.js';
5
5
  import 'react/jsx-runtime';
6
- import '../_dts/types-Db-3NNk1.js';
6
+ import '../_dts/PrefsRow.d-DFpPIeIC.js';
7
+ import '@weasel-js/theme';
7
8
  import '@weasel-js/core';
8
- import '../_dts/PrefsForm.d-DPy9I8It.js';
9
+ import '../_dts/types-B7y8hX1D.js';
9
10
  import '../_dts/types-DJ79Tg5J.js';
10
11
 
11
12
  /** Where a trial's camera takes a wheel event from outside its own element. */
@@ -1,8 +1,9 @@
1
- export { a as LabAsideRegion, b as LabChromeContext, L as LabContribution, e as LabFooterRegion, f as LabHeaderRegion, g as LabRegion, h as LabRegionProps, i as LabSidebarRegion, P as PaletteRegion, j as PaletteRegionProps, S as SidebarRegion, k as SidebarRegionProps, l as StatusRegion, m as StatusRegionProps, T as TitleBarPlacement, n as TitleBarRegion, o as TitleBarRegionProps, p as ToolbarRegion, q as ToolbarRegionProps, V as ViewportRegion, r as ViewportRegionProps, t as builtinContributions, w as contributionsIn, x as labContributions, y as mergeContributions, C as suppressContributions, D as toolContributions, E as useLabChromeContext } from '../_dts/index-DlKolEIZ.js';
2
- export { a4 as ContributionBase, a8 as IconComponent, af as RegionContribution, aj as SidebarSection, ak as SidebarSlotContext, am as StatusReadout, ap as ToolItem, aq as ToolSlotContext, ar as ToolbarItem, as as TrialChromeContext, O as TrialContribution, at as TrialRegion, av as ViewportControl } from '../_dts/frac-B4hFJMiN.js';
3
- import '../_dts/types-Db-3NNk1.js';
1
+ export { a as LabAsideRegion, b as LabChromeContext, L as LabContribution, e as LabFooterRegion, f as LabHeaderRegion, g as LabRegion, h as LabRegionProps, i as LabSidebarRegion, P as PaletteRegion, j as PaletteRegionProps, S as SidebarRegion, k as SidebarRegionProps, l as StatusRegion, m as StatusRegionProps, T as TitleBarPlacement, n as TitleBarRegion, o as TitleBarRegionProps, p as ToolbarRegion, q as ToolbarRegionProps, V as ViewportRegion, r as ViewportRegionProps, t as builtinContributions, w as contributionsIn, x as labContributions, y as mergeContributions, C as suppressContributions, D as toolContributions, E as useLabChromeContext } from '../_dts/index-B0--8PEM.js';
2
+ export { a6 as ContributionBase, aa as IconComponent, af as RegionContribution, aj as SidebarSection, ak as SidebarSlotContext, am as StatusReadout, ap as ToolItem, aq as ToolSlotContext, ar as ToolbarItem, as as TrialChromeContext, R as TrialContribution, at as TrialRegion, av as ViewportControl } from '../_dts/frac-DTRq7Xpb.js';
3
+ import '../_dts/types-B7y8hX1D.js';
4
4
  import '@weasel-js/core';
5
- import '../_dts/PrefsForm.d-DPy9I8It.js';
5
+ import '../_dts/PrefsRow.d-DFpPIeIC.js';
6
+ import '@weasel-js/theme';
6
7
  import 'react';
7
8
  import 'react/jsx-runtime';
8
9
  import '../_dts/types-DJ79Tg5J.js';
@@ -1,13 +1,13 @@
1
- export { LabAsideRegion, LabFooterRegion, LabHeaderRegion, LabSidebarRegion, PaletteRegion, SidebarRegion, StatusRegion, TitleBarRegion, ToolbarRegion, ViewportRegion, builtinContributions, contributionsIn, labContributions, mergeContributions, suppressContributions, toolContributions, useLabChromeContext } from '../chunk-CKUKF72R.js';
2
- import '../chunk-753Y6HE6.js';
1
+ export { LabAsideRegion, LabFooterRegion, LabHeaderRegion, LabSidebarRegion, PaletteRegion, SidebarRegion, StatusRegion, TitleBarRegion, ToolbarRegion, ViewportRegion, builtinContributions, contributionsIn, labContributions, mergeContributions, suppressContributions, toolContributions, useLabChromeContext } from '../chunk-H5VIV7MQ.js';
2
+ import '../chunk-3FI7Y4R4.js';
3
+ import '../chunk-SBXNQW4G.js';
3
4
  import '../chunk-RWNDDKOH.js';
4
- import '../chunk-FKLT3ITC.js';
5
+ import '../chunk-HFRE4QQV.js';
5
6
  import '../chunk-Z76KEZAX.js';
6
- import '../chunk-EMXAIRUJ.js';
7
- import '../chunk-REOBGYMC.js';
7
+ import '../chunk-VQ47KWD5.js';
8
+ import '../chunk-AWH3SRPW.js';
8
9
  import '../chunk-CQLQPQ4P.js';
9
10
  import '../chunk-2YILQ7Y5.js';
10
- import '../chunk-SBXNQW4G.js';
11
- import '../chunk-LASCBWW7.js';
11
+ import '../chunk-WUJXJC5O.js';
12
12
  //# sourceMappingURL=index.js.map
13
13
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=chunk-2RVCB2PT.js.map
3
+ //# sourceMappingURL=chunk-2RVCB2PT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-2RVCB2PT.js"}
@@ -1,5 +1,5 @@
1
1
  import { CanvasStackContext } from './chunk-CQLQPQ4P.js';
2
- import { b3, g } from './chunk-LASCBWW7.js';
2
+ import { b, g } from './chunk-WUJXJC5O.js';
3
3
  import { useRef, useCallback, useEffect, useState, useContext } from 'react';
4
4
  import { jsx, jsxs } from 'react/jsx-runtime';
5
5
  import { useVisibleRaf, normalizeZoom } from '@weasel-js/core';
@@ -222,7 +222,7 @@ function ZoomControl({
222
222
  };
223
223
  return /* @__PURE__ */ jsxs("div", { className: `lk-zoom${className ? ` ${className}` : ""}`, children: [
224
224
  /* @__PURE__ */ jsx(
225
- b3,
225
+ b,
226
226
  {
227
227
  className: "lk-zoom__slider",
228
228
  ariaLabel: "Zoom",
@@ -266,5 +266,5 @@ function Section({ children, end }) {
266
266
  StatusBar.Section = Section;
267
267
 
268
268
  export { FpsMeter, ScaleIndicator, StatusBar, Toolbar, ZoomControl, useRovingTabIndex };
269
- //# sourceMappingURL=chunk-753Y6HE6.js.map
270
- //# sourceMappingURL=chunk-753Y6HE6.js.map
269
+ //# sourceMappingURL=chunk-3FI7Y4R4.js.map
270
+ //# sourceMappingURL=chunk-3FI7Y4R4.js.map