@weasel-js/labkit 1.4.0 → 1.4.2

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 (133) hide show
  1. package/README.md +8 -0
  2. package/dist/_dts/{CanvasStackContext-D6M0o8cQ.d.ts → CanvasStackContext-kjILVnPj.d.ts} +1 -1
  3. package/dist/_dts/PrefsForm.d-CgxUequc.d.ts +20 -0
  4. package/dist/_dts/{frac-QS7XzvpG.d.ts → frac-A4R6v4ld.d.ts} +36 -13
  5. package/dist/_dts/{index-B2X21G1c.d.ts → index-CsU9WhjM.d.ts} +90 -30
  6. package/dist/_dts/{types-ChVJJHvk.d.ts → types-BbXpvQa8.d.ts} +20 -1
  7. package/dist/_dts/{types-C1Zw7s5P.d.ts → types-lg4TSCb2.d.ts} +2 -1
  8. package/dist/_dts/{useTrialState-Cdm13fvl.d.ts → useTrialState-DKjqpv20.d.ts} +5 -1
  9. package/dist/_dts/weasel-canvas-FJTFi3ZZ.d.ts +5041 -0
  10. package/dist/canvas/index.d.ts +16 -16
  11. package/dist/canvas/index.js +2 -2
  12. package/dist/chrome/index.d.ts +15 -9
  13. package/dist/chrome/index.js +5 -5
  14. package/dist/{chunk-7VW3S44V.js → chunk-54ZWZ5FQ.js} +908 -301
  15. package/dist/chunk-54ZWZ5FQ.js.map +1 -0
  16. package/dist/{chunk-Y2HK44TG.js → chunk-D5KQ5OY6.js} +3 -3
  17. package/dist/{chunk-Y2HK44TG.js.map → chunk-D5KQ5OY6.js.map} +1 -1
  18. package/dist/{chunk-VHFQKAUL.js → chunk-FJG4PHTL.js} +83 -75
  19. package/dist/chunk-FJG4PHTL.js.map +1 -0
  20. package/dist/{chunk-LN6JDUGB.js → chunk-HXXTULDN.js} +2 -2
  21. package/dist/{chunk-LN6JDUGB.js.map → chunk-HXXTULDN.js.map} +1 -1
  22. package/dist/{chunk-L5WBGUVR.js → chunk-ISSVF5PT.js} +2753 -2654
  23. package/dist/chunk-ISSVF5PT.js.map +1 -0
  24. package/dist/chunk-NSOVI3AZ.js +170 -0
  25. package/dist/chunk-NSOVI3AZ.js.map +1 -0
  26. package/dist/{chunk-YQGQUBKA.js → chunk-TN7YSJVU.js} +62 -60
  27. package/dist/chunk-TN7YSJVU.js.map +1 -0
  28. package/dist/{chunk-4AMN4MMB.js → chunk-UDXOYZEC.js} +53 -39
  29. package/dist/chunk-UDXOYZEC.js.map +1 -0
  30. package/dist/{chunk-LJSIUFYD.js → chunk-ULDW42CR.js} +23 -2
  31. package/dist/chunk-ULDW42CR.js.map +1 -0
  32. package/dist/{chunk-QR5V3AFG.js → chunk-UTOEDPNU.js} +5 -5
  33. package/dist/{chunk-QR5V3AFG.js.map → chunk-UTOEDPNU.js.map} +1 -1
  34. package/dist/{chunk-UD7TEHZT.js → chunk-W2FJR5FF.js} +35 -22
  35. package/dist/chunk-W2FJR5FF.js.map +1 -0
  36. package/dist/{chunk-B5VWJBWM.js → chunk-WS6ZRV75.js} +3 -3
  37. package/dist/{chunk-B5VWJBWM.js.map → chunk-WS6ZRV75.js.map} +1 -1
  38. package/dist/{chunk-W3KSUXXR.js → chunk-XKENZTNE.js} +45 -14
  39. package/dist/chunk-XKENZTNE.js.map +1 -0
  40. package/dist/controls/index.d.ts +4 -3
  41. package/dist/controls/index.js +3 -3
  42. package/dist/dragdrop/index.d.ts +8 -8
  43. package/dist/dragdrop/index.js +2 -2
  44. package/dist/index.d.ts +75 -110
  45. package/dist/index.js +148 -115
  46. package/dist/index.js.map +1 -1
  47. package/dist/job/index.js +1 -1
  48. package/dist/layers/index.d.ts +26 -11
  49. package/dist/layers/index.js +3 -3
  50. package/dist/loupe/index.d.ts +7 -13
  51. package/dist/loupe/index.js +2 -2
  52. package/dist/passthrough/weasel-canvas.d.ts +3 -343
  53. package/dist/passthrough/weasel-canvas.js +1 -1
  54. package/dist/passthrough/weasel-ui.d.ts +123 -3088
  55. package/dist/passthrough/weasel-ui.js +2 -2
  56. package/dist/primitives/index.js +4 -4
  57. package/dist/state/index.d.ts +3 -3
  58. package/dist/state/index.js +2 -2
  59. package/dist/styles.css +30 -3
  60. package/dist/surface/index.js +2 -2
  61. package/dist/ui/layers/index.d.ts +22 -9
  62. package/dist/ui/layers/index.js +2 -2
  63. package/dist/undo/index.d.ts +6 -5
  64. package/package.json +8 -8
  65. package/src/annotations/AnnotationTargets.tsx +6 -1
  66. package/src/annotations/Annotations.overlay.test.tsx +2 -0
  67. package/src/annotations/types.ts +4 -1
  68. package/src/canvas/CanvasStack.tsx +8 -13
  69. package/src/canvas/useOrbit.test.ts +97 -1
  70. package/src/canvas/useOrbit.ts +41 -34
  71. package/src/canvas/usePanZoom.test.ts +106 -1
  72. package/src/canvas/usePanZoom.ts +54 -36
  73. package/src/chrome/ChromeRegions.stories.tsx +42 -1
  74. package/src/chrome/builtins.test.ts +4 -0
  75. package/src/chrome/builtins.tsx +18 -0
  76. package/src/chrome/index.ts +1 -1
  77. package/src/chrome/regions/SidebarRegion.tsx +7 -6
  78. package/src/chrome/regions/TitleBarRegion.test.tsx +64 -0
  79. package/src/chrome/regions/TitleBarRegion.tsx +16 -7
  80. package/src/chrome/regions/ToolbarRegion.test.tsx +14 -0
  81. package/src/chrome/regions/ToolbarRegion.tsx +2 -2
  82. package/src/chrome/regions/ViewportRegion.tsx +2 -2
  83. package/src/chrome/regions/regions.test.tsx +45 -1
  84. package/src/chrome/types.ts +21 -3
  85. package/src/controls/ControlPanel.test.tsx +38 -0
  86. package/src/controls/ControlPanel.tsx +51 -6
  87. package/src/dragdrop/DragDropRuntime.tsx +74 -61
  88. package/src/dragdrop/Palette.tsx +4 -3
  89. package/src/dragdrop/dragDrop.test.tsx +35 -12
  90. package/src/index.ts +2 -1
  91. package/src/instrument/types.ts +4 -5
  92. package/src/job/useJob.ts +1 -1
  93. package/src/lab/Lab.test.tsx +7 -0
  94. package/src/lab/Lab.tsx +2 -2
  95. package/src/lab/LabContext.ts +5 -1
  96. package/src/lab/LabShell.less +17 -2
  97. package/src/lab/Workspace.tsx +1 -1
  98. package/src/layers/AGENTS.md +29 -7
  99. package/src/layers/LayerList.less +16 -0
  100. package/src/layers/LayerList.stories.tsx +66 -0
  101. package/src/layers/LayerList.test.tsx +185 -2
  102. package/src/layers/LayerList.tsx +221 -74
  103. package/src/layers/index.ts +1 -1
  104. package/src/passthrough/weasel-ui.ts +5 -0
  105. package/src/primitives/FloatingPanel.test.tsx +87 -0
  106. package/src/primitives/FloatingPanel.tsx +59 -41
  107. package/src/state/store.test.ts +78 -0
  108. package/src/state/store.ts +27 -0
  109. package/src/state/types.ts +20 -0
  110. package/src/trial/Trial.less +4 -0
  111. package/src/trial/Trial.targets.test.tsx +96 -0
  112. package/src/trial/Trial.test.tsx +171 -14
  113. package/src/trial/Trial.tsx +4 -1
  114. package/src/trial/TrialChrome.tsx +22 -2
  115. package/src/trial/TrialTitleBar.tsx +7 -3
  116. package/src/trial/index.ts +1 -0
  117. package/src/trial/trialOps.test.ts +49 -1
  118. package/src/trial/trialOps.ts +28 -6
  119. package/dist/_dts/DrawCommand-B3bskUsC.d.ts +0 -564
  120. package/dist/_dts/PrefsForm-BkUJZx0A.d.ts +0 -204
  121. package/dist/_dts/fitViewToBounds-dZ2UDB6e.d.ts +0 -21
  122. package/dist/_dts/shapeKinds-Cx_rxwsa.d.ts +0 -87
  123. package/dist/_dts/types-C-gh9Ap-.d.ts +0 -695
  124. package/dist/chunk-4AMN4MMB.js.map +0 -1
  125. package/dist/chunk-7VW3S44V.js.map +0 -1
  126. package/dist/chunk-L5WBGUVR.js.map +0 -1
  127. package/dist/chunk-LJSIUFYD.js.map +0 -1
  128. package/dist/chunk-UD7TEHZT.js.map +0 -1
  129. package/dist/chunk-VHFQKAUL.js.map +0 -1
  130. package/dist/chunk-W3KSUXXR.js.map +0 -1
  131. package/dist/chunk-XCBCZEBO.js +0 -94
  132. package/dist/chunk-XCBCZEBO.js.map +0 -1
  133. package/dist/chunk-YQGQUBKA.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,35 +1,35 @@
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-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
- import { useJob } from './chunk-LN6JDUGB.js';
7
- export { useJob } from './chunk-LN6JDUGB.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';
3
+ import { useDragDrop, DragOverlay, Palette } from './chunk-TN7YSJVU.js';
4
+ import { useSurfaceOptional, useSurfaceCanvas, useTileId, useTiledSurface, SurfaceContext, SurfaceCanvasContext } from './chunk-WS6ZRV75.js';
5
+ export { SurfaceCanvasContext, SurfaceContext, composeRects, rectsEqual, toDeviceRect, useSurface, useSurfaceCanvas, useSurfaceOptional, useSurfaceTile, useTileId, useTiledSurface } from './chunk-WS6ZRV75.js';
6
+ import { useJob } from './chunk-HXXTULDN.js';
7
+ export { useJob } from './chunk-HXXTULDN.js';
8
+ import { ANNOTATION_WEASEL_TOOLS, annotationToolInfo, suppressContributions, mergeContributions, builtinContributions, TitleBarRegion, ToolbarRegion, PaletteRegion, ViewportRegion, SidebarRegion, StatusRegion, AnnotationsContext } from './chunk-W2FJR5FF.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-W2FJR5FF.js';
10
+ import { JobProgress } from './chunk-UDXOYZEC.js';
11
+ export { FloatingPanel, JobProgress, Legend, Sidebar } from './chunk-UDXOYZEC.js';
12
+ export { FpsMeter, ScaleIndicator, StatusBar, Toolbar, ZoomControl } from './chunk-UTOEDPNU.js';
13
+ import { createLabStore, panelKey, noneAdapter } from './chunk-ULDW42CR.js';
14
+ export { CURRENT_DOCUMENT_VERSION, createMemoryAdapter, dockPanel, labDocumentKey, localStorageAdapter, noneAdapter, panelKey, quarantineKey, sessionStorageAdapter, undockPanel, urlHashAdapter, useTrialState } from './chunk-ULDW42CR.js';
15
15
  import { LabStoreContext, TrialIdProvider } from './chunk-2YILQ7Y5.js';
16
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';
17
+ import { fromConfigFields } from './chunk-XKENZTNE.js';
18
+ export { ControlPanel, fromConfigFields, isLeafVisible } from './chunk-XKENZTNE.js';
19
+ import { CanvasStack } from './chunk-FJG4PHTL.js';
20
+ export { CanvasStack, PITCH_LIMIT, clampPitch, orbitAfterDrag, orbitAfterWheel, useOrbit, usePanZoom, wrapYaw } from './chunk-FJG4PHTL.js';
21
+ import { LayerList } from './chunk-NSOVI3AZ.js';
22
+ export { LayerList } from './chunk-NSOVI3AZ.js';
23
23
  import './chunk-SBXNQW4G.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';
24
+ import { E, w, T, C, h, r4 } from './chunk-ISSVF5PT.js';
25
+ export { M2 as CheckboxRow, j2 as ColorRow, y4 as CurveField, R as EffectCard, B as EffectCardList, c3 as LayerStack, P2 as NumberRow, V as PropertyGroup, T2 as PropertyList, w2 as PropertyPanel, D as PropertyRow, E2 as PropertySpan, F as SelectRow, k as SliderRow, L2 as Subpanel, N2 as TextRow, I as ToggleRow } from './chunk-ISSVF5PT.js';
26
+ import { resolveLoupe, TrialLoupe } from './chunk-D5KQ5OY6.js';
27
+ export { CanvasLoupe, DomLoupe, LOUPE_DEFAULTS, LoupeBubble, TrialLoupe, drawCanvasLens, lensCamera, lensSourceRect, resolveLoupe, sampleStack, useLoupe } from './chunk-D5KQ5OY6.js';
28
28
  export { centerOn, zoomAt } from './chunk-CTRKTLYZ.js';
29
29
  import { applyCamera } from './chunk-2AYGEN57.js';
30
30
  export { DEFAULT_FRAME, applyCamera, resolveFrame, screenToWorld, worldToScreen } from './chunk-2AYGEN57.js';
31
31
  export { CanvasStackContext } from './chunk-CQLQPQ4P.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';
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-54ZWZ5FQ.js';
33
33
  import { createContext, useState, useRef, useEffect, useContext, useMemo, Children, useLayoutEffect, useCallback, useSyncExternalStore } from 'react';
34
34
  import { createPortal } from 'react-dom';
35
35
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -39,14 +39,14 @@ import { Provider, StrategyRegistryProvider, Container, DragProvider, useDragHan
39
39
 
40
40
  // ../svg/dist/index.js
41
41
  var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
42
- function multiply(a, b2) {
42
+ function multiply(a, b) {
43
43
  return [
44
- a[0] * b2[0] + a[2] * b2[1],
45
- a[1] * b2[0] + a[3] * b2[1],
46
- a[0] * b2[2] + a[2] * b2[3],
47
- a[1] * b2[2] + a[3] * b2[3],
48
- a[0] * b2[4] + a[2] * b2[5] + a[4],
49
- a[1] * b2[4] + a[3] * b2[5] + a[5]
44
+ a[0] * b[0] + a[2] * b[1],
45
+ a[1] * b[0] + a[3] * b[1],
46
+ a[0] * b[2] + a[2] * b[3],
47
+ a[1] * b[2] + a[3] * b[3],
48
+ a[0] * b[4] + a[2] * b[5] + a[4],
49
+ a[1] * b[4] + a[3] * b[5] + a[5]
50
50
  ];
51
51
  }
52
52
  function isIdentity(m, eps = 1e-9) {
@@ -108,8 +108,8 @@ function escapeAttr(s) {
108
108
  }
109
109
  function serializePathD(path) {
110
110
  if (path.kind === "rect") {
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
+ const { x, y: y2, width, height } = path;
112
+ return `M${trimNumber(x)} ${trimNumber(y2)}h${trimNumber(width)}v${trimNumber(height)}h${trimNumber(-width)}Z`;
113
113
  }
114
114
  const cmds = path.commands;
115
115
  const xs = path.coords;
@@ -395,9 +395,9 @@ function pathXml(node, registry, namespaces) {
395
395
  attrs.push(`opacity="${trimNumber(node.opacity)}"`);
396
396
  }
397
397
  if (node.rotation != null && node.rotation !== 0) {
398
- const b2 = pathBounds(node.path);
399
- const cx = b2.minX + (b2.maxX - b2.minX) / 2;
400
- const cy = b2.minY + (b2.maxY - b2.minY) / 2;
398
+ const b = pathBounds(node.path);
399
+ const cx = b.minX + (b.maxX - b.minX) / 2;
400
+ const cy = b.minY + (b.maxY - b.minY) / 2;
401
401
  const deg = node.rotation * 180 / Math.PI;
402
402
  attrs.push(`transform="rotate(${trimNumber(deg)} ${trimNumber(cx)} ${trimNumber(cy)})"`);
403
403
  }
@@ -494,11 +494,11 @@ function computeBounds(nodes) {
494
494
  n.children.forEach((c2) => visit(c2, childCtm));
495
495
  return;
496
496
  }
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);
498
- const local = n.rotation ? rotateAboutCenter(ctm, b2, n.rotation) : ctm;
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];
497
+ const b = 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);
498
+ const local = n.rotation ? rotateAboutCenter(ctm, b, n.rotation) : ctm;
499
+ for (const [x, y2] of corners(b)) {
500
+ const px = local[0] * x + local[2] * y2 + local[4];
501
+ const py = local[1] * x + local[3] * y2 + local[5];
502
502
  if (px < minX) minX = px;
503
503
  if (py < minY) minY = py;
504
504
  if (px > maxX) maxX = px;
@@ -511,12 +511,12 @@ function computeBounds(nodes) {
511
511
  }
512
512
  return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
513
513
  }
514
- function corners(b2) {
515
- return [[b2.minX, b2.minY], [b2.maxX, b2.minY], [b2.maxX, b2.maxY], [b2.minX, b2.maxY]];
514
+ function corners(b) {
515
+ return [[b.minX, b.minY], [b.maxX, b.minY], [b.maxX, b.maxY], [b.minX, b.maxY]];
516
516
  }
517
- function rotateAboutCenter(ctm, b2, angle) {
518
- const cx = (b2.minX + b2.maxX) / 2;
519
- const cy = (b2.minY + b2.maxY) / 2;
517
+ function rotateAboutCenter(ctm, b, angle) {
518
+ const cx = (b.minX + b.maxX) / 2;
519
+ const cy = (b.minY + b.maxY) / 2;
520
520
  const c2 = Math.cos(angle);
521
521
  const s = Math.sin(angle);
522
522
  return multiply(ctm, [c2, s, -s, c2, cx - cx * c2 + cy * s, cy - cx * s - cy * c2]);
@@ -628,34 +628,34 @@ function pathBounds(path) {
628
628
  maxY: path.y + path.height
629
629
  };
630
630
  }
631
- const b2 = boundsOfPath(path);
632
- return { minX: b2.x, minY: b2.y, maxX: b2.x + b2.width, maxY: b2.y + b2.height };
631
+ const b = boundsOfPath(path);
632
+ return { minX: b.x, minY: b.y, maxX: b.x + b.width, maxY: b.y + b.height };
633
633
  }
634
634
 
635
635
  // src/annotations/paint.ts
636
636
  var MARK_COLOR = "#e5484d";
637
637
  var MARK_WIDTH = 2;
638
638
  var TEXT_SIZE = 14;
639
- var toWorld = (p2, content) => ({
640
- x: p2.x * content.w,
641
- y: p2.y * content.h
639
+ var toWorld = (p, content) => ({
640
+ x: p.x * content.w,
641
+ y: p.y * content.h
642
642
  });
643
643
  function vertices(m, content) {
644
644
  const stored = m.data.points;
645
- if (stored && stored.length >= 2) return stored.map((p2) => toWorld(p2, content));
646
- const { x: x2, y, width, height } = m.pose;
645
+ if (stored && stored.length >= 2) return stored.map((p) => toWorld(p, content));
646
+ const { x, y: y2, width, height } = m.pose;
647
647
  return [
648
- { x: x2, y },
649
- { x: x2 + width, y: y + height }
648
+ { x, y: y2 },
649
+ { x: x + width, y: y2 + height }
650
650
  ];
651
651
  }
652
652
  function polyline(points) {
653
- const b2 = new PathBuilder();
653
+ const b = new PathBuilder();
654
654
  const first = points[0];
655
655
  if (!first) return rectPath(0, 0, 0, 0);
656
- b2.moveTo(first.x, first.y);
657
- for (const p2 of points.slice(1)) b2.lineTo(p2.x, p2.y);
658
- return b2.build();
656
+ b.moveTo(first.x, first.y);
657
+ for (const p of points.slice(1)) b.lineTo(p.x, p.y);
658
+ return b.build();
659
659
  }
660
660
  function markCommands(m, content, style = {}) {
661
661
  const color = style.color ?? MARK_COLOR;
@@ -674,13 +674,13 @@ function markCommands(m, content, style = {}) {
674
674
  case "ellipse":
675
675
  return [{ kind: "path", path: ellipsePath(m.pose), stroke }];
676
676
  case "line": {
677
- const [a, b2] = vertices(m, content);
678
- return [{ kind: "path", path: linePath(a, b2), stroke }];
677
+ const [a, b] = vertices(m, content);
678
+ return [{ kind: "path", path: linePath(a, b), stroke }];
679
679
  }
680
680
  case "arrow": {
681
- const [a, b2] = vertices(m, content);
681
+ const [a, b] = vertices(m, content);
682
682
  const marked = { ...stroke, markerEnd: "arrow" };
683
- const path = linePath(a, b2);
683
+ const path = linePath(a, b);
684
684
  return [
685
685
  { kind: "path", path, stroke: marked },
686
686
  ...markerDrawCommands(path, marked, MARK_WIDTH, void 0)
@@ -1265,17 +1265,17 @@ function annotationsFromJSON(raw, targets, rest = {}) {
1265
1265
  }
1266
1266
 
1267
1267
  // src/annotations/view.ts
1268
- function toWeaselView(v2) {
1269
- return { x: v2.pan.x, y: v2.pan.y, scale: { x: v2.zoom, y: v2.zoom } };
1268
+ function toWeaselView(v) {
1269
+ return { x: v.pan.x, y: v.pan.y, scale: { x: v.zoom, y: v.zoom } };
1270
1270
  }
1271
- function fromWeaselView(v2) {
1272
- return { zoom: v2.scale.x, pan: { x: v2.x, y: v2.y } };
1271
+ function fromWeaselView(v) {
1272
+ return { zoom: v.scale.x, pan: { x: v.x, y: v.y } };
1273
1273
  }
1274
1274
  function fitView(content, pane) {
1275
1275
  const zoom = content.w > 0 && content.h > 0 ? Math.min(pane.w / content.w, pane.h / content.h) : 1;
1276
1276
  return { zoom: Number.isFinite(zoom) && zoom > 0 ? zoom : 1, pan: { x: 0, y: 0 } };
1277
1277
  }
1278
- var sameRect = (a, b2) => a !== null && a.x === b2.x && a.y === b2.y && a.w === b2.w && a.h === b2.h;
1278
+ var sameRect = (a, b) => a !== null && a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h;
1279
1279
  function ToolBridge({ toolId }) {
1280
1280
  const tools = useActiveToolContext();
1281
1281
  const setActive = tools.setActive;
@@ -1285,9 +1285,9 @@ function ToolBridge({ toolId }) {
1285
1285
  return null;
1286
1286
  }
1287
1287
  function pointsOf(extras, content) {
1288
- const toFrac = (p2) => ({
1289
- x: content.w === 0 ? 0 : p2.x / content.w,
1290
- y: content.h === 0 ? 0 : p2.y / content.h
1288
+ const toFrac = (p) => ({
1289
+ x: content.w === 0 ? 0 : p.x / content.w,
1290
+ y: content.h === 0 ? 0 : p.y / content.h
1291
1291
  });
1292
1292
  if (extras.kind === "line") {
1293
1293
  return [toFrac(extras.a), toFrac(extras.b)];
@@ -1411,10 +1411,11 @@ function AnnotationTargets({
1411
1411
  capability,
1412
1412
  state,
1413
1413
  config,
1414
+ trial,
1414
1415
  annotations,
1415
1416
  activeToolId
1416
1417
  }) {
1417
- const targets = capability.targets(state, config);
1418
+ const targets = capability.targets(state, config, trial);
1418
1419
  return /* @__PURE__ */ jsx(Fragment, { children: targets.map((t) => /* @__PURE__ */ jsx(
1419
1420
  AnnotationOverlay,
1420
1421
  {
@@ -1610,9 +1611,9 @@ function applyRules(seed, ctx, rules) {
1610
1611
  for (const rule of rules) {
1611
1612
  const patch = rule({ ...ctx, leaf: acc });
1612
1613
  if (patch === null) continue;
1613
- for (const [k2, v2] of Object.entries(patch)) {
1614
- if (v2 !== void 0 && acc[k2] === void 0) {
1615
- acc[k2] = v2;
1614
+ for (const [k2, v] of Object.entries(patch)) {
1615
+ if (v !== void 0 && acc[k2] === void 0) {
1616
+ acc[k2] = v;
1616
1617
  }
1617
1618
  }
1618
1619
  }
@@ -1622,7 +1623,7 @@ function applyRules(seed, ctx, rules) {
1622
1623
  // src/config/resolve.ts
1623
1624
  function defined(annotations) {
1624
1625
  const out = {};
1625
- for (const [k2, v2] of Object.entries(annotations)) if (v2 !== void 0) out[k2] = v2;
1626
+ for (const [k2, v] of Object.entries(annotations)) if (v !== void 0) out[k2] = v;
1626
1627
  return out;
1627
1628
  }
1628
1629
  function resolveConfigSchema(schema, rules = []) {
@@ -2304,8 +2305,8 @@ function hexToRgba(hex, alpha) {
2304
2305
  const full = body.length === 3 ? body.replace(/./g, (c2) => c2 + c2) : body;
2305
2306
  const r2 = Number.parseInt(full.slice(0, 2), 16);
2306
2307
  const g = Number.parseInt(full.slice(2, 4), 16);
2307
- const b2 = Number.parseInt(full.slice(4, 6), 16);
2308
- return `rgba(${r2}, ${g}, ${b2}, ${alpha})`;
2308
+ const b = Number.parseInt(full.slice(4, 6), 16);
2309
+ return `rgba(${r2}, ${g}, ${b}, ${alpha})`;
2309
2310
  }
2310
2311
  var REF = /^\{([^}]+)\}$/;
2311
2312
  function refTarget(value) {
@@ -2445,7 +2446,7 @@ function useThemeOptional() {
2445
2446
 
2446
2447
  // ../theme/dist/index.js
2447
2448
  var ALPHA_EXT = "com.weasel.alpha";
2448
- var isToken = (v2) => typeof v2 === "object" && v2 !== null && "$value" in v2;
2449
+ var isToken = (v) => typeof v === "object" && v !== null && "$value" in v;
2449
2450
  function flattenTokens(doc) {
2450
2451
  const out = {};
2451
2452
  for (const [groupName, group] of Object.entries(doc)) {
@@ -2563,11 +2564,11 @@ var interstellarTheme = loadDTCG(interstellar_tokens_default);
2563
2564
  var DEFAULT_VIEW = { zoom: 1, pan: { x: 0, y: 0 } };
2564
2565
  function as2DView(view) {
2565
2566
  if (typeof view !== "object" || view === null) return null;
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 } };
2567
+ const v = view;
2568
+ if (typeof v.zoom !== "number") return null;
2569
+ if (typeof v.pan !== "object" || v.pan === null) return null;
2570
+ if (typeof v.pan.x !== "number" || typeof v.pan.y !== "number") return null;
2571
+ return { zoom: v.zoom, pan: { x: v.pan.x, y: v.pan.y } };
2571
2572
  }
2572
2573
  var ZONE_ID = asNodeId("lk-trial-body");
2573
2574
  var SIDEBAR_ID = asNodeId("lk-trial-sidebar");
@@ -2671,18 +2672,20 @@ var TrialDragContext = createContext(null);
2671
2672
  function useTrialDrag() {
2672
2673
  return useContext(TrialDragContext);
2673
2674
  }
2674
- function Draggable({ nodeId, title, children }) {
2675
+ function Draggable({ nodeId, title, lead, children }) {
2675
2676
  const handlers = useDragHandle(nodeId);
2676
2677
  return /* @__PURE__ */ jsxs("div", { className: "lk-trial__titlebar lk-trial__titlebar--draggable", ...handlers, children: [
2678
+ lead,
2677
2679
  /* @__PURE__ */ jsx("span", { className: "lk-trial__title", children: title }),
2678
2680
  children
2679
2681
  ] });
2680
2682
  }
2681
- function TrialTitleBar({ title, children }) {
2683
+ function TrialTitleBar({ title, lead, children }) {
2682
2684
  const drag = useTrialDrag();
2683
2685
  if (drag)
2684
- return /* @__PURE__ */ jsx(Draggable, { nodeId: drag.nodeId, title, children });
2686
+ return /* @__PURE__ */ jsx(Draggable, { nodeId: drag.nodeId, title, lead, children });
2685
2687
  return /* @__PURE__ */ jsxs("div", { className: "lk-trial__titlebar", children: [
2688
+ lead,
2686
2689
  /* @__PURE__ */ jsx("span", { className: "lk-trial__title", children: title }),
2687
2690
  children
2688
2691
  ] });
@@ -2752,6 +2755,7 @@ function UndockedSections({ trialId, sections, onDock }) {
2752
2755
  }
2753
2756
  var NO_OP = () => {
2754
2757
  };
2758
+ var NO_SECTIONS = Object.freeze({});
2755
2759
  function TrialChrome({
2756
2760
  trialId,
2757
2761
  record,
@@ -2772,17 +2776,21 @@ function TrialChrome({
2772
2776
  if (!storeCtx) throw new Error("[labkit] TrialChrome requires <LabStoreProvider>");
2773
2777
  const updateTrialView = useStore(storeCtx.store, (s) => s.updateTrialView);
2774
2778
  const updateTrialSidebarWidth = useStore(storeCtx.store, (s) => s.updateTrialSidebarWidth);
2779
+ const setTrialTitle = useStore(storeCtx.store, (s) => s.setTrialTitle);
2780
+ const setTrialSectionCollapsed = useStore(storeCtx.store, (s) => s.setTrialSectionCollapsed);
2775
2781
  const updateTrialConfig = useStore(storeCtx.store, (s) => s.updateTrialConfig);
2776
2782
  const updateTrialState = useStore(storeCtx.store, (s) => s.updateTrialState);
2777
2783
  const undockPanelAction = useStore(storeCtx.store, (s) => s.undockPanel);
2778
2784
  const dockPanelAction = useStore(storeCtx.store, (s) => s.dockPanel);
2779
2785
  const undockedPanels = useStore(storeCtx.store, (s) => s.undockedPanels);
2780
2786
  const undockedIds = useMemo(
2781
- () => Object.values(undockedPanels).filter((p2) => p2.trialId === trialId).map((p2) => p2.sectionId),
2787
+ () => Object.values(undockedPanels).filter((p) => p.trialId === trialId).map((p) => p.sectionId),
2782
2788
  [undockedPanels, trialId]
2783
2789
  );
2784
2790
  const view2d = as2DView(record.view);
2785
2791
  const configSchema = useConfigSchema(instrument);
2792
+ const title = record.title ?? record.instrumentName;
2793
+ const collapsedSections = record.collapsedSections ?? NO_SECTIONS;
2786
2794
  const ctx = useMemo(() => {
2787
2795
  const setZoom = (z) => {
2788
2796
  if (!view2d) return;
@@ -2791,6 +2799,8 @@ function TrialChrome({
2791
2799
  return {
2792
2800
  trialId,
2793
2801
  instrumentName: record.instrumentName,
2802
+ title,
2803
+ setTitle: (next) => setTrialTitle(trialId, next),
2794
2804
  isLastTrial,
2795
2805
  zoom: view2d ? view2d.zoom : null,
2796
2806
  setZoom,
@@ -2817,6 +2827,8 @@ function TrialChrome({
2817
2827
  updateTrialState(trialId, nextState);
2818
2828
  }
2819
2829
  },
2830
+ collapsedSections,
2831
+ setSectionCollapsed: (key, collapsed) => setTrialSectionCollapsed(trialId, key, collapsed),
2820
2832
  undockedPanels: undockedIds,
2821
2833
  undockPanel: (sectionId, as) => undockPanelAction(trialId, sectionId, as),
2822
2834
  dockPanel: (sectionId) => dockPanelAction(trialId, sectionId),
@@ -2834,6 +2846,7 @@ function TrialChrome({
2834
2846
  record,
2835
2847
  instrument,
2836
2848
  lab,
2849
+ title,
2837
2850
  isLastTrial,
2838
2851
  updateTrialView,
2839
2852
  updateTrialConfig,
@@ -2846,7 +2859,10 @@ function TrialChrome({
2846
2859
  configSchema,
2847
2860
  undockedIds,
2848
2861
  undockPanelAction,
2849
- dockPanelAction
2862
+ dockPanelAction,
2863
+ setTrialTitle,
2864
+ setTrialSectionCollapsed,
2865
+ collapsedSections
2850
2866
  ]);
2851
2867
  const contributions = useMemo(
2852
2868
  () => suppressContributions(
@@ -2877,11 +2893,18 @@ function TrialChrome({
2877
2893
  "section",
2878
2894
  {
2879
2895
  className: "lk-trial",
2880
- "aria-label": `Trial ${record.instrumentName}`,
2896
+ "aria-label": `Trial ${title}`,
2881
2897
  tabIndex: -1,
2882
2898
  onKeyDown: handleKeyDown,
2883
2899
  children: [
2884
- /* @__PURE__ */ jsx(TrialTitleBar, { title: record.instrumentName, children: /* @__PURE__ */ jsx(TitleBarRegion, { contributions: inRegion("titlebar"), ctx }) }),
2900
+ /* @__PURE__ */ jsx(
2901
+ TrialTitleBar,
2902
+ {
2903
+ title,
2904
+ lead: /* @__PURE__ */ jsx(TitleBarRegion, { placement: "lead", contributions: inRegion("titlebar"), ctx }),
2905
+ children: /* @__PURE__ */ jsx(TitleBarRegion, { contributions: inRegion("titlebar"), ctx })
2906
+ }
2907
+ ),
2885
2908
  /* @__PURE__ */ jsx("div", { className: "lk-trial__toolbar", children: /* @__PURE__ */ jsx(ToolbarRegion, { contributions: inRegion("toolbar"), ctx }) }),
2886
2909
  /* @__PURE__ */ jsxs("div", { className: "lk-trial__body", children: [
2887
2910
  /* @__PURE__ */ jsx(PaletteRegion, { contributions: inRegion("palette"), ctx }),
@@ -2978,7 +3001,7 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
2978
3001
  const snap = structuredClone(current.state);
2979
3002
  updateTrialUndoStack(record.id, (prev) => pushSnapshot(prev, snap, maxDepth));
2980
3003
  };
2981
- const setView = (v2) => updateTrialView(record.id, v2);
3004
+ const setView = (v) => updateTrialView(record.id, v);
2982
3005
  const placeView = (size) => {
2983
3006
  const declared = instrument.canvas?.initialView;
2984
3007
  if (typeof declared !== "function") return;
@@ -2987,7 +3010,7 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
2987
3010
  };
2988
3011
  const annotationsCap = instrument.annotations;
2989
3012
  const targetsRef = useRef(() => []);
2990
- targetsRef.current = () => annotationsCap ? annotationsCap.targets(record.state, record.config) : [];
3013
+ targetsRef.current = () => annotationsCap ? annotationsCap.targets(record.state, record.config, { id: record.id, view: record.view }) : [];
2991
3014
  const annotationsRef = useRef(null);
2992
3015
  const configRef = useRef(record.config);
2993
3016
  configRef.current = record.config;
@@ -3118,11 +3141,11 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
3118
3141
  if (declared == null) return null;
3119
3142
  return resolveLoupe(typeof declared === "function" ? declared(record.config) : declared);
3120
3143
  }, [instrument.loupe, record.config]);
3121
- const loupeBindings = loupeCap ? { on: loupeOn, toggle: () => setLoupeOn((v2) => !v2) } : void 0;
3144
+ const loupeBindings = loupeCap ? { on: loupeOn, toggle: () => setLoupeOn((v) => !v) } : void 0;
3122
3145
  const canvasLayers = useMemo(() => {
3123
3146
  if (!instrument.canvas) return [];
3124
3147
  const baseLayers = instrument.canvas.layers;
3125
- const ordered = layerOrder ? [...baseLayers].sort((a, b2) => layerOrder.indexOf(a.id) - layerOrder.indexOf(b2.id)) : baseLayers;
3148
+ const ordered = layerOrder ? [...baseLayers].sort((a, b) => layerOrder.indexOf(a.id) - layerOrder.indexOf(b.id)) : baseLayers;
3126
3149
  return ordered.map((layer) => ({
3127
3150
  id: layer.id,
3128
3151
  visible: layerVisibility[layer.id] !== false,
@@ -3154,8 +3177,8 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
3154
3177
  });
3155
3178
  const paletteItems = useMemo(() => {
3156
3179
  if (!instrument.dragDrop) return [];
3157
- const p2 = instrument.dragDrop.palette;
3158
- return typeof p2 === "function" ? p2(record.state, record.config) : p2;
3180
+ const p = instrument.dragDrop.palette;
3181
+ return typeof p === "function" ? p(record.state, record.config) : p;
3159
3182
  }, [instrument.dragDrop, record.state, record.config]);
3160
3183
  const layersWithFeedback = useMemo(() => {
3161
3184
  if (!dragDropResult.drag?.feedback) return canvasLayers;
@@ -3269,6 +3292,7 @@ function TrialRuntime({ record, instrument, store, isLast, chrome, suppress }) {
3269
3292
  capability: annotationsCap,
3270
3293
  state: record.state,
3271
3294
  config: record.config,
3295
+ trial: { id: record.id, view: record.view },
3272
3296
  annotations,
3273
3297
  activeToolId: resolvedToolId
3274
3298
  }
@@ -3310,9 +3334,17 @@ function initialView(instrument) {
3310
3334
  if (typeof declared === "function") return null;
3311
3335
  return declared ? structuredClone(declared) : { ...DEFAULT_VIEW2, pan: { ...DEFAULT_VIEW2.pan } };
3312
3336
  }
3313
- function addTrial(trials, instruments, instrumentName) {
3337
+ function seedConfig(defaults, seed) {
3338
+ if (!seed) return defaults;
3339
+ const out = { ...defaults };
3340
+ for (const [key, value] of Object.entries(seed)) {
3341
+ if (value !== void 0) out[key] = value;
3342
+ }
3343
+ return out;
3344
+ }
3345
+ function addTrial(trials, instruments, instrumentName, options = {}) {
3314
3346
  const instrument = findInstrument(instruments, instrumentName);
3315
- const config = instrument.defaultConfig();
3347
+ const config = seedConfig(instrument.defaultConfig(), options.config);
3316
3348
  const state = instrument.initialState(config);
3317
3349
  const record = {
3318
3350
  id: crypto.randomUUID(),
@@ -3322,6 +3354,7 @@ function addTrial(trials, instruments, instrumentName) {
3322
3354
  view: initialView(instrument),
3323
3355
  undoStack: { past: [], future: [] }
3324
3356
  };
3357
+ if (options.config) record.configSeed = options.config;
3325
3358
  return [...trials, record];
3326
3359
  }
3327
3360
  function cloneTrial(trials, id) {
@@ -3348,7 +3381,7 @@ function resetTrial(trials, id, instruments) {
3348
3381
  const current = trials[idx];
3349
3382
  if (!current) return trials;
3350
3383
  const instrument = findInstrument(instruments, current.instrumentName);
3351
- const config = instrument.defaultConfig();
3384
+ const config = seedConfig(instrument.defaultConfig(), current.configSeed);
3352
3385
  const state = instrument.initialState(config);
3353
3386
  const reset = {
3354
3387
  ...current,
@@ -3394,7 +3427,7 @@ function LabHeader() {
3394
3427
  // Held at null so the control stays an "add one" action rather than
3395
3428
  // reading as the current instrument.
3396
3429
  /* @__PURE__ */ jsx(
3397
- p,
3430
+ h,
3398
3431
  {
3399
3432
  className: "lk-lab-header__add-select",
3400
3433
  "aria-label": "Add trial",
@@ -3408,7 +3441,7 @@ function LabHeader() {
3408
3441
  )
3409
3442
  ),
3410
3443
  /* @__PURE__ */ jsx(
3411
- r3,
3444
+ r4,
3412
3445
  {
3413
3446
  className: "lk-lab-header__mode",
3414
3447
  ariaLabel: "Color mode",
@@ -3477,11 +3510,11 @@ var FLOAT_ZONE_ID = asNodeId("lk-workspace-floating");
3477
3510
  var PANEL_KIND = "panel";
3478
3511
  var KIND = "trial";
3479
3512
  function extentOf(store, id) {
3480
- const p2 = store.getNode(id)?.membership?.placement;
3481
- if (!p2) return null;
3513
+ const p = store.getNode(id)?.membership?.placement;
3514
+ if (!p) return null;
3482
3515
  const out = {};
3483
- if (p2.size) out.size = p2.size;
3484
- if (p2.span) out.span = p2.span;
3516
+ if (p.size) out.size = p.size;
3517
+ if (p.span) out.span = p.span;
3485
3518
  return Object.keys(out).length > 0 ? out : null;
3486
3519
  }
3487
3520
  function PanelFrame({ panel, floating = false }) {
@@ -3502,11 +3535,11 @@ function Workspace({
3502
3535
  viewport
3503
3536
  }) {
3504
3537
  const items = Children.toArray(children);
3505
- const tilePanels = useMemo(() => (panels ?? []).filter((p2) => p2.as === "tile"), [panels]);
3506
- const floatPanels = useMemo(() => (panels ?? []).filter((p2) => p2.as === "floating"), [panels]);
3538
+ const tilePanels = useMemo(() => (panels ?? []).filter((p) => p.as === "tile"), [panels]);
3539
+ const floatPanels = useMemo(() => (panels ?? []).filter((p) => p.as === "floating"), [panels]);
3507
3540
  const wantedIds = [
3508
- ...items.map((_2, i) => ids?.[i] ?? `lk-ws-${i}`),
3509
- ...tilePanels.map((p2) => `lk-panel-${p2.key}`)
3541
+ ...items.map((_, i) => ids?.[i] ?? `lk-ws-${i}`),
3542
+ ...tilePanels.map((p) => `lk-panel-${p.key}`)
3510
3543
  ];
3511
3544
  const idKey = wantedIds.join(",");
3512
3545
  const idKeyRef = useRef(null);
@@ -3583,7 +3616,7 @@ function Workspace({
3583
3616
  const chrome = useMemo(() => {
3584
3617
  const byId = new Map(nodeIds.map((id, i) => [id, items[i]]));
3585
3618
  const panelByNode = new Map(
3586
- tilePanels.map((p2) => [`lk-panel-${p2.key}`, p2])
3619
+ tilePanels.map((p) => [`lk-panel-${p.key}`, p])
3587
3620
  );
3588
3621
  return {
3589
3622
  [PANEL_KIND]: ({ node }) => {
@@ -3601,7 +3634,7 @@ function Workspace({
3601
3634
  }
3602
3635
  };
3603
3636
  }, [nodeIds, items, reorderable, tilePanels]);
3604
- const floatingLayer = floatPanels.length === 0 ? null : /* @__PURE__ */ jsx("div", { className: "lk-workspace__floating", children: floatPanels.map((p2) => /* @__PURE__ */ jsx(PanelFrame, { panel: p2, floating: true }, p2.key)) });
3637
+ const floatingLayer = floatPanels.length === 0 ? null : /* @__PURE__ */ jsx("div", { className: "lk-workspace__floating", children: floatPanels.map((p) => /* @__PURE__ */ jsx(PanelFrame, { panel: p, floating: true }, p.key)) });
3605
3638
  const grid = /* @__PURE__ */ jsx(
3606
3639
  Container,
3607
3640
  {
@@ -3644,8 +3677,8 @@ var NEBULA_SLOTS = [
3644
3677
  ];
3645
3678
  function buildNebula(colors) {
3646
3679
  const blobs = colors.map((c2, i) => {
3647
- const p2 = NEBULA_SLOTS[i % NEBULA_SLOTS.length];
3648
- return `radial-gradient(ellipse ${p2.sx} ${p2.sy} at ${p2.cx} ${p2.cy}, color-mix(in srgb, ${c2} 22%, transparent), transparent ${p2.stop})`;
3680
+ const p = NEBULA_SLOTS[i % NEBULA_SLOTS.length];
3681
+ return `radial-gradient(ellipse ${p.sx} ${p.sy} at ${p.cx} ${p.cy}, color-mix(in srgb, ${c2} 22%, transparent), transparent ${p.stop})`;
3649
3682
  });
3650
3683
  blobs.push("radial-gradient(ellipse at center, #0a0a18 0%, #02020a 100%)");
3651
3684
  return blobs.join(", ");
@@ -3734,8 +3767,8 @@ function Lab({
3734
3767
  return {
3735
3768
  instruments,
3736
3769
  trials,
3737
- addTrial: (instrumentName) => {
3738
- const next = addTrial(store.getState().trials, instruments, instrumentName);
3770
+ addTrial: (instrumentName, options) => {
3771
+ const next = addTrial(store.getState().trials, instruments, instrumentName, options);
3739
3772
  replaceTrials(next);
3740
3773
  },
3741
3774
  cloneTrial: (id) => {