@weasel-js/labkit 1.4.0-pre.0 → 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 (103) hide show
  1. package/README.md +226 -55
  2. package/dist/_dts/{CanvasStackContext-DtAn_3O-.d.ts → CanvasStackContext-D6M0o8cQ.d.ts} +1 -1
  3. package/dist/_dts/{frac-Cp3NivlC.d.ts → frac-QS7XzvpG.d.ts} +11 -4
  4. package/dist/_dts/{index-BDVzvRzQ.d.ts → index-B2X21G1c.d.ts} +8 -2
  5. package/dist/_dts/{types-D6s4b7if.d.ts → types-C1Zw7s5P.d.ts} +1 -0
  6. package/dist/_dts/{types-DYMaEvM5.d.ts → types-ChVJJHvk.d.ts} +3 -0
  7. package/dist/_dts/{useTrialState-gmMvZqPc.d.ts → useTrialState-Cdm13fvl.d.ts} +2 -1
  8. package/dist/canvas/index.d.ts +75 -9
  9. package/dist/canvas/index.js +2 -2
  10. package/dist/chrome/index.d.ts +4 -4
  11. package/dist/chrome/index.js +5 -5
  12. package/dist/chunk-2YILQ7Y5.js +33 -0
  13. package/dist/chunk-2YILQ7Y5.js.map +1 -0
  14. package/dist/{chunk-3P5TTTA7.js → chunk-4AMN4MMB.js} +5 -5
  15. package/dist/{chunk-3P5TTTA7.js.map → chunk-4AMN4MMB.js.map} +1 -1
  16. package/dist/{chunk-ROQDRXKW.js → chunk-7VW3S44V.js} +621 -51
  17. package/dist/chunk-7VW3S44V.js.map +1 -0
  18. package/dist/{chunk-4EY67BKJ.js → chunk-B5VWJBWM.js} +12 -6
  19. package/dist/chunk-B5VWJBWM.js.map +1 -0
  20. package/dist/{chunk-HEORBLVT.js → chunk-L5WBGUVR.js} +1664 -1628
  21. package/dist/chunk-L5WBGUVR.js.map +1 -0
  22. package/dist/{chunk-XJ6N32QP.js → chunk-LJSIUFYD.js} +14 -30
  23. package/dist/chunk-LJSIUFYD.js.map +1 -0
  24. package/dist/{chunk-6JMU56CU.js → chunk-QR5V3AFG.js} +6 -4
  25. package/dist/chunk-QR5V3AFG.js.map +1 -0
  26. package/dist/{chunk-LITE5YFB.js → chunk-UD7TEHZT.js} +15 -15
  27. package/dist/chunk-UD7TEHZT.js.map +1 -0
  28. package/dist/{chunk-3ZELRRBV.js → chunk-VHFQKAUL.js} +86 -6
  29. package/dist/chunk-VHFQKAUL.js.map +1 -0
  30. package/dist/{chunk-YQJNT4XQ.js → chunk-W3KSUXXR.js} +16 -13
  31. package/dist/chunk-W3KSUXXR.js.map +1 -0
  32. package/dist/{chunk-JDDIZSLL.js → chunk-XCBCZEBO.js} +3 -3
  33. package/dist/{chunk-JDDIZSLL.js.map → chunk-XCBCZEBO.js.map} +1 -1
  34. package/dist/{chunk-EQIB2PGC.js → chunk-Y2HK44TG.js} +3 -3
  35. package/dist/{chunk-EQIB2PGC.js.map → chunk-Y2HK44TG.js.map} +1 -1
  36. package/dist/{chunk-5TCEVMPI.js → chunk-YQGQUBKA.js} +3 -3
  37. package/dist/{chunk-5TCEVMPI.js.map → chunk-YQGQUBKA.js.map} +1 -1
  38. package/dist/controls/index.d.ts +2 -2
  39. package/dist/controls/index.js +3 -3
  40. package/dist/dragdrop/index.d.ts +3 -3
  41. package/dist/dragdrop/index.js +2 -2
  42. package/dist/index.d.ts +57 -59
  43. package/dist/index.js +335 -240
  44. package/dist/index.js.map +1 -1
  45. package/dist/layers/index.d.ts +4 -4
  46. package/dist/layers/index.js +3 -3
  47. package/dist/loupe/index.d.ts +5 -5
  48. package/dist/loupe/index.js +2 -2
  49. package/dist/passthrough/weasel-canvas.js +1 -1
  50. package/dist/passthrough/weasel-ui.d.ts +336 -15
  51. package/dist/passthrough/weasel-ui.js +2 -2
  52. package/dist/primitives/index.js +4 -4
  53. package/dist/state/index.d.ts +3 -3
  54. package/dist/state/index.js +4 -2
  55. package/dist/state/index.js.map +1 -1
  56. package/dist/styles.css +64 -5
  57. package/dist/surface/index.d.ts +17 -2
  58. package/dist/surface/index.js +3 -2
  59. package/dist/ui/layers/index.js +2 -2
  60. package/dist/undo/index.d.ts +3 -3
  61. package/package.json +8 -8
  62. package/src/annotations/AnnotationOverlay.tsx +10 -6
  63. package/src/annotations/Annotations.less +6 -0
  64. package/src/annotations/Annotations.overlay.test.tsx +113 -0
  65. package/src/annotations/MarkList.tsx +3 -2
  66. package/src/annotations/store.test.ts +72 -0
  67. package/src/annotations/store.ts +30 -0
  68. package/src/annotations/types.ts +9 -2
  69. package/src/canvas/index.ts +11 -0
  70. package/src/config/builder.test.ts +5 -0
  71. package/src/config/builder.ts +6 -0
  72. package/src/config/types.ts +1 -0
  73. package/src/controls/ControlPanel.test.tsx +23 -0
  74. package/src/controls/ControlPanel.tsx +3 -0
  75. package/src/index.test.ts +1 -0
  76. package/src/index.ts +13 -9
  77. package/src/lab/Lab.surface.test.tsx +8 -3
  78. package/src/lab/LabHeader.test.tsx +12 -0
  79. package/src/lab/LabHeader.tsx +16 -6
  80. package/src/passthrough/weasel-ui.test.ts +9 -0
  81. package/src/passthrough/weasel-ui.ts +4 -0
  82. package/src/primitives/ZoomControl.tsx +2 -0
  83. package/src/state/store.ts +10 -0
  84. package/src/state/types.ts +3 -0
  85. package/src/surface/AGENTS.md +11 -1
  86. package/src/surface/index.ts +7 -1
  87. package/src/surface/useSurfaceTile.ts +20 -2
  88. package/src/surface/useTiledSurface.ts +4 -1
  89. package/src/theme/Interstellar.stories.tsx +4 -3
  90. package/src/theme/base.less +39 -1
  91. package/src/trial/Trial.less +27 -5
  92. package/src/trial/TrialBody.test.tsx +67 -0
  93. package/src/trial/TrialBody.tsx +155 -0
  94. package/src/trial/TrialChrome.tsx +18 -10
  95. package/src/trial/index.ts +2 -0
  96. package/dist/chunk-3ZELRRBV.js.map +0 -1
  97. package/dist/chunk-4EY67BKJ.js.map +0 -1
  98. package/dist/chunk-6JMU56CU.js.map +0 -1
  99. package/dist/chunk-HEORBLVT.js.map +0 -1
  100. package/dist/chunk-LITE5YFB.js.map +0 -1
  101. package/dist/chunk-ROQDRXKW.js.map +0 -1
  102. package/dist/chunk-XJ6N32QP.js.map +0 -1
  103. package/dist/chunk-YQJNT4XQ.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-5TCEVMPI.js';
4
- import { useSurfaceOptional, useSurfaceCanvas, useTiledSurface, SurfaceContext, SurfaceCanvasContext } from './chunk-4EY67BKJ.js';
5
- export { SurfaceCanvasContext, SurfaceContext, composeRects, rectsEqual, toDeviceRect, useSurface, useSurfaceCanvas, useSurfaceOptional, useSurfaceTile, useTiledSurface } from './chunk-4EY67BKJ.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-LITE5YFB.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-LITE5YFB.js';
10
- import { JobProgress } from './chunk-3P5TTTA7.js';
11
- export { FloatingPanel, JobProgress, Legend, Sidebar } from './chunk-3P5TTTA7.js';
12
- export { FpsMeter, ScaleIndicator, StatusBar, Toolbar, ZoomControl } from './chunk-6JMU56CU.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-YQJNT4XQ.js';
16
- export { ControlPanel, fromConfigFields, isLeafVisible } from './chunk-YQJNT4XQ.js';
17
- import { CanvasStack } from './chunk-3ZELRRBV.js';
18
- export { CanvasStack } from './chunk-3ZELRRBV.js';
19
- import { LayerList } from './chunk-JDDIZSLL.js';
20
- export { LayerList } from './chunk-JDDIZSLL.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-HEORBLVT.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-HEORBLVT.js';
24
- import { resolveLoupe, TrialLoupe } from './chunk-EQIB2PGC.js';
25
- export { CanvasLoupe, DomLoupe, LOUPE_DEFAULTS, LoupeBubble, TrialLoupe, drawCanvasLens, lensCamera, lensSourceRect, resolveLoupe, sampleStack, useLoupe } from './chunk-EQIB2PGC.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-ROQDRXKW.js';
31
- import { createContext, useState, useRef, useEffect, useCallback, useContext, useMemo, Children, useLayoutEffect, useSyncExternalStore } from 'react';
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';
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");
@@ -1153,6 +1155,34 @@ function createAnnotationStore(opts) {
1153
1155
  redo() {
1154
1156
  return history.redo(historySceneAt);
1155
1157
  },
1158
+ selection() {
1159
+ const out = [];
1160
+ for (const target of liveTargets()) {
1161
+ const scene = scenes.get(target);
1162
+ if (!scene) continue;
1163
+ for (const node of scene.getSelection()) {
1164
+ if (scene.get(node)) out.push(`${target}/${String(node)}`);
1165
+ }
1166
+ }
1167
+ return out;
1168
+ },
1169
+ setSelection(ids) {
1170
+ const wanted = /* @__PURE__ */ new Map();
1171
+ for (const id of ids) {
1172
+ const found = nodeOf(id);
1173
+ if (!found) continue;
1174
+ const node = String(found.node.id);
1175
+ const on = wanted.get(found.target);
1176
+ if (!on) wanted.set(found.target, [node]);
1177
+ else if (!on.includes(node)) on.push(node);
1178
+ }
1179
+ for (const [target, scene] of scenes) {
1180
+ const next = wanted.get(target) ?? [];
1181
+ const now = scene.getSelection();
1182
+ if (now.length === next.length && next.every((n, i) => String(now[i]) === n)) continue;
1183
+ scene.setSelection(next.map(asNodeId$1));
1184
+ }
1185
+ },
1156
1186
  subscribe(fn) {
1157
1187
  subs.add(fn);
1158
1188
  return () => {
@@ -1235,11 +1265,11 @@ function annotationsFromJSON(raw, targets, rest = {}) {
1235
1265
  }
1236
1266
 
1237
1267
  // src/annotations/view.ts
1238
- function toWeaselView(v) {
1239
- 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 } };
1240
1270
  }
1241
- function fromWeaselView(v) {
1242
- 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 } };
1243
1273
  }
1244
1274
  function fitView(content, pane) {
1245
1275
  const zoom = content.w > 0 && content.h > 0 ? Math.min(pane.w / content.w, pane.h / content.h) : 1;
@@ -1284,26 +1314,27 @@ function AnnotationOverlay({
1284
1314
  const configRef = useRef(config);
1285
1315
  configRef.current = config;
1286
1316
  const id = target.id;
1317
+ const tileId = useTileId(id);
1287
1318
  const lastTile = useRef(null);
1288
1319
  useEffect(() => {
1289
1320
  const el = target.ref.current ?? null;
1290
1321
  if (el === lastTile.current) return;
1291
1322
  lastTile.current = el;
1292
- surface?.registerTile(id, el);
1323
+ surface?.registerTile(tileId, el);
1293
1324
  });
1294
1325
  useEffect(
1295
1326
  () => () => {
1296
1327
  lastTile.current = null;
1297
- surface?.registerTile(id, null);
1328
+ surface?.registerTile(tileId, null);
1298
1329
  },
1299
- [surface, id]
1330
+ [surface, tileId]
1300
1331
  );
1301
1332
  useEffect(
1302
- () => surface?.registerPainter(id, (next) => {
1333
+ () => surface?.registerPainter(tileId, (next) => {
1303
1334
  setRect((prev) => sameRect(prev, next) ? prev : next);
1304
1335
  sceneCanvas.current?.requestRedraw();
1305
1336
  }),
1306
- [surface, id]
1337
+ [surface, tileId]
1307
1338
  );
1308
1339
  const container = surface?.getContainer() ?? null;
1309
1340
  if (!container || !rect) return null;
@@ -1396,86 +1427,6 @@ function AnnotationTargets({
1396
1427
  t.id
1397
1428
  )) });
1398
1429
  }
1399
- var PITCH_LIMIT = Math.PI / 2 - 0.01;
1400
- var YAW_PER_PX = 8e-3;
1401
- var PITCH_PER_PX = 8e-3;
1402
- var DISTANCE_PER_NOTCH = 15e-4;
1403
- var DRAG_THRESHOLD = 3;
1404
- function clampPitch(pitch) {
1405
- return Math.min(PITCH_LIMIT, Math.max(-PITCH_LIMIT, pitch));
1406
- }
1407
- function wrapYaw(yaw) {
1408
- const wrapped = ((yaw + Math.PI) % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI) - Math.PI;
1409
- return wrapped === -Math.PI ? Math.PI : wrapped;
1410
- }
1411
- function orbitAfterDrag(start, dx, dy) {
1412
- return {
1413
- ...start,
1414
- yaw: wrapYaw(start.yaw + dx * YAW_PER_PX),
1415
- pitch: clampPitch(start.pitch + dy * PITCH_PER_PX)
1416
- };
1417
- }
1418
- function orbitAfterWheel(view, deltaY, minDistance, maxDistance) {
1419
- const factor = Math.exp(deltaY * DISTANCE_PER_NOTCH);
1420
- return {
1421
- ...view,
1422
- distance: Math.min(maxDistance, Math.max(minDistance, view.distance * factor))
1423
- };
1424
- }
1425
- function useOrbit({
1426
- view,
1427
- onViewChange,
1428
- home,
1429
- minDistance = 0.1,
1430
- maxDistance = 1e3
1431
- }) {
1432
- const dragRef = useRef(null);
1433
- const viewRef = useRef(view);
1434
- viewRef.current = view;
1435
- const homeRef = useRef(home ?? view);
1436
- if (home) homeRef.current = home;
1437
- const onWheel = useCallback(
1438
- (e) => {
1439
- e.preventDefault();
1440
- onViewChange(orbitAfterWheel(viewRef.current, e.deltaY, minDistance, maxDistance));
1441
- },
1442
- [onViewChange, minDistance, maxDistance]
1443
- );
1444
- const onPointerDown = useCallback((e) => {
1445
- if (e.button !== 0) return;
1446
- e.currentTarget.setPointerCapture(e.pointerId);
1447
- dragRef.current = {
1448
- pointerId: e.pointerId,
1449
- startX: e.clientX,
1450
- startY: e.clientY,
1451
- startView: viewRef.current,
1452
- moved: false
1453
- };
1454
- }, []);
1455
- const onPointerMove = useCallback(
1456
- (e) => {
1457
- const drag = dragRef.current;
1458
- if (!drag || drag.pointerId !== e.pointerId) return;
1459
- const dx = e.clientX - drag.startX;
1460
- const dy = e.clientY - drag.startY;
1461
- if (!drag.moved && Math.hypot(dx, dy) < DRAG_THRESHOLD) return;
1462
- drag.moved = true;
1463
- onViewChange(orbitAfterDrag(drag.startView, dx, dy));
1464
- },
1465
- [onViewChange]
1466
- );
1467
- const onPointerUp = useCallback((e) => {
1468
- const drag = dragRef.current;
1469
- if (!drag || drag.pointerId !== e.pointerId) return;
1470
- if (e.currentTarget.hasPointerCapture(e.pointerId)) {
1471
- e.currentTarget.releasePointerCapture(e.pointerId);
1472
- }
1473
- dragRef.current = null;
1474
- }, []);
1475
- const onDoubleClick = useCallback(() => onViewChange(homeRef.current), [onViewChange]);
1476
- const isDragging = useCallback(() => dragRef.current?.moved === true, []);
1477
- return { onWheel, onPointerDown, onPointerMove, onPointerUp, onDoubleClick, isDragging };
1478
- }
1479
1430
 
1480
1431
  // src/config/builder.ts
1481
1432
  var BaseNode = class {
@@ -1540,6 +1491,11 @@ var NumberNode = class extends BaseNode {
1540
1491
  step(step) {
1541
1492
  return this.ann({ step });
1542
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
+ }
1543
1499
  /** Force a slider even without both bounds. */
1544
1500
  slider() {
1545
1501
  return this.ann({ control: "slider" });
@@ -1654,9 +1610,9 @@ function applyRules(seed, ctx, rules) {
1654
1610
  for (const rule of rules) {
1655
1611
  const patch = rule({ ...ctx, leaf: acc });
1656
1612
  if (patch === null) continue;
1657
- for (const [k2, v] of Object.entries(patch)) {
1658
- if (v !== void 0 && acc[k2] === void 0) {
1659
- 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;
1660
1616
  }
1661
1617
  }
1662
1618
  }
@@ -1666,7 +1622,7 @@ function applyRules(seed, ctx, rules) {
1666
1622
  // src/config/resolve.ts
1667
1623
  function defined(annotations) {
1668
1624
  const out = {};
1669
- 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;
1670
1626
  return out;
1671
1627
  }
1672
1628
  function resolveConfigSchema(schema, rules = []) {
@@ -1779,7 +1735,7 @@ function validateConfigSchema(fields) {
1779
1735
  return { valid: errors.length === 0, errors };
1780
1736
  }
1781
1737
 
1782
- // ../theme/dist/chunk-O32P3G22.js
1738
+ // ../theme/dist/chunk-CXVFAOTM.js
1783
1739
  var THEME_SOURCES = {
1784
1740
  "weasel": {
1785
1741
  "primitives": {
@@ -1998,6 +1954,31 @@ var THEME_SOURCES = {
1998
1954
  "value": "24px",
1999
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."
2000
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
+ },
2001
1982
  "glass-blur": {
2002
1983
  "type": "dimension",
2003
1984
  "value": "3px",
@@ -2317,21 +2298,21 @@ var weaselTheme = {
2317
2298
  modes: THEME_SOURCES.weasel.modes
2318
2299
  };
2319
2300
  function hexToRgba(hex, alpha) {
2320
- const m2 = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
2321
- if (!m2) throw new Error(`Expected a hex color, got "${hex}"`);
2322
- 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];
2323
2304
  const full = body.length === 3 ? body.replace(/./g, (c2) => c2 + c2) : body;
2324
2305
  const r2 = Number.parseInt(full.slice(0, 2), 16);
2325
- const g2 = Number.parseInt(full.slice(2, 4), 16);
2306
+ const g = Number.parseInt(full.slice(2, 4), 16);
2326
2307
  const b2 = Number.parseInt(full.slice(4, 6), 16);
2327
- return `rgba(${r2}, ${g2}, ${b2}, ${alpha})`;
2308
+ return `rgba(${r2}, ${g}, ${b2}, ${alpha})`;
2328
2309
  }
2329
2310
  var REF = /^\{([^}]+)\}$/;
2330
2311
  function refTarget(value) {
2331
2312
  if (typeof value !== "string") return null;
2332
- const m2 = REF.exec(value.trim());
2333
- if (!m2) return null;
2334
- const path = m2[1];
2313
+ const m = REF.exec(value.trim());
2314
+ if (!m) return null;
2315
+ const path = m[1];
2335
2316
  const dot = path.indexOf(".");
2336
2317
  return dot === -1 ? path : path.slice(dot + 1);
2337
2318
  }
@@ -2464,15 +2445,15 @@ function useThemeOptional() {
2464
2445
 
2465
2446
  // ../theme/dist/index.js
2466
2447
  var ALPHA_EXT = "com.weasel.alpha";
2467
- var isToken = (v) => typeof v === "object" && v !== null && "$value" in v;
2448
+ var isToken = (v2) => typeof v2 === "object" && v2 !== null && "$value" in v2;
2468
2449
  function flattenTokens(doc) {
2469
2450
  const out = {};
2470
2451
  for (const [groupName, group] of Object.entries(doc)) {
2471
2452
  if (groupName.startsWith("$")) continue;
2472
2453
  if (typeof group !== "object" || group === null) continue;
2473
- const g2 = group;
2474
- const groupType = g2.$type;
2475
- 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)) {
2476
2457
  if (leaf.startsWith("$")) continue;
2477
2458
  if (!isToken(node)) continue;
2478
2459
  const type = node.$type ?? groupType;
@@ -2582,11 +2563,109 @@ var interstellarTheme = loadDTCG(interstellar_tokens_default);
2582
2563
  var DEFAULT_VIEW = { zoom: 1, pan: { x: 0, y: 0 } };
2583
2564
  function as2DView(view) {
2584
2565
  if (typeof view !== "object" || view === null) return null;
2585
- const v = view;
2586
- if (typeof v.zoom !== "number") return null;
2587
- if (typeof v.pan !== "object" || v.pan === null) return null;
2588
- if (typeof v.pan.x !== "number" || typeof v.pan.y !== "number") return null;
2589
- 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
+ ) }) });
2590
2669
  }
2591
2670
  var TrialDragContext = createContext(null);
2592
2671
  function useTrialDrag() {
@@ -2692,6 +2771,7 @@ function TrialChrome({
2692
2771
  const storeCtx = useContext(LabStoreContext);
2693
2772
  if (!storeCtx) throw new Error("[labkit] TrialChrome requires <LabStoreProvider>");
2694
2773
  const updateTrialView = useStore(storeCtx.store, (s) => s.updateTrialView);
2774
+ const updateTrialSidebarWidth = useStore(storeCtx.store, (s) => s.updateTrialSidebarWidth);
2695
2775
  const updateTrialConfig = useStore(storeCtx.store, (s) => s.updateTrialConfig);
2696
2776
  const updateTrialState = useStore(storeCtx.store, (s) => s.updateTrialState);
2697
2777
  const undockPanelAction = useStore(storeCtx.store, (s) => s.undockPanel);
@@ -2805,21 +2885,29 @@ function TrialChrome({
2805
2885
  /* @__PURE__ */ jsx("div", { className: "lk-trial__toolbar", children: /* @__PURE__ */ jsx(ToolbarRegion, { contributions: inRegion("toolbar"), ctx }) }),
2806
2886
  /* @__PURE__ */ jsxs("div", { className: "lk-trial__body", children: [
2807
2887
  /* @__PURE__ */ jsx(PaletteRegion, { contributions: inRegion("palette"), ctx }),
2808
- /* @__PURE__ */ jsxs("div", { className: "lk-trial__sidebar", children: [
2809
- /* @__PURE__ */ jsx(SidebarRegion, { contributions: inRegion("sidebar"), ctx }),
2810
- /* @__PURE__ */ jsx(
2811
- UndockedSections,
2812
- {
2813
- trialId,
2814
- sections: inRegion("sidebar").filter((c2) => undockedIds.includes(c2.id)),
2815
- onDock: ctx.dockPanel
2816
- }
2817
- )
2818
- ] }),
2819
- /* @__PURE__ */ jsxs("div", { className: `lk-trial__content${instrument.canvas ? " lk-trial__content--flush" : ""}`, children: [
2820
- children,
2821
- /* @__PURE__ */ jsx(ViewportRegion, { contributions: inRegion("viewport"), ctx })
2822
- ] })
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
+ )
2823
2911
  ] }),
2824
2912
  /* @__PURE__ */ jsxs("div", { className: "lk-trial__status", children: [
2825
2913
  job ? /* @__PURE__ */ jsx(JobProgress, { job }) : null,
@@ -2833,7 +2921,7 @@ function Trial({ id, chrome, suppress }) {
2833
2921
  const lab = useLabContext();
2834
2922
  const storeCtx = useContext(LabStoreContext);
2835
2923
  if (!storeCtx) throw new Error("[labkit] <Trial> requires <LabStoreProvider>");
2836
- 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));
2837
2925
  if (!record) {
2838
2926
  return /* @__PURE__ */ jsxs("div", { className: "lk-trial lk-trial--unknown", children: [
2839
2927
  "Trial not found: ",
@@ -2885,12 +2973,12 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
2885
2973
  const maxDepth = undoCap?.maxDepth ?? 50;
2886
2974
  const snapshotIfNeeded = (event) => {
2887
2975
  if (!undoCap || !undoEvents.has(event)) return;
2888
- const current = store.getState().trials.find((w2) => w2.id === record.id);
2976
+ const current = store.getState().trials.find((w3) => w3.id === record.id);
2889
2977
  if (!current) return;
2890
2978
  const snap = structuredClone(current.state);
2891
2979
  updateTrialUndoStack(record.id, (prev) => pushSnapshot(prev, snap, maxDepth));
2892
2980
  };
2893
- const setView = (v) => updateTrialView(record.id, v);
2981
+ const setView = (v2) => updateTrialView(record.id, v2);
2894
2982
  const placeView = (size) => {
2895
2983
  const declared = instrument.canvas?.initialView;
2896
2984
  if (typeof declared !== "function") return;
@@ -3030,7 +3118,7 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
3030
3118
  if (declared == null) return null;
3031
3119
  return resolveLoupe(typeof declared === "function" ? declared(record.config) : declared);
3032
3120
  }, [instrument.loupe, record.config]);
3033
- const loupeBindings = loupeCap ? { on: loupeOn, toggle: () => setLoupeOn((v) => !v) } : void 0;
3121
+ const loupeBindings = loupeCap ? { on: loupeOn, toggle: () => setLoupeOn((v2) => !v2) } : void 0;
3034
3122
  const canvasLayers = useMemo(() => {
3035
3123
  if (!instrument.canvas) return [];
3036
3124
  const baseLayers = instrument.canvas.layers;
@@ -3237,7 +3325,7 @@ function addTrial(trials, instruments, instrumentName) {
3237
3325
  return [...trials, record];
3238
3326
  }
3239
3327
  function cloneTrial(trials, id) {
3240
- const sourceIdx = trials.findIndex((w2) => w2.id === id);
3328
+ const sourceIdx = trials.findIndex((w3) => w3.id === id);
3241
3329
  const source = trials[sourceIdx];
3242
3330
  if (!source) return trials;
3243
3331
  const clone = {
@@ -3252,11 +3340,11 @@ function cloneTrial(trials, id) {
3252
3340
  }
3253
3341
  function closeTrial(trials, id) {
3254
3342
  if (trials.length <= 1) return trials;
3255
- const next = trials.filter((w2) => w2.id !== id);
3343
+ const next = trials.filter((w3) => w3.id !== id);
3256
3344
  return next.length === trials.length ? trials : next;
3257
3345
  }
3258
3346
  function resetTrial(trials, id, instruments) {
3259
- const idx = trials.findIndex((w2) => w2.id === id);
3347
+ const idx = trials.findIndex((w3) => w3.id === id);
3260
3348
  const current = trials[idx];
3261
3349
  if (!current) return trials;
3262
3350
  const instrument = findInstrument(instruments, current.instrumentName);
@@ -3271,17 +3359,22 @@ function resetTrial(trials, id, instruments) {
3271
3359
  return [...trials.slice(0, idx), reset, ...trials.slice(idx + 1)];
3272
3360
  }
3273
3361
  function reorderTrials(trials, ids) {
3274
- const byId = new Map(trials.map((w2) => [w2.id, w2]));
3275
- const named = ids.map((id) => byId.get(id)).filter((w2) => w2 !== void 0);
3276
- const seen = new Set(named.map((w2) => w2.id));
3277
- 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));
3278
3366
  return [...named, ...rest];
3279
3367
  }
3280
3368
  var MODES = [
3281
- { value: "auto", label: "Auto" },
3282
- { value: "light", label: "Light" },
3283
- { 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 }) }
3284
3372
  ];
3373
+ var MODE_ITEMS = MODES.map(({ value, label, glyph }) => ({
3374
+ value,
3375
+ label: glyph,
3376
+ ariaLabel: label
3377
+ }));
3285
3378
  function LabHeader() {
3286
3379
  const lab = useLabContext();
3287
3380
  const only = lab.instruments.length === 1 ? lab.instruments[0] : null;
@@ -3315,11 +3408,13 @@ function LabHeader() {
3315
3408
  )
3316
3409
  ),
3317
3410
  /* @__PURE__ */ jsx(
3318
- r2,
3411
+ r3,
3319
3412
  {
3320
3413
  className: "lk-lab-header__mode",
3321
3414
  ariaLabel: "Color mode",
3322
- items: MODES,
3415
+ size: "sm",
3416
+ variant: "flat",
3417
+ items: MODE_ITEMS,
3323
3418
  value: lab.mode,
3324
3419
  onChange: (next) => {
3325
3420
  if (next) lab.setMode(next);
@@ -3376,8 +3471,8 @@ function LabShell({ title, children, header, footer, mode = "auto" }) {
3376
3471
  ] });
3377
3472
  return outer ? shell : /* @__PURE__ */ jsx(ThemeProvider, { theme: interstellarTheme, mode: resolved, children: shell });
3378
3473
  }
3379
- var ZONE_ID = asNodeId("lk-workspace");
3380
- var STRATEGIES = { grid: gridStrategy, floating: floatingStrategy };
3474
+ var ZONE_ID2 = asNodeId("lk-workspace");
3475
+ var STRATEGIES2 = { grid: gridStrategy, floating: floatingStrategy };
3381
3476
  var FLOAT_ZONE_ID = asNodeId("lk-workspace-floating");
3382
3477
  var PANEL_KIND = "panel";
3383
3478
  var KIND = "trial";
@@ -3431,7 +3526,7 @@ function Workspace({
3431
3526
  store2.registerNode(
3432
3527
  createNode({
3433
3528
  kind: "zone",
3434
- id: ZONE_ID,
3529
+ id: ZONE_ID2,
3435
3530
  container: { strategyId: "grid", config: { resizable, gap, padding } }
3436
3531
  })
3437
3532
  );
@@ -3447,10 +3542,10 @@ function Workspace({
3447
3542
  const store = storeRef.current;
3448
3543
  const surface = useSurfaceOptional();
3449
3544
  useLayoutEffect(() => {
3450
- store.updateContainerConfig(ZONE_ID, { resizable, gap, padding });
3545
+ store.updateContainerConfig(ZONE_ID2, { resizable, gap, padding });
3451
3546
  }, [store, resizable, gap, padding]);
3452
3547
  useLayoutEffect(() => {
3453
- const present = new Set(store.getContainerView(ZONE_ID)?.childOrder ?? []);
3548
+ const present = new Set(store.getContainerView(ZONE_ID2)?.childOrder ?? []);
3454
3549
  const wanted = new Set(nodeIds);
3455
3550
  for (const id of present) {
3456
3551
  if (!wanted.has(id)) store.unregisterNode(id);
@@ -3458,12 +3553,12 @@ function Workspace({
3458
3553
  for (const id of nodeIds) {
3459
3554
  if (present.has(id)) continue;
3460
3555
  const kind = String(id).startsWith("lk-panel-") ? PANEL_KIND : KIND;
3461
- store.registerNode(createNode({ kind, id, parentId: ZONE_ID, focus: true }));
3556
+ store.registerNode(createNode({ kind, id, parentId: ZONE_ID2, focus: true }));
3462
3557
  store.showNode(id);
3463
3558
  const saved = layoutRef.current?.[id];
3464
3559
  if (saved) store.patchPlacement(id, saved);
3465
3560
  }
3466
- store.setChildOrder(ZONE_ID, [...nodeIds]);
3561
+ store.setChildOrder(ZONE_ID2, [...nodeIds]);
3467
3562
  }, [store, nodeIds]);
3468
3563
  useEffect(() => {
3469
3564
  if (!surface) return;
@@ -3474,7 +3569,7 @@ function Workspace({
3474
3569
  if (!onLayoutChange) return;
3475
3570
  return store.events.on("node.placementChanged", () => {
3476
3571
  const next = {};
3477
- for (const child of store.getChildren(ZONE_ID)) {
3572
+ for (const child of store.getChildren(ZONE_ID2)) {
3478
3573
  const extent = extentOf(store, child.id);
3479
3574
  if (extent) next[child.id] = extent;
3480
3575
  }
@@ -3510,7 +3605,7 @@ function Workspace({
3510
3605
  const grid = /* @__PURE__ */ jsx(
3511
3606
  Container,
3512
3607
  {
3513
- parentId: ZONE_ID,
3608
+ parentId: ZONE_ID2,
3514
3609
  chrome,
3515
3610
  className: "lk-workspace windease-zone",
3516
3611
  affordances: resizable,
@@ -3518,7 +3613,7 @@ function Workspace({
3518
3613
  onChildOrderChange: reorderable ? commitOrder : void 0
3519
3614
  }
3520
3615
  );
3521
- 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 ? (
3522
3617
  // The floating layer insets against the workspace, so it needs a
3523
3618
  // positioned box around both. Without one it insets against the page
3524
3619
  // and lands over the lab header.
@@ -3601,12 +3696,12 @@ function Lab({
3601
3696
  const onFrame = useCallback((frame) => {
3602
3697
  const c2 = ownCanvasRef.current;
3603
3698
  if (!c2) return;
3604
- const w2 = Math.round(frame.size.width * frame.dpr);
3605
- const h = Math.round(frame.size.height * frame.dpr);
3606
- if (bufferRef.current.w === w2 && bufferRef.current.h === h) return;
3607
- bufferRef.current = { w: w2, h };
3608
- c2.width = w2;
3609
- 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;
3610
3705
  c2.style.width = `${frame.size.width}px`;
3611
3706
  c2.style.height = `${frame.size.height}px`;
3612
3707
  surfaceRef.current?.invalidateAll();
@@ -3631,8 +3726,8 @@ function Lab({
3631
3726
  }, [mode, store]);
3632
3727
  const contextValue = useMemo(() => {
3633
3728
  const replaceTrials = (next) => {
3634
- const currentById = new Map(store.getState().trials.map((w2) => [w2.id, w2]));
3635
- 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);
3636
3731
  store.setState({ trials: merged });
3637
3732
  store.getState().setMode(store.getState().mode);
3638
3733
  };
@@ -3656,11 +3751,11 @@ function Lab({
3656
3751
  },
3657
3752
  resetTrial: (id) => {
3658
3753
  const next = resetTrial(store.getState().trials, id, instruments);
3659
- const record = next.find((w2) => w2.id === id);
3754
+ const record = next.find((w3) => w3.id === id);
3660
3755
  if (!record) return;
3661
3756
  store.setState((s) => ({
3662
3757
  trials: s.trials.map(
3663
- (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
3664
3759
  )
3665
3760
  }));
3666
3761
  store.getState().updateTrialView(id, record.view);
@@ -3677,8 +3772,8 @@ function Lab({
3677
3772
  store.getState().deleteSnapshot(snapshotId);
3678
3773
  },
3679
3774
  mode: modeValue,
3680
- setMode: (m2) => {
3681
- store.getState().setMode(m2);
3775
+ setMode: (m) => {
3776
+ store.getState().setMode(m);
3682
3777
  },
3683
3778
  configRules,
3684
3779
  controls
@@ -3728,13 +3823,13 @@ function Lab({
3728
3823
  Workspace,
3729
3824
  {
3730
3825
  panels: workspacePanels,
3731
- ids: trials.map((w2) => w2.id),
3826
+ ids: trials.map((w3) => w3.id),
3732
3827
  resizable: true,
3733
3828
  reorderable: true,
3734
3829
  onReorder: (ids) => contextValue.reorderTrials(ids),
3735
3830
  layout,
3736
3831
  onLayoutChange: (next) => store.getState().setLayout(next),
3737
- 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))
3738
3833
  }
3739
3834
  )
3740
3835
  ]
@@ -3746,6 +3841,6 @@ function Lab({
3746
3841
  ) }) });
3747
3842
  }
3748
3843
 
3749
- export { AnnotationOverlay, AnnotationTargets, DEFAULT_VIEW, Lab, LabContext, LabPalette, LabShell, PITCH_LIMIT, Trial, TrialChrome, Workspace, addTrial, annotationsFromJSON, applyRules, as2DView, builtinRules, capturePlan, captureTarget, clampPitch, cloneTrial, closeTrial, composeCaptureSvg, createAnnotationScene, createAnnotationStore, createMarkDrawOne, defineInstrument, f, fitView, fracContains, fracIntersects, fracToWorld, fromWeaselView, interstellarTheme, isStale, markCommands, markSvgNodes, orbitAfterDrag, orbitAfterWheel, reorderTrials, resetTrial, resolveConfigSchema, resolveMarkStyle, roundFrac, seenFrom, titleCase, toWeaselView, useConfigSchema, useLabContext, useOrbit, validateConfigSchema, worldToFrac, wrapYaw };
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 };
3750
3845
  //# sourceMappingURL=index.js.map
3751
3846
  //# sourceMappingURL=index.js.map