@weasel-js/labkit 1.4.0-pre.1 → 1.4.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 (95) hide show
  1. package/dist/_dts/{CanvasStackContext-LnCfqNBA.d.ts → CanvasStackContext-D6M0o8cQ.d.ts} +1 -1
  2. package/dist/_dts/{frac-C-2c72Ij.d.ts → frac-QS7XzvpG.d.ts} +2 -2
  3. package/dist/_dts/{index-BDVzvRzQ.d.ts → index-B2X21G1c.d.ts} +8 -2
  4. package/dist/_dts/{types-D6s4b7if.d.ts → types-C1Zw7s5P.d.ts} +1 -0
  5. package/dist/_dts/{types-DYMaEvM5.d.ts → types-ChVJJHvk.d.ts} +3 -0
  6. package/dist/_dts/{useTrialState-gmMvZqPc.d.ts → useTrialState-Cdm13fvl.d.ts} +2 -1
  7. package/dist/canvas/index.d.ts +6 -6
  8. package/dist/canvas/index.js +2 -2
  9. package/dist/chrome/index.d.ts +4 -4
  10. package/dist/chrome/index.js +5 -5
  11. package/dist/chunk-2YILQ7Y5.js +33 -0
  12. package/dist/chunk-2YILQ7Y5.js.map +1 -0
  13. package/dist/{chunk-QB5SM4BM.js → chunk-4AMN4MMB.js} +5 -5
  14. package/dist/{chunk-QB5SM4BM.js.map → chunk-4AMN4MMB.js.map} +1 -1
  15. package/dist/{chunk-L5QJOOLV.js → chunk-7VW3S44V.js} +512 -56
  16. package/dist/chunk-7VW3S44V.js.map +1 -0
  17. package/dist/{chunk-ESIQNQ6K.js → chunk-B5VWJBWM.js} +12 -6
  18. package/dist/chunk-B5VWJBWM.js.map +1 -0
  19. package/dist/{chunk-RBGKL7NF.js → chunk-L5WBGUVR.js} +1664 -1628
  20. package/dist/chunk-L5WBGUVR.js.map +1 -0
  21. package/dist/{chunk-XJ6N32QP.js → chunk-LJSIUFYD.js} +14 -30
  22. package/dist/chunk-LJSIUFYD.js.map +1 -0
  23. package/dist/{chunk-HHGVISVZ.js → chunk-QR5V3AFG.js} +6 -4
  24. package/dist/chunk-QR5V3AFG.js.map +1 -0
  25. package/dist/{chunk-6OEYTYML.js → chunk-UD7TEHZT.js} +15 -15
  26. package/dist/chunk-UD7TEHZT.js.map +1 -0
  27. package/dist/{chunk-HMFODCOX.js → chunk-VHFQKAUL.js} +3 -3
  28. package/dist/{chunk-HMFODCOX.js.map → chunk-VHFQKAUL.js.map} +1 -1
  29. package/dist/{chunk-TO2FUOKF.js → chunk-W3KSUXXR.js} +16 -13
  30. package/dist/chunk-W3KSUXXR.js.map +1 -0
  31. package/dist/{chunk-AE5CNVRU.js → chunk-XCBCZEBO.js} +3 -3
  32. package/dist/{chunk-AE5CNVRU.js.map → chunk-XCBCZEBO.js.map} +1 -1
  33. package/dist/{chunk-E2UQVZ44.js → chunk-Y2HK44TG.js} +3 -3
  34. package/dist/{chunk-E2UQVZ44.js.map → chunk-Y2HK44TG.js.map} +1 -1
  35. package/dist/{chunk-M75ZU6ZZ.js → chunk-YQGQUBKA.js} +3 -3
  36. package/dist/{chunk-M75ZU6ZZ.js.map → chunk-YQGQUBKA.js.map} +1 -1
  37. package/dist/controls/index.d.ts +2 -2
  38. package/dist/controls/index.js +3 -3
  39. package/dist/dragdrop/index.d.ts +3 -3
  40. package/dist/dragdrop/index.js +2 -2
  41. package/dist/index.d.ts +55 -12
  42. package/dist/index.js +306 -159
  43. package/dist/index.js.map +1 -1
  44. package/dist/layers/index.d.ts +4 -4
  45. package/dist/layers/index.js +3 -3
  46. package/dist/loupe/index.d.ts +5 -5
  47. package/dist/loupe/index.js +2 -2
  48. package/dist/passthrough/weasel-canvas.js +1 -1
  49. package/dist/passthrough/weasel-ui.d.ts +336 -15
  50. package/dist/passthrough/weasel-ui.js +2 -2
  51. package/dist/primitives/index.js +4 -4
  52. package/dist/state/index.d.ts +3 -3
  53. package/dist/state/index.js +4 -2
  54. package/dist/state/index.js.map +1 -1
  55. package/dist/styles.css +64 -5
  56. package/dist/surface/index.d.ts +17 -2
  57. package/dist/surface/index.js +3 -2
  58. package/dist/ui/layers/index.js +2 -2
  59. package/dist/undo/index.d.ts +3 -3
  60. package/package.json +8 -8
  61. package/src/annotations/AnnotationOverlay.tsx +10 -6
  62. package/src/annotations/Annotations.less +6 -0
  63. package/src/annotations/Annotations.overlay.test.tsx +113 -0
  64. package/src/annotations/MarkList.tsx +3 -2
  65. package/src/config/builder.test.ts +5 -0
  66. package/src/config/builder.ts +6 -0
  67. package/src/config/types.ts +1 -0
  68. package/src/controls/ControlPanel.test.tsx +23 -0
  69. package/src/controls/ControlPanel.tsx +3 -0
  70. package/src/lab/Lab.surface.test.tsx +8 -3
  71. package/src/lab/LabHeader.test.tsx +12 -0
  72. package/src/lab/LabHeader.tsx +16 -6
  73. package/src/passthrough/weasel-ui.test.ts +9 -0
  74. package/src/passthrough/weasel-ui.ts +4 -0
  75. package/src/primitives/ZoomControl.tsx +2 -0
  76. package/src/state/store.ts +10 -0
  77. package/src/state/types.ts +3 -0
  78. package/src/surface/AGENTS.md +11 -1
  79. package/src/surface/index.ts +7 -1
  80. package/src/surface/useSurfaceTile.ts +20 -2
  81. package/src/surface/useTiledSurface.ts +4 -1
  82. package/src/theme/Interstellar.stories.tsx +4 -3
  83. package/src/theme/base.less +39 -1
  84. package/src/trial/Trial.less +27 -5
  85. package/src/trial/TrialBody.test.tsx +67 -0
  86. package/src/trial/TrialBody.tsx +155 -0
  87. package/src/trial/TrialChrome.tsx +18 -10
  88. package/src/trial/index.ts +2 -0
  89. package/dist/chunk-6OEYTYML.js.map +0 -1
  90. package/dist/chunk-ESIQNQ6K.js.map +0 -1
  91. package/dist/chunk-HHGVISVZ.js.map +0 -1
  92. package/dist/chunk-L5QJOOLV.js.map +0 -1
  93. package/dist/chunk-RBGKL7NF.js.map +0 -1
  94. package/dist/chunk-TO2FUOKF.js.map +0 -1
  95. package/dist/chunk-XJ6N32QP.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,39 +1,41 @@
1
1
  import { createEventBus, pushSnapshot, redo, undo } from './chunk-RL2LOLNI.js';
2
2
  export { clearUndo, createEventBus, emptyStack, pushSnapshot, redo, undo } from './chunk-RL2LOLNI.js';
3
- import { useDragDrop, DragOverlay, Palette } from './chunk-M75ZU6ZZ.js';
4
- import { useSurfaceOptional, useSurfaceCanvas, useTiledSurface, SurfaceContext, SurfaceCanvasContext } from './chunk-ESIQNQ6K.js';
5
- export { SurfaceCanvasContext, SurfaceContext, composeRects, rectsEqual, toDeviceRect, useSurface, useSurfaceCanvas, useSurfaceOptional, useSurfaceTile, useTiledSurface } from './chunk-ESIQNQ6K.js';
3
+ import { useDragDrop, DragOverlay, Palette } from './chunk-YQGQUBKA.js';
4
+ import { useSurfaceOptional, useSurfaceCanvas, useTileId, useTiledSurface, SurfaceContext, SurfaceCanvasContext } from './chunk-B5VWJBWM.js';
5
+ export { SurfaceCanvasContext, SurfaceContext, composeRects, rectsEqual, toDeviceRect, useSurface, useSurfaceCanvas, useSurfaceOptional, useSurfaceTile, useTileId, useTiledSurface } from './chunk-B5VWJBWM.js';
6
6
  import { useJob } from './chunk-LN6JDUGB.js';
7
7
  export { useJob } from './chunk-LN6JDUGB.js';
8
- import { ANNOTATION_WEASEL_TOOLS, annotationToolInfo, suppressContributions, mergeContributions, builtinContributions, TitleBarRegion, ToolbarRegion, PaletteRegion, SidebarRegion, ViewportRegion, StatusRegion, AnnotationsContext } from './chunk-6OEYTYML.js';
9
- export { ANNOTATION_TOOLS, ANNOTATION_WEASEL_TOOLS, AnnotationsContext, ExportMenu, MarkList, PaletteRegion, SidebarRegion, StatusRegion, TitleBarRegion, ToolbarRegion, ViewportRegion, annotationToolInfo, builtinContributions, mergeContributions, suppressContributions, useAnnotations, useAnnotationsOptional } from './chunk-6OEYTYML.js';
10
- import { JobProgress } from './chunk-QB5SM4BM.js';
11
- export { FloatingPanel, JobProgress, Legend, Sidebar } from './chunk-QB5SM4BM.js';
12
- export { FpsMeter, ScaleIndicator, StatusBar, Toolbar, ZoomControl } from './chunk-HHGVISVZ.js';
13
- import { LabStoreContext, TrialIdProvider, createLabStore, panelKey, noneAdapter } from './chunk-XJ6N32QP.js';
14
- export { CURRENT_DOCUMENT_VERSION, LabStoreContext, LabStoreProvider, TrialIdContext, TrialIdProvider, createMemoryAdapter, dockPanel, labDocumentKey, localStorageAdapter, noneAdapter, panelKey, quarantineKey, sessionStorageAdapter, undockPanel, urlHashAdapter, useLabStore, useTrialId, useTrialState } from './chunk-XJ6N32QP.js';
15
- import { fromConfigFields } from './chunk-TO2FUOKF.js';
16
- export { ControlPanel, fromConfigFields, isLeafVisible } from './chunk-TO2FUOKF.js';
17
- import { CanvasStack } from './chunk-HMFODCOX.js';
18
- export { CanvasStack, PITCH_LIMIT, clampPitch, orbitAfterDrag, orbitAfterWheel, useOrbit, usePanZoom, wrapYaw } from './chunk-HMFODCOX.js';
19
- import { LayerList } from './chunk-AE5CNVRU.js';
20
- export { LayerList } from './chunk-AE5CNVRU.js';
8
+ import { ANNOTATION_WEASEL_TOOLS, annotationToolInfo, suppressContributions, mergeContributions, builtinContributions, TitleBarRegion, ToolbarRegion, PaletteRegion, ViewportRegion, SidebarRegion, StatusRegion, AnnotationsContext } from './chunk-UD7TEHZT.js';
9
+ export { ANNOTATION_TOOLS, ANNOTATION_WEASEL_TOOLS, AnnotationsContext, ExportMenu, MarkList, PaletteRegion, SidebarRegion, StatusRegion, TitleBarRegion, ToolbarRegion, ViewportRegion, annotationToolInfo, builtinContributions, mergeContributions, suppressContributions, useAnnotations, useAnnotationsOptional } from './chunk-UD7TEHZT.js';
10
+ import { JobProgress } from './chunk-4AMN4MMB.js';
11
+ export { FloatingPanel, JobProgress, Legend, Sidebar } from './chunk-4AMN4MMB.js';
12
+ export { FpsMeter, ScaleIndicator, StatusBar, Toolbar, ZoomControl } from './chunk-QR5V3AFG.js';
13
+ import { createLabStore, panelKey, noneAdapter } from './chunk-LJSIUFYD.js';
14
+ export { CURRENT_DOCUMENT_VERSION, createMemoryAdapter, dockPanel, labDocumentKey, localStorageAdapter, noneAdapter, panelKey, quarantineKey, sessionStorageAdapter, undockPanel, urlHashAdapter, useTrialState } from './chunk-LJSIUFYD.js';
15
+ import { LabStoreContext, TrialIdProvider } from './chunk-2YILQ7Y5.js';
16
+ export { LabStoreContext, LabStoreProvider, TrialIdContext, TrialIdProvider, useLabStore, useTrialId } from './chunk-2YILQ7Y5.js';
17
+ import { fromConfigFields } from './chunk-W3KSUXXR.js';
18
+ export { ControlPanel, fromConfigFields, isLeafVisible } from './chunk-W3KSUXXR.js';
19
+ import { CanvasStack } from './chunk-VHFQKAUL.js';
20
+ export { CanvasStack, PITCH_LIMIT, clampPitch, orbitAfterDrag, orbitAfterWheel, useOrbit, usePanZoom, wrapYaw } from './chunk-VHFQKAUL.js';
21
+ import { LayerList } from './chunk-XCBCZEBO.js';
22
+ export { LayerList } from './chunk-XCBCZEBO.js';
21
23
  import './chunk-SBXNQW4G.js';
22
- import { C, p, r2 } from './chunk-RBGKL7NF.js';
23
- export { k2 as CheckboxRow, O2 as ColorRow, m2 as CurveField, _3 as EffectCard, y2 as EffectCardList, c3 as LayerStack, j2 as NumberRow, b3 as PropertyGroup, S2 as PropertyList, x3 as PropertyPanel, w as PropertyRow, C2 as PropertySpan, M as SelectRow, E as SliderRow, g as Subpanel, A2 as TextRow, N as ToggleRow } from './chunk-RBGKL7NF.js';
24
- import { resolveLoupe, TrialLoupe } from './chunk-E2UQVZ44.js';
25
- export { CanvasLoupe, DomLoupe, LOUPE_DEFAULTS, LoupeBubble, TrialLoupe, drawCanvasLens, lensCamera, lensSourceRect, resolveLoupe, sampleStack, useLoupe } from './chunk-E2UQVZ44.js';
24
+ import { E, w, T, C, p, r3 } from './chunk-L5WBGUVR.js';
25
+ export { A2 as CheckboxRow, k as ColorRow, h2 as CurveField, v3 as EffectCard, b3 as EffectCardList, c3 as LayerStack, M2 as NumberRow, x3 as PropertyGroup, C2 as PropertyList, S2 as PropertyPanel, T2 as PropertyRow, w2 as PropertySpan, N2 as SelectRow, D as SliderRow, _3 as Subpanel, j2 as TextRow, P2 as ToggleRow } from './chunk-L5WBGUVR.js';
26
+ import { resolveLoupe, TrialLoupe } from './chunk-Y2HK44TG.js';
27
+ export { CanvasLoupe, DomLoupe, LOUPE_DEFAULTS, LoupeBubble, TrialLoupe, drawCanvasLens, lensCamera, lensSourceRect, resolveLoupe, sampleStack, useLoupe } from './chunk-Y2HK44TG.js';
26
28
  export { centerOn, zoomAt } from './chunk-CTRKTLYZ.js';
27
29
  import { applyCamera } from './chunk-2AYGEN57.js';
28
30
  export { DEFAULT_FRAME, applyCamera, resolveFrame, screenToWorld, worldToScreen } from './chunk-2AYGEN57.js';
29
31
  export { CanvasStackContext } from './chunk-CQLQPQ4P.js';
30
- import { textCommand, linePath, markerDrawCommands, ellipsePath, rectPath, createScene, asNodeId as asNodeId$1, WeaselProvider, SceneCanvas, sceneFromJSON, PathBuilder, renderSceneToPixels, useActiveToolContext, resolveStrokeWidth, getMarker, getPaintKind, boundsOfPath, tileGeometry, PATH_M, PATH_L, PATH_C, PATH_Q, PATH_Z, SCRIPT_METRICS } from './chunk-L5QJOOLV.js';
32
+ import { textCommand, linePath, markerDrawCommands, ellipsePath, rectPath, createScene, asNodeId as asNodeId$1, WeaselProvider, SceneCanvas, sceneFromJSON, PathBuilder, renderSceneToPixels, useActiveToolContext, resolveStrokeWidth, getMarker, getPaintKind, boundsOfPath, tileGeometry, PATH_M, PATH_L, PATH_C, PATH_Q, PATH_Z, SCRIPT_METRICS } from './chunk-7VW3S44V.js';
31
33
  import { createContext, useState, useRef, useEffect, useContext, useMemo, Children, useLayoutEffect, useCallback, useSyncExternalStore } from 'react';
32
34
  import { createPortal } from 'react-dom';
33
- import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
35
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
34
36
  import { useStore } from 'zustand/react';
35
- import { Provider, StrategyRegistryProvider, DragProvider, Container, useDragHandle } from 'windease/react';
36
- import { asNodeId, Store, createNode, floatingStrategy, gridStrategy } from 'windease';
37
+ import { asNodeId, Store, createNode, stripStrategy, floatingStrategy, gridStrategy } from 'windease';
38
+ import { Provider, StrategyRegistryProvider, Container, DragProvider, useDragHandle } from 'windease/react';
37
39
 
38
40
  // ../svg/dist/index.js
39
41
  var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
@@ -47,12 +49,12 @@ function multiply(a, b2) {
47
49
  a[1] * b2[4] + a[3] * b2[5] + a[5]
48
50
  ];
49
51
  }
50
- function isIdentity(m2, eps = 1e-9) {
51
- return Math.abs(m2[0] - 1) < eps && Math.abs(m2[1]) < eps && Math.abs(m2[2]) < eps && Math.abs(m2[3] - 1) < eps && Math.abs(m2[4]) < eps && Math.abs(m2[5]) < eps;
52
+ function isIdentity(m, eps = 1e-9) {
53
+ return Math.abs(m[0] - 1) < eps && Math.abs(m[1]) < eps && Math.abs(m[2]) < eps && Math.abs(m[3] - 1) < eps && Math.abs(m[4]) < eps && Math.abs(m[5]) < eps;
52
54
  }
53
- function formatMatrix(m2) {
54
- if (isIdentity(m2)) return null;
55
- return `matrix(${m2.map((n) => trimNumber(n)).join(" ")})`;
55
+ function formatMatrix(m) {
56
+ if (isIdentity(m)) return null;
57
+ return `matrix(${m.map((n) => trimNumber(n)).join(" ")})`;
56
58
  }
57
59
  function trimNumber(n) {
58
60
  if (!Number.isFinite(n)) return "0";
@@ -106,8 +108,8 @@ function escapeAttr(s) {
106
108
  }
107
109
  function serializePathD(path) {
108
110
  if (path.kind === "rect") {
109
- const { x: x2, y: y2, width, height } = path;
110
- return `M${trimNumber(x2)} ${trimNumber(y2)}h${trimNumber(width)}v${trimNumber(height)}h${trimNumber(-width)}Z`;
111
+ const { x: x2, y, width, height } = path;
112
+ return `M${trimNumber(x2)} ${trimNumber(y)}h${trimNumber(width)}v${trimNumber(height)}h${trimNumber(-width)}Z`;
111
113
  }
112
114
  const cmds = path.commands;
113
115
  const xs = path.coords;
@@ -215,10 +217,10 @@ function gradientXml(id, paint) {
215
217
  return "";
216
218
  }
217
219
  function stopXml(s) {
218
- const m2 = /^#([0-9a-f]{6})([0-9a-f]{2})$/i.exec(s.color);
219
- if (m2) {
220
- const alpha = parseInt(m2[2], 16) / 255;
221
- return `<stop offset="${trimNumber(s.offset)}" stop-color="#${m2[1].toLowerCase()}" stop-opacity="${trimNumber(alpha)}"/>`;
220
+ const m = /^#([0-9a-f]{6})([0-9a-f]{2})$/i.exec(s.color);
221
+ if (m) {
222
+ const alpha = parseInt(m[2], 16) / 255;
223
+ return `<stop offset="${trimNumber(s.offset)}" stop-color="#${m[1].toLowerCase()}" stop-opacity="${trimNumber(alpha)}"/>`;
222
224
  }
223
225
  return `<stop offset="${trimNumber(s.offset)}" stop-color="${s.color}"/>`;
224
226
  }
@@ -365,8 +367,8 @@ function imageXml(node, namespaces) {
365
367
  function groupXml(node, registry, namespaces) {
366
368
  const attrs = [];
367
369
  if (node.transform) {
368
- const m2 = formatMatrix(node.transform);
369
- if (m2) attrs.push(`transform="${m2}"`);
370
+ const m = formatMatrix(node.transform);
371
+ if (m) attrs.push(`transform="${m}"`);
370
372
  }
371
373
  if (node.opacity != null && node.opacity !== 1) {
372
374
  attrs.push(`opacity="${trimNumber(node.opacity)}"`);
@@ -494,9 +496,9 @@ function computeBounds(nodes) {
494
496
  }
495
497
  const b2 = n.kind === "text" || n.kind === "image" ? { minX: n.x, minY: n.y, maxX: n.x + n.width, maxY: n.y + n.height } : pathBounds(n.path);
496
498
  const local = n.rotation ? rotateAboutCenter(ctm, b2, n.rotation) : ctm;
497
- for (const [x2, y2] of corners(b2)) {
498
- const px = local[0] * x2 + local[2] * y2 + local[4];
499
- const py = local[1] * x2 + local[3] * y2 + local[5];
499
+ for (const [x2, y] of corners(b2)) {
500
+ const px = local[0] * x2 + local[2] * y + local[4];
501
+ const py = local[1] * x2 + local[3] * y + local[5];
500
502
  if (px < minX) minX = px;
501
503
  if (py < minY) minY = py;
502
504
  if (px > maxX) maxX = px;
@@ -638,13 +640,13 @@ var toWorld = (p2, content) => ({
638
640
  x: p2.x * content.w,
639
641
  y: p2.y * content.h
640
642
  });
641
- function vertices(m2, content) {
642
- const stored = m2.data.points;
643
+ function vertices(m, content) {
644
+ const stored = m.data.points;
643
645
  if (stored && stored.length >= 2) return stored.map((p2) => toWorld(p2, content));
644
- const { x: x2, y: y2, width, height } = m2.pose;
646
+ const { x: x2, y, width, height } = m.pose;
645
647
  return [
646
- { x: x2, y: y2 },
647
- { x: x2 + width, y: y2 + height }
648
+ { x: x2, y },
649
+ { x: x2 + width, y: y + height }
648
650
  ];
649
651
  }
650
652
  function polyline(points) {
@@ -655,7 +657,7 @@ function polyline(points) {
655
657
  for (const p2 of points.slice(1)) b2.lineTo(p2.x, p2.y);
656
658
  return b2.build();
657
659
  }
658
- function markCommands(m2, content, style = {}) {
660
+ function markCommands(m, content, style = {}) {
659
661
  const color = style.color ?? MARK_COLOR;
660
662
  const stroke = {
661
663
  paint: { color },
@@ -664,19 +666,19 @@ function markCommands(m2, content, style = {}) {
664
666
  join: "round",
665
667
  ...style.stale ? { dash: [6, 4] } : {}
666
668
  };
667
- switch (m2.data.kind) {
669
+ switch (m.data.kind) {
668
670
  case "rect":
669
671
  return [
670
- { kind: "path", path: rectPath(m2.pose.x, m2.pose.y, m2.pose.width, m2.pose.height), stroke }
672
+ { kind: "path", path: rectPath(m.pose.x, m.pose.y, m.pose.width, m.pose.height), stroke }
671
673
  ];
672
674
  case "ellipse":
673
- return [{ kind: "path", path: ellipsePath(m2.pose), stroke }];
675
+ return [{ kind: "path", path: ellipsePath(m.pose), stroke }];
674
676
  case "line": {
675
- const [a, b2] = vertices(m2, content);
677
+ const [a, b2] = vertices(m, content);
676
678
  return [{ kind: "path", path: linePath(a, b2), stroke }];
677
679
  }
678
680
  case "arrow": {
679
- const [a, b2] = vertices(m2, content);
681
+ const [a, b2] = vertices(m, content);
680
682
  const marked = { ...stroke, markerEnd: "arrow" };
681
683
  const path = linePath(a, b2);
682
684
  return [
@@ -685,14 +687,14 @@ function markCommands(m2, content, style = {}) {
685
687
  ];
686
688
  }
687
689
  case "stroke":
688
- return [{ kind: "path", path: polyline(vertices(m2, content)), stroke }];
690
+ return [{ kind: "path", path: polyline(vertices(m, content)), stroke }];
689
691
  case "text": {
690
- const text = m2.data.title;
692
+ const text = m.data.title;
691
693
  if (!text) return [];
692
694
  return [
693
695
  textCommand(
694
- m2.pose.x,
695
- m2.pose.y,
696
+ m.pose.x,
697
+ m.pose.y,
696
698
  text,
697
699
  { fontSize: TEXT_SIZE },
698
700
  void 0,
@@ -764,7 +766,7 @@ function toSvgStroke(stroke) {
764
766
  ...stroke.miterLimit != null ? { miterLimit: stroke.miterLimit } : {}
765
767
  };
766
768
  }
767
- function toSvgNode(cmd, m2) {
769
+ function toSvgNode(cmd, m) {
768
770
  if (cmd.kind === "path") {
769
771
  return {
770
772
  kind: "path",
@@ -778,8 +780,8 @@ function toSvgNode(cmd, m2) {
778
780
  kind: "text",
779
781
  x: cmd.x,
780
782
  y: cmd.y,
781
- width: m2.pose.width,
782
- height: m2.pose.height,
783
+ width: m.pose.width,
784
+ height: m.pose.height,
783
785
  text: cmd.runs.map((run) => run.text).join(""),
784
786
  style: cmd.style,
785
787
  ...cmd.runs[0]?.fill ? { fill: cmd.runs[0].fill } : {}
@@ -787,8 +789,8 @@ function toSvgNode(cmd, m2) {
787
789
  }
788
790
  throw new Error(`[labkit] a mark drew a '${cmd.kind}' command, which has no vector form`);
789
791
  }
790
- function markSvgNodes(m2, content, style = {}) {
791
- return markCommands(m2, content, style).map((cmd) => toSvgNode(cmd, m2));
792
+ function markSvgNodes(m, content, style = {}) {
793
+ return markCommands(m, content, style).map((cmd) => toSvgNode(cmd, m));
792
794
  }
793
795
 
794
796
  // src/annotations/capture.ts
@@ -797,7 +799,7 @@ function capturePlan(base, format) {
797
799
  if (format === "svg") return "svg-document";
798
800
  return base?.kind === "svg" ? "svg-document" : "raster-stack";
799
801
  }
800
- function nestBaseSvg(markup, w2, h) {
802
+ function nestBaseSvg(markup, w3, h2) {
801
803
  const doc = new DOMParser().parseFromString(markup, "image/svg+xml");
802
804
  const root = doc.documentElement;
803
805
  if (doc.getElementsByTagName("parsererror").length > 0 || root.localName !== "svg") {
@@ -805,17 +807,17 @@ function nestBaseSvg(markup, w2, h) {
805
807
  }
806
808
  root.setAttribute("x", "0");
807
809
  root.setAttribute("y", "0");
808
- root.setAttribute("width", String(w2));
809
- root.setAttribute("height", String(h));
810
+ root.setAttribute("width", String(w3));
811
+ root.setAttribute("height", String(h2));
810
812
  return new XMLSerializer().serializeToString(root);
811
813
  }
812
- function embedRasterBase(base, w2, h, onWarn) {
814
+ function embedRasterBase(base, w3, h2, onWarn) {
813
815
  const href = base.kind === "canvas" ? base.canvas.toDataURL() : base.kind === "image" ? base.src : "";
814
816
  if (base.kind === "image" && !href.startsWith("data:")) {
815
817
  onWarn?.(`base image '${href}' is a reference, so this SVG is not self-contained`);
816
818
  }
817
- return serializeSvg([{ kind: "image", href, x: 0, y: 0, width: w2, height: h }], {
818
- viewBox: { x: 0, y: 0, width: w2, height: h }
819
+ return serializeSvg([{ kind: "image", href, x: 0, y: 0, width: w3, height: h2 }], {
820
+ viewBox: { x: 0, y: 0, width: w3, height: h2 }
819
821
  });
820
822
  }
821
823
  function markNodesOf(scene, draw) {
@@ -831,15 +833,15 @@ function markNodesOf(scene, draw) {
831
833
  }
832
834
  function composeCaptureSvg(args) {
833
835
  const { base, scene, draw, scale, onWarn } = args;
834
- const { w: w2, h } = draw.content;
835
- const baseXml = base === void 0 ? "" : base.kind === "svg" ? nestBaseSvg(base.markup, w2, h) : embedRasterBase(base, w2, h, onWarn);
836
+ const { w: w3, h: h2 } = draw.content;
837
+ const baseXml = base === void 0 ? "" : base.kind === "svg" ? nestBaseSvg(base.markup, w3, h2) : embedRasterBase(base, w3, h2, onWarn);
836
838
  const marksXml = serializeSvg(markNodesOf(scene, draw), {
837
- viewBox: { x: 0, y: 0, width: w2, height: h },
839
+ viewBox: { x: 0, y: 0, width: w3, height: h2 },
838
840
  onWarn
839
841
  });
840
- const outW = Math.max(1, Math.round(w2 * scale));
841
- const outH = Math.max(1, Math.round(h * scale));
842
- return `<svg xmlns="${SVG_NS}" width="${outW}" height="${outH}" viewBox="0 0 ${w2} ${h}">${baseXml}${marksXml}</svg>`;
842
+ const outW = Math.max(1, Math.round(w3 * scale));
843
+ const outH = Math.max(1, Math.round(h2 * scale));
844
+ return `<svg xmlns="${SVG_NS}" width="${outW}" height="${outH}" viewBox="0 0 ${w3} ${h2}">${baseXml}${marksXml}</svg>`;
843
845
  }
844
846
  function context2d(width, height) {
845
847
  const canvas = document.createElement("canvas");
@@ -1263,11 +1265,11 @@ function annotationsFromJSON(raw, targets, rest = {}) {
1263
1265
  }
1264
1266
 
1265
1267
  // src/annotations/view.ts
1266
- function toWeaselView(v) {
1267
- return { x: v.pan.x, y: v.pan.y, scale: { x: v.zoom, y: v.zoom } };
1268
+ function toWeaselView(v2) {
1269
+ return { x: v2.pan.x, y: v2.pan.y, scale: { x: v2.zoom, y: v2.zoom } };
1268
1270
  }
1269
- function fromWeaselView(v) {
1270
- return { zoom: v.scale.x, pan: { x: v.x, y: v.y } };
1271
+ function fromWeaselView(v2) {
1272
+ return { zoom: v2.scale.x, pan: { x: v2.x, y: v2.y } };
1271
1273
  }
1272
1274
  function fitView(content, pane) {
1273
1275
  const zoom = content.w > 0 && content.h > 0 ? Math.min(pane.w / content.w, pane.h / content.h) : 1;
@@ -1312,26 +1314,27 @@ function AnnotationOverlay({
1312
1314
  const configRef = useRef(config);
1313
1315
  configRef.current = config;
1314
1316
  const id = target.id;
1317
+ const tileId = useTileId(id);
1315
1318
  const lastTile = useRef(null);
1316
1319
  useEffect(() => {
1317
1320
  const el = target.ref.current ?? null;
1318
1321
  if (el === lastTile.current) return;
1319
1322
  lastTile.current = el;
1320
- surface?.registerTile(id, el);
1323
+ surface?.registerTile(tileId, el);
1321
1324
  });
1322
1325
  useEffect(
1323
1326
  () => () => {
1324
1327
  lastTile.current = null;
1325
- surface?.registerTile(id, null);
1328
+ surface?.registerTile(tileId, null);
1326
1329
  },
1327
- [surface, id]
1330
+ [surface, tileId]
1328
1331
  );
1329
1332
  useEffect(
1330
- () => surface?.registerPainter(id, (next) => {
1333
+ () => surface?.registerPainter(tileId, (next) => {
1331
1334
  setRect((prev) => sameRect(prev, next) ? prev : next);
1332
1335
  sceneCanvas.current?.requestRedraw();
1333
1336
  }),
1334
- [surface, id]
1337
+ [surface, tileId]
1335
1338
  );
1336
1339
  const container = surface?.getContainer() ?? null;
1337
1340
  if (!container || !rect) return null;
@@ -1488,6 +1491,11 @@ var NumberNode = class extends BaseNode {
1488
1491
  step(step) {
1489
1492
  return this.ann({ step });
1490
1493
  }
1494
+ /** A display suffix for the value — `'px'`, `'ms'`, `'°'`. Presentation only:
1495
+ * it is never parsed, and the stored value stays a plain number. */
1496
+ suffix(suffix) {
1497
+ return this.ann({ suffix });
1498
+ }
1491
1499
  /** Force a slider even without both bounds. */
1492
1500
  slider() {
1493
1501
  return this.ann({ control: "slider" });
@@ -1602,9 +1610,9 @@ function applyRules(seed, ctx, rules) {
1602
1610
  for (const rule of rules) {
1603
1611
  const patch = rule({ ...ctx, leaf: acc });
1604
1612
  if (patch === null) continue;
1605
- for (const [k2, v] of Object.entries(patch)) {
1606
- if (v !== void 0 && acc[k2] === void 0) {
1607
- acc[k2] = v;
1613
+ for (const [k2, v2] of Object.entries(patch)) {
1614
+ if (v2 !== void 0 && acc[k2] === void 0) {
1615
+ acc[k2] = v2;
1608
1616
  }
1609
1617
  }
1610
1618
  }
@@ -1614,7 +1622,7 @@ function applyRules(seed, ctx, rules) {
1614
1622
  // src/config/resolve.ts
1615
1623
  function defined(annotations) {
1616
1624
  const out = {};
1617
- for (const [k2, v] of Object.entries(annotations)) if (v !== void 0) out[k2] = v;
1625
+ for (const [k2, v2] of Object.entries(annotations)) if (v2 !== void 0) out[k2] = v2;
1618
1626
  return out;
1619
1627
  }
1620
1628
  function resolveConfigSchema(schema, rules = []) {
@@ -1727,7 +1735,7 @@ function validateConfigSchema(fields) {
1727
1735
  return { valid: errors.length === 0, errors };
1728
1736
  }
1729
1737
 
1730
- // ../theme/dist/chunk-O32P3G22.js
1738
+ // ../theme/dist/chunk-CXVFAOTM.js
1731
1739
  var THEME_SOURCES = {
1732
1740
  "weasel": {
1733
1741
  "primitives": {
@@ -1946,6 +1954,31 @@ var THEME_SOURCES = {
1946
1954
  "value": "24px",
1947
1955
  "description": "Height of an interactive control \u2014 button, input, select, segmented control. Distinct from tb-height, which sizes the strip a row of them sits in."
1948
1956
  },
1957
+ "slider-track-h": {
1958
+ "type": "dimension",
1959
+ "value": "4px",
1960
+ "description": "Height of a linear range track. The kit's single-thumb ranges all wear this; Slider's default 24px canvas track does not."
1961
+ },
1962
+ "slider-thumb-size": {
1963
+ "type": "dimension",
1964
+ "value": "8px",
1965
+ "description": "Diameter of a linear range thumb."
1966
+ },
1967
+ "slider-track-tint": {
1968
+ "type": "dimension",
1969
+ "value": "18%",
1970
+ "description": "Accent proportion in a range track. Mixed into a real property, never into another custom property."
1971
+ },
1972
+ "slider-thumb-tint": {
1973
+ "type": "dimension",
1974
+ "value": "70%",
1975
+ "description": "Accent proportion in a range thumb."
1976
+ },
1977
+ "field-pad-x": {
1978
+ "type": "dimension",
1979
+ "value": "8px",
1980
+ "description": "Horizontal padding inside a boxed field. Pairs with --wzl-field-h, which is a container override and has no :root default."
1981
+ },
1949
1982
  "glass-blur": {
1950
1983
  "type": "dimension",
1951
1984
  "value": "3px",
@@ -2265,21 +2298,21 @@ var weaselTheme = {
2265
2298
  modes: THEME_SOURCES.weasel.modes
2266
2299
  };
2267
2300
  function hexToRgba(hex, alpha) {
2268
- const m2 = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
2269
- if (!m2) throw new Error(`Expected a hex color, got "${hex}"`);
2270
- const body = m2[1];
2301
+ const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
2302
+ if (!m) throw new Error(`Expected a hex color, got "${hex}"`);
2303
+ const body = m[1];
2271
2304
  const full = body.length === 3 ? body.replace(/./g, (c2) => c2 + c2) : body;
2272
2305
  const r2 = Number.parseInt(full.slice(0, 2), 16);
2273
- const g2 = Number.parseInt(full.slice(2, 4), 16);
2306
+ const g = Number.parseInt(full.slice(2, 4), 16);
2274
2307
  const b2 = Number.parseInt(full.slice(4, 6), 16);
2275
- return `rgba(${r2}, ${g2}, ${b2}, ${alpha})`;
2308
+ return `rgba(${r2}, ${g}, ${b2}, ${alpha})`;
2276
2309
  }
2277
2310
  var REF = /^\{([^}]+)\}$/;
2278
2311
  function refTarget(value) {
2279
2312
  if (typeof value !== "string") return null;
2280
- const m2 = REF.exec(value.trim());
2281
- if (!m2) return null;
2282
- const path = m2[1];
2313
+ const m = REF.exec(value.trim());
2314
+ if (!m) return null;
2315
+ const path = m[1];
2283
2316
  const dot = path.indexOf(".");
2284
2317
  return dot === -1 ? path : path.slice(dot + 1);
2285
2318
  }
@@ -2412,15 +2445,15 @@ function useThemeOptional() {
2412
2445
 
2413
2446
  // ../theme/dist/index.js
2414
2447
  var ALPHA_EXT = "com.weasel.alpha";
2415
- var isToken = (v) => typeof v === "object" && v !== null && "$value" in v;
2448
+ var isToken = (v2) => typeof v2 === "object" && v2 !== null && "$value" in v2;
2416
2449
  function flattenTokens(doc) {
2417
2450
  const out = {};
2418
2451
  for (const [groupName, group] of Object.entries(doc)) {
2419
2452
  if (groupName.startsWith("$")) continue;
2420
2453
  if (typeof group !== "object" || group === null) continue;
2421
- const g2 = group;
2422
- const groupType = g2.$type;
2423
- for (const [leaf, node] of Object.entries(g2)) {
2454
+ const g = group;
2455
+ const groupType = g.$type;
2456
+ for (const [leaf, node] of Object.entries(g)) {
2424
2457
  if (leaf.startsWith("$")) continue;
2425
2458
  if (!isToken(node)) continue;
2426
2459
  const type = node.$type ?? groupType;
@@ -2530,11 +2563,109 @@ var interstellarTheme = loadDTCG(interstellar_tokens_default);
2530
2563
  var DEFAULT_VIEW = { zoom: 1, pan: { x: 0, y: 0 } };
2531
2564
  function as2DView(view) {
2532
2565
  if (typeof view !== "object" || view === null) return null;
2533
- const v = view;
2534
- if (typeof v.zoom !== "number") return null;
2535
- if (typeof v.pan !== "object" || v.pan === null) return null;
2536
- if (typeof v.pan.x !== "number" || typeof v.pan.y !== "number") return null;
2537
- return { zoom: v.zoom, pan: { x: v.pan.x, y: v.pan.y } };
2566
+ const v2 = view;
2567
+ if (typeof v2.zoom !== "number") return null;
2568
+ if (typeof v2.pan !== "object" || v2.pan === null) return null;
2569
+ if (typeof v2.pan.x !== "number" || typeof v2.pan.y !== "number") return null;
2570
+ return { zoom: v2.zoom, pan: { x: v2.pan.x, y: v2.pan.y } };
2571
+ }
2572
+ var ZONE_ID = asNodeId("lk-trial-body");
2573
+ var SIDEBAR_ID = asNodeId("lk-trial-sidebar");
2574
+ var CONTENT_ID = asNodeId("lk-trial-content");
2575
+ var STRATEGIES = { strip: stripStrategy };
2576
+ function TrialBody({
2577
+ sidebar,
2578
+ children,
2579
+ contentClassName,
2580
+ width,
2581
+ defaultWidth = 320,
2582
+ minWidth = 140,
2583
+ maxWidth = 720,
2584
+ contentMinWidth = 160,
2585
+ onWidthChange,
2586
+ viewport
2587
+ }) {
2588
+ const storeRef = useRef(null);
2589
+ if (storeRef.current === null) {
2590
+ const store2 = new Store();
2591
+ store2.registerNode(
2592
+ createNode({
2593
+ kind: "zone",
2594
+ id: ZONE_ID,
2595
+ container: { strategyId: "strip", config: { axis: "x", resizable: true } }
2596
+ })
2597
+ );
2598
+ store2.registerNode(
2599
+ createNode({
2600
+ kind: "sidebar",
2601
+ id: SIDEBAR_ID,
2602
+ parentId: ZONE_ID,
2603
+ meta: { title: "Sidebar" },
2604
+ placement: { size: { w: width ?? defaultWidth } },
2605
+ hints: { minSize: { w: minWidth, h: 0 }, maxSize: { w: maxWidth, h: 0 } }
2606
+ })
2607
+ );
2608
+ store2.registerNode(
2609
+ createNode({
2610
+ kind: "content",
2611
+ id: CONTENT_ID,
2612
+ parentId: ZONE_ID,
2613
+ meta: { title: "Content" },
2614
+ hints: { minSize: { w: contentMinWidth, h: 0 } }
2615
+ })
2616
+ );
2617
+ store2.showNode(SIDEBAR_ID);
2618
+ store2.showNode(CONTENT_ID);
2619
+ storeRef.current = store2;
2620
+ }
2621
+ const store = storeRef.current;
2622
+ const widthRef = useRef(width ?? defaultWidth);
2623
+ useEffect(() => {
2624
+ store.setHints(SIDEBAR_ID, {
2625
+ minSize: { w: minWidth, h: 0 },
2626
+ maxSize: { w: maxWidth, h: 0 }
2627
+ });
2628
+ store.setHints(CONTENT_ID, { minSize: { w: contentMinWidth, h: 0 } });
2629
+ }, [store, minWidth, maxWidth, contentMinWidth]);
2630
+ useEffect(() => {
2631
+ if (width === void 0 || width === widthRef.current) return;
2632
+ widthRef.current = width;
2633
+ store.patchPlacement(SIDEBAR_ID, { size: { w: width } });
2634
+ }, [store, width]);
2635
+ const onWidthChangeRef = useRef(onWidthChange);
2636
+ onWidthChangeRef.current = onWidthChange;
2637
+ useEffect(
2638
+ () => store.events.on("node.placementChanged", () => {
2639
+ const next = store.getPlacement(SIDEBAR_ID)?.size?.w;
2640
+ if (typeof next !== "number" || next === widthRef.current) return;
2641
+ widthRef.current = next;
2642
+ onWidthChangeRef.current?.(next);
2643
+ }),
2644
+ [store]
2645
+ );
2646
+ const chrome = useMemo(
2647
+ () => ({
2648
+ sidebar: () => /* @__PURE__ */ jsx("div", { className: "lk-trial__sidebar", children: sidebar }),
2649
+ content: () => /* @__PURE__ */ jsx(
2650
+ "div",
2651
+ {
2652
+ className: contentClassName ? `lk-trial__content ${contentClassName}` : "lk-trial__content",
2653
+ children
2654
+ }
2655
+ )
2656
+ }),
2657
+ [sidebar, children, contentClassName]
2658
+ );
2659
+ return /* @__PURE__ */ jsx(Provider, { store, children: /* @__PURE__ */ jsx(StrategyRegistryProvider, { strategies: STRATEGIES, children: /* @__PURE__ */ jsx(
2660
+ Container,
2661
+ {
2662
+ parentId: ZONE_ID,
2663
+ chrome,
2664
+ className: "lk-trial__panes windease-zone",
2665
+ affordances: true,
2666
+ viewport
2667
+ }
2668
+ ) }) });
2538
2669
  }
2539
2670
  var TrialDragContext = createContext(null);
2540
2671
  function useTrialDrag() {
@@ -2640,6 +2771,7 @@ function TrialChrome({
2640
2771
  const storeCtx = useContext(LabStoreContext);
2641
2772
  if (!storeCtx) throw new Error("[labkit] TrialChrome requires <LabStoreProvider>");
2642
2773
  const updateTrialView = useStore(storeCtx.store, (s) => s.updateTrialView);
2774
+ const updateTrialSidebarWidth = useStore(storeCtx.store, (s) => s.updateTrialSidebarWidth);
2643
2775
  const updateTrialConfig = useStore(storeCtx.store, (s) => s.updateTrialConfig);
2644
2776
  const updateTrialState = useStore(storeCtx.store, (s) => s.updateTrialState);
2645
2777
  const undockPanelAction = useStore(storeCtx.store, (s) => s.undockPanel);
@@ -2753,21 +2885,29 @@ function TrialChrome({
2753
2885
  /* @__PURE__ */ jsx("div", { className: "lk-trial__toolbar", children: /* @__PURE__ */ jsx(ToolbarRegion, { contributions: inRegion("toolbar"), ctx }) }),
2754
2886
  /* @__PURE__ */ jsxs("div", { className: "lk-trial__body", children: [
2755
2887
  /* @__PURE__ */ jsx(PaletteRegion, { contributions: inRegion("palette"), ctx }),
2756
- /* @__PURE__ */ jsxs("div", { className: "lk-trial__sidebar", children: [
2757
- /* @__PURE__ */ jsx(SidebarRegion, { contributions: inRegion("sidebar"), ctx }),
2758
- /* @__PURE__ */ jsx(
2759
- UndockedSections,
2760
- {
2761
- trialId,
2762
- sections: inRegion("sidebar").filter((c2) => undockedIds.includes(c2.id)),
2763
- onDock: ctx.dockPanel
2764
- }
2765
- )
2766
- ] }),
2767
- /* @__PURE__ */ jsxs("div", { className: `lk-trial__content${instrument.canvas ? " lk-trial__content--flush" : ""}`, children: [
2768
- children,
2769
- /* @__PURE__ */ jsx(ViewportRegion, { contributions: inRegion("viewport"), ctx })
2770
- ] })
2888
+ /* @__PURE__ */ jsxs(
2889
+ TrialBody,
2890
+ {
2891
+ width: record.sidebarWidth,
2892
+ onWidthChange: (w3) => updateTrialSidebarWidth(trialId, w3),
2893
+ contentClassName: instrument.canvas ? "lk-trial__content--flush" : void 0,
2894
+ sidebar: /* @__PURE__ */ jsxs(Fragment, { children: [
2895
+ /* @__PURE__ */ jsx(SidebarRegion, { contributions: inRegion("sidebar"), ctx }),
2896
+ /* @__PURE__ */ jsx(
2897
+ UndockedSections,
2898
+ {
2899
+ trialId,
2900
+ sections: inRegion("sidebar").filter((c2) => undockedIds.includes(c2.id)),
2901
+ onDock: ctx.dockPanel
2902
+ }
2903
+ )
2904
+ ] }),
2905
+ children: [
2906
+ children,
2907
+ /* @__PURE__ */ jsx(ViewportRegion, { contributions: inRegion("viewport"), ctx })
2908
+ ]
2909
+ }
2910
+ )
2771
2911
  ] }),
2772
2912
  /* @__PURE__ */ jsxs("div", { className: "lk-trial__status", children: [
2773
2913
  job ? /* @__PURE__ */ jsx(JobProgress, { job }) : null,
@@ -2781,7 +2921,7 @@ function Trial({ id, chrome, suppress }) {
2781
2921
  const lab = useLabContext();
2782
2922
  const storeCtx = useContext(LabStoreContext);
2783
2923
  if (!storeCtx) throw new Error("[labkit] <Trial> requires <LabStoreProvider>");
2784
- const record = useStore(storeCtx.store, (s) => s.trials.find((w2) => w2.id === id));
2924
+ const record = useStore(storeCtx.store, (s) => s.trials.find((w3) => w3.id === id));
2785
2925
  if (!record) {
2786
2926
  return /* @__PURE__ */ jsxs("div", { className: "lk-trial lk-trial--unknown", children: [
2787
2927
  "Trial not found: ",
@@ -2833,12 +2973,12 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
2833
2973
  const maxDepth = undoCap?.maxDepth ?? 50;
2834
2974
  const snapshotIfNeeded = (event) => {
2835
2975
  if (!undoCap || !undoEvents.has(event)) return;
2836
- const current = store.getState().trials.find((w2) => w2.id === record.id);
2976
+ const current = store.getState().trials.find((w3) => w3.id === record.id);
2837
2977
  if (!current) return;
2838
2978
  const snap = structuredClone(current.state);
2839
2979
  updateTrialUndoStack(record.id, (prev) => pushSnapshot(prev, snap, maxDepth));
2840
2980
  };
2841
- const setView = (v) => updateTrialView(record.id, v);
2981
+ const setView = (v2) => updateTrialView(record.id, v2);
2842
2982
  const placeView = (size) => {
2843
2983
  const declared = instrument.canvas?.initialView;
2844
2984
  if (typeof declared !== "function") return;
@@ -2978,7 +3118,7 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
2978
3118
  if (declared == null) return null;
2979
3119
  return resolveLoupe(typeof declared === "function" ? declared(record.config) : declared);
2980
3120
  }, [instrument.loupe, record.config]);
2981
- const loupeBindings = loupeCap ? { on: loupeOn, toggle: () => setLoupeOn((v) => !v) } : void 0;
3121
+ const loupeBindings = loupeCap ? { on: loupeOn, toggle: () => setLoupeOn((v2) => !v2) } : void 0;
2982
3122
  const canvasLayers = useMemo(() => {
2983
3123
  if (!instrument.canvas) return [];
2984
3124
  const baseLayers = instrument.canvas.layers;
@@ -3185,7 +3325,7 @@ function addTrial(trials, instruments, instrumentName) {
3185
3325
  return [...trials, record];
3186
3326
  }
3187
3327
  function cloneTrial(trials, id) {
3188
- const sourceIdx = trials.findIndex((w2) => w2.id === id);
3328
+ const sourceIdx = trials.findIndex((w3) => w3.id === id);
3189
3329
  const source = trials[sourceIdx];
3190
3330
  if (!source) return trials;
3191
3331
  const clone = {
@@ -3200,11 +3340,11 @@ function cloneTrial(trials, id) {
3200
3340
  }
3201
3341
  function closeTrial(trials, id) {
3202
3342
  if (trials.length <= 1) return trials;
3203
- const next = trials.filter((w2) => w2.id !== id);
3343
+ const next = trials.filter((w3) => w3.id !== id);
3204
3344
  return next.length === trials.length ? trials : next;
3205
3345
  }
3206
3346
  function resetTrial(trials, id, instruments) {
3207
- const idx = trials.findIndex((w2) => w2.id === id);
3347
+ const idx = trials.findIndex((w3) => w3.id === id);
3208
3348
  const current = trials[idx];
3209
3349
  if (!current) return trials;
3210
3350
  const instrument = findInstrument(instruments, current.instrumentName);
@@ -3219,17 +3359,22 @@ function resetTrial(trials, id, instruments) {
3219
3359
  return [...trials.slice(0, idx), reset, ...trials.slice(idx + 1)];
3220
3360
  }
3221
3361
  function reorderTrials(trials, ids) {
3222
- const byId = new Map(trials.map((w2) => [w2.id, w2]));
3223
- const named = ids.map((id) => byId.get(id)).filter((w2) => w2 !== void 0);
3224
- const seen = new Set(named.map((w2) => w2.id));
3225
- const rest = trials.filter((w2) => !seen.has(w2.id));
3362
+ const byId = new Map(trials.map((w3) => [w3.id, w3]));
3363
+ const named = ids.map((id) => byId.get(id)).filter((w3) => w3 !== void 0);
3364
+ const seen = new Set(named.map((w3) => w3.id));
3365
+ const rest = trials.filter((w3) => !seen.has(w3.id));
3226
3366
  return [...named, ...rest];
3227
3367
  }
3228
3368
  var MODES = [
3229
- { value: "auto", label: "Auto" },
3230
- { value: "light", label: "Light" },
3231
- { value: "dark", label: "Dark" }
3369
+ { value: "auto", label: "Auto", glyph: /* @__PURE__ */ jsx(E, { size: 14 }) },
3370
+ { value: "light", label: "Light", glyph: /* @__PURE__ */ jsx(w, { size: 14 }) },
3371
+ { value: "dark", label: "Dark", glyph: /* @__PURE__ */ jsx(T, { size: 14 }) }
3232
3372
  ];
3373
+ var MODE_ITEMS = MODES.map(({ value, label, glyph }) => ({
3374
+ value,
3375
+ label: glyph,
3376
+ ariaLabel: label
3377
+ }));
3233
3378
  function LabHeader() {
3234
3379
  const lab = useLabContext();
3235
3380
  const only = lab.instruments.length === 1 ? lab.instruments[0] : null;
@@ -3263,11 +3408,13 @@ function LabHeader() {
3263
3408
  )
3264
3409
  ),
3265
3410
  /* @__PURE__ */ jsx(
3266
- r2,
3411
+ r3,
3267
3412
  {
3268
3413
  className: "lk-lab-header__mode",
3269
3414
  ariaLabel: "Color mode",
3270
- items: MODES,
3415
+ size: "sm",
3416
+ variant: "flat",
3417
+ items: MODE_ITEMS,
3271
3418
  value: lab.mode,
3272
3419
  onChange: (next) => {
3273
3420
  if (next) lab.setMode(next);
@@ -3324,8 +3471,8 @@ function LabShell({ title, children, header, footer, mode = "auto" }) {
3324
3471
  ] });
3325
3472
  return outer ? shell : /* @__PURE__ */ jsx(ThemeProvider, { theme: interstellarTheme, mode: resolved, children: shell });
3326
3473
  }
3327
- var ZONE_ID = asNodeId("lk-workspace");
3328
- var STRATEGIES = { grid: gridStrategy, floating: floatingStrategy };
3474
+ var ZONE_ID2 = asNodeId("lk-workspace");
3475
+ var STRATEGIES2 = { grid: gridStrategy, floating: floatingStrategy };
3329
3476
  var FLOAT_ZONE_ID = asNodeId("lk-workspace-floating");
3330
3477
  var PANEL_KIND = "panel";
3331
3478
  var KIND = "trial";
@@ -3379,7 +3526,7 @@ function Workspace({
3379
3526
  store2.registerNode(
3380
3527
  createNode({
3381
3528
  kind: "zone",
3382
- id: ZONE_ID,
3529
+ id: ZONE_ID2,
3383
3530
  container: { strategyId: "grid", config: { resizable, gap, padding } }
3384
3531
  })
3385
3532
  );
@@ -3395,10 +3542,10 @@ function Workspace({
3395
3542
  const store = storeRef.current;
3396
3543
  const surface = useSurfaceOptional();
3397
3544
  useLayoutEffect(() => {
3398
- store.updateContainerConfig(ZONE_ID, { resizable, gap, padding });
3545
+ store.updateContainerConfig(ZONE_ID2, { resizable, gap, padding });
3399
3546
  }, [store, resizable, gap, padding]);
3400
3547
  useLayoutEffect(() => {
3401
- const present = new Set(store.getContainerView(ZONE_ID)?.childOrder ?? []);
3548
+ const present = new Set(store.getContainerView(ZONE_ID2)?.childOrder ?? []);
3402
3549
  const wanted = new Set(nodeIds);
3403
3550
  for (const id of present) {
3404
3551
  if (!wanted.has(id)) store.unregisterNode(id);
@@ -3406,12 +3553,12 @@ function Workspace({
3406
3553
  for (const id of nodeIds) {
3407
3554
  if (present.has(id)) continue;
3408
3555
  const kind = String(id).startsWith("lk-panel-") ? PANEL_KIND : KIND;
3409
- store.registerNode(createNode({ kind, id, parentId: ZONE_ID, focus: true }));
3556
+ store.registerNode(createNode({ kind, id, parentId: ZONE_ID2, focus: true }));
3410
3557
  store.showNode(id);
3411
3558
  const saved = layoutRef.current?.[id];
3412
3559
  if (saved) store.patchPlacement(id, saved);
3413
3560
  }
3414
- store.setChildOrder(ZONE_ID, [...nodeIds]);
3561
+ store.setChildOrder(ZONE_ID2, [...nodeIds]);
3415
3562
  }, [store, nodeIds]);
3416
3563
  useEffect(() => {
3417
3564
  if (!surface) return;
@@ -3422,7 +3569,7 @@ function Workspace({
3422
3569
  if (!onLayoutChange) return;
3423
3570
  return store.events.on("node.placementChanged", () => {
3424
3571
  const next = {};
3425
- for (const child of store.getChildren(ZONE_ID)) {
3572
+ for (const child of store.getChildren(ZONE_ID2)) {
3426
3573
  const extent = extentOf(store, child.id);
3427
3574
  if (extent) next[child.id] = extent;
3428
3575
  }
@@ -3458,7 +3605,7 @@ function Workspace({
3458
3605
  const grid = /* @__PURE__ */ jsx(
3459
3606
  Container,
3460
3607
  {
3461
- parentId: ZONE_ID,
3608
+ parentId: ZONE_ID2,
3462
3609
  chrome,
3463
3610
  className: "lk-workspace windease-zone",
3464
3611
  affordances: resizable,
@@ -3466,7 +3613,7 @@ function Workspace({
3466
3613
  onChildOrderChange: reorderable ? commitOrder : void 0
3467
3614
  }
3468
3615
  );
3469
- return /* @__PURE__ */ jsx(Provider, { store, children: /* @__PURE__ */ jsx(StrategyRegistryProvider, { strategies: STRATEGIES, children: floatingLayer ? (
3616
+ return /* @__PURE__ */ jsx(Provider, { store, children: /* @__PURE__ */ jsx(StrategyRegistryProvider, { strategies: STRATEGIES2, children: floatingLayer ? (
3470
3617
  // The floating layer insets against the workspace, so it needs a
3471
3618
  // positioned box around both. Without one it insets against the page
3472
3619
  // and lands over the lab header.
@@ -3549,12 +3696,12 @@ function Lab({
3549
3696
  const onFrame = useCallback((frame) => {
3550
3697
  const c2 = ownCanvasRef.current;
3551
3698
  if (!c2) return;
3552
- const w2 = Math.round(frame.size.width * frame.dpr);
3553
- const h = Math.round(frame.size.height * frame.dpr);
3554
- if (bufferRef.current.w === w2 && bufferRef.current.h === h) return;
3555
- bufferRef.current = { w: w2, h };
3556
- c2.width = w2;
3557
- c2.height = h;
3699
+ const w3 = Math.round(frame.size.width * frame.dpr);
3700
+ const h2 = Math.round(frame.size.height * frame.dpr);
3701
+ if (bufferRef.current.w === w3 && bufferRef.current.h === h2) return;
3702
+ bufferRef.current = { w: w3, h: h2 };
3703
+ c2.width = w3;
3704
+ c2.height = h2;
3558
3705
  c2.style.width = `${frame.size.width}px`;
3559
3706
  c2.style.height = `${frame.size.height}px`;
3560
3707
  surfaceRef.current?.invalidateAll();
@@ -3579,8 +3726,8 @@ function Lab({
3579
3726
  }, [mode, store]);
3580
3727
  const contextValue = useMemo(() => {
3581
3728
  const replaceTrials = (next) => {
3582
- const currentById = new Map(store.getState().trials.map((w2) => [w2.id, w2]));
3583
- const merged = next.map((w2) => currentById.get(w2.id) ?? w2);
3729
+ const currentById = new Map(store.getState().trials.map((w3) => [w3.id, w3]));
3730
+ const merged = next.map((w3) => currentById.get(w3.id) ?? w3);
3584
3731
  store.setState({ trials: merged });
3585
3732
  store.getState().setMode(store.getState().mode);
3586
3733
  };
@@ -3604,11 +3751,11 @@ function Lab({
3604
3751
  },
3605
3752
  resetTrial: (id) => {
3606
3753
  const next = resetTrial(store.getState().trials, id, instruments);
3607
- const record = next.find((w2) => w2.id === id);
3754
+ const record = next.find((w3) => w3.id === id);
3608
3755
  if (!record) return;
3609
3756
  store.setState((s) => ({
3610
3757
  trials: s.trials.map(
3611
- (w2) => w2.id === id ? { ...w2, config: record.config, state: record.state, view: record.view } : w2
3758
+ (w3) => w3.id === id ? { ...w3, config: record.config, state: record.state, view: record.view } : w3
3612
3759
  )
3613
3760
  }));
3614
3761
  store.getState().updateTrialView(id, record.view);
@@ -3625,8 +3772,8 @@ function Lab({
3625
3772
  store.getState().deleteSnapshot(snapshotId);
3626
3773
  },
3627
3774
  mode: modeValue,
3628
- setMode: (m2) => {
3629
- store.getState().setMode(m2);
3775
+ setMode: (m) => {
3776
+ store.getState().setMode(m);
3630
3777
  },
3631
3778
  configRules,
3632
3779
  controls
@@ -3676,13 +3823,13 @@ function Lab({
3676
3823
  Workspace,
3677
3824
  {
3678
3825
  panels: workspacePanels,
3679
- ids: trials.map((w2) => w2.id),
3826
+ ids: trials.map((w3) => w3.id),
3680
3827
  resizable: true,
3681
3828
  reorderable: true,
3682
3829
  onReorder: (ids) => contextValue.reorderTrials(ids),
3683
3830
  layout,
3684
3831
  onLayoutChange: (next) => store.getState().setLayout(next),
3685
- children: trials.map((w2) => /* @__PURE__ */ jsx(Trial, { id: w2.id, chrome, suppress }, w2.id))
3832
+ children: trials.map((w3) => /* @__PURE__ */ jsx(Trial, { id: w3.id, chrome, suppress }, w3.id))
3686
3833
  }
3687
3834
  )
3688
3835
  ]
@@ -3694,6 +3841,6 @@ function Lab({
3694
3841
  ) }) });
3695
3842
  }
3696
3843
 
3697
- export { AnnotationOverlay, AnnotationTargets, DEFAULT_VIEW, Lab, LabContext, LabPalette, LabShell, Trial, TrialChrome, Workspace, addTrial, annotationsFromJSON, applyRules, as2DView, builtinRules, capturePlan, captureTarget, cloneTrial, closeTrial, composeCaptureSvg, createAnnotationScene, createAnnotationStore, createMarkDrawOne, defineInstrument, f, fitView, fracContains, fracIntersects, fracToWorld, fromWeaselView, interstellarTheme, isStale, markCommands, markSvgNodes, reorderTrials, resetTrial, resolveConfigSchema, resolveMarkStyle, roundFrac, seenFrom, titleCase, toWeaselView, useConfigSchema, useLabContext, validateConfigSchema, worldToFrac };
3844
+ export { AnnotationOverlay, AnnotationTargets, DEFAULT_VIEW, Lab, LabContext, LabPalette, LabShell, Trial, TrialBody, TrialChrome, Workspace, addTrial, annotationsFromJSON, applyRules, as2DView, builtinRules, capturePlan, captureTarget, cloneTrial, closeTrial, composeCaptureSvg, createAnnotationScene, createAnnotationStore, createMarkDrawOne, defineInstrument, f, fitView, fracContains, fracIntersects, fracToWorld, fromWeaselView, interstellarTheme, isStale, markCommands, markSvgNodes, reorderTrials, resetTrial, resolveConfigSchema, resolveMarkStyle, roundFrac, seenFrom, titleCase, toWeaselView, useConfigSchema, useLabContext, validateConfigSchema, worldToFrac };
3698
3845
  //# sourceMappingURL=index.js.map
3699
3846
  //# sourceMappingURL=index.js.map