@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
@@ -1,590 +0,0 @@
1
- import { fromConfigFields, isLeafVisible } from './chunk-EMXAIRUJ.js';
2
- import { resolveAutoConfig, schemaNodeAtPath, valueAtPath, auto } from './chunk-REOBGYMC.js';
3
- import { O, X, n2, j2, z2, G2, W, V, B, I2, U, r5, b2, a3, H, e } from './chunk-LASCBWW7.js';
4
- import { isBuiltinToolPref } from '@weasel-js/core';
5
- import { useMemo, useState, Fragment as Fragment$1, useRef, useEffect } from 'react';
6
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
-
8
- var gridOf = (pack) => pack === "one-up" ? "auto-color" : "pairs";
9
- function sectionRows(section, rows) {
10
- return {
11
- pack: section.pack ?? rows.pack,
12
- layout: section.layout ?? rows.layout,
13
- grid: section.pack ? gridOf(section.pack) : rows.grid
14
- };
15
- }
16
- var NO_AUTO = /* @__PURE__ */ new Set();
17
- function ControlPanel({
18
- schema,
19
- fields,
20
- config,
21
- setConfig,
22
- renderers,
23
- pack = "pairs",
24
- layout,
25
- density,
26
- align,
27
- collapse,
28
- collapsed,
29
- onCollapse,
30
- auto: given,
31
- showHidden = false,
32
- className
33
- }) {
34
- const resolved = useMemo(() => schema ?? fromConfigFields(fields ?? []), [schema, fields]);
35
- const [ownAuto, setOwnAuto] = useState(NO_AUTO);
36
- const auto2 = given ?? ownAuto;
37
- const setRowAuto = (path, next, value) => {
38
- if (given) {
39
- setConfig(path, next ? auto : value);
40
- return;
41
- }
42
- setOwnAuto((prev) => {
43
- const now = new Set(prev);
44
- if (next) now.add(path);
45
- else now.delete(path);
46
- return now;
47
- });
48
- if (!next) setConfig(path, value);
49
- };
50
- const shown = useMemo(
51
- () => auto2 && auto2.size > 0 ? resolveAutoConfig(resolved, config, auto2) : config,
52
- [resolved, config, auto2]
53
- );
54
- const gridPack = gridOf(pack);
55
- const folds = collapse !== void 0 || collapsed !== void 0 || onCollapse !== void 0 || resolved.sections.some((s) => s.collapsed !== void 0);
56
- const startsFolded = collapse === "closed";
57
- const fold = (key, declared, grid = gridPack) => ({
58
- pack: grid,
59
- collapsible: folds,
60
- defaultCollapsed: declared ?? startsFolded,
61
- collapsed: collapsed ? collapsed[key] ?? declared ?? startsFolded : void 0,
62
- onCollapsedChange: onCollapse ? (next) => onCollapse(key, next) : void 0
63
- });
64
- const node = (path, rows) => {
65
- const found = schemaNodeAtPath(resolved.group, path);
66
- if (!found) return null;
67
- if (!isLeafVisible(resolved, path, config, showHidden)) return null;
68
- if (n2(found)) {
69
- return /* @__PURE__ */ jsx(
70
- ControlRow,
71
- {
72
- path,
73
- leaf: found,
74
- resolved,
75
- config,
76
- shown,
77
- setConfig,
78
- renderers,
79
- pack: rows.pack,
80
- layout: rows.layout,
81
- auto: auto2,
82
- setRowAuto
83
- },
84
- path
85
- );
86
- }
87
- if (found.name === "") return /* @__PURE__ */ jsx(Fragment$1, { children: body(found, path, rows) }, path);
88
- return /* @__PURE__ */ jsx(
89
- X,
90
- {
91
- title: found.name,
92
- description: found.description,
93
- span: true,
94
- ...fold(path, void 0, rows.grid),
95
- children: body(found, path, rows)
96
- },
97
- path
98
- );
99
- };
100
- const pairable = (path) => {
101
- const found = schemaNodeAtPath(resolved.group, path);
102
- if (!found || !n2(found)) return void 0;
103
- const pair = extra(found, "pair");
104
- if (pair === void 0) return void 0;
105
- if (!isLeafVisible(resolved, path, config, showHidden))
106
- return void 0;
107
- if (renderers?.[path] ?? resolved.renderers[path] ?? renderers?.[found.kind]) return void 0;
108
- if (!isBuiltinToolPref(found)) return void 0;
109
- if (found.kind === "paint" || found.kind === "object" || isSliderLeaf(found)) return void 0;
110
- return { leaf: found, pair };
111
- };
112
- const rowsFor = (paths, rows) => {
113
- const out = [];
114
- for (let i = 0; i < paths.length; ) {
115
- const head = pairable(paths[i]);
116
- if (!head) {
117
- out.push(node(paths[i], rows));
118
- i += 1;
119
- continue;
120
- }
121
- const { pair } = head;
122
- const cells = [{ path: paths[i], leaf: head.leaf }];
123
- let j2 = i + 1;
124
- for (; j2 < paths.length; j2 += 1) {
125
- const next = pairable(paths[j2]);
126
- if (!next || next.pair !== pair) break;
127
- cells.push({ path: paths[j2], leaf: next.leaf });
128
- }
129
- out.push(
130
- /* @__PURE__ */ jsx(
131
- PairedRow,
132
- {
133
- label: pair,
134
- cells,
135
- shown,
136
- setConfig,
137
- layout: rows.layout,
138
- span: cells.some(({ leaf }) => extra(leaf, "control") === "radio")
139
- },
140
- paths[i]
141
- )
142
- );
143
- i = j2;
144
- }
145
- return out;
146
- };
147
- const body = (group, at, rows) => {
148
- const sections = resolved.sections.filter((s) => s.at === at);
149
- const sectioned = new Set(sections.flatMap((s) => s.paths));
150
- const paths = Object.keys(group.children).map((key) => at === "" ? key : `${at}.${key}`);
151
- return /* @__PURE__ */ jsxs(Fragment, { children: [
152
- rowsFor(
153
- paths.filter((p) => !sectioned.has(p)),
154
- rows
155
- ),
156
- sections.map((section) => {
157
- const inner = sectionRows(section, rows);
158
- return /* @__PURE__ */ jsx(
159
- X,
160
- {
161
- title: section.label,
162
- ...fold(sectionKey(section), section.collapsed, inner.grid),
163
- children: rowsFor(section.paths, inner)
164
- },
165
- sectionKey(section)
166
- );
167
- })
168
- ] });
169
- };
170
- return /* @__PURE__ */ jsx(
171
- O,
172
- {
173
- pack: gridPack,
174
- density,
175
- align,
176
- className: className ? `lk-control-panel ${className}` : "lk-control-panel",
177
- children: body(resolved.group, "", { pack, layout, grid: gridPack })
178
- }
179
- );
180
- }
181
- function sectionKey(section) {
182
- return section.at === "" ? section.label : `${section.at}.${section.label}`;
183
- }
184
- function isSliderLeaf(leaf) {
185
- return leaf.kind === "number" && extra(leaf, "control") === "slider" && extra(leaf, "min") !== void 0 && extra(leaf, "max") !== void 0;
186
- }
187
- function extra(leaf, key) {
188
- return leaf[key];
189
- }
190
- function ControlRow({
191
- path,
192
- leaf,
193
- resolved,
194
- config,
195
- shown,
196
- setConfig,
197
- renderers,
198
- pack,
199
- layout,
200
- auto: auto2,
201
- setRowAuto
202
- }) {
203
- const write = (value2) => setConfig(path, value2);
204
- const fallback = extra(leaf, "default");
205
- const pinned = valueAtPath(config, path) ?? fallback;
206
- const isAutoRow = auto2?.has(path) ?? false;
207
- const canAuto = !extra(leaf, "manual");
208
- const resolvedValue = isAutoRow ? valueAtPath(shown, path) : void 0;
209
- const value = resolvedValue ?? pinned;
210
- const setAuto = (next) => setRowAuto(path, next, value);
211
- const onAutoChange = canAuto ? setAuto : void 0;
212
- const autoReadout = isAutoRow ? "auto" : void 0;
213
- const autoProps = { auto: isAutoRow, onAutoChange };
214
- const custom = renderers?.[path] ?? resolved.renderers[path] ?? renderers?.[leaf.kind];
215
- if (custom) return custom({ path, pref: leaf, value, setValue: write, auto: isAutoRow, setAuto });
216
- const label = leaf.name;
217
- const description = leaf.description;
218
- const read = () => value;
219
- const wide = pack === "auto";
220
- if (!isBuiltinToolPref(leaf))
221
- return /* @__PURE__ */ jsx(UnwiredRow, { label, kind: leaf.kind, description });
222
- switch (leaf.kind) {
223
- case "number": {
224
- const field = numberField(leaf, read());
225
- const suffix = extra(leaf, "suffix") ?? field.unit?.suffix;
226
- const notation = extra(leaf, "format");
227
- if (isSliderLeaf(leaf)) {
228
- return /* @__PURE__ */ jsx(
229
- I2,
230
- {
231
- label,
232
- value: field.shown,
233
- min: field.min,
234
- max: field.max,
235
- step: field.step,
236
- notation,
237
- unit: suffix,
238
- onChange: (n2) => write(field.store(n2)),
239
- readout: autoReadout,
240
- layout,
241
- span: wide,
242
- description,
243
- ...autoProps
244
- }
245
- );
246
- }
247
- return /* @__PURE__ */ jsx(
248
- U,
249
- {
250
- label,
251
- value: field.shown,
252
- min: field.min,
253
- max: field.max,
254
- step: field.step,
255
- unit: suffix,
256
- onChange: (n2) => write(field.store(n2)),
257
- readout: autoReadout,
258
- layout,
259
- description,
260
- ...autoProps
261
- }
262
- );
263
- }
264
- case "boolean": {
265
- const Row = extra(leaf, "control") === "switch" ? V : B;
266
- return /* @__PURE__ */ jsx(
267
- Row,
268
- {
269
- label,
270
- value: read(),
271
- onChange: write,
272
- readout: autoReadout,
273
- layout,
274
- description,
275
- ...autoProps
276
- }
277
- );
278
- }
279
- case "enum": {
280
- const options = extra(leaf, "options") ?? [];
281
- const segmented = extra(leaf, "control") === "radio";
282
- const Row = segmented ? G2 : W;
283
- return /* @__PURE__ */ jsx(
284
- Row,
285
- {
286
- label,
287
- value: read(),
288
- options,
289
- onChange: write,
290
- readout: autoReadout,
291
- layout,
292
- span: segmented && wide,
293
- description,
294
- ...autoProps
295
- }
296
- );
297
- }
298
- case "string":
299
- return /* @__PURE__ */ jsx(
300
- DebouncedTextRow,
301
- {
302
- leaf,
303
- label,
304
- value: read(),
305
- write,
306
- readout: autoReadout,
307
- layout,
308
- span: wide,
309
- description,
310
- ...autoProps
311
- }
312
- );
313
- case "color": {
314
- const common = {
315
- label,
316
- readout: autoReadout,
317
- layout,
318
- description,
319
- ...autoProps
320
- };
321
- if (extra(leaf, "alpha") !== true)
322
- return /* @__PURE__ */ jsx(z2, { ...common, value: read(), onChange: write });
323
- const { rgb, alpha } = splitHexAlpha(read());
324
- return /* @__PURE__ */ jsx(
325
- z2,
326
- {
327
- ...common,
328
- value: rgb,
329
- onChange: (next) => write(joinHexAlpha(next, alpha)),
330
- alpha,
331
- onAlphaChange: (next) => write(joinHexAlpha(rgb, next))
332
- }
333
- );
334
- }
335
- case "paint":
336
- case "object":
337
- return /* @__PURE__ */ jsx(UnwiredRow, { label, kind: leaf.kind, description });
338
- default: {
339
- const _exhaustive = leaf;
340
- throw new Error(
341
- `ControlPanel: no control for built-in pref kind "${_exhaustive.kind}"`
342
- );
343
- }
344
- }
345
- }
346
- function UnwiredRow({
347
- label,
348
- kind,
349
- description
350
- }) {
351
- return /* @__PURE__ */ jsx(j2, { label, description, children: /* @__PURE__ */ jsxs("span", { className: "lk-control-panel__unknown", children: [
352
- "no control for \u201C",
353
- kind,
354
- "\u201D"
355
- ] }) });
356
- }
357
- function DebouncedTextRow({
358
- leaf,
359
- label,
360
- value,
361
- write,
362
- layout,
363
- span,
364
- description,
365
- readout,
366
- auto: auto2,
367
- onAutoChange
368
- }) {
369
- const text = useDebouncedText(
370
- value,
371
- write,
372
- extra(leaf, "debounceMs") ?? 150
373
- );
374
- return /* @__PURE__ */ jsx(
375
- H,
376
- {
377
- label,
378
- readout,
379
- layout,
380
- span,
381
- description,
382
- auto: auto2,
383
- onAutoChange,
384
- value: text.local,
385
- placeholder: extra(leaf, "placeholder"),
386
- maxLength: extra(leaf, "maxLength"),
387
- onChange: text.type
388
- }
389
- );
390
- }
391
- function numberField(leaf, value) {
392
- const unit = extra(leaf, "unit");
393
- const bounds = unit ? e(leaf) : {
394
- min: extra(leaf, "min"),
395
- max: extra(leaf, "max"),
396
- step: extra(leaf, "step")
397
- };
398
- const lo = bounds.min ?? Number.NEGATIVE_INFINITY;
399
- const hi = bounds.max ?? Number.POSITIVE_INFINITY;
400
- return {
401
- unit,
402
- min: bounds.min,
403
- max: bounds.max,
404
- step: bounds.step,
405
- shown: unit ? unit.toDisplay(value) : value,
406
- store: (shown) => {
407
- const clamped = Math.min(hi, Math.max(lo, shown));
408
- return unit ? unit.fromDisplay(clamped) : clamped;
409
- }
410
- };
411
- }
412
- function splitHexAlpha(value) {
413
- const digits = /^#([0-9a-f]{3,8})$/i.exec(value?.trim() ?? "")?.[1];
414
- const twice = (s) => s.split("").map((c) => c + c).join("");
415
- const byte = (s) => Number.parseInt(s, 16) / 255;
416
- switch (digits?.length) {
417
- case 3:
418
- return { rgb: `#${twice(digits)}`, alpha: 1 };
419
- case 4:
420
- return { rgb: `#${twice(digits.slice(0, 3))}`, alpha: byte(twice(digits.slice(3))) };
421
- case 6:
422
- return { rgb: `#${digits}`, alpha: 1 };
423
- case 8:
424
- return { rgb: `#${digits.slice(0, 6)}`, alpha: byte(digits.slice(6)) };
425
- default:
426
- return { rgb: "#000000", alpha: 1 };
427
- }
428
- }
429
- function joinHexAlpha(rgb, alpha) {
430
- const byte = Math.max(0, Math.min(255, Math.round(alpha * 255)));
431
- return `${rgb}${byte.toString(16).padStart(2, "0")}`;
432
- }
433
- function PairedRow({
434
- label,
435
- cells,
436
- shown,
437
- setConfig,
438
- layout,
439
- span
440
- }) {
441
- return /* @__PURE__ */ jsx(j2, { group: true, span, label, layout, children: cells.map(({ path, leaf }) => (
442
- // The row is named by the pair, so a cell that shares it says which
443
- // knob it is — without a caption a paired switch reads only to a
444
- // screen reader. A cell whose leaf is deliberately unnamed keeps none.
445
- /* @__PURE__ */ jsxs("span", { className: "lk-pair-cell", children: [
446
- leaf.name ? /* @__PURE__ */ jsx("span", { className: "lk-pair-label", children: leaf.name }) : null,
447
- /* @__PURE__ */ jsx(
448
- PairCell,
449
- {
450
- leaf,
451
- value: valueAtPath(shown, path) ?? extra(leaf, "default"),
452
- write: (value) => setConfig(path, value)
453
- }
454
- )
455
- ] }, path)
456
- )) });
457
- }
458
- function PairCell({
459
- leaf,
460
- value,
461
- write
462
- }) {
463
- const name = leaf.name;
464
- const title = leaf.description;
465
- const text = useDebouncedText(
466
- typeof value === "string" ? value : "",
467
- write,
468
- extra(leaf, "debounceMs") ?? 150
469
- );
470
- switch (leaf.kind) {
471
- case "number": {
472
- const field = numberField(leaf, typeof value === "number" ? value : 0);
473
- return /* @__PURE__ */ jsx(
474
- "input",
475
- {
476
- type: "number",
477
- "aria-label": name,
478
- title,
479
- value: field.shown,
480
- min: field.min,
481
- max: field.max,
482
- step: field.step,
483
- onChange: (e2) => {
484
- const n2 = Number(e2.target.value);
485
- if (e2.target.value !== "" && Number.isFinite(n2)) write(field.store(n2));
486
- }
487
- }
488
- );
489
- }
490
- case "boolean":
491
- return extra(leaf, "control") === "switch" ? /* @__PURE__ */ jsx(a3, { "aria-label": name, isSelected: value === true, onChange: write }) : /* @__PURE__ */ jsx(
492
- "input",
493
- {
494
- type: "checkbox",
495
- "aria-label": name,
496
- title,
497
- checked: value === true,
498
- onChange: (e2) => write(e2.target.checked)
499
- }
500
- );
501
- case "string":
502
- return /* @__PURE__ */ jsx(
503
- "input",
504
- {
505
- type: "text",
506
- "aria-label": name,
507
- title,
508
- value: text.local,
509
- placeholder: extra(leaf, "placeholder"),
510
- maxLength: extra(leaf, "maxLength"),
511
- onChange: (e2) => text.type(e2.target.value)
512
- }
513
- );
514
- case "color": {
515
- const { rgb, alpha } = splitHexAlpha(typeof value === "string" ? value : "");
516
- const carriesAlpha = extra(leaf, "alpha") === true;
517
- return /* @__PURE__ */ jsx(
518
- "input",
519
- {
520
- type: "color",
521
- "aria-label": name,
522
- title,
523
- value: rgb,
524
- onChange: (e2) => write(carriesAlpha ? joinHexAlpha(e2.target.value, alpha) : e2.target.value)
525
- }
526
- );
527
- }
528
- case "enum": {
529
- const options = extra(leaf, "options") ?? [];
530
- if (extra(leaf, "control") === "radio") {
531
- return /* @__PURE__ */ jsx("span", { title, children: /* @__PURE__ */ jsx(
532
- r5,
533
- {
534
- ariaLabel: name,
535
- size: "sm",
536
- value: typeof value === "string" ? value : null,
537
- onChange: (next) => next !== null && write(next),
538
- items: options.map((o) => ({ value: o.value, label: o.label }))
539
- }
540
- ) });
541
- }
542
- return /* @__PURE__ */ jsx("span", { title, children: /* @__PURE__ */ jsx(
543
- b2,
544
- {
545
- variant: "bare",
546
- "aria-label": name,
547
- options,
548
- selectedKey: typeof value === "string" ? value : null,
549
- onSelectionChange: write
550
- }
551
- ) });
552
- }
553
- default:
554
- return null;
555
- }
556
- }
557
- function useDebouncedText(value, write, debounceMs) {
558
- const [local, setLocal] = useState(value);
559
- const timer = useRef(null);
560
- const lastExternal = useRef(value);
561
- useEffect(() => {
562
- if (value !== lastExternal.current) {
563
- lastExternal.current = value;
564
- setLocal(value);
565
- }
566
- }, [value]);
567
- useEffect(
568
- () => () => {
569
- if (timer.current) clearTimeout(timer.current);
570
- },
571
- []
572
- );
573
- return {
574
- local,
575
- type: (next) => {
576
- setLocal(next);
577
- if (timer.current) clearTimeout(timer.current);
578
- const commit = () => {
579
- lastExternal.current = next;
580
- write(next);
581
- };
582
- if (debounceMs === 0) commit();
583
- else timer.current = setTimeout(commit, debounceMs);
584
- }
585
- };
586
- }
587
-
588
- export { ControlPanel };
589
- //# sourceMappingURL=chunk-FKLT3ITC.js.map
590
- //# sourceMappingURL=chunk-FKLT3ITC.js.map