@weasel-js/labkit 1.5.2 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/README.md +3 -7
  2. package/dist/_dts/{CanvasStackContext-CMMo180l.d.ts → CanvasStackContext-Wo6OwIVM.d.ts} +1 -1
  3. package/dist/_dts/FloatingPanel-7j0IcXwz.d.ts +30 -0
  4. package/dist/_dts/LayerList.d-BoELAMsM.d.ts +83 -0
  5. package/dist/_dts/{PrefsForm.d-DPy9I8It.d.ts → PrefsRow.d-DFpPIeIC.d.ts} +16 -1
  6. package/dist/_dts/PropertyField.d-D0ndfIIt.d.ts +404 -0
  7. package/dist/_dts/{Select.d-ZTiwzYyT.d.ts → Select.d-Cr8-zoup.d.ts} +105 -15
  8. package/dist/_dts/{usePanZoom-SSTZe410.d.ts → Stage-DWBBi8IU.d.ts} +80 -35
  9. package/dist/_dts/{index-DlKolEIZ.d.ts → index-CKyX0ME_.d.ts} +12 -13
  10. package/dist/_dts/{frac-B4hFJMiN.d.ts → types-B7yBTTF9.d.ts} +709 -651
  11. package/dist/_dts/{types-Db-3NNk1.d.ts → types-DTuViBvc.d.ts} +21 -2
  12. package/dist/_dts/{useTrialState-pnAE-UBD.d.ts → useTrialState-CMaV6UD4.d.ts} +2 -3
  13. package/dist/canvas/index.d.ts +41 -8
  14. package/dist/canvas/index.js +5 -3
  15. package/dist/chrome/index.d.ts +6 -6
  16. package/dist/chrome/index.js +9 -7
  17. package/dist/chunk-24W5VEJW.js +18 -0
  18. package/dist/chunk-24W5VEJW.js.map +1 -0
  19. package/dist/chunk-2RVCB2PT.js +3 -0
  20. package/dist/chunk-2RVCB2PT.js.map +1 -0
  21. package/dist/chunk-3RY6WL5T.js +38 -0
  22. package/dist/chunk-3RY6WL5T.js.map +1 -0
  23. package/dist/{chunk-CKUKF72R.js → chunk-4TT3PNPR.js} +68 -55
  24. package/dist/chunk-4TT3PNPR.js.map +1 -0
  25. package/dist/{chunk-KJCUERQF.js → chunk-6FLXL3I7.js} +9 -164
  26. package/dist/chunk-6FLXL3I7.js.map +1 -0
  27. package/dist/chunk-ASOK76ZH.js +431 -0
  28. package/dist/chunk-ASOK76ZH.js.map +1 -0
  29. package/dist/chunk-AWQG7FCC.js +19 -0
  30. package/dist/chunk-AWQG7FCC.js.map +1 -0
  31. package/dist/chunk-CGHF6RZV.js +300 -0
  32. package/dist/chunk-CGHF6RZV.js.map +1 -0
  33. package/dist/chunk-DWMSB4CA.js +228 -0
  34. package/dist/chunk-DWMSB4CA.js.map +1 -0
  35. package/dist/chunk-GHGO52SO.js +92 -0
  36. package/dist/chunk-GHGO52SO.js.map +1 -0
  37. package/dist/{chunk-LRI3MBGR.js → chunk-HKV7LRHY.js} +4 -4
  38. package/dist/{chunk-LRI3MBGR.js.map → chunk-HKV7LRHY.js.map} +1 -1
  39. package/dist/{chunk-EMXAIRUJ.js → chunk-IRTGUQ5O.js} +5 -4
  40. package/dist/chunk-IRTGUQ5O.js.map +1 -0
  41. package/dist/{chunk-H6ZAOWNE.js → chunk-JVBYV6TL.js} +3 -3
  42. package/dist/{chunk-H6ZAOWNE.js.map → chunk-JVBYV6TL.js.map} +1 -1
  43. package/dist/chunk-KHK5FGER.js +145 -0
  44. package/dist/chunk-KHK5FGER.js.map +1 -0
  45. package/dist/chunk-NEXR37YJ.js +144 -0
  46. package/dist/chunk-NEXR37YJ.js.map +1 -0
  47. package/dist/{chunk-A2ACAGW6.js → chunk-O6FFFOHS.js} +32 -7
  48. package/dist/chunk-O6FFFOHS.js.map +1 -0
  49. package/dist/chunk-QGMOA2YE.js +731 -0
  50. package/dist/chunk-QGMOA2YE.js.map +1 -0
  51. package/dist/{chunk-ERFMC6WU.js → chunk-S4NCI32V.js} +19 -10
  52. package/dist/chunk-S4NCI32V.js.map +1 -0
  53. package/dist/{chunk-753Y6HE6.js → chunk-SLAQPUI4.js} +4 -4
  54. package/dist/chunk-SLAQPUI4.js.map +1 -0
  55. package/dist/{chunk-REOBGYMC.js → chunk-TJ77A4A3.js} +3 -5
  56. package/dist/chunk-TJ77A4A3.js.map +1 -0
  57. package/dist/chunk-TYN6MLXX.js +13864 -0
  58. package/dist/chunk-TYN6MLXX.js.map +1 -0
  59. package/dist/{chunk-RS3HRQWU.js → chunk-WPLAANH4.js} +4 -3
  60. package/dist/chunk-WPLAANH4.js.map +1 -0
  61. package/dist/config/index.d.ts +51 -7
  62. package/dist/config/index.js +4 -5
  63. package/dist/controls/index.d.ts +92 -5
  64. package/dist/controls/index.js +5 -4
  65. package/dist/dragdrop/index.d.ts +9 -6
  66. package/dist/dragdrop/index.js +3 -2
  67. package/dist/index.d.ts +108 -154
  68. package/dist/index.js +456 -600
  69. package/dist/index.js.map +1 -1
  70. package/dist/layers/index.d.ts +5 -35
  71. package/dist/layers/index.js +3 -3
  72. package/dist/loupe/index.d.ts +21 -17
  73. package/dist/loupe/index.js +5 -3
  74. package/dist/overview/index.d.ts +56 -0
  75. package/dist/overview/index.js +288 -0
  76. package/dist/overview/index.js.map +1 -0
  77. package/dist/passthrough/weasel-ui.d.ts +434 -231
  78. package/dist/passthrough/weasel-ui.js +2 -1
  79. package/dist/primitives/index.d.ts +19 -54
  80. package/dist/primitives/index.js +6 -4
  81. package/dist/state/index.d.ts +8 -7
  82. package/dist/state/index.js +4 -5
  83. package/dist/state/index.js.map +1 -1
  84. package/dist/styles.css +360 -174
  85. package/dist/undo/index.d.ts +5 -4
  86. package/package.json +13 -13
  87. package/src/annotations/frac.test.ts +7 -0
  88. package/src/annotations/index.ts +1 -0
  89. package/src/annotations/paint.test.ts +21 -0
  90. package/src/annotations/paint.ts +12 -0
  91. package/src/annotations/store.test.ts +57 -0
  92. package/src/annotations/store.ts +25 -6
  93. package/src/annotations/toolMap.test.ts +37 -2
  94. package/src/annotations/toolMap.ts +13 -0
  95. package/src/annotations/types.ts +30 -2
  96. package/src/canvas/AGENTS.md +8 -10
  97. package/src/canvas/CameraInput.test.tsx +136 -0
  98. package/src/canvas/CameraInput.tsx +311 -0
  99. package/src/canvas/CanvasStack.tsx +42 -58
  100. package/src/canvas/LinkedCursor.less +16 -0
  101. package/src/canvas/LinkedCursor.tsx +54 -0
  102. package/src/canvas/Stage.test.tsx +18 -0
  103. package/src/canvas/Stage.tsx +42 -37
  104. package/src/canvas/cameraView.test.ts +60 -0
  105. package/src/canvas/cameraView.ts +61 -0
  106. package/src/canvas/index.ts +11 -2
  107. package/src/chrome/regions/SidebarRegion.less +197 -0
  108. package/src/chrome/regions/SidebarRegion.test.tsx +27 -0
  109. package/src/chrome/regions/SidebarRegion.tsx +7 -2
  110. package/src/chrome/types.ts +5 -2
  111. package/src/config/autoConfig.ts +2 -2
  112. package/src/config/builder.ts +21 -2
  113. package/src/config/entry.test.ts +10 -0
  114. package/src/config/fromConfigField.ts +1 -0
  115. package/src/config/index.ts +5 -0
  116. package/src/config/resolve.ts +11 -1
  117. package/src/config/rules.ts +2 -0
  118. package/src/config/sectionTree.test.ts +152 -0
  119. package/src/config/sectionTree.ts +134 -0
  120. package/src/config/types.ts +21 -0
  121. package/src/controls/ControlPanel.less +4 -0
  122. package/src/controls/ControlPanel.stories.tsx +55 -2
  123. package/src/controls/ControlPanel.test.tsx +40 -3
  124. package/src/controls/ControlPanel.tsx +134 -336
  125. package/src/controls/inDialog.test.tsx +100 -0
  126. package/src/controls/inDialog.tsx +38 -0
  127. package/src/controls/index.ts +1 -0
  128. package/src/index.test.ts +1 -1
  129. package/src/index.ts +18 -29
  130. package/src/instrument/SineWave.smoke.test.tsx +2 -0
  131. package/src/instrument/types.ts +15 -3
  132. package/src/lab/Lab.nebula.test.tsx +40 -0
  133. package/src/lab/Lab.test.tsx +3 -1
  134. package/src/lab/Lab.tsx +42 -19
  135. package/src/lab/LabBoundary.test.tsx +66 -0
  136. package/src/lab/LabBoundary.tsx +56 -0
  137. package/src/lab/LabHeader.tsx +4 -23
  138. package/src/lab/LabRoot.tsx +2 -3
  139. package/src/lab/LabShell.less +0 -1
  140. package/src/lab/LabShell.stories.tsx +15 -0
  141. package/src/lab/LabShell.test.tsx +18 -0
  142. package/src/lab/LabShell.tsx +14 -1
  143. package/src/lab/LabSwitcher.less +1 -1
  144. package/src/lab/LabSwitcher.test.tsx +41 -0
  145. package/src/lab/LabSwitcher.tsx +30 -8
  146. package/src/lab/index.ts +1 -0
  147. package/src/layers/AGENTS.md +18 -74
  148. package/src/layers/index.ts +4 -2
  149. package/src/loupe/AGENTS.md +7 -6
  150. package/src/loupe/LoupeGestures.tsx +14 -9
  151. package/src/loupe/TrialLoupe.tsx +5 -5
  152. package/src/loupe/index.ts +1 -0
  153. package/src/loupe/loupeActions.ts +6 -2
  154. package/src/overview/AGENTS.md +38 -0
  155. package/src/overview/OverviewMarks.test.tsx +100 -0
  156. package/src/overview/OverviewMarks.tsx +93 -0
  157. package/src/overview/TrialOverview.less +53 -0
  158. package/src/overview/TrialOverview.stories.tsx +50 -0
  159. package/src/overview/TrialOverview.test.tsx +116 -0
  160. package/src/overview/TrialOverview.tsx +374 -0
  161. package/src/overview/index.ts +2 -0
  162. package/src/passthrough/weasel-ui.ts +32 -10
  163. package/src/primitives/index.ts +0 -2
  164. package/src/specimen/Specimen.tsx +59 -56
  165. package/src/state/labRecords.test.ts +4 -1
  166. package/src/state/store.test.ts +4 -4
  167. package/src/state/types.ts +2 -1
  168. package/src/state/useTrialState.test.tsx +1 -1
  169. package/src/styles.less +6 -2
  170. package/src/theme/Interstellar.stories.tsx +8 -13
  171. package/src/theme/base.less +3 -20
  172. package/src/theme/interstellar.test.ts +6 -3
  173. package/src/theme/interstellar.theme.json +0 -56
  174. package/src/theme/interstellar.ts +2 -2
  175. package/src/tools/labTool.ts +6 -2
  176. package/src/trial/Trial.annotations.test.tsx +16 -0
  177. package/src/trial/Trial.canvas.test.tsx +1 -1
  178. package/src/trial/Trial.loupe.test.tsx +73 -1
  179. package/src/trial/Trial.stories.tsx +2 -0
  180. package/src/trial/Trial.tsx +47 -9
  181. package/src/trial/trialOps.test.ts +4 -4
  182. package/dist/_dts/index-8LiKe3A-.d.ts +0 -410
  183. package/dist/chunk-2AYGEN57.js +0 -32
  184. package/dist/chunk-2AYGEN57.js.map +0 -1
  185. package/dist/chunk-6BUQ77ZV.js +0 -18
  186. package/dist/chunk-6BUQ77ZV.js.map +0 -1
  187. package/dist/chunk-753Y6HE6.js.map +0 -1
  188. package/dist/chunk-A2ACAGW6.js.map +0 -1
  189. package/dist/chunk-CKUKF72R.js.map +0 -1
  190. package/dist/chunk-D35PFTS7.js +0 -170
  191. package/dist/chunk-D35PFTS7.js.map +0 -1
  192. package/dist/chunk-EMXAIRUJ.js.map +0 -1
  193. package/dist/chunk-ERFMC6WU.js.map +0 -1
  194. package/dist/chunk-FKLT3ITC.js +0 -590
  195. package/dist/chunk-FKLT3ITC.js.map +0 -1
  196. package/dist/chunk-KJCUERQF.js.map +0 -1
  197. package/dist/chunk-LASCBWW7.js +0 -13456
  198. package/dist/chunk-LASCBWW7.js.map +0 -1
  199. package/dist/chunk-REOBGYMC.js.map +0 -1
  200. package/dist/chunk-RS3HRQWU.js.map +0 -1
  201. package/dist/chunk-TT5C5S6R.js +0 -368
  202. package/dist/chunk-TT5C5S6R.js.map +0 -1
  203. package/dist/ui/layers/index.d.ts +0 -66
  204. package/dist/ui/layers/index.js +0 -3
  205. package/dist/ui/layers/index.js.map +0 -1
  206. package/src/canvas/usePanZoom.test.ts +0 -244
  207. package/src/canvas/usePanZoom.ts +0 -147
  208. package/src/lab/useSystemMode.ts +0 -30
  209. package/src/layers/LayerList.less +0 -93
  210. package/src/layers/LayerList.stories.tsx +0 -66
  211. package/src/layers/LayerList.test.tsx +0 -232
  212. package/src/layers/LayerList.tsx +0 -268
  213. package/src/primitives/Sidebar.less +0 -112
  214. package/src/primitives/Sidebar.stories.tsx +0 -38
  215. package/src/primitives/Sidebar.test.tsx +0 -74
  216. package/src/primitives/Sidebar.tsx +0 -35
  217. 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