@weasel-js/labkit 1.1.0 → 1.3.0-pre.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 (226) hide show
  1. package/README.md +27 -0
  2. package/dist/_dts/{DrawCommand-BkZztJsW.d.ts → DrawCommand-DBB45NfN.d.ts} +244 -58
  3. package/dist/_dts/PrefsForm-BYa6cWnO.d.ts +201 -0
  4. package/dist/_dts/index-DgcNdEdh.d.ts +314 -0
  5. package/dist/_dts/types-1Sdxy_Pv.d.ts +90 -0
  6. package/dist/_dts/types-AHlQxBNN.d.ts +162 -0
  7. package/dist/_dts/types-DJ79Tg5J.d.ts +56 -0
  8. package/dist/_dts/{useTrialState-DYe2vUwN.d.ts → useTrialState-CsGMjhu9.d.ts} +6 -92
  9. package/dist/canvas/index.d.ts +8 -3
  10. package/dist/canvas/index.js +2 -1
  11. package/dist/chrome/index.d.ts +86 -0
  12. package/dist/chrome/index.js +8 -0
  13. package/dist/chrome/index.js.map +1 -0
  14. package/dist/{chunk-3TYUJR7Z.js → chunk-4TMMVIDM.js} +9 -3
  15. package/dist/chunk-4TMMVIDM.js.map +1 -0
  16. package/dist/chunk-HFDVGF4X.js +170 -0
  17. package/dist/chunk-HFDVGF4X.js.map +1 -0
  18. package/dist/chunk-LN6JDUGB.js +106 -0
  19. package/dist/chunk-LN6JDUGB.js.map +1 -0
  20. package/dist/{chunk-574LJAV4.js → chunk-NS54R52R.js} +31 -21
  21. package/dist/chunk-NS54R52R.js.map +1 -0
  22. package/dist/{chunk-NRKWVTVT.js → chunk-O2BULFHX.js} +5 -14
  23. package/dist/chunk-O2BULFHX.js.map +1 -0
  24. package/dist/chunk-O7NVSCLN.js +266 -0
  25. package/dist/chunk-O7NVSCLN.js.map +1 -0
  26. package/dist/chunk-PO6L3NSH.js +366 -0
  27. package/dist/chunk-PO6L3NSH.js.map +1 -0
  28. package/dist/chunk-PQJ5B2U2.js +27 -0
  29. package/dist/chunk-PQJ5B2U2.js.map +1 -0
  30. package/dist/chunk-RVNN4CHQ.js +212 -0
  31. package/dist/chunk-RVNN4CHQ.js.map +1 -0
  32. package/dist/chunk-TLGRYALP.js +6151 -0
  33. package/dist/chunk-TLGRYALP.js.map +1 -0
  34. package/dist/{chunk-SFL7NFKN.js → chunk-V7PRSIRQ.js} +15 -7
  35. package/dist/chunk-V7PRSIRQ.js.map +1 -0
  36. package/dist/{chunk-DJLDIRFN.js → chunk-Z6HJ5FEM.js} +9626 -6050
  37. package/dist/chunk-Z6HJ5FEM.js.map +1 -0
  38. package/dist/chunk-ZZAYVX4X.js +505 -0
  39. package/dist/chunk-ZZAYVX4X.js.map +1 -0
  40. package/dist/controls/index.d.ts +19 -5
  41. package/dist/controls/index.js +3 -1
  42. package/dist/dragdrop/index.d.ts +5 -2
  43. package/dist/index.d.ts +307 -107
  44. package/dist/index.js +797 -540
  45. package/dist/index.js.map +1 -1
  46. package/dist/job/index.d.ts +13 -0
  47. package/dist/job/index.js +3 -0
  48. package/dist/job/index.js.map +1 -0
  49. package/dist/layers/index.d.ts +6 -3
  50. package/dist/layers/index.js +2 -1
  51. package/dist/passthrough/weasel-canvas.d.ts +69 -13
  52. package/dist/passthrough/weasel-canvas.js +1 -1
  53. package/dist/passthrough/weasel-ui.d.ts +3804 -3661
  54. package/dist/passthrough/weasel-ui.js +2 -2
  55. package/dist/primitives/index.d.ts +102 -8
  56. package/dist/primitives/index.js +4 -1
  57. package/dist/state/index.d.ts +3 -2
  58. package/dist/state/index.js +2 -2
  59. package/dist/styles.css +543 -144
  60. package/dist/surface/index.d.ts +77 -0
  61. package/dist/surface/index.js +4 -0
  62. package/dist/surface/index.js.map +1 -0
  63. package/dist/ui/layers/index.js +4 -3
  64. package/dist/undo/index.d.ts +5 -2
  65. package/package.json +24 -7
  66. package/src/canvas/AGENTS.md +12 -0
  67. package/src/canvas/CanvasStack.tsx +6 -2
  68. package/src/canvas/useLayerScheduler.ts +22 -15
  69. package/src/canvas/useOrbit.test.ts +71 -0
  70. package/src/canvas/useOrbit.ts +149 -0
  71. package/src/canvas/usePanZoom.test.ts +80 -0
  72. package/src/canvas/usePanZoom.ts +16 -2
  73. package/src/chrome/ChromeRegions.stories.tsx +106 -0
  74. package/src/chrome/builtins.test.ts +153 -0
  75. package/src/chrome/builtins.tsx +205 -0
  76. package/src/chrome/index.ts +25 -0
  77. package/src/chrome/merge.test.ts +51 -0
  78. package/src/chrome/merge.ts +52 -0
  79. package/src/chrome/regions/PaletteRegion.less +14 -0
  80. package/src/chrome/regions/PaletteRegion.test.tsx +36 -0
  81. package/src/chrome/regions/PaletteRegion.tsx +41 -0
  82. package/src/chrome/regions/SidebarRegion.tsx +56 -0
  83. package/src/chrome/regions/StatusRegion.tsx +31 -0
  84. package/src/chrome/regions/TitleBarRegion.tsx +41 -0
  85. package/src/chrome/regions/ToolbarRegion.test.tsx +82 -0
  86. package/src/chrome/regions/ToolbarRegion.tsx +71 -0
  87. package/src/chrome/regions/ViewportRegion.less +42 -0
  88. package/src/chrome/regions/ViewportRegion.tsx +37 -0
  89. package/src/chrome/regions/regions.test.tsx +81 -0
  90. package/src/chrome/types.ts +127 -0
  91. package/src/config/builder.test.ts +87 -0
  92. package/src/config/builder.ts +224 -0
  93. package/src/config/fromConfigField.test.ts +93 -0
  94. package/src/config/fromConfigField.ts +74 -0
  95. package/src/config/index.ts +22 -0
  96. package/src/config/resolve.test.ts +120 -0
  97. package/src/config/resolve.ts +67 -0
  98. package/src/config/rules.ts +68 -0
  99. package/src/config/types.ts +108 -0
  100. package/src/config/useConfigSchema.ts +30 -0
  101. package/src/config/visible.ts +20 -0
  102. package/src/controls/ControlPanel.less +4 -58
  103. package/src/controls/ControlPanel.stories.tsx +86 -0
  104. package/src/controls/ControlPanel.test.tsx +223 -10
  105. package/src/controls/ControlPanel.tsx +202 -203
  106. package/src/dragdrop/DragDropRuntime.tsx +36 -18
  107. package/src/dragdrop/DragGhost.less +1 -1
  108. package/src/dragdrop/Palette.less +1 -1
  109. package/src/index.test.ts +18 -0
  110. package/src/index.ts +38 -3
  111. package/src/instrument/SineWave.smoke.test.tsx +9 -1
  112. package/src/instrument/defineInstrument.test.ts +38 -1
  113. package/src/instrument/defineInstrument.ts +31 -4
  114. package/src/instrument/index.ts +1 -0
  115. package/src/instrument/types.ts +38 -3
  116. package/src/instrument/validateConfigSchema.test.ts +13 -3
  117. package/src/instrument/validateConfigSchema.ts +7 -10
  118. package/src/job/index.ts +3 -0
  119. package/src/job/types.ts +47 -0
  120. package/src/job/useJob.test.tsx +210 -0
  121. package/src/job/useJob.ts +134 -0
  122. package/src/lab/Lab.chrome.test.tsx +65 -0
  123. package/src/lab/Lab.stories.tsx +1 -2
  124. package/src/lab/Lab.tsx +58 -15
  125. package/src/lab/LabContext.ts +6 -0
  126. package/src/lab/LabFullChrome.stories.tsx +308 -0
  127. package/src/lab/LabHeader.test.tsx +42 -0
  128. package/src/lab/LabHeader.tsx +57 -0
  129. package/src/lab/LabPalette.test.tsx +35 -0
  130. package/src/lab/LabPalette.tsx +34 -0
  131. package/src/lab/LabShell.less +50 -3
  132. package/src/lab/Workspace.less +1 -30
  133. package/src/lab/Workspace.surface.test.tsx +49 -0
  134. package/src/lab/Workspace.test.tsx +20 -10
  135. package/src/lab/Workspace.tsx +19 -9
  136. package/src/lab/index.ts +2 -0
  137. package/src/layers/AGENTS.md +1 -1
  138. package/src/layers/LayerList.less +31 -7
  139. package/src/layers/LayerList.tsx +17 -4
  140. package/src/passthrough/weasel-ui.ts +23 -0
  141. package/src/primitives/DragHandleGlyph.tsx +29 -0
  142. package/src/primitives/FloatingPanel.less +28 -0
  143. package/src/primitives/FloatingPanel.stories.tsx +46 -0
  144. package/src/primitives/FloatingPanel.test.tsx +240 -0
  145. package/src/primitives/FloatingPanel.tsx +190 -0
  146. package/src/primitives/FpsMeter.test.tsx +44 -0
  147. package/src/primitives/FpsMeter.tsx +20 -9
  148. package/src/primitives/JobProgress.less +69 -0
  149. package/src/primitives/JobProgress.stories.tsx +48 -0
  150. package/src/primitives/JobProgress.tsx +65 -0
  151. package/src/primitives/Legend.less +47 -0
  152. package/src/primitives/Legend.stories.tsx +32 -0
  153. package/src/primitives/Legend.test.tsx +57 -0
  154. package/src/primitives/Legend.tsx +40 -0
  155. package/src/primitives/Sidebar.less +32 -2
  156. package/src/primitives/StatusBar.less +7 -0
  157. package/src/primitives/StatusBar.tsx +10 -3
  158. package/src/primitives/Toolbar.less +50 -3
  159. package/src/primitives/Toolbar.test.tsx +67 -1
  160. package/src/primitives/Toolbar.tsx +72 -6
  161. package/src/primitives/ZoomControl.less +31 -0
  162. package/src/primitives/ZoomControl.tsx +76 -0
  163. package/src/primitives/floating.entry.test.tsx +14 -0
  164. package/src/primitives/index.ts +10 -2
  165. package/src/primitives/useRovingTabIndex.test.ts +27 -0
  166. package/src/primitives/useRovingTabIndex.ts +79 -0
  167. package/src/state/store.ts +16 -2
  168. package/src/state/toolSlot.test.ts +36 -0
  169. package/src/state/types.ts +8 -2
  170. package/src/state/view.test.ts +127 -0
  171. package/src/state/view.ts +18 -0
  172. package/src/styles.less +6 -0
  173. package/src/surface/AGENTS.md +64 -0
  174. package/src/surface/SurfaceContext.ts +5 -0
  175. package/src/surface/composeRects.test.ts +50 -0
  176. package/src/surface/composeRects.ts +19 -0
  177. package/src/surface/deviceRect.test.ts +40 -0
  178. package/src/surface/deviceRect.ts +19 -0
  179. package/src/surface/index.ts +7 -0
  180. package/src/surface/rect.ts +16 -0
  181. package/src/surface/useSurfaceTile.test.tsx +67 -0
  182. package/src/surface/useSurfaceTile.ts +32 -0
  183. package/src/surface/useTiledSurface.test.tsx +231 -0
  184. package/src/surface/useTiledSurface.ts +161 -0
  185. package/src/test-setup.ts +26 -0
  186. package/src/theme/base.less +19 -2
  187. package/src/theme/interstellar.tokens.json +6 -2
  188. package/src/tools/types.ts +24 -0
  189. package/src/trial/Trial.config.test.tsx +113 -0
  190. package/src/trial/Trial.job.test.tsx +73 -0
  191. package/src/trial/Trial.less +121 -4
  192. package/src/trial/Trial.stories.tsx +9 -1
  193. package/src/trial/Trial.test.tsx +67 -96
  194. package/src/trial/Trial.tsx +112 -45
  195. package/src/trial/TrialChrome.tsx +110 -64
  196. package/src/trial/TrialDragContext.ts +10 -0
  197. package/src/trial/TrialTitleBar.tsx +41 -0
  198. package/src/trial/index.ts +1 -15
  199. package/src/trial/trialChrome.borders.test.ts +38 -0
  200. package/src/ui/layers/LayerStack.less +9 -9
  201. package/src/ui/layers/LayerStack.tsx +1 -13
  202. package/src/ui/properties/CurveField.less +2 -2
  203. package/src/ui/properties/CurveField.test.tsx +11 -11
  204. package/src/ui/properties/PropertyGroup.less +2 -2
  205. package/src/ui/properties/PropertyPanel.less +55 -41
  206. package/src/ui/properties/SpeechBalloonPanels.stories.tsx +4 -2
  207. package/src/ui/properties/storyLayouts.tsx +2 -2
  208. package/dist/_dts/index-iAP6XbH3.d.ts +0 -132
  209. package/dist/_dts/types-x92Kfeme.d.ts +0 -62
  210. package/dist/chunk-3TYUJR7Z.js.map +0 -1
  211. package/dist/chunk-574LJAV4.js.map +0 -1
  212. package/dist/chunk-5R2ATYPJ.js +0 -201
  213. package/dist/chunk-5R2ATYPJ.js.map +0 -1
  214. package/dist/chunk-C6GJKPUI.js +0 -5986
  215. package/dist/chunk-C6GJKPUI.js.map +0 -1
  216. package/dist/chunk-DJLDIRFN.js.map +0 -1
  217. package/dist/chunk-NRKWVTVT.js.map +0 -1
  218. package/dist/chunk-PMAU3SEE.js +0 -126
  219. package/dist/chunk-PMAU3SEE.js.map +0 -1
  220. package/dist/chunk-SFL7NFKN.js.map +0 -1
  221. package/src/instrument/capabilityDetector.test.ts +0 -64
  222. package/src/instrument/capabilityDetector.ts +0 -20
  223. package/src/trial/DefaultSidebar.tsx +0 -29
  224. package/src/trial/DefaultStatusBar.tsx +0 -19
  225. package/src/trial/DefaultToolbar.tsx +0 -92
  226. package/src/trial/slotTypes.ts +0 -53
@@ -14,7 +14,7 @@
14
14
  margin: 0;
15
15
  font-family: var(--wzl-font-display);
16
16
  font-weight: var(--wzl-font-weight-light);
17
- font-size: 1rem;
17
+ font-size: var(--wzl-font-size-lg);
18
18
  color: var(--wzl-fg);
19
19
  }
20
20
 
@@ -28,7 +28,7 @@
28
28
  border: 1px dashed var(--wzl-border);
29
29
  border-radius: var(--wzl-radius-sm);
30
30
  padding: 2px 8px;
31
- font-size: 0.78rem;
31
+ font-size: var(--wzl-font-size-sm);
32
32
  color: var(--wzl-fg-subtle);
33
33
  cursor: pointer;
34
34
  }
@@ -46,12 +46,12 @@
46
46
  .lk-layer-stack__drop-hint {
47
47
  height: 2px;
48
48
  background: var(--wzl-accent);
49
- border-radius: 1px;
49
+ border-radius: var(--wzl-radius-sm);
50
50
  }
51
51
 
52
52
  .lk-layer-stack__empty {
53
53
  color: var(--wzl-fg-subtle);
54
- font-size: 0.85rem;
54
+ font-size: var(--wzl-font-size);
55
55
  padding: 8px;
56
56
  text-align: center;
57
57
  }
@@ -96,8 +96,8 @@
96
96
  min-width: 22px;
97
97
  height: 22px;
98
98
  border-radius: var(--wzl-radius-sm);
99
- font-weight: 600;
100
- font-size: 0.85rem;
99
+ font-weight: var(--wzl-font-weight-bold);
100
+ font-size: var(--wzl-font-size);
101
101
  }
102
102
 
103
103
  .lk-layer-card.has-accent .lk-layer-card__handle {
@@ -112,7 +112,7 @@
112
112
  .lk-layer-card__kind,
113
113
  .lk-layer-card__primary {
114
114
  flex: 1;
115
- font-size: 0.9rem;
115
+ font-size: var(--wzl-font-size);
116
116
  }
117
117
 
118
118
  .lk-layer-card__primary {
@@ -121,14 +121,14 @@
121
121
  color: var(--wzl-fg);
122
122
  font: inherit;
123
123
  text-transform: uppercase;
124
- letter-spacing: 0.06em;
124
+ letter-spacing: var(--wzl-tracking-wide);
125
125
  }
126
126
 
127
127
  .lk-root .lk-layer-card__remove {
128
128
  background: transparent;
129
129
  border: 0;
130
130
  color: var(--wzl-fg-subtle);
131
- font-size: 1rem;
131
+ font-size: var(--wzl-font-size-lg);
132
132
  cursor: pointer;
133
133
  padding: 2px 6px;
134
134
  }
@@ -1,5 +1,6 @@
1
1
  import { type CSSProperties, type ReactNode, type RefCallback, useEffect, useState } from 'react';
2
2
  import { dlog, useReorderDragList } from '../../passthrough/weasel-ui';
3
+ import { DragHandleGlyph } from '../../primitives/DragHandleGlyph';
3
4
 
4
5
  /** One card in a layer stack: its identity, the label shown when collapsed,
5
6
  * and the optional select hoisted into its header. */
@@ -205,16 +206,3 @@ export function LayerStack({
205
206
  </div>
206
207
  );
207
208
  }
208
-
209
- function DragHandleGlyph() {
210
- return (
211
- <svg width="12" height="16" viewBox="0 0 8 16" fill="currentColor" aria-hidden="true">
212
- <circle cx="2" cy="3" r="1.1" />
213
- <circle cx="6" cy="3" r="1.1" />
214
- <circle cx="2" cy="8" r="1.1" />
215
- <circle cx="6" cy="8" r="1.1" />
216
- <circle cx="2" cy="13" r="1.1" />
217
- <circle cx="6" cy="13" r="1.1" />
218
- </svg>
219
- );
220
- }
@@ -19,7 +19,7 @@
19
19
  display: flex;
20
20
  justify-content: space-between;
21
21
  font-variant-numeric: tabular-nums;
22
- font-size: 0.85rem;
22
+ font-size: var(--wzl-font-size);
23
23
  color: var(--wzl-fg-subtle);
24
24
  }
25
25
 
@@ -39,7 +39,7 @@
39
39
  border-radius: var(--wzl-radius-sm);
40
40
  color: var(--wzl-fg);
41
41
  padding: 4px 10px;
42
- font-size: 0.78rem;
42
+ font-size: var(--wzl-font-size-sm);
43
43
  cursor: pointer;
44
44
  }
45
45
 
@@ -1,5 +1,5 @@
1
1
  import { render, screen } from '@testing-library/react';
2
- import { describe, expect, it, vi } from 'vitest';
2
+ import { assert, describe, expect, it, vi } from 'vitest';
3
3
 
4
4
  // CurveEditor uses weasel's own bundled React and fails in jsdom.
5
5
  // Mock it out — these tests only assert on readouts and the flip button.
@@ -81,13 +81,13 @@ describe('CurveField', () => {
81
81
  />,
82
82
  );
83
83
  const overlay = container.querySelector('.lk-curve-field__marks');
84
- expect(overlay).not.toBeNull();
85
- const rect = overlay!.querySelector('rect');
86
- expect(rect).not.toBeNull();
84
+ assert(overlay);
85
+ const rect = overlay.querySelector('rect');
86
+ assert(rect);
87
87
  // x = 0.2 * 200 = 40; width = (0.4 - 0.2) * 200 = 40.
88
- expect(rect!.getAttribute('x')).toBe('40');
89
- expect(rect!.getAttribute('width')).toBe('40');
90
- expect(rect!.getAttribute('fill')).toBe('#ffcc00');
88
+ expect(rect.getAttribute('x')).toBe('40');
89
+ expect(rect.getAttribute('width')).toBe('40');
90
+ expect(rect.getAttribute('fill')).toBe('#ffcc00');
91
91
  });
92
92
 
93
93
  it('renders a line mark as a positioned line', () => {
@@ -104,10 +104,10 @@ describe('CurveField', () => {
104
104
  />,
105
105
  );
106
106
  const line = container.querySelector('.lk-curve-field__marks line');
107
- expect(line).not.toBeNull();
108
- expect(line!.getAttribute('x1')).toBe('140');
109
- expect(line!.getAttribute('x2')).toBe('140');
110
- expect(line!.getAttribute('stroke')).toBe('#ffcc00');
107
+ assert(line);
108
+ expect(line.getAttribute('x1')).toBe('140');
109
+ expect(line.getAttribute('x2')).toBe('140');
110
+ expect(line.getAttribute('stroke')).toBe('#ffcc00');
111
111
  });
112
112
 
113
113
  it('omits the marks overlay when marks is undefined', () => {
@@ -13,8 +13,8 @@
13
13
  margin: 0 0 8px;
14
14
  font-family: var(--wzl-font-display);
15
15
  font-weight: var(--wzl-font-weight-light);
16
- font-size: 0.78rem;
17
- letter-spacing: 0.08em;
16
+ font-size: var(--wzl-font-size-sm);
17
+ letter-spacing: var(--wzl-tracking-wider);
18
18
  text-transform: uppercase;
19
19
  color: var(--wzl-fg-subtle);
20
20
  }
@@ -15,8 +15,8 @@
15
15
  .lk-property-panel__title {
16
16
  font-family: var(--wzl-font-display);
17
17
  font-weight: var(--wzl-font-weight-light);
18
- font-size: 1.05rem;
19
- line-height: 1.2;
18
+ font-size: var(--wzl-font-size-lg);
19
+ line-height: var(--wzl-leading-snug);
20
20
  letter-spacing: 0.01em;
21
21
  color: var(--wzl-fg);
22
22
  margin: 0 0 12px;
@@ -85,8 +85,8 @@
85
85
  .lk-property-row__label {
86
86
  font-family: var(--wzl-font-display);
87
87
  font-weight: var(--wzl-font-weight-light);
88
- font-size: 0.78rem;
89
- line-height: 1.2;
88
+ font-size: var(--wzl-font-size-sm);
89
+ line-height: var(--wzl-leading-snug);
90
90
  color: var(--wzl-fg-muted);
91
91
  text-transform: uppercase;
92
92
  letter-spacing: 0.05em;
@@ -108,14 +108,13 @@
108
108
  .lk-property-row__readout-input {
109
109
  font: inherit;
110
110
  font-weight: var(--wzl-font-weight-medium);
111
- // Force line-height 1 (the label parent inherits 1.2, which would make
112
- // the input box ~3px taller than the SB reference). Matches SB
113
- // styles.css:385 `font: 500 0.78rem/1`.
114
- line-height: 1;
111
+ // Force leading-tight the label parent inherits 1.2, which would make the
112
+ // input box ~3px taller than the speech-balloons reference.
113
+ line-height: var(--wzl-leading-tight);
115
114
  color: var(--wzl-accent);
116
115
  background: transparent;
117
116
  border: 1px solid transparent;
118
- border-radius: 3px;
117
+ border-radius: var(--wzl-radius-sm);
119
118
  padding: 1px 0;
120
119
  margin: 0;
121
120
  width: calc(5em - 24px);
@@ -126,10 +125,10 @@
126
125
  -moz-appearance: textfield;
127
126
  }
128
127
  .lk-property-row__readout-input:hover {
129
- background: rgba(255, 255, 255, 0.06);
128
+ background: color-mix(in srgb, currentColor 8%, transparent);
130
129
  }
131
130
  .lk-property-row__readout-input:focus {
132
- background: rgba(0, 0, 0, 0.4);
131
+ background: var(--wzl-surface-sunken);
133
132
  border-color: var(--wzl-accent);
134
133
  }
135
134
  .lk-property-row__readout-input::-webkit-inner-spin-button,
@@ -148,8 +147,8 @@
148
147
  // Word-style units (e.g. "px") read as a small dim suffix next to the digits.
149
148
  .lk-property-row__readout-unit {
150
149
  color: var(--wzl-fg-muted);
151
- font-size: 0.78em;
152
- line-height: 1;
150
+ font-size: var(--wzl-font-size-2xs);
151
+ line-height: var(--wzl-leading-tight);
153
152
  pointer-events: none;
154
153
  }
155
154
  // Symbol units (e.g. `<sup>°</sup>`) lean on the browser's native super
@@ -157,7 +156,7 @@
157
156
  // would re-collapse the glyph onto the baseline.
158
157
  .lk-property-row__readout-group sup {
159
158
  color: var(--wzl-fg-muted);
160
- font-size: 0.7em;
159
+ font-size: var(--wzl-font-size-2xs);
161
160
  pointer-events: none;
162
161
  }
163
162
 
@@ -174,12 +173,12 @@
174
173
  }
175
174
  .lk-property-row input[type='range']::-webkit-slider-runnable-track {
176
175
  height: 4px;
177
- border-radius: 2px;
176
+ border-radius: var(--wzl-radius-sm);
178
177
  background: color-mix(in srgb, var(--wzl-accent) 18%, transparent);
179
178
  }
180
179
  .lk-property-row input[type='range']::-moz-range-track {
181
180
  height: 4px;
182
- border-radius: 2px;
181
+ border-radius: var(--wzl-radius-sm);
183
182
  background: color-mix(in srgb, var(--wzl-accent) 18%, transparent);
184
183
  }
185
184
  .lk-property-row input[type='range']::-webkit-slider-thumb {
@@ -225,12 +224,15 @@
225
224
 
226
225
  .lk-property-row input[type='text']:not(.lk-property-row__readout-input),
227
226
  .lk-property-row input[type='number'] {
228
- background: rgba(0, 0, 0, 0.35);
227
+ background: var(--wzl-surface-sunken);
229
228
  color: var(--wzl-fg);
230
229
  border: 1px solid var(--wzl-border);
231
230
  border-radius: var(--wzl-radius-sm);
232
231
  padding: 6px 8px;
233
- font: 400 0.85rem/1 var(--wzl-font-ui);
232
+ font-family: var(--wzl-font-ui);
233
+ font-weight: var(--wzl-font-weight-medium);
234
+ font-size: var(--wzl-font-size);
235
+ line-height: var(--wzl-leading-tight);
234
236
  }
235
237
 
236
238
  .lk-property-row input[type='text']:not(.lk-property-row__readout-input):focus,
@@ -248,7 +250,10 @@
248
250
  border: 0;
249
251
  border-radius: 0;
250
252
  padding: 2px 0;
251
- font: 400 0.85rem/1 var(--wzl-font-ui);
253
+ font-family: var(--wzl-font-ui);
254
+ font-weight: var(--wzl-font-weight-medium);
255
+ font-size: var(--wzl-font-size);
256
+ line-height: var(--wzl-leading-tight);
252
257
  cursor: pointer;
253
258
  }
254
259
  .lk-property-row select:focus {
@@ -344,7 +349,7 @@
344
349
  .lk-property-row--checkbox .lk-property-row__label {
345
350
  text-transform: none;
346
351
  letter-spacing: normal;
347
- font-size: 0.85rem;
352
+ font-size: var(--wzl-font-size);
348
353
  color: var(--wzl-fg);
349
354
  // Place the label after the checkbox visually.
350
355
  order: 1;
@@ -374,7 +379,10 @@
374
379
  color: var(--wzl-fg-muted);
375
380
  border: 0;
376
381
  border-radius: 0;
377
- font: 400 0.78rem/1 var(--wzl-font-ui);
382
+ font-family: var(--wzl-font-ui);
383
+ font-weight: var(--wzl-font-weight-medium);
384
+ font-size: var(--wzl-font-size-sm);
385
+ line-height: var(--wzl-leading-tight);
378
386
  cursor: pointer;
379
387
  backdrop-filter: none;
380
388
  -webkit-backdrop-filter: none;
@@ -391,12 +399,12 @@
391
399
 
392
400
  .lk-property-row__toggle-button--selected {
393
401
  background: var(--wzl-accent);
394
- color: #0a0a14;
402
+ color: var(--wzl-fg-on-accent);
395
403
  font-weight: var(--wzl-font-weight-medium);
396
404
  }
397
405
 
398
406
  .lk-property-row__toggle-button--selected:hover {
399
- color: #0a0a14;
407
+ color: var(--wzl-fg-on-accent);
400
408
  }
401
409
 
402
410
  // ── Subpanel ────────────────────────────────────────────────────────
@@ -420,12 +428,12 @@
420
428
  }
421
429
  .lk-subpanel__title {
422
430
  font-family: var(--wzl-font-display);
423
- font-weight: 400;
424
- font-size: 0.72rem;
425
- line-height: 1;
431
+ font-weight: var(--wzl-font-weight-medium);
432
+ font-size: var(--wzl-font-size-sm);
433
+ line-height: var(--wzl-leading-tight);
426
434
  color: var(--wzl-fg);
427
435
  text-transform: uppercase;
428
- letter-spacing: 0.08em;
436
+ letter-spacing: var(--wzl-tracking-wider);
429
437
  margin: 0;
430
438
  padding: 6px 0 4px;
431
439
  display: flex;
@@ -508,24 +516,27 @@
508
516
  min-width: 14px;
509
517
  padding: 0 2px;
510
518
  color: var(--lk-panel-accent, var(--wzl-fg-muted));
511
- font-size: 14px;
512
- font-weight: 700;
513
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
519
+ font-size: var(--wzl-font-size);
520
+ font-weight: var(--wzl-font-weight-bold);
521
+ font-family: var(--wzl-font-mono);
514
522
  line-height: 1;
515
523
  }
516
524
 
517
525
  .lk-effect-card__title {
518
526
  font-family: var(--wzl-font-display);
519
- font-weight: 500;
520
- font-size: 0.9rem;
521
- line-height: 1;
527
+ font-weight: var(--wzl-font-weight-medium);
528
+ font-size: var(--wzl-font-size);
529
+ line-height: var(--wzl-leading-tight);
522
530
  color: var(--wzl-fg);
523
531
  text-transform: uppercase;
524
532
  letter-spacing: 0.04em;
525
533
  }
526
534
 
527
535
  .lk-effect-card__primary {
528
- font: 400 0.75rem/1 var(--wzl-font-ui);
536
+ font-family: var(--wzl-font-ui);
537
+ font-weight: var(--wzl-font-weight-medium);
538
+ font-size: var(--wzl-font-size-sm);
539
+ line-height: var(--wzl-leading-tight);
529
540
  color: var(--wzl-fg);
530
541
  background: rgba(0, 0, 0, 0.3);
531
542
  border: 1px solid var(--wzl-border);
@@ -547,19 +558,19 @@
547
558
  background: transparent;
548
559
  color: var(--wzl-fg-muted);
549
560
  border: 1px solid transparent;
550
- border-radius: 4px;
561
+ border-radius: var(--wzl-radius-sm);
551
562
  width: 22px;
552
563
  height: 22px;
553
- font-size: 0.85rem;
564
+ font-size: var(--wzl-font-size);
554
565
  cursor: pointer;
555
566
  display: inline-flex;
556
567
  align-items: center;
557
568
  justify-content: center;
558
569
  }
559
570
  .lk-effect-card__remove:hover {
560
- color: #ffb3a8;
561
- border-color: rgba(240, 68, 56, 0.4);
562
- background: rgba(240, 68, 56, 0.08);
571
+ color: var(--wzl-danger-fg, var(--wzl-danger));
572
+ border-color: color-mix(in srgb, var(--wzl-danger) 40%, transparent);
573
+ background: color-mix(in srgb, var(--wzl-danger) 8%, transparent);
563
574
  }
564
575
 
565
576
  .lk-effect-card__body {
@@ -578,12 +589,15 @@
578
589
  .lk-effect-card-list__drop-hint {
579
590
  height: 0;
580
591
  border-top: 2px solid var(--wzl-accent);
581
- border-radius: 2px;
592
+ border-radius: var(--wzl-radius-sm);
582
593
  margin: 2px 0;
583
594
  box-shadow: 0 0 6px color-mix(in srgb, var(--wzl-accent) 60%, transparent);
584
595
  }
585
596
  .lk-effect-card-list__empty {
586
- font: var(--wzl-font-weight-light) 0.78rem/1.4 var(--wzl-font-display);
597
+ font-family: var(--wzl-font-display);
598
+ font-weight: var(--wzl-font-weight-light);
599
+ font-size: var(--wzl-font-size-sm);
600
+ line-height: var(--wzl-leading);
587
601
  color: var(--wzl-fg-muted);
588
602
  text-align: center;
589
603
  padding: 16px 12px;
@@ -299,7 +299,8 @@ export const RightSidebarTails: Story = {
299
299
  const next = prev.filter((t) => t.id !== sourceId);
300
300
  const idx = next.findIndex((t) => t.id === targetId);
301
301
  const insertAt = position === 'before' ? idx : idx + 1;
302
- const src = prev.find((t) => t.id === sourceId)!;
302
+ const src = prev.find((t) => t.id === sourceId);
303
+ if (!src) return prev;
303
304
  next.splice(insertAt, 0, src);
304
305
  return next;
305
306
  });
@@ -348,7 +349,8 @@ export const LayerStack: Story = {
348
349
  const next = prev.filter((t) => t.id !== sourceId);
349
350
  const idx = next.findIndex((t) => t.id === targetId);
350
351
  const insertAt = position === 'before' ? idx : idx + 1;
351
- const src = prev.find((t) => t.id === sourceId)!;
352
+ const src = prev.find((t) => t.id === sourceId);
353
+ if (!src) return prev;
352
354
  next.splice(insertAt, 0, src);
353
355
  return next;
354
356
  });
@@ -7,10 +7,10 @@ import type { ReactNode } from 'react';
7
7
  */
8
8
  export function SideBySide({ block, inline }: { block: ReactNode; inline: ReactNode }) {
9
9
  const labelStyle = {
10
- font: '300 0.72rem/1 Oswald, system-ui',
10
+ font: 'var(--wzl-font-weight-normal) var(--wzl-font-size-sm)/var(--wzl-leading-tight) var(--wzl-font-ui)',
11
11
  color: 'var(--wzl-fg-muted)',
12
12
  textTransform: 'uppercase' as const,
13
- letterSpacing: '0.08em',
13
+ letterSpacing: 'var(--wzl-tracking-wider)',
14
14
  margin: '0 0 8px',
15
15
  };
16
16
  return (
@@ -1,132 +0,0 @@
1
- import { ReactNode } from 'react';
2
- import { C as ConfigField } from './types-x92Kfeme.js';
3
-
4
- /** What an instrument's `render` is handed: its state and config, the setters
5
- * for both, the trial it is mounted in, and a way to emit named events. */
6
- interface RenderContext<TS = unknown, TC = unknown> {
7
- state: TS;
8
- config: TC;
9
- setState: (next: TS | ((prev: TS) => TS)) => void;
10
- setConfig: (key: keyof TC, value: unknown) => void;
11
- trial: {
12
- id: string;
13
- zoom: number;
14
- setZoom: (z: number) => void;
15
- };
16
- emit: (event: string) => void;
17
- }
18
- /** One 2D canvas layer of an instrument, drawn in declaration order.
19
- *
20
- * `draw` is called with the camera already applied, so it works in world
21
- * coordinates. `zoom` is passed for the things that must not scale with it —
22
- * set `ctx.lineWidth = 1 / zoom` to keep a hairline hairline. */
23
- interface CanvasLayer<TS = unknown, TC = unknown> {
24
- id: string;
25
- draw: (ctx: CanvasRenderingContext2D, args: {
26
- state: TS;
27
- config: TC;
28
- zoom: number;
29
- }) => void;
30
- }
31
- /** Declares that an instrument draws to a canvas: its layers, and where the
32
- * view starts. */
33
- interface CanvasCapability<TS = unknown, TC = unknown> {
34
- layers: CanvasLayer<TS, TC>[];
35
- initialView?: {
36
- zoom: number;
37
- pan: {
38
- x: number;
39
- y: number;
40
- };
41
- };
42
- }
43
- /** Declares which of an instrument's layers the trial should offer
44
- * show/hide controls for. */
45
- interface LayerCapability {
46
- ids: string[];
47
- }
48
- /** Declares that an instrument accepts items dragged from a palette: what the
49
- * palette offers, what a drop does to the state, and — optionally — live
50
- * feedback during the drag and the ability to drag existing items back out. */
51
- interface DragDropCapability<TS = unknown, TC = unknown> {
52
- palette: PaletteItem[] | ((state: TS, config: TC) => PaletteItem[]);
53
- onDrop: (worldPos: Point, item: PaletteItem, state: TS, config: TC) => TS;
54
- onDragOver?: (worldPos: Point, item: PaletteItem, state: TS, config: TC) => DragFeedback | null;
55
- pickUp?: (hit: HitResult, state: TS, config: TC) => {
56
- item: PaletteItem;
57
- state: TS;
58
- } | null;
59
- }
60
- /** Declares that an instrument's state is undoable: which emitted events
61
- * snapshot it, and how many snapshots to keep. */
62
- interface UndoCapability {
63
- snapshotOn?: string[];
64
- maxDepth?: number;
65
- }
66
- /** The name of an event an instrument emits through `RenderContext.emit`. */
67
- type SystemEvent = string;
68
- /** A point in world coordinates. */
69
- type Point = {
70
- x: number;
71
- y: number;
72
- };
73
- /** What a hit-test found, and where. */
74
- type HitResult = {
75
- hit: boolean;
76
- layerId?: string;
77
- pointId?: string;
78
- };
79
- /** A trial's camera. */
80
- type ViewTransform = {
81
- zoom: number;
82
- pan: Point;
83
- };
84
- /** A layer as the layer list shows it. `alwaysOn` layers cannot be hidden. */
85
- type LayerDescriptor = {
86
- id: string;
87
- label: string;
88
- alwaysOn?: boolean;
89
- };
90
- /** One draggable entry in an instrument's palette. */
91
- type PaletteItem = {
92
- id: string;
93
- label: string;
94
- data?: unknown;
95
- };
96
- /** Whether a drop would be accepted at the current position, and why not if
97
- * it would not. */
98
- type DragFeedback = {
99
- ok: boolean;
100
- reason?: string;
101
- };
102
- /**
103
- * An instrument: one self-contained interactive experiment a lab can host.
104
- *
105
- * It owns two pieces of data — `config`, the settings the control panel edits,
106
- * and `state`, what the experiment is currently doing — and renders from both.
107
- * The optional capability fields declare what else it wants from the runtime:
108
- * a canvas, a layer list, palette drag-and-drop, undo. Declaring a capability
109
- * is what makes the trial provide the corresponding chrome.
110
- */
111
- interface Instrument<TS = unknown, TC = unknown> {
112
- name: string;
113
- defaultConfig: () => TC;
114
- initialState: (config: TC) => TS;
115
- configSchema?: () => ConfigField[];
116
- /** The instrument's DOM. With `canvas`, this renders as an overlay above the
117
- * layers rather than instead of them; return `null` for canvas only. */
118
- render: (ctx: RenderContext<TS, TC>) => ReactNode;
119
- onConfigChange?: (config: TC, prev: TC, state: TS) => TS;
120
- serialize?: (state: TS) => unknown;
121
- deserialize?: (data: unknown, config: TC) => TS;
122
- canvas?: CanvasCapability<TS, TC>;
123
- layers?: LayerCapability;
124
- dragDrop?: DragDropCapability<TS, TC>;
125
- undo?: UndoCapability;
126
- }
127
- /** Instruments as a lab receives them. `any` rather than `unknown` because
128
- * parameter contravariance keeps a `defineInstrument<TS, TC>` result out of
129
- * an `Instrument<unknown, unknown>[]`; it is contained to this alias. */
130
- type InstrumentList = readonly Instrument<any, any>[];
131
-
132
- export type { CanvasCapability as C, DragDropCapability as D, HitResult as H, Instrument as I, LayerCapability as L, PaletteItem as P, RenderContext as R, SystemEvent as S, UndoCapability as U, ViewTransform as V, InstrumentList as a, CanvasLayer as b, DragFeedback as c, LayerDescriptor as d, Point as e };
@@ -1,62 +0,0 @@
1
- /** Which control a config field is edited with. */
2
- type ConfigFieldType = 'slider' | 'checkbox' | 'select' | 'number' | 'text' | 'color';
3
- /** What every config field carries: the config key it writes, the label shown
4
- * beside it, and which control renders it. */
5
- interface ConfigFieldBase {
6
- key: string;
7
- label: string;
8
- type: ConfigFieldType;
9
- }
10
- /** A bounded number edited by dragging. */
11
- interface SliderField extends ConfigFieldBase {
12
- type: 'slider';
13
- default: number;
14
- min: number;
15
- max: number;
16
- step?: number;
17
- }
18
- /** A boolean. */
19
- interface CheckboxField extends ConfigFieldBase {
20
- type: 'checkbox';
21
- default: boolean;
22
- }
23
- /** One choice in a select field. */
24
- interface SelectOption {
25
- value: string;
26
- label: string;
27
- }
28
- /** A fixed set of labeled choices. */
29
- interface SelectField extends ConfigFieldBase {
30
- type: 'select';
31
- default: string;
32
- options: SelectOption[];
33
- }
34
- /** A number typed directly, optionally bounded. Use a slider field instead
35
- * when the range matters more than the exact value. */
36
- interface NumberField extends ConfigFieldBase {
37
- type: 'number';
38
- default: number;
39
- min?: number;
40
- max?: number;
41
- step?: number;
42
- }
43
- /** A free-text string. Writes are debounced so typing does not re-run the
44
- * instrument on every keystroke. */
45
- interface TextField extends ConfigFieldBase {
46
- type: 'text';
47
- default: string;
48
- placeholder?: string;
49
- maxLength?: number;
50
- /** Milliseconds to debounce live setConfig calls. Default 150 ms. Set to 0 to disable. */
51
- debounceMs?: number;
52
- }
53
- /** A color, as a CSS color string. */
54
- interface ColorField extends ConfigFieldBase {
55
- type: 'color';
56
- default: string;
57
- }
58
- /** One field of an instrument's config schema. The schema is what the control
59
- * panel renders, and what `validateConfigSchema` checks. */
60
- type ConfigField = SliderField | CheckboxField | SelectField | NumberField | TextField | ColorField;
61
-
62
- export type { ConfigField as C, NumberField as N, SelectField as S, TextField as T, CheckboxField as a, ColorField as b, ConfigFieldBase as c, ConfigFieldType as d, SelectOption as e, SliderField as f };