@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
@@ -2,12 +2,9 @@ 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
4
  import {
5
- CheckboxRow,
6
- ColorRow,
5
+ PropertyField,
7
6
  PropertyList,
8
7
  PropertyPanel,
9
- SliderRow,
10
- TextRow,
11
8
  } from '@weasel-js/ui';
12
9
  import './Interstellar.stories.less';
13
10
 
@@ -148,7 +145,9 @@ function LivePanelPreview() {
148
145
  <h3 className="lk-theme-doc__heading">Live panel</h3>
149
146
  <PropertyPanel title="Sample">
150
147
  <PropertyList>
151
- <SliderRow
148
+ <PropertyField
149
+ kind="number"
150
+ control="slider"
152
151
  label="Opacity"
153
152
  value={0.65}
154
153
  min={0}
@@ -157,10 +156,10 @@ function LivePanelPreview() {
157
156
  onChange={() => {}}
158
157
  format={(v) => v.toFixed(2)}
159
158
  />
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={() => {}} />
159
+ <PropertyField kind="color" label="Fill" value="#b08adb" onChange={() => {}} />
160
+ <PropertyField kind="color" label="Stroke" value="#1a1428" onChange={() => {}} />
161
+ <PropertyField kind="string" label="Name" value="Untitled" onChange={() => {}} />
162
+ <PropertyField kind="boolean" label="Visible" value={true} onChange={() => {}} />
164
163
  </PropertyList>
165
164
  </PropertyPanel>
166
165
  </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({
@@ -185,7 +185,7 @@ describe('LabProps.instruments', () => {
185
185
  render(<Lab instruments={[instrument]} defaultInstrument="Probe" />);
186
186
  expect([...seen]).toEqual(['grid', 'glyph']);
187
187
 
188
- await userEvent.click(screen.getByRole('checkbox', { name: 'Toggle grid' }));
188
+ await userEvent.click(screen.getByRole('checkbox', { name: 'Show grid' }));
189
189
  expect([...seen]).toEqual(['glyph']);
190
190
  });
191
191
  });
@@ -24,9 +24,9 @@ import type {
24
24
  } from '../instrument/types';
25
25
  import { useJob } from '../job/useJob';
26
26
  import { useLabContext } from '../lab/LabContext';
27
- import { LayerList } from '../layers/LayerList';
28
27
  import { TrialLoupe } from '../loupe/TrialLoupe';
29
28
  import { resolveLoupe } from '../loupe/types';
29
+ import { LayerList, type LayerListItem, moveLayers } from '../passthrough/weasel-ui';
30
30
  import { LabStoreContext, TrialIdProvider } from '../state/context';
31
31
  import type { LabStore } from '../state/store';
32
32
  import type { TrialRecord } from '../state/types';
@@ -401,6 +401,24 @@ function TrialRuntime({
401
401
  return instrument.layers.ids.map((l) => (typeof l === 'string' ? { id: l, label: l } : l));
402
402
  }, [instrument.layers]);
403
403
 
404
+ // In the order the canvas draws them, with the always-on layers pinned
405
+ // below the rest, where a drag cannot reach past them.
406
+ const layerItems: LayerListItem[] = useMemo(() => {
407
+ const ordered = layerOrder
408
+ ? [...layerDescriptors].sort((a, b) => layerOrder.indexOf(a.id) - layerOrder.indexOf(b.id))
409
+ : layerDescriptors;
410
+ const pinnedLast = [
411
+ ...ordered.filter((l) => !l.alwaysOn),
412
+ ...ordered.filter((l) => l.alwaysOn),
413
+ ];
414
+ return pinnedLast.map((l) => ({
415
+ id: l.id,
416
+ label: l.label,
417
+ locked: l.alwaysOn === true,
418
+ visible: l.alwaysOn === true || layerVisibility[l.id] !== false,
419
+ }));
420
+ }, [layerDescriptors, layerOrder, layerVisibility]);
421
+
404
422
  const dragDropResult = useDragDrop({
405
423
  capability: instrument.dragDrop ?? { palette: [], onDrop: (_p, _i, s) => s },
406
424
  canvasContainerRef,
@@ -492,7 +510,12 @@ function TrialRuntime({
492
510
  minZoom={stage.minZoom}
493
511
  maxZoom={stage.maxZoom}
494
512
  hostRef={loupeHostRef}
495
- overlay={lens}
513
+ overlay={
514
+ <>
515
+ {stage.overlay?.(renderCtx)}
516
+ {lens}
517
+ </>
518
+ }
496
519
  >
497
520
  {instrument.render(renderCtx)}
498
521
  </Stage>
@@ -519,19 +542,18 @@ function TrialRuntime({
519
542
  () =>
520
543
  instrument.layers && layerDescriptors.length > 0 ? (
521
544
  <LayerList
522
- layers={layerDescriptors}
523
- visibility={layerVisibility}
524
- onReorder={(next) => {
525
- setLayerOrder(next.map((l) => l.id));
545
+ items={layerItems}
546
+ onReorder={(move) => {
547
+ setLayerOrder(moveLayers(layerItems, move).map((l) => l.id));
526
548
  bus.emit('layers.reorder');
527
549
  }}
528
- onToggle={(lid, visible) => {
550
+ onVisibilityChange={(lid, visible) => {
529
551
  setLayerVisibility((prev) => ({ ...prev, [lid]: visible }));
530
552
  bus.emit('layers.toggle');
531
553
  }}
532
554
  />
533
555
  ) : null,
534
- [instrument.layers, layerDescriptors, layerVisibility, bus],
556
+ [instrument.layers, layerDescriptors, layerItems, bus],
535
557
  );
536
558
 
537
559
  const extraChrome = useMemo<TrialContribution[]>(() => {