@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,15 +1,13 @@
1
1
  import type { FillStyle, GradientFill, Track } from '@weasel-js/core';
2
2
  import {
3
- ActionsBar,
4
3
  Badge,
5
4
  type Band,
6
5
  BandEditor,
7
6
  Button,
7
+ ButtonBar,
8
8
  Callout,
9
9
  Checkbox,
10
- CheckboxRow,
11
10
  ColorField,
12
- ColorRow,
13
11
  ComboBox,
14
12
  type ControlPoint,
15
13
  CurveEditor,
@@ -24,11 +22,11 @@ import {
24
22
  ItemList,
25
23
  KeyCap,
26
24
  KeySequence,
27
- LayerStack,
28
- type LayerStackItem,
25
+ LayerList,
26
+ type LayerListItem,
29
27
  MenuButton,
28
+ moveLayers,
30
29
  NumberField,
31
- NumberRow,
32
30
  OptionsBar,
33
31
  PaintInput,
34
32
  Plot2D,
@@ -36,6 +34,7 @@ import {
36
34
  Powerline,
37
35
  type PrefGroup,
38
36
  PrefsForm,
37
+ PropertyField,
39
38
  PropertyGroup,
40
39
  PropertyList,
41
40
  PropertyPanel,
@@ -44,10 +43,9 @@ import {
44
43
  RangeSlider,
45
44
  ResizeHandle,
46
45
  Select,
47
- SelectRow,
46
+ Sidebar,
48
47
  SidebarPanel,
49
48
  Slider,
50
- SliderRow,
51
49
  StatusBarItem,
52
50
  StatusBarSpacer,
53
51
  Switch,
@@ -55,18 +53,15 @@ import {
55
53
  TabList,
56
54
  TabPanel,
57
55
  Tabs,
58
- TextRow,
59
56
  type Thumb,
60
57
  Timeline,
61
58
  ToastRegion,
62
59
  ToggleBar,
63
- ToggleRow,
64
60
  ToolButton,
65
61
  ToolGroup,
66
62
  ToolOptionsBar,
67
63
  Tooltip,
68
64
  TooltipTrigger,
69
- Sidebar as UiSidebar,
70
65
  StatusBar as UiStatusBar,
71
66
  } from '@weasel-js/ui';
72
67
  import { type CSSProperties, type ReactNode, useId, useMemo, useRef, useState } from 'react';
@@ -76,7 +71,6 @@ import { FloatingPanel } from '../primitives/FloatingPanel';
76
71
  import { JobProgress } from '../primitives/JobProgress';
77
72
  import { Legend, type LegendEntry } from '../primitives/Legend';
78
73
  import { ScaleIndicator } from '../primitives/ScaleIndicator';
79
- import { Sidebar } from '../primitives/Sidebar';
80
74
  import { Split } from '../primitives/Split';
81
75
  import { StatusBar } from '../primitives/StatusBar';
82
76
  import { Toolbar } from '../primitives/Toolbar';
@@ -213,19 +207,19 @@ function ButtonsAndToggles() {
213
207
  </Cell>
214
208
  <Cell label="Badge">
215
209
  <div className="lk-specimen__row">
216
- <Badge shape="pill" tone="accent" variant="outline" size="sm">
210
+ <Badge shape="pill" status="accent" variant="outline" size="sm">
217
211
  ACCENT
218
212
  </Badge>
219
- <Badge tone="info" variant="subtle">
213
+ <Badge status="info" variant="subtle">
220
214
  INFO
221
215
  </Badge>
222
- <Badge tone="warn" variant="solid">
216
+ <Badge status="warn" variant="solid">
223
217
  WARN
224
218
  </Badge>
225
- <Badge tone="danger" variant="solid">
219
+ <Badge status="danger" variant="solid">
226
220
  DANGER
227
221
  </Badge>
228
- <Badge tone="muted">MUTED</Badge>
222
+ <Badge status="muted">MUTED</Badge>
229
223
  </div>
230
224
  </Cell>
231
225
  <Cell label="Checkbox, Switch">
@@ -269,7 +263,7 @@ function ButtonsAndToggles() {
269
263
  ]}
270
264
  />
271
265
  </Cell>
272
- <Cell label="OptionsBar, ActionsBar">
266
+ <Cell label="OptionsBar, ButtonBar">
273
267
  <div className="lk-specimen__row">
274
268
  <OptionsBar
275
269
  ariaLabel="Text style"
@@ -278,7 +272,7 @@ function ButtonsAndToggles() {
278
272
  { value: 'i', label: 'I', selected: italic, onChange: setItalic },
279
273
  ]}
280
274
  />
281
- <ActionsBar
275
+ <ButtonBar
282
276
  items={[
283
277
  { value: 'undo', label: 'Undo', onAction: () => {} },
284
278
  { value: 'redo', label: 'Redo', onAction: () => {} },
@@ -297,7 +291,6 @@ function ButtonsAndToggles() {
297
291
  ]}
298
292
  />
299
293
  <Disclosure open={open} onToggle={() => setOpen((o) => !o)} label="Shapes" />
300
- <Disclosure open={false} onToggle={() => {}} label="Down" direction="down" />
301
294
  </div>
302
295
  </Cell>
303
296
  <Cell label="Tabs">
@@ -436,7 +429,7 @@ function PanelsAndRows() {
436
429
  const [label, setLabel] = useState('Badge');
437
430
  const [visible, setVisible] = useState(true);
438
431
  const [prefs, setPrefs] = useState<Record<string, unknown>>({});
439
- const [layers, setLayers] = useState<LayerStackItem[]>([
432
+ const [layers, setLayers] = useState<LayerListItem[]>([
440
433
  { id: 'base', label: 'Base coat' },
441
434
  { id: 'wash', label: 'Wash' },
442
435
  ]);
@@ -446,7 +439,9 @@ function PanelsAndRows() {
446
439
  <Cell label="PropertyPanel">
447
440
  <PropertyPanel title="Shape">
448
441
  <PropertyList>
449
- <SliderRow
442
+ <PropertyField
443
+ kind="number"
444
+ control="slider"
450
445
  label="Opacity"
451
446
  value={opacity}
452
447
  min={0}
@@ -454,7 +449,9 @@ function PanelsAndRows() {
454
449
  step={0.01}
455
450
  onChange={setOpacity}
456
451
  />
457
- <SliderRow
452
+ <PropertyField
453
+ kind="number"
454
+ control="slider"
458
455
  label="Radius"
459
456
  value={radius}
460
457
  min={0}
@@ -462,16 +459,26 @@ function PanelsAndRows() {
462
459
  unit="px"
463
460
  onChange={setRadius}
464
461
  />
465
- <ColorRow
462
+ <PropertyField
463
+ kind="color"
466
464
  label="Fill"
467
465
  value={fill}
468
466
  onChange={setFill}
469
467
  alpha={alpha}
470
468
  onAlphaChange={setAlpha}
471
469
  />
472
- <ColorRow label="Stroke" value={stroke} onChange={setStroke} />
473
- <NumberRow label="Count" value={count} min={0} max={100} step={1} onChange={setCount} />
474
- <SelectRow
470
+ <PropertyField kind="color" label="Stroke" value={stroke} onChange={setStroke} />
471
+ <PropertyField
472
+ kind="number"
473
+ label="Count"
474
+ value={count}
475
+ min={0}
476
+ max={100}
477
+ step={1}
478
+ onChange={setCount}
479
+ />
480
+ <PropertyField
481
+ kind="enum"
475
482
  label="Line cap"
476
483
  value={cap}
477
484
  onChange={setCap}
@@ -480,7 +487,9 @@ function PanelsAndRows() {
480
487
  { value: 'round', label: 'Round' },
481
488
  ]}
482
489
  />
483
- <ToggleRow
490
+ <PropertyField
491
+ kind="enum"
492
+ control="toggle"
484
493
  label="Align"
485
494
  value={side}
486
495
  onChange={setSide}
@@ -490,10 +499,19 @@ function PanelsAndRows() {
490
499
  { value: 'right', label: 'R' },
491
500
  ]}
492
501
  />
493
- <TextRow label="Label" value={label} onChange={setLabel} />
494
- <CheckboxRow label="Visible" value={visible} onChange={setVisible} />
502
+ <PropertyField kind="string" label="Label" value={label} onChange={setLabel} />
503
+ <PropertyField kind="boolean" label="Visible" value={visible} onChange={setVisible} />
495
504
  <PropertyGroup title="Bevel" defaultCollapsed>
496
- <SliderRow label="Rings" value={32} min={4} max={96} step={1} onChange={() => {}} />
505
+ <PropertyField
506
+ kind="number"
507
+ control="slider"
508
+ label="Rings"
509
+ value={32}
510
+ min={4}
511
+ max={96}
512
+ step={1}
513
+ onChange={() => {}}
514
+ />
497
515
  </PropertyGroup>
498
516
  </PropertyList>
499
517
  </PropertyPanel>
@@ -505,18 +523,16 @@ function PanelsAndRows() {
505
523
  onChange={(path, value) => setPrefs((prev) => setAt(prev, path, value))}
506
524
  />
507
525
  </Cell>
508
- <Cell label="LayerStack">
509
- <LayerStack
526
+ <Cell label="LayerList">
527
+ <LayerList
510
528
  items={layers}
511
- onReorder={(ids) =>
512
- setLayers(ids.flatMap((id) => layers.filter((item) => item.id === id)))
513
- }
529
+ onReorder={(move) => setLayers(moveLayers(layers, move))}
514
530
  renderBody={(item) => <div>settings for {item.label}</div>}
515
531
  />
516
532
  </Cell>
517
533
  <Cell label="Sidebar, SidebarPanel">
518
534
  <div className="lk-specimen__box">
519
- <UiSidebar side="right" ariaLabel="Specimen sidebar">
535
+ <Sidebar side="right" ariaLabel="Specimen sidebar">
520
536
  <SidebarPanel
521
537
  title="Selection"
522
538
  collapsed={panelCollapsed}
@@ -525,7 +541,7 @@ function PanelsAndRows() {
525
541
  >
526
542
  <div className="lk-specimen__pad">2 items selected</div>
527
543
  </SidebarPanel>
528
- </UiSidebar>
544
+ </Sidebar>
529
545
  </div>
530
546
  </Cell>
531
547
  </Section>
@@ -554,8 +570,8 @@ function OverlaysAndFeedback() {
554
570
  triggerRef={calloutAnchor}
555
571
  isOpen={calloutOpen}
556
572
  onOpenChange={setCalloutOpen}
557
- tone="info"
558
- title="Info"
573
+ stance="notice"
574
+ title="Notice"
559
575
  >
560
576
  Neutral guidance.
561
577
  </Callout>
@@ -587,9 +603,9 @@ function OverlaysAndFeedback() {
587
603
  variant="solid"
588
604
  size="sm"
589
605
  segments={[
590
- { text: 'main', tone: 'accent', endCap: 'chevron' },
591
- { text: '✓ 12', tone: 'info', endCap: 'slant' },
592
- { text: '~/proj', tone: 'muted' },
606
+ { text: 'main', status: 'accent', endCap: 'chevron' },
607
+ { text: '✓ 12', status: 'info', endCap: 'slant' },
608
+ { text: '~/proj', status: 'muted' },
593
609
  ]}
594
610
  />
595
611
  </Cell>
@@ -799,7 +815,6 @@ function EditorsAndPlots() {
799
815
  }
800
816
 
801
817
  function LabChrome() {
802
- const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
803
818
  const [zoom, setZoom] = useState(1);
804
819
  return (
805
820
  <Section title="Lab chrome">
@@ -823,18 +838,6 @@ function LabChrome() {
823
838
  <StatusBar.Section>Zoom: 100%</StatusBar.Section>
824
839
  </StatusBar>
825
840
  </Cell>
826
- <Cell label="Sidebar">
827
- <div className="lk-specimen__box">
828
- <div className="lk-specimen__box-main">main</div>
829
- <Sidebar
830
- title="Controls"
831
- collapsed={sidebarCollapsed}
832
- onToggle={() => setSidebarCollapsed((v) => !v)}
833
- >
834
- <p>One slider</p>
835
- </Sidebar>
836
- </div>
837
- </Cell>
838
841
  <Cell label="Legend, JobProgress">
839
842
  <Legend entries={LEGEND} />
840
843
  <JobProgress job={JOB} />
@@ -96,7 +96,10 @@ describe('documents and records', () => {
96
96
  ['trial:z', { instrumentName: 'T', order: 0 }],
97
97
  ['trial:m', { instrumentName: 'T', order: 0 }],
98
98
  ]);
99
- expect((joined?.doc.trials as { id: string }[]).map((t) => t.id)).toEqual(['m', 'z']);
99
+ expect((joined?.doc.trials as { id: string }[] | undefined)?.map((t) => t.id)).toEqual([
100
+ 'm',
101
+ 'z',
102
+ ]);
100
103
  });
101
104
 
102
105
  it('is null without a meta record', () => {
@@ -84,7 +84,7 @@ describe('updateTrialState', () => {
84
84
  s.getState().updateTrialState('w1', (prev: unknown) => ({
85
85
  n: (prev as { n: number }).n + 1,
86
86
  }));
87
- expect((s.getState().trials[0]?.state as { n: number }).n).toBe(2);
87
+ expect((s.getState().trials[0]?.state as { n: number } | undefined)?.n).toBe(2);
88
88
  });
89
89
  });
90
90
 
@@ -99,8 +99,8 @@ describe('updateTrialConfig', () => {
99
99
  view: { zoom: 1, pan: { x: 0, y: 0 } },
100
100
  });
101
101
  s.getState().updateTrialConfig('w1', 'x', 99);
102
- expect((s.getState().trials[0]?.config as { x: number }).x).toBe(99);
103
- expect((s.getState().trials[0]?.config as { y: number }).y).toBe(2);
102
+ expect((s.getState().trials[0]?.config as { x: number } | undefined)?.x).toBe(99);
103
+ expect((s.getState().trials[0]?.config as { y: number } | undefined)?.y).toBe(2);
104
104
  });
105
105
 
106
106
  it('writes down a dotted path without disturbing its siblings', () => {
@@ -161,7 +161,7 @@ describe('save/load/delete snapshots', () => {
161
161
  s.getState().updateTrialState('w1', { n: 99 });
162
162
  const snapId = s.getState().savedSnapshots[0]?.id ?? '';
163
163
  s.getState().loadSnapshot(snapId, 'w1');
164
- expect((s.getState().trials[0]?.state as { n: number }).n).toBe(5);
164
+ expect((s.getState().trials[0]?.state as { n: number } | undefined)?.n).toBe(5);
165
165
  });
166
166
 
167
167
  it('loadSnapshot restores the auto paths the trial had when saved', () => {
@@ -1,3 +1,4 @@
1
+ import type { ColorModePreference } from '@weasel-js/theme';
1
2
  import type { Auto } from '../config/auto';
2
3
  import type { ConfigPath, ResolvedConfig, ValueAtPath } from '../config/types';
3
4
  import type { InstrumentList } from '../instrument/types';
@@ -78,7 +79,7 @@ export interface SavedSnapshot {
78
79
  }
79
80
 
80
81
  /** `auto` follows the OS; the other two are an explicit choice. */
81
- export type LabMode = 'auto' | 'light' | 'dark';
82
+ export type LabMode = ColorModePreference;
82
83
 
83
84
  /** How much room the lab's own chrome takes — the theme's density axis, named
84
85
  * the way the theme names it. A lab's instruments are unaffected: this sizes
@@ -173,7 +173,7 @@ describe('auto paths', () => {
173
173
  store.getState().updateTrialConfig('w1', 'gap', 30);
174
174
  const rec = store.getState().trials[0];
175
175
  expect(rec?.auto).toBeUndefined();
176
- expect((rec?.config as { gap: number }).gap).toBe(30);
176
+ expect((rec?.config as { gap: number } | undefined)?.gap).toBe(30);
177
177
  });
178
178
 
179
179
  it('does not allocate a new record when the path is already auto', () => {
package/src/styles.less CHANGED
@@ -1,10 +1,12 @@
1
1
  // Imports compile-time only. Component .less files are added in later steps.
2
+ // Left as a CSS @import for the consumer's bundler to resolve; build-css.mjs
3
+ // swaps it for the faces themselves, pointed at dist/fonts/.
4
+ @import (css) '@weasel-js/theme/faces.css';
2
5
  @import './theme/base.less';
3
6
  @import './lab/Workspace.less';
4
7
  @import './lab/LabShell.less';
5
8
  @import './lab/LabSwitcher.less';
6
9
  @import './primitives/Toolbar.less';
7
- @import './primitives/Sidebar.less';
8
10
  @import './primitives/Split.less';
9
11
  @import './primitives/ZoomControl.less';
10
12
  @import './primitives/StatusBar.less';
@@ -17,11 +19,13 @@
17
19
  @import './trial/Trial.less';
18
20
  @import './chrome/regions/ViewportRegion.less';
19
21
  @import './chrome/regions/PaletteRegion.less';
22
+ @import './chrome/regions/SidebarRegion.less';
20
23
  @import './controls/ControlPanel.less';
21
24
  @import './canvas/CanvasStack.less';
22
25
  @import './canvas/Stage.less';
26
+ @import './canvas/LinkedCursor.less';
27
+ @import './overview/TrialOverview.less';
23
28
  @import './loupe/Loupe.less';
24
- @import './layers/LayerList.less';
25
29
  @import './annotations/Annotations.less';
26
30
  @import './dragdrop/Palette.less';
27
31
  @import './dragdrop/DragGhost.less';
@@ -1,14 +1,7 @@
1
1
  import type { Meta, StoryObj } from '@weasel-js/forge';
2
2
  import type { TokenName } from '@weasel-js/theme';
3
3
  import { useTheme } from '@weasel-js/theme/react';
4
- import {
5
- CheckboxRow,
6
- ColorRow,
7
- PropertyList,
8
- PropertyPanel,
9
- SliderRow,
10
- TextRow,
11
- } from '@weasel-js/ui';
4
+ import { PropertyField, PropertyList, PropertyPanel } from '@weasel-js/ui';
12
5
  import './Interstellar.stories.less';
13
6
 
14
7
  const meta: Meta = {
@@ -148,7 +141,9 @@ function LivePanelPreview() {
148
141
  <h3 className="lk-theme-doc__heading">Live panel</h3>
149
142
  <PropertyPanel title="Sample">
150
143
  <PropertyList>
151
- <SliderRow
144
+ <PropertyField
145
+ kind="number"
146
+ control="slider"
152
147
  label="Opacity"
153
148
  value={0.65}
154
149
  min={0}
@@ -157,10 +152,10 @@ function LivePanelPreview() {
157
152
  onChange={() => {}}
158
153
  format={(v) => v.toFixed(2)}
159
154
  />
160
- <ColorRow label="Fill" value="#b08adb" onChange={() => {}} />
161
- <ColorRow label="Stroke" value="#1a1428" onChange={() => {}} />
162
- <TextRow label="Name" value="Untitled" onChange={() => {}} />
163
- <CheckboxRow label="Visible" value={true} onChange={() => {}} />
155
+ <PropertyField kind="color" label="Fill" value="#b08adb" onChange={() => {}} />
156
+ <PropertyField kind="color" label="Stroke" value="#1a1428" onChange={() => {}} />
157
+ <PropertyField kind="string" label="Name" value="Untitled" onChange={() => {}} />
158
+ <PropertyField kind="boolean" label="Visible" value={true} onChange={() => {}} />
164
159
  </PropertyList>
165
160
  </PropertyPanel>
166
161
  </div>
@@ -1,25 +1,8 @@
1
- // Self-hosted Oswald (variable font, wght axis 200–700) for the headline
2
- // typeface. Sourced from @weasel-js/theme; build:fonts copies it into dist/fonts.
3
- @font-face {
4
- font-family: 'Oswald';
5
- font-style: normal;
6
- font-weight: 200 700;
7
- font-display: swap;
8
- // URL is resolved relative to the COMPILED stylesheet at dist/styles.css,
9
- // so this points to dist/fonts/. Source-tree usage (less compilation
10
- // happens before the file is moved) doesn't need to resolve the URL.
11
- src: url('./fonts/oswald-latin-variable.woff2') format('woff2');
12
- unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
13
- U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
14
- U+2212, U+2215, U+FEFF, U+FFFD;
15
- }
1
+ // The Oswald and Inter faces come from @weasel-js/theme/faces.css, imported at
2
+ // the top of styles.less.
16
3
 
17
4
  // Resets and global element defaults.
18
5
  .lk-root {
19
- // labkit's own vocabulary over the theme's raw scale — named for the thing
20
- // rather than the step, so "the workspace is too tight" has one place to go.
21
- --lk-workspace-pad: var(--wzl-space-sm);
22
-
23
6
  font-family: var(--wzl-font-ui);
24
7
  font-size: var(--wzl-font-size);
25
8
  font-weight: var(--wzl-font-weight-light);
@@ -52,7 +35,7 @@
52
35
 
53
36
  &:hover {
54
37
  border-color: var(--wzl-accent);
55
- color: var(--wzl-accent);
38
+ color: var(--wzl-accent-fg);
56
39
  }
57
40
 
58
41
  &:focus-visible {
@@ -1,4 +1,4 @@
1
- import { resolveTheme, THEME_SOURCES, type ThemeDefinition } from '@weasel-js/theme';
1
+ import { resolveTheme, THEME_SOURCES, type ThemeDefinition, weaselTheme } from '@weasel-js/theme';
2
2
  import { derive } from '@weasel-js/theme/engine';
3
3
  import { describe, expect, it } from 'vitest';
4
4
  import { interstellarTheme } from './interstellar';
@@ -19,8 +19,11 @@ describe('interstellarTheme', () => {
19
19
  // Values carried over verbatim from the retired Less.
20
20
  expect(dark['--wzl-surface']).toBe('#0a0a14');
21
21
  expect(dark['--wzl-accent']).toBe('#b08adb');
22
- expect(light['--wzl-surface']).toBe('#fafaf7');
23
- expect(light['--wzl-accent']).toBe('#a86f3c');
22
+ // Light mode is weasel's own: interstellar restyles the dark only.
23
+ const weasel = resolveTheme(weaselTheme, { mode: 'light' });
24
+ expect(light['--wzl-surface']).toBe(weasel['--wzl-surface']);
25
+ expect(light['--wzl-accent']).toBe(weasel['--wzl-accent']);
26
+ expect(light['--wzl-border']).toBe(weasel['--wzl-border']);
24
27
 
25
28
  // Inherited from weasel — interstellar overrides values, not the token set.
26
29
  expect(dark['--wzl-space-md']).toBe('12px');
@@ -27,10 +27,6 @@
27
27
  "dark": {
28
28
  "value": "#0a0a14",
29
29
  "type": "color"
30
- },
31
- "light": {
32
- "value": "#fafaf7",
33
- "type": "color"
34
30
  }
35
31
  },
36
32
  "surface-raised": {
@@ -38,10 +34,6 @@
38
34
  "dark": {
39
35
  "value": "rgba(20, 20, 36, 0.55)",
40
36
  "type": "color"
41
- },
42
- "light": {
43
- "value": "#ffffff",
44
- "type": "color"
45
37
  }
46
38
  },
47
39
  "surface-sunken": {
@@ -49,10 +41,6 @@
49
41
  "dark": {
50
42
  "value": "#02020a",
51
43
  "type": "color"
52
- },
53
- "light": {
54
- "value": "#f0ebe2",
55
- "type": "color"
56
44
  }
57
45
  },
58
46
  "border": {
@@ -60,10 +48,6 @@
60
48
  "dark": {
61
49
  "value": "rgba(255, 255, 255, 0.14)",
62
50
  "type": "color"
63
- },
64
- "light": {
65
- "value": "#c9bba5",
66
- "type": "color"
67
51
  }
68
52
  },
69
53
  "line-subtle": {
@@ -71,10 +55,6 @@
71
55
  "dark": {
72
56
  "value": "rgba(255, 255, 255, 0.08)",
73
57
  "type": "color"
74
- },
75
- "light": {
76
- "value": "rgba(0, 0, 0, 0.08)",
77
- "type": "color"
78
58
  }
79
59
  },
80
60
  "fg": {
@@ -82,10 +62,6 @@
82
62
  "dark": {
83
63
  "value": "#e8e8f0",
84
64
  "type": "color"
85
- },
86
- "light": {
87
- "value": "#2a2018",
88
- "type": "color"
89
65
  }
90
66
  },
91
67
  "fg-muted": {
@@ -93,10 +69,6 @@
93
69
  "dark": {
94
70
  "value": "#a8a8c0",
95
71
  "type": "color"
96
- },
97
- "light": {
98
- "value": "#6b5c4a",
99
- "type": "color"
100
72
  }
101
73
  },
102
74
  "fg-subtle": {
@@ -104,10 +76,6 @@
104
76
  "dark": {
105
77
  "value": "#5a5a78",
106
78
  "type": "color"
107
- },
108
- "light": {
109
- "value": "#a89b85",
110
- "type": "color"
111
79
  }
112
80
  },
113
81
  "accent": {
@@ -115,10 +83,6 @@
115
83
  "dark": {
116
84
  "value": "#b08adb",
117
85
  "type": "color"
118
- },
119
- "light": {
120
- "value": "#a86f3c",
121
- "type": "color"
122
86
  }
123
87
  },
124
88
  "accent-hover": {
@@ -126,10 +90,6 @@
126
90
  "dark": {
127
91
  "value": "#c6a3ee",
128
92
  "type": "color"
129
- },
130
- "light": {
131
- "value": "#c2854f",
132
- "type": "color"
133
93
  }
134
94
  },
135
95
  "focus-ring": {
@@ -137,10 +97,6 @@
137
97
  "dark": {
138
98
  "value": "rgba(176, 138, 219, 0.45)",
139
99
  "type": "color"
140
- },
141
- "light": {
142
- "value": "rgba(168, 111, 60, 0.35)",
143
- "type": "color"
144
100
  }
145
101
  },
146
102
  "shadow": {
@@ -148,10 +104,6 @@
148
104
  "dark": {
149
105
  "value": "rgba(0, 0, 0, 0.7)",
150
106
  "type": "color"
151
- },
152
- "light": {
153
- "value": "rgba(80, 60, 40, 0.18)",
154
- "type": "color"
155
107
  }
156
108
  },
157
109
  "border-strong": {
@@ -159,10 +111,6 @@
159
111
  "dark": {
160
112
  "value": "rgba(255, 255, 255, 0.36)",
161
113
  "type": "color"
162
- },
163
- "light": {
164
- "value": "#9e8b71",
165
- "type": "color"
166
114
  }
167
115
  },
168
116
  "backdrop": {
@@ -170,10 +118,6 @@
170
118
  "dark": {
171
119
  "value": "radial-gradient(ellipse 60% 80% at 18% 30%, rgba(150, 80, 200, 0.22), transparent 70%), radial-gradient(ellipse 55% 90% at 78% 70%, rgba(220, 100, 140, 0.18), transparent 65%), radial-gradient(ellipse 50% 70% at 50% 50%, rgba(60, 110, 200, 0.18), transparent 75%), radial-gradient(ellipse at center, #0a0a18 0%, #02020a 100%)",
172
120
  "type": "gradient"
173
- },
174
- "light": {
175
- "value": "none",
176
- "type": "gradient"
177
121
  }
178
122
  }
179
123
  }
@@ -2,8 +2,8 @@ import { defineTheme, type Theme, type ThemeInput, weaselTheme } from '@weasel-j
2
2
  import definition from './interstellar.theme.json' with { type: 'json' };
3
3
 
4
4
  /**
5
- * labkit's theme: a cosmic dark and a warm parchment light, extending the
6
- * built-in weasel theme. A pins-only theme definition: nothing in it is
5
+ * labkit's theme: a cosmic dark over the built-in weasel theme, whose light
6
+ * mode it keeps as is. A pins-only theme definition: nothing in it is
7
7
  * derived, so it loads without the engine.
8
8
  */
9
9
  export const interstellarTheme: Theme = defineTheme({
@@ -1,11 +1,15 @@
1
+ import { labAnnotationTools } from '../annotations/toolMap';
1
2
  import type { InstrumentList } from '../instrument/types';
2
3
 
3
4
  /**
4
5
  * The tool the lab's slot holds, once its default is applied: a lab with any
5
6
  * annotating instrument starts in `pointer`, so a first click reaches the
6
- * instrument rather than the marks over it.
7
+ * instrument rather than the marks over it, or in the first annotation tool
8
+ * its rail carries when `pointer` is not among them.
7
9
  */
8
10
  export function resolveLabTool(slot: string | null, instruments: InstrumentList): string | null {
9
11
  if (slot !== null) return slot;
10
- return instruments.some((i) => i.annotations != null) ? 'pointer' : null;
12
+ const tools = labAnnotationTools(instruments);
13
+ if (tools.some((t) => t.id === 'pointer')) return 'pointer';
14
+ return tools[0]?.id ?? null;
11
15
  }