@weasel-js/labkit 1.8.0 → 1.8.1

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 (189) hide show
  1. package/README.md +24 -0
  2. package/dist/_dts/{CanvasStackContext-C0fzFsQg.d.ts → CanvasStackContext-B05ZM04l.d.ts} +3 -1
  3. package/dist/_dts/{Stage-Cvwxdnfp.d.ts → Stage-CIJlbjrE.d.ts} +5 -3
  4. package/dist/_dts/{index-DRaswz91.d.ts → index-8geQNQet.d.ts} +83 -11
  5. package/dist/_dts/{index-DEHRoTXK.d.ts → index-DRi3V5sN.d.ts} +5 -3
  6. package/dist/_dts/{useTrialState-CS4TdWPW.d.ts → useTrialState-Ar0AFpCe.d.ts} +94 -87
  7. package/dist/canvas/index.d.ts +4 -4
  8. package/dist/canvas/index.js +2 -2
  9. package/dist/chrome/index.d.ts +2 -2
  10. package/dist/chrome/index.js +7 -7
  11. package/dist/{chunk-YX75W6EK.js → chunk-4RCVWX4M.js} +8 -8
  12. package/dist/chunk-4RCVWX4M.js.map +1 -0
  13. package/dist/{chunk-WIG6XHJ7.js → chunk-6IIKLXBZ.js} +13 -4
  14. package/dist/chunk-6IIKLXBZ.js.map +1 -0
  15. package/dist/{chunk-F4ACIK7Z.js → chunk-6Q6ACJ3H.js} +3 -3
  16. package/dist/{chunk-F4ACIK7Z.js.map → chunk-6Q6ACJ3H.js.map} +1 -1
  17. package/dist/{chunk-PMLZP7RT.js → chunk-7ALJOVGP.js} +5 -9
  18. package/dist/chunk-7ALJOVGP.js.map +1 -0
  19. package/dist/{chunk-XE5HJOPL.js → chunk-7FMZT2WL.js} +3 -3
  20. package/dist/{chunk-XE5HJOPL.js.map → chunk-7FMZT2WL.js.map} +1 -1
  21. package/dist/{chunk-XVUKDSYV.js → chunk-AVOILDN2.js} +2 -2
  22. package/dist/{chunk-XVUKDSYV.js.map → chunk-AVOILDN2.js.map} +1 -1
  23. package/dist/{chunk-2N7UBSS2.js → chunk-J5MDJEOZ.js} +3 -3
  24. package/dist/{chunk-2N7UBSS2.js.map → chunk-J5MDJEOZ.js.map} +1 -1
  25. package/dist/{chunk-NVKBL7ZE.js → chunk-KHB3KRRQ.js} +7 -7
  26. package/dist/chunk-KHB3KRRQ.js.map +1 -0
  27. package/dist/{chunk-XXACAPVU.js → chunk-NHIO2O3O.js} +31 -30
  28. package/dist/chunk-NHIO2O3O.js.map +1 -0
  29. package/dist/{chunk-EESJUSYR.js → chunk-QMAY74TP.js} +56 -5
  30. package/dist/chunk-QMAY74TP.js.map +1 -0
  31. package/dist/{chunk-YDE6XATS.js → chunk-RN7T7OBO.js} +44 -22
  32. package/dist/chunk-RN7T7OBO.js.map +1 -0
  33. package/dist/{chunk-3L4MLLQB.js → chunk-RXO5UEGT.js} +4 -11
  34. package/dist/chunk-RXO5UEGT.js.map +1 -0
  35. package/dist/{chunk-MOTDMMMN.js → chunk-SQX35X5V.js} +3 -3
  36. package/dist/{chunk-MOTDMMMN.js.map → chunk-SQX35X5V.js.map} +1 -1
  37. package/dist/{chunk-J5HJJDLF.js → chunk-T5OEUNCB.js} +7 -2
  38. package/dist/chunk-T5OEUNCB.js.map +1 -0
  39. package/dist/{chunk-RL2LOLNI.js → chunk-UX66USTN.js} +2 -2
  40. package/dist/chunk-UX66USTN.js.map +1 -0
  41. package/dist/{chunk-WURZTJGQ.js → chunk-V4SMGEYY.js} +4858 -4806
  42. package/dist/chunk-V4SMGEYY.js.map +1 -0
  43. package/dist/{chunk-E23Q6A3Y.js → chunk-VCK5T6EP.js} +3 -3
  44. package/dist/{chunk-E23Q6A3Y.js.map → chunk-VCK5T6EP.js.map} +1 -1
  45. package/dist/{chunk-T6DLGEJB.js → chunk-XWR2P6MR.js} +23 -6
  46. package/dist/chunk-XWR2P6MR.js.map +1 -0
  47. package/dist/{chunk-7KAETQSS.js → chunk-YZA73U4A.js} +7 -7
  48. package/dist/chunk-YZA73U4A.js.map +1 -0
  49. package/dist/config/index.d.ts +2 -2
  50. package/dist/config/index.js +2 -2
  51. package/dist/controls/index.js +5 -5
  52. package/dist/dragdrop/index.d.ts +1 -1
  53. package/dist/dragdrop/index.js +2 -2
  54. package/dist/index.d.ts +157 -15
  55. package/dist/index.js +893 -258
  56. package/dist/index.js.map +1 -1
  57. package/dist/layers/index.d.ts +1 -1
  58. package/dist/layers/index.js +3 -3
  59. package/dist/loupe/index.d.ts +175 -27
  60. package/dist/loupe/index.js +547 -141
  61. package/dist/loupe/index.js.map +1 -1
  62. package/dist/overview/index.js +5 -5
  63. package/dist/passthrough/weasel-ui.js +3 -3
  64. package/dist/primitives/index.d.ts +4 -19
  65. package/dist/primitives/index.js +7 -7
  66. package/dist/state/index.d.ts +5 -7
  67. package/dist/state/index.js +5 -5
  68. package/dist/styles.css +173 -38
  69. package/dist/surface/index.d.ts +20 -1
  70. package/dist/surface/index.js +2 -2
  71. package/dist/undo/index.d.ts +18 -7
  72. package/dist/undo/index.js +1 -1
  73. package/package.json +11 -11
  74. package/src/annotations/AnnotationOverlay.abandon.test.tsx +4 -0
  75. package/src/annotations/AnnotationOverlay.tsx +10 -1
  76. package/src/annotations/Annotations.less +2 -0
  77. package/src/annotations/Annotations.overlay.test.tsx +29 -0
  78. package/src/canvas/CanvasStack.test.tsx +35 -0
  79. package/src/canvas/CanvasStack.tsx +12 -1
  80. package/src/canvas/useLayerScheduler.ts +14 -0
  81. package/src/chrome/builtins.test.ts +14 -0
  82. package/src/chrome/builtins.tsx +17 -2
  83. package/src/chrome/regions/StatusRegion.tsx +20 -14
  84. package/src/chrome/regions/regions.test.tsx +28 -0
  85. package/src/chrome/types.ts +8 -0
  86. package/src/clock/clock.browser.test.less +5 -0
  87. package/src/clock/clock.browser.test.tsx +77 -0
  88. package/src/clock/clock.integration.test.tsx +95 -0
  89. package/src/clock/clockRegistry.test.ts +33 -0
  90. package/src/clock/clockRegistry.ts +46 -0
  91. package/src/clock/hooks.ts +44 -0
  92. package/src/clock/index.ts +2 -0
  93. package/src/clock/trialClock.test.ts +265 -0
  94. package/src/clock/trialClock.ts +245 -0
  95. package/src/clock/useClockLoop.ts +45 -0
  96. package/src/config/resolve.test.ts +12 -0
  97. package/src/config/resolve.ts +5 -0
  98. package/src/controls/ControlMatrix.tsx +56 -54
  99. package/src/index.ts +3 -2
  100. package/src/instrument/types.ts +21 -1
  101. package/src/lab/Lab.chrome.test.tsx +8 -1
  102. package/src/lab/Lab.presentation.test.tsx +113 -0
  103. package/src/lab/Lab.surface.test.tsx +20 -9
  104. package/src/lab/Lab.tsx +132 -257
  105. package/src/lab/LabLightbox.browser.test.less +17 -0
  106. package/src/lab/LabLightbox.browser.test.tsx +216 -0
  107. package/src/lab/LabShell.less +10 -3
  108. package/src/lab/LabSurface.tsx +129 -0
  109. package/src/lab/Workspace.stories.tsx +18 -0
  110. package/src/lab/Workspace.surface.test.tsx +4 -0
  111. package/src/lab/Workspace.test.tsx +37 -1
  112. package/src/lab/Workspace.tsx +34 -6
  113. package/src/lab/index.ts +2 -0
  114. package/src/lab/nebula.ts +30 -0
  115. package/src/lab/openLab.test.ts +81 -0
  116. package/src/lab/openLab.ts +119 -0
  117. package/src/lab/presentation.browser.test.less +5 -0
  118. package/src/lab/presentation.browser.test.tsx +119 -0
  119. package/src/lab/presentation.less +64 -0
  120. package/src/lab/presentation.tsx +47 -0
  121. package/src/lab/useFocusPick.ts +43 -0
  122. package/src/lightbox/Lightbox.browser.test.less +14 -0
  123. package/src/lightbox/Lightbox.browser.test.tsx +113 -0
  124. package/src/lightbox/Lightbox.less +128 -0
  125. package/src/lightbox/Lightbox.test.tsx +195 -0
  126. package/src/lightbox/Lightbox.tsx +205 -0
  127. package/src/lightbox/LightboxContext.ts +38 -0
  128. package/src/lightbox/LightboxLayers.tsx +40 -0
  129. package/src/lightbox/index.ts +8 -0
  130. package/src/lightbox/modal.ts +94 -0
  131. package/src/loupe/AGENTS.md +40 -2
  132. package/src/loupe/CanvasLoupe.tsx +5 -4
  133. package/src/loupe/DomLoupe.tsx +7 -5
  134. package/src/loupe/Loupe.less +15 -0
  135. package/src/loupe/LoupeBubble.tsx +35 -13
  136. package/src/loupe/SourceLoupe.browser.test.tsx +204 -0
  137. package/src/loupe/SourceLoupe.tsx +60 -0
  138. package/src/loupe/TrialLoupe.browser.test.tsx +342 -0
  139. package/src/loupe/TrialLoupe.tsx +126 -28
  140. package/src/loupe/canvasLens.ts +20 -17
  141. package/src/loupe/index.ts +26 -2
  142. package/src/loupe/sourceLens.test.ts +65 -0
  143. package/src/loupe/sourceLens.ts +97 -0
  144. package/src/loupe/types.test.ts +6 -0
  145. package/src/loupe/types.ts +109 -7
  146. package/src/loupe/useLoupe.test.tsx +188 -3
  147. package/src/loupe/useLoupe.ts +106 -11
  148. package/src/primitives/index.ts +2 -2
  149. package/src/specimen/Specimen.tsx +0 -7
  150. package/src/state/helpers.test.ts +4 -11
  151. package/src/state/helpers.ts +2 -8
  152. package/src/state/index.ts +0 -2
  153. package/src/state/openLabStore.test.ts +10 -8
  154. package/src/state/openLabStore.ts +1 -1
  155. package/src/state/store.test.ts +2 -2
  156. package/src/state/store.ts +15 -16
  157. package/src/state/types.ts +9 -9
  158. package/src/styles.less +2 -1
  159. package/src/surface/AGENTS.md +13 -0
  160. package/src/surface/index.ts +1 -0
  161. package/src/surface/useSurfaceTile.test.tsx +4 -0
  162. package/src/surface/useSurfaceTile.ts +17 -1
  163. package/src/surface/useTiledSurface.test.tsx +65 -0
  164. package/src/surface/useTiledSurface.ts +68 -1
  165. package/src/trial/Trial.stories.tsx +0 -1
  166. package/src/trial/Trial.test.tsx +40 -3
  167. package/src/trial/Trial.tsx +87 -43
  168. package/src/trial/TrialChrome.tsx +20 -2
  169. package/src/trial/trialOps.test.ts +1 -2
  170. package/src/trial/trialOps.ts +2 -3
  171. package/src/undo/stateOp.ts +21 -0
  172. package/src/undo/undoStack.ts +18 -6
  173. package/dist/chunk-3L4MLLQB.js.map +0 -1
  174. package/dist/chunk-7KAETQSS.js.map +0 -1
  175. package/dist/chunk-EESJUSYR.js.map +0 -1
  176. package/dist/chunk-J5HJJDLF.js.map +0 -1
  177. package/dist/chunk-NVKBL7ZE.js.map +0 -1
  178. package/dist/chunk-PMLZP7RT.js.map +0 -1
  179. package/dist/chunk-RL2LOLNI.js.map +0 -1
  180. package/dist/chunk-T6DLGEJB.js.map +0 -1
  181. package/dist/chunk-WIG6XHJ7.js.map +0 -1
  182. package/dist/chunk-WURZTJGQ.js.map +0 -1
  183. package/dist/chunk-XXACAPVU.js.map +0 -1
  184. package/dist/chunk-YDE6XATS.js.map +0 -1
  185. package/dist/chunk-YX75W6EK.js.map +0 -1
  186. package/src/primitives/StatusBar.less +0 -34
  187. package/src/primitives/StatusBar.stories.tsx +0 -24
  188. package/src/primitives/StatusBar.test.tsx +0 -25
  189. package/src/primitives/StatusBar.tsx +0 -28
@@ -1,11 +1,11 @@
1
1
  import { useAnnotationsOptional, useAnnotations } from './chunk-3RY6WL5T.js';
2
- import { Toolbar, ZoomControl, ScaleIndicator, FpsMeter, StatusBar } from './chunk-3L4MLLQB.js';
3
- import { usePersistedState } from './chunk-PMLZP7RT.js';
4
- import { ControlPanel } from './chunk-7KAETQSS.js';
2
+ import { Toolbar, ZoomControl, ScaleIndicator, FpsMeter } from './chunk-RXO5UEGT.js';
3
+ import { usePersistedState } from './chunk-7ALJOVGP.js';
4
+ import { ControlPanel } from './chunk-YZA73U4A.js';
5
5
  import { useLabContext } from './chunk-Z76KEZAX.js';
6
6
  import { LabStoreContext } from './chunk-2YILQ7Y5.js';
7
- import { b, A, E, C, o, O, h, P, H, v3, a2, u2, F, k, fe, X, U, Z, p6, z, B, Q, V, c3, n8, o4, n3 } from './chunk-WURZTJGQ.js';
8
- import { useState, useId, useContext, useMemo } from 'react';
7
+ import { b, A, E, C, o, O, h, P, H, v3, a2, u2, F, k, he, Z, U, te, p6, we, de, z, B, ie, V, n7, i5, r5, c3, n8, o4, n3 } from './chunk-V4SMGEYY.js';
8
+ import { useState, useId, Fragment as Fragment$1, useContext, useMemo } from 'react';
9
9
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
10
10
  import { useStore } from 'zustand/react';
11
11
 
@@ -195,7 +195,7 @@ function groupFolds(collapsedSections, id) {
195
195
  }
196
196
  return out;
197
197
  }
198
- var TRANSIENT_BUILTINS = /* @__PURE__ */ new Set(["loupe"]);
198
+ var TRANSIENT_BUILTINS = /* @__PURE__ */ new Set(["loupe", "expand"]);
199
199
  function builtinContributions(instrument, ctx, controls) {
200
200
  const out = [];
201
201
  const zoom = ctx.zoom;
@@ -239,7 +239,7 @@ function builtinContributions(instrument, ctx, controls) {
239
239
  region: "toolbar",
240
240
  group: "view",
241
241
  item: {
242
- icon: fe,
242
+ icon: he,
243
243
  label: "Loupe",
244
244
  pressed: ctx.loupeOn,
245
245
  onActivate: ctx.toggleLoupe
@@ -252,7 +252,7 @@ function builtinContributions(instrument, ctx, controls) {
252
252
  region: "viewport",
253
253
  group: "zoom",
254
254
  item: {
255
- icon: X,
255
+ icon: Z,
256
256
  label: "Zoom out",
257
257
  onActivate: () => ctx.setZoom(zoom / ZOOM_STEP)
258
258
  }
@@ -271,7 +271,7 @@ function builtinContributions(instrument, ctx, controls) {
271
271
  id: "actual-size",
272
272
  region: "viewport",
273
273
  group: "zoom",
274
- item: { icon: Z, label: "Actual size", onActivate: () => ctx.setZoom(1) }
274
+ item: { icon: te, label: "Actual size", onActivate: () => ctx.setZoom(1) }
275
275
  });
276
276
  out.push({
277
277
  id: "zoom-control",
@@ -350,6 +350,18 @@ function builtinContributions(instrument, ctx, controls) {
350
350
  )
351
351
  });
352
352
  }
353
+ if (ctx.canExpand) {
354
+ out.push({
355
+ id: "expand",
356
+ region: "titlebar",
357
+ end: true,
358
+ item: {
359
+ icon: ctx.expanded ? we : de,
360
+ label: ctx.expanded ? "Close expanded view" : "Expand trial",
361
+ onActivate: ctx.toggleExpanded
362
+ }
363
+ });
364
+ }
353
365
  out.push({
354
366
  id: "clone",
355
367
  region: "titlebar",
@@ -367,7 +379,7 @@ function builtinContributions(instrument, ctx, controls) {
367
379
  region: "titlebar",
368
380
  end: true,
369
381
  item: {
370
- icon: Q,
382
+ icon: ie,
371
383
  label: "Save snapshot",
372
384
  shortcut: "Mod+S",
373
385
  onActivate: () => ctx.saveSnapshot()
@@ -411,10 +423,10 @@ var ANNOTATION_TOOLS = [
411
423
  ];
412
424
  function labAnnotationTools(instruments) {
413
425
  const wanted = /* @__PURE__ */ new Set();
414
- for (const i of instruments) {
415
- if (i.annotations == null) continue;
416
- if (i.annotations.tools == null) return ANNOTATION_TOOLS;
417
- for (const id of i.annotations.tools) wanted.add(id);
426
+ for (const i2 of instruments) {
427
+ if (i2.annotations == null) continue;
428
+ if (i2.annotations.tools == null) return ANNOTATION_TOOLS;
429
+ for (const id of i2.annotations.tools) wanted.add(id);
418
430
  }
419
431
  return ANNOTATION_TOOLS.filter((t) => wanted.has(t.id));
420
432
  }
@@ -530,12 +542,22 @@ function StatusRegion({
530
542
  region = "status"
531
543
  }) {
532
544
  if (contributions.length === 0) return null;
533
- return /* @__PURE__ */ jsx(StatusBar, { children: contributions.map((c2) => {
534
- if (c2.render)
535
- return /* @__PURE__ */ jsx(StatusBar.Section, { end: c2.end, children: c2.render(ctx) }, c2.id);
536
- if (c2.region !== region || !c2.item) return null;
537
- const item = c2.item;
538
- return /* @__PURE__ */ jsx(StatusBar.Section, { end: c2.end, children: /* @__PURE__ */ jsx("span", { title: item.title, children: item.text }) }, c2.id);
545
+ let spaced = false;
546
+ return /* @__PURE__ */ jsx(n7, { divided: true, children: contributions.map((c2) => {
547
+ let body;
548
+ let title;
549
+ if (c2.render) body = c2.render(ctx);
550
+ else if (c2.region === region && c2.item) {
551
+ const item = c2.item;
552
+ body = item.text;
553
+ title = item.title;
554
+ } else return null;
555
+ const spacer = c2.end && !spaced;
556
+ if (spacer) spaced = true;
557
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
558
+ spacer && /* @__PURE__ */ jsx(i5, {}),
559
+ /* @__PURE__ */ jsx(r5, { title, children: body })
560
+ ] }, c2.id);
539
561
  }) });
540
562
  }
541
563
  function groupsOf(contributions) {
@@ -779,5 +801,5 @@ function ViewportRegion({ contributions, ctx }) {
779
801
  }
780
802
 
781
803
  export { ANNOTATION_TOOLS, ANNOTATION_WEASEL_TOOLS, ExportMenu, LabAsideRegion, LabFooterRegion, LabHeaderRegion, LabSidebarRegion, MarkList, PaletteRegion, SidebarRegion, StatusRegion, TRANSIENT_BUILTINS, TitleBarRegion, ToolbarRegion, ViewportRegion, annotationToolInfo, builtinContributions, contributionsIn, labAnnotationTools, labContributions, mergeContributions, resolveLabTool, suppressContributions, toolContributions, useLabChromeContext };
782
- //# sourceMappingURL=chunk-YDE6XATS.js.map
783
- //# sourceMappingURL=chunk-YDE6XATS.js.map
804
+ //# sourceMappingURL=chunk-RN7T7OBO.js.map
805
+ //# sourceMappingURL=chunk-RN7T7OBO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/annotations/ExportMenu.tsx","../src/annotations/MarkList.tsx","../src/chrome/builtins.tsx","../src/annotations/toolMap.ts","../src/tools/labTool.ts","../src/chrome/merge.ts","../src/chrome/regions/SidebarRegion.tsx","../src/chrome/regions/StatusRegion.tsx","../src/chrome/regions/ToolbarRegion.tsx","../src/chrome/LabChrome.tsx","../src/chrome/regions/PaletteRegion.tsx","../src/chrome/regions/TitleBarRegion.tsx","../src/chrome/regions/ViewportRegion.tsx"],"names":["a","v","jsx","jsxs","u","c","p","i","n","Fragment","r","o","renderEntry"],"mappings":"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","file":"chunk-RN7T7OBO.js","sourcesContent":["import { ExportIcon } from '@weasel-js/ui';\nimport { useId, useState } from 'react';\nimport { Callout } from '../passthrough/weasel-ui';\nimport { Toolbar } from '../primitives/Toolbar';\nimport { useAnnotationsOptional } from './AnnotationsContext';\nimport type { CaptureResult } from './types';\n\nconst SCALES = [1, 2, 4] as const;\nconst FORMATS = [\n { id: 'png', label: 'PNG' },\n { id: 'svg', label: 'SVG' },\n] as const;\n\ntype Format = (typeof FORMATS)[number]['id'];\n\nfunction fileName(result: CaptureResult): string {\n return `${result.target.replace(/[^a-z0-9._-]+/gi, '-')}.${result.format}`;\n}\n\n/** A blob the person asked for, handed to the browser's own save flow. The\n * object URL is revoked on the next turn — revoking it synchronously races\n * the navigation the click starts. */\nfunction download(result: CaptureResult): void {\n const url = URL.createObjectURL(result.blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = fileName(result);\n a.click();\n setTimeout(() => URL.revokeObjectURL(url), 0);\n}\n\n/** PNG goes to the clipboard as an image. SVG goes as text: `image/svg+xml`\n * needs Chromium's `web ` prefix and nothing else reads it, while the markup\n * is what a person pasting a vector export actually wants. */\nasync function copy(result: CaptureResult): Promise<void> {\n if (result.format === 'svg') {\n await navigator.clipboard.writeText(await result.blob.text());\n return;\n }\n await navigator.clipboard.write([new ClipboardItem({ 'image/png': result.blob })]);\n}\n\n/**\n * Export a target's picture with its marks on it.\n *\n * A panel rather than a button because a mark belongs to a target: with more\n * than one pane, \"export\" on its own does not say what.\n *\n * Reads the store through the hook the way `<MarkList>` does — the chrome\n * context carries no instrument state and is not the route to it.\n */\nexport function ExportMenu() {\n const marks = useAnnotationsOptional();\n const [anchor, setAnchor] = useState<HTMLSpanElement | null>(null);\n const [open, setOpen] = useState(false);\n const [target, setTarget] = useState<string | null>(null);\n const [format, setFormat] = useState<Format>('png');\n const [scale, setScale] = useState<number>(2);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const titleId = useId();\n\n if (!marks) return null;\n const targets = marks.targets();\n const chosen = target ?? targets[0]?.id ?? null;\n\n const run = async (then: (r: CaptureResult) => void | Promise<void>): Promise<void> => {\n if (!chosen) return;\n setBusy(true);\n setError(null);\n try {\n await then(await marks.capture(chosen, { format, scale }));\n setOpen(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <span className=\"lk-export\" ref={setAnchor}>\n <Toolbar.Button\n iconOnly\n pressed={open}\n aria-label=\"Export\"\n title=\"Export\"\n onClick={() => setOpen((was) => !was)}\n >\n <ExportIcon size={16} />\n </Toolbar.Button>\n <Callout\n triggerRef={{ current: anchor }}\n isOpen={open}\n onOpenChange={setOpen}\n onDismiss={() => setOpen(false)}\n placement=\"bottom end\"\n aria-labelledby={titleId}\n >\n <div className=\"lk-export__panel\">\n <span className=\"lk-export__title\" id={titleId}>\n Export\n </span>\n\n <label className=\"lk-export__row\">\n <span>Target</span>\n {/* Native controls: role, name and keyboard operability for free,\n and a five-control panel is not the place to rebuild them. */}\n <select\n aria-label=\"Target\"\n value={chosen ?? ''}\n onChange={(e) => setTarget(e.target.value)}\n >\n {targets.map((t) => (\n <option key={t.id} value={t.id}>\n {t.id}\n </option>\n ))}\n </select>\n </label>\n\n {/* A <fieldset>/<legend> cannot do this: a legend is outside its\n fieldset's grid formatting context, so it will not take a grid\n column and every choice row drops below its own label. */}\n <div className=\"lk-export__row\">\n <span>Format</span>\n <span className=\"lk-export__choices\" role=\"radiogroup\" aria-label=\"Format\">\n {FORMATS.map((f) => (\n <label key={f.id}>\n <input\n type=\"radio\"\n name=\"lk-export-format\"\n checked={format === f.id}\n onChange={() => setFormat(f.id)}\n />\n {f.label}\n </label>\n ))}\n </span>\n </div>\n\n <div className=\"lk-export__row\">\n <span>Scale</span>\n <span className=\"lk-export__choices\" role=\"radiogroup\" aria-label=\"Scale\">\n {SCALES.map((s) => (\n <label key={s}>\n <input\n type=\"radio\"\n name=\"lk-export-scale\"\n checked={scale === s}\n onChange={() => setScale(s)}\n />\n {s}×\n </label>\n ))}\n </span>\n </div>\n\n <div className=\"lk-export__actions\">\n <button type=\"button\" disabled={busy || !chosen} onClick={() => run(download)}>\n Download\n </button>\n <button type=\"button\" disabled={busy || !chosen} onClick={() => run(copy)}>\n Copy\n </button>\n </div>\n\n {error ? (\n <p className=\"lk-export__error\" role=\"alert\">\n {error}\n </p>\n ) : null}\n </div>\n </Callout>\n </span>\n );\n}\n","import { CloseButton, Input } from '../passthrough/weasel-ui';\nimport { useAnnotations } from './AnnotationsContext';\nimport type { AnnotationMeaning } from './types';\n\n/** Props for `<MarkList>`. */\nexport interface MarkListProps {\n /** The instrument's vocabulary. Omitted, rows carry no status control —\n * a host that declared none owns what a mark means. */\n meaning?: AnnotationMeaning;\n /** The live config, for answering staleness. */\n config: unknown;\n}\n\n/** Every mark on this trial's targets: what it says, and whether it still\n * describes the picture under it.\n *\n * Reads the store through the hook, which re-renders on every mutation — so a\n * mark drawn on a pane appears here without anything wiring the two. */\nexport function MarkList({ meaning, config }: MarkListProps) {\n const marks = useAnnotations();\n const all = marks.query();\n const statuses = meaning?.statuses ?? [];\n\n if (all.length === 0) {\n return <div className=\"lk-mark-list__empty\">No marks yet</div>;\n }\n\n return (\n <ul className=\"lk-mark-list\">\n {all.map((a) => {\n const stale = marks.isStale(a, config);\n return (\n <li className=\"lk-mark-list__row\" key={a.id}>\n <div className=\"lk-mark-list__head\">\n <span className=\"lk-mark-list__kind\">{a.kind}</span>\n <span className=\"lk-mark-list__target\">{a.target}</span>\n {stale ? <span className=\"lk-mark-list__stale\">stale</span> : null}\n <CloseButton\n className=\"lk-mark-list__drop\"\n ariaLabel={`Remove ${a.kind} on ${a.target}`}\n onClick={() => marks.remove(a.id)}\n />\n </div>\n <Input\n aria-label={`Title of ${a.kind} on ${a.target}`}\n value={a.title ?? ''}\n placeholder=\"Untitled\"\n onChange={(next) => marks.update(a.id, { title: next })}\n />\n {statuses.length > 0 ? (\n // A native select: it carries role, name and keyboard operability\n // for free, and a sidebar row is not the place to rebuild them.\n <select\n className=\"lk-mark-list__status\"\n aria-label={`Status of ${a.kind} on ${a.target}`}\n value={a.status ?? ''}\n onChange={(e) => marks.update(a.id, { status: e.target.value })}\n >\n <option value=\"\">—</option>\n {statuses.map((s) => (\n <option key={s.id} value={s.id}>\n {s.label}\n </option>\n ))}\n </select>\n ) : null}\n </li>\n );\n })}\n </ul>\n );\n}\n","import {\n CloneIcon,\n CloseIcon,\n CollapseIcon,\n FitIcon,\n FullscreenIcon,\n LoupeIcon,\n RedoIcon,\n ResetIcon,\n SnapshotIcon,\n UndoIcon,\n ZoomInIcon,\n ZoomOutIcon,\n} from '@weasel-js/ui';\nimport { ExportMenu } from '../annotations/ExportMenu';\nimport { MarkList } from '../annotations/MarkList';\nimport type { ControlRenderer } from '../config/types';\nimport { ControlPanel } from '../controls/ControlPanel';\nimport type { Instrument } from '../instrument/types';\nimport { MenuButton } from '../passthrough/weasel-ui';\nimport { FpsMeter } from '../primitives/FpsMeter';\nimport { ScaleIndicator } from '../primitives/ScaleIndicator';\nimport { ZoomControl } from '../primitives/ZoomControl';\nimport type { TrialChromeContext, TrialContribution } from './types';\n\nconst ZOOM_STEP = 1.25;\n\n/** The property groups inside a contribution are folded under keys prefixed\n * with that contribution's id, so a group called `marks` cannot fold the\n * sidebar section of the same name. `ControlPanel` keys by bare label, so the\n * prefix goes on here and comes off again. */\nfunction groupFolds(\n collapsedSections: Readonly<Record<string, boolean>>,\n id: string,\n): Record<string, boolean> {\n const prefix = `${id}/`;\n const out: Record<string, boolean> = {};\n for (const [key, value] of Object.entries(collapsedSections)) {\n if (key.startsWith(prefix)) out[key.slice(prefix.length)] = value;\n }\n return out;\n}\n\n/** Built-in ids contributed only while runtime state says so — the loupe\n * toggle appears once a lens mounts, after the trial's first render, and the\n * expand toggle only inside a lightbox. */\nexport const TRANSIENT_BUILTINS: ReadonlySet<string> = new Set(['loupe', 'expand']);\n\n/**\n * The contributions a trial gets from what its instrument declared. This is\n * the whole \"declaring a capability provides the chrome\" rule — it replaces\n * the presence checks that used to be spread across the runtime.\n */\nexport function builtinContributions(\n instrument: Instrument,\n ctx: TrialChromeContext,\n controls?: Record<string, ControlRenderer>,\n): TrialContribution[] {\n const out: TrialContribution[] = [];\n const zoom = ctx.zoom;\n\n // Weasel history is the undo authority for marks, so declaring `annotations`\n // gets the buttons whether or not the instrument also declares `undo`.\n if (instrument.undo != null || instrument.annotations != null) {\n out.push({\n id: 'undo',\n region: 'toolbar',\n group: 'history',\n item: {\n icon: UndoIcon,\n label: 'Undo',\n shortcut: 'Mod+Z',\n disabled: !ctx.canUndo,\n onActivate: ctx.undo,\n },\n });\n out.push({\n id: 'redo',\n region: 'toolbar',\n group: 'history',\n item: {\n icon: RedoIcon,\n label: 'Redo',\n shortcut: 'Mod+Shift+Z',\n disabled: !ctx.canRedo,\n onActivate: ctx.redo,\n },\n });\n }\n\n // A tool id shares the contribution namespace, so a tool called `close`\n // collides with the built-in close button and throws. Annotation tools are\n // not here: the lab's rail carries them.\n for (const t of instrument.tools?.tools ?? []) {\n out.push({\n id: t.id,\n region: 'palette',\n group: t.group,\n item: { icon: t.icon, label: t.label, shortcut: t.shortcut },\n });\n }\n\n if (ctx.hasLoupe) {\n out.push({\n id: 'loupe',\n region: 'toolbar',\n group: 'view',\n item: {\n icon: LoupeIcon,\n label: 'Loupe',\n pressed: ctx.loupeOn,\n onActivate: ctx.toggleLoupe,\n },\n });\n }\n\n // Zoom acts on the view of the trial, so it is a viewport control. A trial\n // holding a non-2D view reports zoom as null and gets none of this.\n if ((instrument.canvas != null || instrument.stage != null) && zoom !== null) {\n out.push({\n id: 'zoom-out',\n region: 'viewport',\n group: 'zoom',\n item: {\n icon: ZoomOutIcon,\n label: 'Zoom out',\n onActivate: () => ctx.setZoom(zoom / ZOOM_STEP),\n },\n });\n out.push({\n id: 'zoom-in',\n region: 'viewport',\n group: 'zoom',\n item: {\n icon: ZoomInIcon,\n label: 'Zoom in',\n onActivate: () => ctx.setZoom(zoom * ZOOM_STEP),\n },\n });\n out.push({\n id: 'actual-size',\n region: 'viewport',\n group: 'zoom',\n item: { icon: FitIcon, label: 'Actual size', onActivate: () => ctx.setZoom(1) },\n });\n out.push({\n id: 'zoom-control',\n region: 'viewport',\n group: 'zoom',\n render: (c) => <ZoomControl zoom={c.zoom ?? 1} onZoomChange={c.setZoom} />,\n });\n out.push({\n id: 'scale',\n region: 'status',\n group: 'view',\n render: (c) => <ScaleIndicator zoom={c.zoom ?? 1} />,\n });\n if (instrument.canvas != null) {\n out.push({\n id: 'fps',\n region: 'status',\n group: 'view',\n end: true,\n render: () => <FpsMeter />,\n });\n }\n }\n\n if (instrument.annotations != null) {\n // A popover, so it takes the `render` escape: the panel picks a target,\n // and an icon button cannot ask which one.\n out.push({\n id: 'export',\n region: 'toolbar',\n group: 'trial',\n end: true,\n render: () => <ExportMenu />,\n });\n out.push({\n id: 'marks',\n region: 'sidebar',\n item: {\n title: 'Marks',\n body: <MarkList meaning={instrument.annotations.meaning} config={ctx.config} />,\n },\n });\n }\n\n if (Object.keys(ctx.configSchema.group.children).length > 0) {\n out.push({\n id: 'settings',\n region: 'sidebar',\n item: {\n title: 'Settings',\n body: (\n <ControlPanel\n schema={ctx.configSchema}\n config={ctx.config as Record<string, unknown>}\n auto={ctx.auto}\n setConfig={(key, value) => ctx.setConfig(String(key), value)}\n renderers={controls}\n collapsed={groupFolds(ctx.collapsedSections, 'settings')}\n onCollapse={(label, next) => ctx.setSectionCollapsed(`settings/${label}`, next)}\n />\n ),\n },\n });\n }\n\n if (ctx.savedSnapshots.length > 0) {\n // A menu is not an icon button, so it takes the render escape.\n out.push({\n id: 'snapshot-load',\n region: 'toolbar',\n group: 'trial',\n end: true,\n render: (c) => (\n <MenuButton\n aria-label=\"Load snapshot\"\n label=\"Load…\"\n items={c.savedSnapshots.map((sn) => ({ value: sn.id, label: sn.name }))}\n onAction={(id) => c.loadSnapshot(id)}\n />\n ),\n });\n }\n if (ctx.canExpand) {\n out.push({\n id: 'expand',\n region: 'titlebar',\n end: true,\n item: {\n icon: ctx.expanded ? CollapseIcon : FullscreenIcon,\n label: ctx.expanded ? 'Close expanded view' : 'Expand trial',\n onActivate: ctx.toggleExpanded,\n },\n });\n }\n out.push({\n id: 'clone',\n region: 'titlebar',\n end: true,\n item: { icon: CloneIcon, label: 'Clone trial', onActivate: ctx.clone },\n });\n out.push({\n id: 'reset',\n region: 'titlebar',\n end: true,\n item: { icon: ResetIcon, label: 'Reset trial', onActivate: ctx.reset },\n });\n out.push({\n id: 'snapshot',\n region: 'titlebar',\n end: true,\n item: {\n icon: SnapshotIcon,\n label: 'Save snapshot',\n shortcut: 'Mod+S',\n onActivate: () => ctx.saveSnapshot(),\n },\n });\n out.push({\n id: 'close',\n region: 'titlebar',\n end: true,\n item: {\n icon: CloseIcon,\n label: ctx.isLastTrial ? 'Cannot close the last trial' : 'Close trial',\n danger: true,\n disabled: ctx.isLastTrial,\n onActivate: ctx.close,\n },\n });\n\n return out;\n}\n","import type { BuiltinToolId } from '@weasel-js/core';\nimport {\n ArrowIcon,\n EllipseIcon,\n HandIcon,\n LineIcon,\n PencilIcon,\n RectIcon,\n SelectIcon,\n TextIcon,\n} from '@weasel-js/ui';\nimport type { InstrumentList } from '../instrument/types';\nimport type { TrialTool } from '../tools/types';\nimport type { AnnotationKind } from './types';\n\n/** Which weasel tool an annotation tool drives, and what kind of mark that\n * makes. `select` makes none. */\nexport interface AnnotationToolInfo {\n weaselTool: BuiltinToolId;\n kind?: AnnotationKind;\n}\n\n// `arrow` and `stroke` have no weasel tool of their own, so the kind cannot be\n// read back from the insert — the overlay takes it from the tool id it holds.\nconst TOOLS: Record<string, AnnotationToolInfo> = {\n select: { weaselTool: 'select' },\n stroke: { weaselTool: 'pencil', kind: 'stroke' },\n line: { weaselTool: 'line', kind: 'line' },\n arrow: { weaselTool: 'line', kind: 'arrow' },\n rect: { weaselTool: 'rect', kind: 'rect' },\n ellipse: { weaselTool: 'ellipse', kind: 'ellipse' },\n text: { weaselTool: 'text', kind: 'text' },\n};\n\n/** The tools a lab's rail gets when any instrument declares `annotations`.\n * Ids share the lab's contribution namespace with its own `tools`, and the\n * lab's tool slot holds whichever is active. */\nexport const ANNOTATION_TOOLS: readonly TrialTool[] = [\n // Not in `TOOLS`: an id the map does not carry is what leaves the overlay idle.\n { id: 'pointer', label: 'Interact', icon: HandIcon, group: 'annotate' },\n { id: 'select', label: 'Select', icon: SelectIcon, group: 'annotate' },\n { id: 'stroke', label: 'Freehand', icon: PencilIcon, group: 'annotate' },\n { id: 'line', label: 'Line', icon: LineIcon, group: 'annotate' },\n { id: 'arrow', label: 'Arrow', icon: ArrowIcon, group: 'annotate' },\n { id: 'rect', label: 'Rectangle', icon: RectIcon, group: 'annotate' },\n { id: 'ellipse', label: 'Ellipse', icon: EllipseIcon, group: 'annotate' },\n { id: 'text', label: 'Text', icon: TextIcon, group: 'annotate' },\n];\n\n/** The annotation tools a lab's rail carries for these instruments: none\n * without an annotating instrument, else the union of what each asks for. */\nexport function labAnnotationTools(instruments: InstrumentList): readonly TrialTool[] {\n const wanted = new Set<string>();\n for (const i of instruments) {\n if (i.annotations == null) continue;\n if (i.annotations.tools == null) return ANNOTATION_TOOLS;\n for (const id of i.annotations.tools) wanted.add(id);\n }\n return ANNOTATION_TOOLS.filter((t) => wanted.has(t.id));\n}\n\n/** What `id` drives, or undefined for a tool that is not one of these. */\nexport function annotationToolInfo(id: string | null): AnnotationToolInfo | undefined {\n return id === null ? undefined : TOOLS[id];\n}\n\n/** Every weasel tool the overlay has to mount for this palette to work. */\nexport const ANNOTATION_WEASEL_TOOLS: readonly BuiltinToolId[] = [\n ...new Set(Object.values(TOOLS).map((t) => t.weaselTool)),\n];\n","import { labAnnotationTools } from '../annotations/toolMap';\nimport type { InstrumentList } from '../instrument/types';\n\n/**\n * The tool the lab's slot holds, once its default is applied: a lab with any\n * annotating instrument starts in `pointer`, so a first click reaches the\n * instrument rather than the marks over it, or in the first annotation tool\n * its rail carries when `pointer` is not among them.\n */\nexport function resolveLabTool(slot: string | null, instruments: InstrumentList): string | null {\n if (slot !== null) return slot;\n const tools = labAnnotationTools(instruments);\n if (tools.some((t) => t.id === 'pointer')) return 'pointer';\n return tools[0]?.id ?? null;\n}\n","/**\n * Concatenate contribution bundles into one list, preserving order — order\n * decides how a region lays its contributions out, so it is part of the\n * result, not an accident of it.\n *\n * Throws on a duplicate id rather than dropping one: a contribution silently\n * losing to a later bundle is the failure a registry exists to prevent.\n */\nexport function mergeContributions<T extends { id: string }>(\n ...bundles: readonly (readonly T[])[]\n): T[] {\n const out: T[] = [];\n const seen = new Set<string>();\n for (const bundle of bundles) {\n for (const entry of bundle) {\n if (seen.has(entry.id)) {\n throw new Error(\n `[labkit] mergeContributions: duplicate contribution id \"${entry.id}\". ` +\n 'Two bundles registered the same id; rename one, or suppress the ' +\n 'built-in before adding yours.',\n );\n }\n seen.add(entry.id);\n out.push(entry);\n }\n }\n return out;\n}\n\n/**\n * Drop contributions by id. Throws when an id is not present: a typo that\n * silently suppresses nothing is the same class of bug as a duplicate id\n * silently winning. `transient` names ids that are legitimately absent at\n * times — contributed only while some runtime state holds.\n */\nexport function suppressContributions<T extends { id: string }>(\n bundle: readonly T[],\n ids: readonly string[],\n transient: ReadonlySet<string> = new Set(),\n): T[] {\n const present = new Set(bundle.map((c) => c.id));\n for (const id of ids) {\n if (!present.has(id) && !transient.has(id)) {\n throw new Error(\n `[labkit] cannot suppress \"${id}\": no contribution with that id. ` +\n `Present ids: ${[...present].join(', ')}`,\n );\n }\n }\n const drop = new Set(ids);\n return bundle.filter((c) => !drop.has(c.id));\n}\n","import { type StanceProps, useStance } from '@weasel-js/ui';\nimport type { ReactNode } from 'react';\nimport type {\n RegionContribution,\n SidebarSection,\n SidebarSlotContext,\n TrialChromeContext,\n} from '../types';\n\n/** Props for `<SidebarRegion>`. */\nexport interface SidebarRegionProps<TCtx extends SidebarSlotContext = TrialChromeContext> {\n contributions: readonly RegionContribution<NoInfer<TCtx>>[];\n ctx: TCtx;\n /** Which region's items to lay out. */\n region?: string;\n}\n\nfunction Section({\n title,\n collapsed,\n onCollapsedChange,\n onUndock,\n children,\n stance,\n tone,\n}: StanceProps & {\n title: string;\n collapsed: boolean;\n onCollapsedChange: (collapsed: boolean) => void;\n onUndock?: () => void;\n children: ReactNode;\n}) {\n return (\n <section className=\"lk-sidebar-section\" {...useStance({ stance, tone })}>\n <div className=\"lk-sidebar-section__bar\">\n <button\n type=\"button\"\n className=\"lk-sidebar-section__head\"\n aria-expanded={!collapsed}\n onClick={() => onCollapsedChange(!collapsed)}\n >\n {title}\n </button>\n {onUndock ? (\n <button\n type=\"button\"\n className=\"lk-sidebar-section__undock\"\n aria-label={`Undock ${title}`}\n title={`Undock ${title}`}\n onClick={onUndock}\n >\n ⧉\n </button>\n ) : null}\n </div>\n {collapsed ? null : <div className=\"lk-sidebar-section__body\">{children}</div>}\n </section>\n );\n}\n\n/** Lays `sidebar` contributions out as titled, collapsible sections — a\n * trial's, or the lab's. A section the trial has torn out is not rendered\n * here — `Trial` portals its body into the workspace instead. A context with\n * no `undockPanel` offers no tear-out. */\nexport function SidebarRegion<TCtx extends SidebarSlotContext = TrialChromeContext>({\n contributions,\n ctx,\n region = 'sidebar',\n}: SidebarRegionProps<TCtx>) {\n if (contributions.length === 0) return null;\n return (\n <>\n {contributions.map((c) => {\n if (c.render) return <div key={c.id}>{c.render(ctx)}</div>;\n if (c.region !== region || !c.item) return null;\n if (ctx.undockedPanels?.includes(c.id)) return null;\n const item = c.item as SidebarSection;\n return (\n <Section\n key={c.id}\n title={item.title}\n stance={item.stance}\n tone={item.tone}\n collapsed={ctx.collapsedSections[c.id] ?? item.defaultCollapsed ?? false}\n onCollapsedChange={(next) => ctx.setSectionCollapsed(c.id, next)}\n onUndock={\n ctx.undockPanel && item.undockable !== false\n ? () => ctx.undockPanel?.(c.id, item.undockAs ?? 'tile')\n : undefined\n }\n >\n {item.body}\n </Section>\n );\n })}\n </>\n );\n}\n","import { StatusBar, StatusBarItem, StatusBarSpacer } from '@weasel-js/ui';\nimport { Fragment, type ReactNode } from 'react';\nimport type { RegionContribution, StatusReadout, TrialChromeContext } from '../types';\n\n/** Props for `<StatusRegion>`. */\nexport interface StatusRegionProps<TCtx = TrialChromeContext> {\n contributions: readonly RegionContribution<NoInfer<TCtx>>[];\n ctx: TCtx;\n /** Which region's readouts to lay out; the lab's footer is `footer`. */\n region?: string;\n}\n\n/** Lays a bar's readouts out. The first one marked `end`, and every one after\n * it, sits at the far end. */\nexport function StatusRegion<TCtx = TrialChromeContext>({\n contributions,\n ctx,\n region = 'status',\n}: StatusRegionProps<TCtx>) {\n if (contributions.length === 0) return null;\n let spaced = false;\n return (\n <StatusBar divided>\n {contributions.map((c) => {\n let body: ReactNode;\n let title: string | undefined;\n if (c.render) body = c.render(ctx);\n else if (c.region === region && c.item) {\n const item = c.item as StatusReadout;\n body = item.text;\n title = item.title;\n } else return null;\n const spacer = c.end && !spaced;\n if (spacer) spaced = true;\n return (\n <Fragment key={c.id}>\n {spacer && <StatusBarSpacer />}\n <StatusBarItem title={title}>{body}</StatusBarItem>\n </Fragment>\n );\n })}\n </StatusBar>\n );\n}\n","import type { ReactNode } from 'react';\nimport { Toolbar } from '../../primitives/Toolbar';\nimport type { RegionContribution, ToolbarItem, TrialChromeContext } from '../types';\n\n/** Props for `<ToolbarRegion>`. */\nexport interface ToolbarRegionProps<TCtx = TrialChromeContext> {\n contributions: readonly RegionContribution<NoInfer<TCtx>>[];\n ctx: TCtx;\n /** Which region's items to lay out; the lab's action bar is `header`. */\n region?: string;\n /** The bar's accessible name. */\n label?: string;\n}\n\ninterface Group<TCtx> {\n key: string;\n end: boolean;\n entries: RegionContribution<TCtx>[];\n}\n\n/** Bucket by group, preserving first-appearance order. Ungrouped\n * contributions each become their own bucket so they stay in place. */\nfunction groupsOf<TCtx>(contributions: readonly RegionContribution<TCtx>[]): Group<TCtx>[] {\n const groups: Group<TCtx>[] = [];\n const byKey = new Map<string, Group<TCtx>>();\n for (const c of contributions) {\n if (c.group == null) {\n groups.push({ key: c.id, end: c.end ?? false, entries: [c] });\n continue;\n }\n let g = byKey.get(c.group);\n if (!g) {\n g = { key: c.group, end: c.end ?? false, entries: [] };\n byKey.set(c.group, g);\n groups.push(g);\n }\n g.entries.push(c);\n }\n return groups;\n}\n\nfunction renderEntry<TCtx>(c: RegionContribution<TCtx>, ctx: TCtx, region: string): ReactNode {\n if (c.render) return <span key={c.id}>{c.render(ctx)}</span>;\n if (c.region !== region || !c.item) return null;\n const {\n icon: Icon,\n label,\n shortcut,\n disabled,\n danger,\n showLabel,\n pressed,\n onActivate,\n } = c.item as ToolbarItem<TCtx>;\n return (\n <Toolbar.Button\n key={c.id}\n iconOnly={!showLabel}\n variant={danger ? 'danger' : 'default'}\n disabled={disabled}\n pressed={pressed}\n aria-label={label}\n title={shortcut ? `${label} (${shortcut})` : label}\n onClick={() => onActivate(ctx)}\n >\n <Icon size={16} />\n {showLabel ? <span>{label}</span> : null}\n </Toolbar.Button>\n );\n}\n\n/** Lays a bar's contributions out, grouped by their `group`. */\nexport function ToolbarRegion<TCtx = TrialChromeContext>({\n contributions,\n ctx,\n region = 'toolbar',\n label = 'Trial actions',\n}: ToolbarRegionProps<TCtx>) {\n if (contributions.length === 0) return null;\n const groups = groupsOf(contributions);\n return (\n <Toolbar aria-label={label}>\n {groups.map((g) => (\n <Toolbar.Group key={g.key} end={g.end} aria-label={g.key}>\n {g.entries.map((c) => renderEntry(c, ctx, region))}\n </Toolbar.Group>\n ))}\n </Toolbar>\n );\n}\n","import { useContext, useMemo } from 'react';\nimport { useStore } from 'zustand/react';\nimport { useLabContext } from '../lab/LabContext';\nimport { LabStoreContext } from '../state/context';\nimport { usePersistedState } from '../state/usePersistedState';\nimport { resolveLabTool } from '../tools/labTool';\nimport type { TrialTool } from '../tools/types';\nimport type { LabChromeContext, LabContribution, LabRegion } from './labTypes';\nimport { mergeContributions } from './merge';\nimport { SidebarRegion } from './regions/SidebarRegion';\nimport { StatusRegion } from './regions/StatusRegion';\nimport { ToolbarRegion } from './regions/ToolbarRegion';\n\n/** The lab's chrome context: the lab itself, plus the lab's tool slot. Throws\n * outside a `<Lab>`. */\nexport function useLabChromeContext(): LabChromeContext {\n const lab = useLabContext();\n const storeCtx = useContext(LabStoreContext);\n if (!storeCtx) throw new Error('[labkit] lab chrome requires <LabStoreProvider>');\n const slot = useStore(storeCtx.store, (s) => s.activeToolId);\n const storeInstruments = useStore(storeCtx.store, (s) => s.instruments);\n const activeToolId = resolveLabTool(slot, storeInstruments ?? lab.instruments);\n const setLabTool = useStore(storeCtx.store, (s) => s.setLabTool);\n return useMemo(\n () => ({ ...lab, activeToolId, setActiveTool: setLabTool }),\n [lab, activeToolId, setLabTool],\n );\n}\n\n/** The lab's contributions for one region, in declaration order. */\nexport function contributionsIn<TCtx = LabChromeContext>(\n contributions: readonly LabContribution<TCtx>[],\n region: LabRegion,\n): LabContribution<TCtx>[] {\n return contributions.filter((c) => c.region === region);\n}\n\n/** The palette contributions a lab's `tools` stand for. Declaring a tool is\n * shorthand for contributing a palette item that writes the lab's tool\n * slot. */\nexport function toolContributions(tools: readonly TrialTool[]): LabContribution[] {\n return tools.map((t) => ({\n id: t.id,\n region: 'palette' as const,\n ...(t.group === undefined ? {} : { group: t.group }),\n item: {\n icon: t.icon,\n label: t.label,\n ...(t.shortcut === undefined ? {} : { shortcut: t.shortcut }),\n },\n }));\n}\n\n/** Everything the lab's chrome renders, from both of the ways a lab declares\n * it. Tools and contributions share one id namespace, so a tool colliding\n * with a contribution throws here rather than one of them silently losing. */\nexport function labContributions(\n tools: readonly TrialTool[] | undefined,\n chrome: readonly LabContribution[] | undefined,\n): LabContribution[] {\n return mergeContributions<LabContribution>(toolContributions(tools ?? []), [...(chrome ?? [])]);\n}\n\n/** Props shared by the lab's region mounts. Each takes the lab's whole\n * contribution list and lays out the part addressed to it. */\nexport interface LabRegionProps {\n contributions: readonly LabContribution[];\n}\n\n/** The lab's action bar, in the shell header beside the title. */\nexport function LabHeaderRegion({ contributions }: LabRegionProps) {\n const ctx = useLabChromeContext();\n return (\n <ToolbarRegion\n region=\"header\"\n label=\"Lab actions\"\n contributions={contributionsIn(contributions, 'header')}\n ctx={ctx}\n />\n );\n}\n\nfunction LabSections({ contributions, region }: LabRegionProps & { region: 'sidebar' | 'aside' }) {\n const lab = useLabChromeContext();\n const [collapsedSections, setFolds] = usePersistedState<Record<string, boolean>>(\n `lk-lab-${region}-folds`,\n {},\n { scope: 'lab' },\n );\n const ctx = useMemo(\n () => ({\n ...lab,\n collapsedSections,\n setSectionCollapsed: (key: string, collapsed: boolean) =>\n setFolds((f) => ({ ...f, [key]: collapsed })),\n }),\n [lab, collapsedSections, setFolds],\n );\n return (\n <SidebarRegion\n contributions={contributionsIn(contributions, region)}\n ctx={ctx}\n region={region}\n />\n );\n}\n\n/** The lab's sidebar sections. Fold state is a lab-scoped persisted value, so it\n * survives a reload in a stored lab and lasts the session in an unstored one. */\nexport function LabSidebarRegion({ contributions }: LabRegionProps) {\n return <LabSections contributions={contributions} region=\"sidebar\" />;\n}\n\n/** The lab's aside sections, on the far side of the workspace from the sidebar.\n * Fold state persists the way the sidebar's does. */\nexport function LabAsideRegion({ contributions }: LabRegionProps) {\n return <LabSections contributions={contributions} region=\"aside\" />;\n}\n\n/** The lab's readouts, in the shell footer below the workspace. */\nexport function LabFooterRegion({ contributions }: LabRegionProps) {\n const ctx = useLabChromeContext();\n return (\n <StatusRegion\n region=\"footer\"\n contributions={contributionsIn(contributions, 'footer')}\n ctx={ctx}\n />\n );\n}\n","import { ToolButton, ToolGroup, useRovingTabIndex } from '../../passthrough/weasel-ui';\nimport type { RegionContribution, ToolItem, ToolSlotContext, TrialChromeContext } from '../types';\n\n/** Props for `<PaletteRegion>`. */\nexport interface PaletteRegionProps<TCtx extends ToolSlotContext = TrialChromeContext> {\n contributions: readonly RegionContribution<NoInfer<TCtx>>[];\n ctx: TCtx;\n /** Which region's items to lay out. The lab's rail names the same box\n * `palette` too, so this only moves for a chrome that does not. */\n region?: string;\n}\n\n/** A tool strip: a trial's, or the lab's. Selection lives in whichever tool\n * slot the context carries; this region only reflects it. An item carrying\n * `onActivate` is a command instead — it presses, and never latches. */\nexport function PaletteRegion<TCtx extends ToolSlotContext = TrialChromeContext>({\n contributions,\n ctx,\n region = 'palette',\n}: PaletteRegionProps<TCtx>) {\n const { rootRef, onKeyDown } = useRovingTabIndex<HTMLDivElement>({ orientation: 'vertical' });\n if (contributions.length === 0) return null;\n return (\n <div\n ref={rootRef}\n className=\"lk-palette-region\"\n role=\"toolbar\"\n aria-label=\"Tools\"\n aria-orientation=\"vertical\"\n onKeyDown={onKeyDown}\n >\n <ToolGroup orientation=\"vertical\">\n {contributions.map((c) => {\n if (c.render) return <span key={c.id}>{c.render(ctx)}</span>;\n if (c.region !== region || !c.item) return null;\n const { icon: Icon, label, shortcut, disabled, onActivate } = c.item as ToolItem<TCtx>;\n return (\n <ToolButton\n key={c.id}\n icon={<Icon size={16} />}\n label={label}\n shortcut={shortcut}\n active={onActivate ? false : ctx.activeToolId === c.id}\n disabled={disabled}\n onClick={onActivate ? () => onActivate(ctx) : () => ctx.setActiveTool(c.id)}\n />\n );\n })}\n </ToolGroup>\n </div>\n );\n}\n","import type { ReactNode } from 'react';\nimport type { TrialChromeContext, TrialContribution } from '../types';\n\n/** Which end of the title bar a `<TitleBarRegion>` lays out: `'lead'` renders\n * the contributions that leave `end` unset, before the title; `'actions'`\n * renders the ones that set it, after everything else. */\nexport type TitleBarPlacement = 'lead' | 'actions';\n\n/** Props for `<TitleBarRegion>`. */\nexport interface TitleBarRegionProps {\n contributions: readonly TrialContribution[];\n ctx: TrialChromeContext;\n /** Defaults to `'actions'`, where the built-ins live. */\n placement?: TitleBarPlacement;\n}\n\nfunction renderEntry(c: TrialContribution, ctx: TrialChromeContext): ReactNode {\n if (c.render) return <span key={c.id}>{c.render(ctx)}</span>;\n if (c.region !== 'titlebar' || !c.item) return null;\n const { icon: Icon, label, shortcut, disabled, danger, onActivate } = c.item;\n return (\n <button\n key={c.id}\n type=\"button\"\n className={`lk-titlebar-button${danger ? ' lk-titlebar-button--danger' : ''}`}\n disabled={disabled}\n aria-label={label}\n title={shortcut ? `${label} (${shortcut})` : label}\n onClick={() => onActivate(ctx)}\n // The bar behind these buttons is the window drag surface, so a press\n // that lands here must not also start a drag.\n onPointerDown={(e) => e.stopPropagation()}\n onMouseDown={(e) => e.stopPropagation()}\n >\n <Icon size={16} />\n </button>\n );\n}\n\n/** Lays a trial's `titlebar` contributions out at one end of the bar. */\nexport function TitleBarRegion({ contributions, ctx, placement = 'actions' }: TitleBarRegionProps) {\n const wanted = placement === 'actions';\n const mine = contributions.filter((c) => (c.end ?? false) === wanted);\n if (mine.length === 0) return null;\n return (\n <span className={wanted ? 'lk-trial__titlebar-actions' : 'lk-trial__titlebar-lead'}>\n {mine.map((c) => renderEntry(c, ctx))}\n </span>\n );\n}\n","import { useRovingTabIndex } from '../../passthrough/weasel-ui';\nimport type { TrialChromeContext, TrialContribution } from '../types';\n\n/** Props for `<ViewportRegion>`. */\nexport interface ViewportRegionProps {\n contributions: readonly TrialContribution[];\n ctx: TrialChromeContext;\n}\n\n/**\n * Controls acting on the view of a trial. Anchored inside the content well\n * rather than in the toolbar, which acts on the trial itself.\n */\nexport function ViewportRegion({ contributions, ctx }: ViewportRegionProps) {\n const { rootRef, onKeyDown } = useRovingTabIndex<HTMLDivElement>({ orientation: 'horizontal' });\n if (contributions.length === 0) return null;\n return (\n <div\n ref={rootRef}\n className=\"lk-viewport-controls\"\n role=\"toolbar\"\n aria-label=\"View\"\n onKeyDown={onKeyDown}\n >\n {contributions.map((c) => {\n if (c.render) {\n return (\n <span key={c.id} className=\"lk-viewport-controls__item\">\n {c.render(ctx)}\n </span>\n );\n }\n if (c.region !== 'viewport' || !c.item) return null;\n const { icon: Icon, label, disabled, onActivate } = c.item;\n return (\n <button\n key={c.id}\n type=\"button\"\n className=\"lk-viewport-controls__button\"\n aria-label={label}\n title={label}\n disabled={disabled}\n onClick={() => onActivate(ctx)}\n >\n <Icon size={16} />\n </button>\n );\n })}\n </div>\n );\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { CanvasStackContext } from './chunk-ZQIHJXP4.js';
2
- import { c3, w2, y } from './chunk-WURZTJGQ.js';
2
+ import { c3, w2, y } from './chunk-V4SMGEYY.js';
3
3
  import { jsx, jsxs } from 'react/jsx-runtime';
4
4
  import { useVisibleRaf, normalizeZoom } from '@weasel-js/core';
5
5
  import { useState, useRef, useEffect, useContext } from 'react';
@@ -204,14 +204,7 @@ function ZoomControl({
204
204
  )
205
205
  ] });
206
206
  }
207
- function StatusBar({ children }) {
208
- return /* @__PURE__ */ jsx("div", { className: "lk-status-bar", children });
209
- }
210
- function Section({ children, end }) {
211
- return /* @__PURE__ */ jsx("span", { className: `lk-status-bar-section${end ? " lk-status-bar-section--end" : ""}`, children });
212
- }
213
- StatusBar.Section = Section;
214
207
 
215
- export { FpsMeter, ScaleIndicator, StatusBar, Toolbar, ZoomControl };
216
- //# sourceMappingURL=chunk-3L4MLLQB.js.map
217
- //# sourceMappingURL=chunk-3L4MLLQB.js.map
208
+ export { FpsMeter, ScaleIndicator, Toolbar, ZoomControl };
209
+ //# sourceMappingURL=chunk-RXO5UEGT.js.map
210
+ //# sourceMappingURL=chunk-RXO5UEGT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/primitives/Toolbar.tsx","../src/primitives/fpsAverage.ts","../src/primitives/FpsMeter.tsx","../src/primitives/ScaleIndicator.tsx","../src/primitives/ZoomControl.tsx"],"names":["c","jsx","jsxs","w"],"mappings":"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","file":"chunk-RXO5UEGT.js","sourcesContent":["import type { MouseEventHandler, ReactNode } from 'react';\nimport { useRovingTabIndex } from '../passthrough/weasel-ui';\n\n/** Props for `<Toolbar>`. */\nexport interface ToolbarProps {\n children: ReactNode;\n /** Names the toolbar for assistive tech. Required by the APG pattern when a\n * view holds more than one. */\n 'aria-label'?: string;\n}\n\n/** A horizontal bar of controls. Fill it with `<Toolbar.Title>`,\n * `<Toolbar.Group>`, `<Toolbar.Button>` and `<Toolbar.Spacer>`. */\nexport function Toolbar({ children, 'aria-label': ariaLabel }: ToolbarProps) {\n const { rootRef, onKeyDown } = useRovingTabIndex<HTMLDivElement>({ orientation: 'horizontal' });\n return (\n <div\n ref={rootRef}\n className=\"lk-toolbar\"\n role=\"toolbar\"\n aria-label={ariaLabel}\n onKeyDown={onKeyDown}\n >\n {children}\n </div>\n );\n}\n\ninterface TitleProps {\n children: ReactNode;\n}\nfunction Title({ children }: TitleProps) {\n return <span className=\"lk-toolbar-title\">{children}</span>;\n}\n\n/** Props for `<Toolbar.Group>`. */\nexport interface ToolbarGroupProps {\n children: ReactNode;\n /** Pushes this group, and everything after it, to the far end. */\n end?: boolean;\n 'aria-label'?: string;\n}\n\n/** Related controls, kept tight and ruled off from their neighbors. Group\n * rather than separate with repeated `<Toolbar.Spacer>`: consecutive spacers\n * share the free space, so every gap collapses to the same slack and the bar\n * reads as one undifferentiated run. */\nfunction Group({ children, end, 'aria-label': ariaLabel }: ToolbarGroupProps) {\n return (\n // biome-ignore lint/a11y/useSemanticElements: a group inside role=\"toolbar\" is the APG pattern; the rule is local and cannot see the parent. <fieldset> means form controls, needs a <legend> to be named, and its UA min-width breaks flex children.\n <div\n className={`lk-toolbar-group${end ? ' lk-toolbar-group--end' : ''}`}\n role=\"group\"\n aria-label={ariaLabel}\n >\n {children}\n </div>\n );\n}\n\n/** Props for `<Toolbar.Button>`. */\nexport interface ToolbarButtonProps {\n children: ReactNode;\n onClick: MouseEventHandler<HTMLButtonElement>;\n disabled?: boolean;\n title?: string;\n /** Required when `children` is an icon with no text of its own. */\n 'aria-label'?: string;\n /** `danger` reddens on hover — for actions that discard work. */\n variant?: 'default' | 'danger';\n /** Square, for a button whose whole content is one glyph. */\n iconOnly?: boolean;\n /** Makes this a toggle: `aria-pressed`, and held down while true. */\n pressed?: boolean;\n}\n\nfunction Button({\n children,\n onClick,\n disabled,\n title,\n 'aria-label': ariaLabel,\n variant = 'default',\n iconOnly,\n pressed,\n}: ToolbarButtonProps) {\n const cls = [\n 'lk-toolbar-button',\n variant === 'danger' && 'lk-toolbar-button--danger',\n iconOnly && 'lk-toolbar-button--icon',\n pressed && 'lk-toolbar-button--pressed',\n ]\n .filter(Boolean)\n .join(' ');\n return (\n <button\n type=\"button\"\n className={cls}\n onClick={onClick}\n disabled={disabled}\n title={title}\n aria-label={ariaLabel ?? title}\n aria-pressed={pressed}\n >\n {children}\n </button>\n );\n}\n\nfunction Spacer() {\n return <span className=\"lk-toolbar-spacer\" aria-hidden=\"true\" />;\n}\n\n/** A label within a toolbar. */\nToolbar.Title = Title;\n/** A ruled-off run of related controls. */\nToolbar.Group = Group;\n/** A button within a toolbar. */\nToolbar.Button = Button;\n/** Flexible space that pushes what follows to the far end of the toolbar. */\nToolbar.Spacer = Spacer;\n","export function rollingAverage(samples: readonly number[]): number {\n if (samples.length === 0) return 0;\n const sum = samples.reduce((acc, n) => acc + n, 0);\n return Math.round(sum / samples.length);\n}\n","import { useVisibleRaf } from '@weasel-js/core';\nimport { useEffect, useRef, useState } from 'react';\nimport { rollingAverage } from './fpsAverage';\n\nconst SAMPLE_WINDOW = 30;\n\n/** A live frame-rate readout, averaged over the last 30 frames. */\nexport function FpsMeter() {\n const [fps, setFps] = useState(0);\n const samplesRef = useRef<number[]>([]);\n const lastTimeRef = useRef<number | null>(null);\n const rootRef = useRef<HTMLDivElement | null>(null);\n\n const loop = useVisibleRaf(\n (time) => {\n const last = lastTimeRef.current;\n if (last !== null) {\n const delta = time - last;\n if (delta > 0) {\n const samples = samplesRef.current;\n samples.push(1000 / delta);\n if (samples.length > SAMPLE_WINDOW) samples.shift();\n setFps(rollingAverage(samples));\n }\n }\n lastTimeRef.current = time;\n loop.request();\n },\n {\n target: rootRef,\n // The gap spent suspended is not a frame that took an hour to draw.\n onResume: () => {\n lastTimeRef.current = null;\n },\n },\n );\n\n useEffect(() => {\n loop.request();\n return () => loop.cancel();\n }, [loop]);\n\n return (\n <div className=\"lk-fps-meter\" ref={rootRef}>\n <span className=\"lk-fps-meter-label\">FPS</span>\n {/* The number is its own cell because it is the only part that changes:\n in one box with the label, every digit it gains or loses resizes the\n readout, and the status bar it sits in wraps to a second line and\n back — which in a small trial resizes the window. */}\n <span className=\"lk-fps-meter-value\">{fps}</span>\n </div>\n );\n}\n","import { useContext } from 'react';\nimport { CanvasStackContext } from '../canvas/CanvasStackContext';\n\n/** Props for `<ScaleIndicator>`. */\nexport interface ScaleIndicatorProps {\n /** Current view zoom factor. If omitted, reads from CanvasStackContext. Defaults to 1. */\n zoom?: number;\n /** World pixels per unit at zoom=1. Default: 1. */\n pixelsPerUnit?: number;\n /** Unit label. Default: 'px'. */\n unit?: string;\n /** Target bar width in display pixels. Default: 100. */\n targetWidth?: number;\n}\n\n// Rounds n to a \"nice\" number — 1, 2, 5, 10, 20, 50, ...\nfunction niceNumber(n: number): number {\n if (n <= 0) return 1;\n const exp = Math.floor(Math.log10(n));\n const fraction = n / 10 ** exp;\n let nice: number;\n if (fraction < 1.5) nice = 1;\n else if (fraction < 3.5) nice = 2;\n else if (fraction < 7.5) nice = 5;\n else nice = 10;\n return nice * 10 ** exp;\n}\n\n/** A scale bar: a labeled rule showing how far a round number of world units\n * is at the current zoom. Reads the zoom from the surrounding canvas stack\n * unless one is passed. */\nexport function ScaleIndicator({\n zoom,\n pixelsPerUnit = 1,\n unit = 'px',\n targetWidth = 100,\n}: ScaleIndicatorProps) {\n const ctxValue = useContext(CanvasStackContext);\n const effectiveZoom = zoom ?? ctxValue?.view.zoom ?? 1;\n const effectivePxPerUnit = pixelsPerUnit * effectiveZoom;\n const targetUnits = targetWidth / effectivePxPerUnit;\n const niceUnits = niceNumber(targetUnits);\n const barWidth = niceUnits * effectivePxPerUnit;\n return (\n <div className=\"lk-scale-indicator\" role=\"img\" aria-label={`Scale: ${niceUnits} ${unit}`}>\n <div className=\"lk-scale-indicator-bar\" style={{ width: `${barWidth}px` }} />\n <span className=\"lk-scale-indicator-label\">\n {niceUnits} {unit}\n </span>\n </div>\n );\n}\n","import { normalizeZoom } from '@weasel-js/core';\nimport { NumberField, Slider } from '../passthrough/weasel-ui';\n\n/** Props for `<ZoomControl>`. */\nexport interface ZoomControlProps {\n /** Current zoom as a scale factor: 1 is actual size. */\n zoom: number;\n /** Fired continuously while the slider drags. */\n onZoomChange: (zoom: number) => void;\n /** Fired when a drag settles or the field is committed. Defaults to\n * `onZoomChange`. */\n onZoomCommit?: (zoom: number) => void;\n /** Smallest selectable zoom. Defaults to 0.1 (10%). */\n min?: number;\n /** Largest selectable zoom. Defaults to 8 (800%). */\n max?: number;\n className?: string;\n}\n\n// Zoom is perceptually geometric — halving is as big a step as doubling — so\n// the slider runs on log2 and 100% lands mid-track for a symmetric range.\nconst toTrack = (zoom: number): number => Math.log2(zoom);\nconst fromTrack = (v: number): number => 2 ** v;\n\nconst clamp = (v: number, lo: number, hi: number): number => Math.max(lo, Math.min(hi, v));\n\n/** Zoom as a log-scaled slider with an editable percentage beside it. Pair it\n * with zoom-in / zoom-out buttons in a `<Toolbar.Group>`; this component owns\n * only the continuous part. */\nexport function ZoomControl({\n zoom,\n onZoomChange,\n onZoomCommit,\n min: minProp = 0.1,\n max: maxProp = 8,\n className,\n}: ZoomControlProps) {\n const min = normalizeZoom(minProp);\n const max = normalizeZoom(maxProp);\n const commit = onZoomCommit ?? onZoomChange;\n const emit = (track: number, done: boolean): void => {\n const next = clamp(fromTrack(track), min, max);\n (done ? commit : onZoomChange)(next);\n };\n\n return (\n <div className={`lk-zoom${className ? ` ${className}` : ''}`}>\n <Slider\n className=\"lk-zoom__slider\"\n ariaLabel=\"Zoom\"\n density=\"slim\"\n min={toTrack(min)}\n max={toTrack(max)}\n // Detents at the octaves plus actual size, so 25/50/100/200/400% are\n // reachable by drag rather than only by typing.\n stops={[-2, -1, 0, 1, 2]}\n thumbs={[{ value: clamp(toTrack(zoom), toTrack(min), toTrack(max)) }]}\n onInput={(next) => emit(next[0].value, false)}\n onChange={(next) => emit(next[0].value, true)}\n readoutPlacement=\"none\"\n />\n {/* `style: 'percent'` formats a fraction, so the field carries the scale\n factor itself and renders 0.5 as \"50%\". */}\n <NumberField\n className=\"lk-zoom__field\"\n width=\"fit\"\n aria-label=\"Zoom\"\n value={zoom}\n minValue={min}\n maxValue={max}\n step={0.01}\n hideSteppers\n ghost\n formatOptions={{ style: 'percent', maximumFractionDigits: 0 }}\n onChange={(next) => {\n if (!Number.isFinite(next)) return;\n commit(clamp(next, min, max));\n }}\n />\n </div>\n );\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { LabContext } from './chunk-Z76KEZAX.js';
2
- import { resolveConfigSchema } from './chunk-J5HJJDLF.js';
2
+ import { resolveConfigSchema } from './chunk-T5OEUNCB.js';
3
3
  import { isLeafVisible, fromConfigFields } from './chunk-WLMDTI2Q.js';
4
4
  import { valueAtPath } from './chunk-READTDQX.js';
5
5
  import { useContext, useMemo } from 'react';
@@ -88,5 +88,5 @@ function useConfigSchema(instrument) {
88
88
  }
89
89
 
90
90
  export { sectionTree, useConfigSchema };
91
- //# sourceMappingURL=chunk-MOTDMMMN.js.map
92
- //# sourceMappingURL=chunk-MOTDMMMN.js.map
91
+ //# sourceMappingURL=chunk-SQX35X5V.js.map
92
+ //# sourceMappingURL=chunk-SQX35X5V.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config/sectionTree.ts","../src/config/useConfigSchema.ts"],"names":[],"mappings":"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","file":"chunk-MOTDMMMN.js","sourcesContent":["import type { PrefGroup, PrefLeaf } from '@weasel-js/ui';\nimport { valueAtPath } from './path';\nimport type { ResolvedConfig } from './types';\nimport { isLeafVisible } from './visible';\n\n/**\n * A resolved schema rearranged so its sections are groups.\n *\n * `PrefsForm`'s rail navigates the schema tree — top-level groups are its\n * items, their group children indent under them. A resolved schema keeps its\n * sections beside the tree instead, so a flat schema's rail comes out as one\n * unnamed item however many headings the panel draws. This puts the sections\n * where the rail looks for them.\n */\nexport interface SectionTree {\n /** The rail's schema. Top-level children are the schema's root sections,\n * followed by any root node no section claimed. */\n group: PrefGroup;\n /** `config` renested to mirror `group` — the value tree `PrefsForm` takes. */\n values: Record<string, unknown>;\n /** The config path a path within `group` stands for. */\n pathAt: (railPath: string) => string;\n}\n\n/** Structural rather than `isPrefLeaf`, so the config entry stays free of a\n * runtime import from ui. */\nfunction isLeaf(node: PrefLeaf | PrefGroup): node is PrefLeaf {\n return 'kind' in node;\n}\n\n/** A section label as an object key: no dots, since a dotted path is how every\n * caller addresses a node, and a label is free to hold one. */\nfunction slugOf(label: string, taken: ReadonlySet<string>): string {\n const base =\n label\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '') || 'section';\n if (!taken.has(base)) return base;\n for (let n = 2; ; n += 1) if (!taken.has(`${base}-${n}`)) return `${base}-${n}`;\n}\n\n/** A subtree with everything `showIf` or `hidden` rules out cut away, or null\n * when that is all of it. A group whose children all went is not a rail item\n * the reader can open onto nothing. */\nfunction prune(\n resolved: ResolvedConfig,\n group: PrefGroup,\n at: string,\n config: Record<string, unknown>,\n showHidden: boolean,\n): PrefGroup | null {\n const children: PrefGroup['children'] = {};\n for (const [key, child] of Object.entries(group.children)) {\n const path = at === '' ? key : `${at}.${key}`;\n if (!isLeafVisible(resolved, path, config, showHidden)) continue;\n if (isLeaf(child)) {\n children[key] = child;\n continue;\n }\n const kept = prune(resolved, child, path, config, showHidden);\n if (kept) children[key] = kept;\n }\n return Object.keys(children).length === 0 ? null : { ...group, children };\n}\n\n/**\n * Turn a resolved schema's root sections into the top level of a `PrefGroup`,\n * for `PrefsForm`'s rail layout.\n *\n * A section naming a group brings that group in whole, so it lands as an\n * indented rail item; a section naming a leaf drops it loose into the\n * section's own pane. Sections nested under a group are left alone — they\n * bucket rows inside a pane, which is the panel's job and not the rail's.\n */\nexport function sectionTree(\n resolved: ResolvedConfig,\n config: Record<string, unknown>,\n showHidden = false,\n): SectionTree {\n const children: PrefGroup['children'] = {};\n const values: Record<string, unknown> = {};\n const slugs = new Set<string>();\n const claimed = new Set<string>();\n\n for (const section of resolved.sections) {\n if (section.at !== '') continue;\n const kept: PrefGroup['children'] = {};\n const held: Record<string, unknown> = {};\n for (const path of section.paths) {\n claimed.add(path);\n const child = resolved.group.children[path];\n if (!child || !isLeafVisible(resolved, path, config, showHidden)) continue;\n if (isLeaf(child)) {\n kept[path] = child;\n } else {\n const subtree = prune(resolved, child, path, config, showHidden);\n if (!subtree) continue;\n kept[path] = subtree;\n }\n held[path] = valueAtPath(config, path);\n }\n if (Object.keys(kept).length === 0) continue;\n const slug = slugOf(section.label, slugs);\n slugs.add(slug);\n children[slug] = { name: section.label, children: kept };\n values[slug] = held;\n }\n\n // Whatever no section claimed stays at the top level, where `prefRailItems`\n // gathers loose leaves into one item named for the root.\n for (const [key, child] of Object.entries(resolved.group.children)) {\n if (claimed.has(key) || !isLeafVisible(resolved, key, config, showHidden)) continue;\n if (isLeaf(child)) {\n children[key] = child;\n } else {\n const subtree = prune(resolved, child, key, config, showHidden);\n if (!subtree) continue;\n children[key] = subtree;\n }\n values[key] = valueAtPath(config, key);\n }\n\n return {\n group: { name: resolved.group.name, children },\n values,\n pathAt: (railPath) => {\n const cut = railPath.indexOf('.');\n const head = cut === -1 ? railPath : railPath.slice(0, cut);\n if (!slugs.has(head)) return railPath;\n return cut === -1 ? '' : railPath.slice(cut + 1);\n },\n };\n}\n","import { useContext, useMemo } from 'react';\nimport type { Instrument } from '../instrument/types';\nimport { LabContext } from '../lab/LabContext';\nimport { fromConfigFields } from './fromConfigField';\nimport { resolveConfigSchema } from './resolve';\nimport type { ResolvedConfig } from './types';\n\nconst NO_RULES: readonly [] = [];\n\n/**\n * An instrument's controls, resolved against the surrounding lab's rules.\n *\n * Resolution cannot happen in `defineInstrument` — rules are lab-scoped and\n * that runs at module load — so it happens here, at render. Both instrument\n * paths land in the same shape, so callers never branch on which was used.\n */\nexport function useConfigSchema(\n // biome-ignore lint/suspicious/noExplicitAny: instruments are stored contravariantly; see InstrumentList\n instrument: Instrument<any, any, any>, // eslint-disable-line @typescript-eslint/no-explicit-any\n): ResolvedConfig {\n const lab = useContext(LabContext);\n const rules = lab?.configRules ?? NO_RULES;\n return useMemo(\n () =>\n instrument.config\n ? resolveConfigSchema(instrument.config, rules)\n : fromConfigFields(instrument.configSchema?.() ?? []),\n [instrument, rules],\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/config/sectionTree.ts","../src/config/useConfigSchema.ts"],"names":[],"mappings":";;;;;;;AA0BA,SAAS,OAAO,IAAA,EAA8C;AAC5D,EAAA,OAAO,MAAA,IAAU,IAAA;AACnB;AAIA,SAAS,MAAA,CAAO,OAAe,KAAA,EAAoC;AACjE,EAAA,MAAM,IAAA,GACJ,KAAA,CACG,WAAA,EAAY,CACZ,OAAA,CAAQ,aAAA,EAAe,GAAG,CAAA,CAC1B,OAAA,CAAQ,QAAA,EAAU,EAAE,CAAA,IAAK,SAAA;AAC9B,EAAA,IAAI,CAAC,KAAA,CAAM,GAAA,CAAI,IAAI,GAAG,OAAO,IAAA;AAC7B,EAAA,KAAA,IAAS,IAAI,CAAA,IAAK,CAAA,IAAK,GAAG,IAAI,CAAC,MAAM,GAAA,CAAI,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,CAAC,CAAA,CAAE,CAAA,SAAU,CAAA,EAAG,IAAI,IAAI,CAAC,CAAA,CAAA;AAC/E;AAKA,SAAS,KAAA,CACP,QAAA,EACA,KAAA,EACA,EAAA,EACA,QACA,UAAA,EACkB;AAClB,EAAA,MAAM,WAAkC,EAAC;AACzC,EAAA,KAAA,MAAW,CAAC,KAAK,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,KAAA,CAAM,QAAQ,CAAA,EAAG;AACzD,IAAA,MAAM,OAAO,EAAA,KAAO,EAAA,GAAK,MAAM,CAAA,EAAG,EAAE,IAAI,GAAG,CAAA,CAAA;AAC3C,IAAA,IAAI,CAAC,aAAA,CAAc,QAAA,EAAU,IAAA,EAAM,MAAA,EAAQ,UAAU,CAAA,EAAG;AACxD,IAAA,IAAI,MAAA,CAAO,KAAK,CAAA,EAAG;AACjB,MAAA,QAAA,CAAS,GAAG,CAAA,GAAI,KAAA;AAChB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,OAAO,KAAA,CAAM,QAAA,EAAU,KAAA,EAAO,IAAA,EAAM,QAAQ,UAAU,CAAA;AAC5D,IAAA,IAAI,IAAA,EAAM,QAAA,CAAS,GAAG,CAAA,GAAI,IAAA;AAAA,EAC5B;AACA,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA,CAAE,MAAA,KAAW,IAAI,IAAA,GAAO,EAAE,GAAG,KAAA,EAAO,QAAA,EAAS;AAC1E;AAWO,SAAS,WAAA,CACd,QAAA,EACA,MAAA,EACA,UAAA,GAAa,KAAA,EACA;AACb,EAAA,MAAM,WAAkC,EAAC;AACzC,EAAA,MAAM,SAAkC,EAAC;AACzC,EAAA,MAAM,KAAA,uBAAY,GAAA,EAAY;AAC9B,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAY;AAEhC,EAAA,KAAA,MAAW,OAAA,IAAW,SAAS,QAAA,EAAU;AACvC,IAAA,IAAI,OAAA,CAAQ,OAAO,EAAA,EAAI;AACvB,IAAA,MAAM,OAA8B,EAAC;AACrC,IAAA,MAAM,OAAgC,EAAC;AACvC,IAAA,KAAA,MAAW,IAAA,IAAQ,QAAQ,KAAA,EAAO;AAChC,MAAA,OAAA,CAAQ,IAAI,IAAI,CAAA;AAChB,MAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,KAAA,CAAM,QAAA,CAAS,IAAI,CAAA;AAC1C,MAAA,IAAI,CAAC,SAAS,CAAC,aAAA,CAAc,UAAU,IAAA,EAAM,MAAA,EAAQ,UAAU,CAAA,EAAG;AAClE,MAAA,IAAI,MAAA,CAAO,KAAK,CAAA,EAAG;AACjB,QAAA,IAAA,CAAK,IAAI,CAAA,GAAI,KAAA;AAAA,MACf,CAAA,MAAO;AACL,QAAA,MAAM,UAAU,KAAA,CAAM,QAAA,EAAU,KAAA,EAAO,IAAA,EAAM,QAAQ,UAAU,CAAA;AAC/D,QAAA,IAAI,CAAC,OAAA,EAAS;AACd,QAAA,IAAA,CAAK,IAAI,CAAA,GAAI,OAAA;AAAA,MACf;AACA,MAAA,IAAA,CAAK,IAAI,CAAA,GAAI,WAAA,CAAY,MAAA,EAAQ,IAAI,CAAA;AAAA,IACvC;AACA,IAAA,IAAI,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,WAAW,CAAA,EAAG;AACpC,IAAA,MAAM,IAAA,GAAO,MAAA,CAAO,OAAA,CAAQ,KAAA,EAAO,KAAK,CAAA;AACxC,IAAA,KAAA,CAAM,IAAI,IAAI,CAAA;AACd,IAAA,QAAA,CAAS,IAAI,CAAA,GAAI,EAAE,MAAM,OAAA,CAAQ,KAAA,EAAO,UAAU,IAAA,EAAK;AACvD,IAAA,MAAA,CAAO,IAAI,CAAA,GAAI,IAAA;AAAA,EACjB;AAIA,EAAA,KAAA,MAAW,CAAC,KAAK,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,QAAA,CAAS,KAAA,CAAM,QAAQ,CAAA,EAAG;AAClE,IAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA,IAAK,CAAC,cAAc,QAAA,EAAU,GAAA,EAAK,MAAA,EAAQ,UAAU,CAAA,EAAG;AAC3E,IAAA,IAAI,MAAA,CAAO,KAAK,CAAA,EAAG;AACjB,MAAA,QAAA,CAAS,GAAG,CAAA,GAAI,KAAA;AAAA,IAClB,CAAA,MAAO;AACL,MAAA,MAAM,UAAU,KAAA,CAAM,QAAA,EAAU,KAAA,EAAO,GAAA,EAAK,QAAQ,UAAU,CAAA;AAC9D,MAAA,IAAI,CAAC,OAAA,EAAS;AACd,MAAA,QAAA,CAAS,GAAG,CAAA,GAAI,OAAA;AAAA,IAClB;AACA,IAAA,MAAA,CAAO,GAAG,CAAA,GAAI,WAAA,CAAY,MAAA,EAAQ,GAAG,CAAA;AAAA,EACvC;AAEA,EAAA,OAAO;AAAA,IACL,OAAO,EAAE,IAAA,EAAM,QAAA,CAAS,KAAA,CAAM,MAAM,QAAA,EAAS;AAAA,IAC7C,MAAA;AAAA,IACA,MAAA,EAAQ,CAAC,QAAA,KAAa;AACpB,MAAA,MAAM,GAAA,GAAM,QAAA,CAAS,OAAA,CAAQ,GAAG,CAAA;AAChC,MAAA,MAAM,OAAO,GAAA,KAAQ,EAAA,GAAK,WAAW,QAAA,CAAS,KAAA,CAAM,GAAG,GAAG,CAAA;AAC1D,MAAA,IAAI,CAAC,KAAA,CAAM,GAAA,CAAI,IAAI,GAAG,OAAO,QAAA;AAC7B,MAAA,OAAO,QAAQ,EAAA,GAAK,EAAA,GAAK,QAAA,CAAS,KAAA,CAAM,MAAM,CAAC,CAAA;AAAA,IACjD;AAAA,GACF;AACF;AC9HA,IAAM,WAAwB,EAAC;AASxB,SAAS,gBAEd,UAAA,EACgB;AAChB,EAAA,MAAM,GAAA,GAAM,WAAW,UAAU,CAAA;AACjC,EAAA,MAAM,KAAA,GAAQ,KAAK,WAAA,IAAe,QAAA;AAClC,EAAA,OAAO,OAAA;AAAA,IACL,MACE,UAAA,CAAW,MAAA,GACP,mBAAA,CAAoB,UAAA,CAAW,MAAA,EAAQ,KAAK,CAAA,GAC5C,gBAAA,CAAiB,UAAA,CAAW,YAAA,IAAe,IAAK,EAAE,CAAA;AAAA,IACxD,CAAC,YAAY,KAAK;AAAA,GACpB;AACF","file":"chunk-SQX35X5V.js","sourcesContent":["import type { PrefGroup, PrefLeaf } from '@weasel-js/ui';\nimport { valueAtPath } from './path';\nimport type { ResolvedConfig } from './types';\nimport { isLeafVisible } from './visible';\n\n/**\n * A resolved schema rearranged so its sections are groups.\n *\n * `PrefsForm`'s rail navigates the schema tree — top-level groups are its\n * items, their group children indent under them. A resolved schema keeps its\n * sections beside the tree instead, so a flat schema's rail comes out as one\n * unnamed item however many headings the panel draws. This puts the sections\n * where the rail looks for them.\n */\nexport interface SectionTree {\n /** The rail's schema. Top-level children are the schema's root sections,\n * followed by any root node no section claimed. */\n group: PrefGroup;\n /** `config` renested to mirror `group` — the value tree `PrefsForm` takes. */\n values: Record<string, unknown>;\n /** The config path a path within `group` stands for. */\n pathAt: (railPath: string) => string;\n}\n\n/** Structural rather than `isPrefLeaf`, so the config entry stays free of a\n * runtime import from ui. */\nfunction isLeaf(node: PrefLeaf | PrefGroup): node is PrefLeaf {\n return 'kind' in node;\n}\n\n/** A section label as an object key: no dots, since a dotted path is how every\n * caller addresses a node, and a label is free to hold one. */\nfunction slugOf(label: string, taken: ReadonlySet<string>): string {\n const base =\n label\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '') || 'section';\n if (!taken.has(base)) return base;\n for (let n = 2; ; n += 1) if (!taken.has(`${base}-${n}`)) return `${base}-${n}`;\n}\n\n/** A subtree with everything `showIf` or `hidden` rules out cut away, or null\n * when that is all of it. A group whose children all went is not a rail item\n * the reader can open onto nothing. */\nfunction prune(\n resolved: ResolvedConfig,\n group: PrefGroup,\n at: string,\n config: Record<string, unknown>,\n showHidden: boolean,\n): PrefGroup | null {\n const children: PrefGroup['children'] = {};\n for (const [key, child] of Object.entries(group.children)) {\n const path = at === '' ? key : `${at}.${key}`;\n if (!isLeafVisible(resolved, path, config, showHidden)) continue;\n if (isLeaf(child)) {\n children[key] = child;\n continue;\n }\n const kept = prune(resolved, child, path, config, showHidden);\n if (kept) children[key] = kept;\n }\n return Object.keys(children).length === 0 ? null : { ...group, children };\n}\n\n/**\n * Turn a resolved schema's root sections into the top level of a `PrefGroup`,\n * for `PrefsForm`'s rail layout.\n *\n * A section naming a group brings that group in whole, so it lands as an\n * indented rail item; a section naming a leaf drops it loose into the\n * section's own pane. Sections nested under a group are left alone — they\n * bucket rows inside a pane, which is the panel's job and not the rail's.\n */\nexport function sectionTree(\n resolved: ResolvedConfig,\n config: Record<string, unknown>,\n showHidden = false,\n): SectionTree {\n const children: PrefGroup['children'] = {};\n const values: Record<string, unknown> = {};\n const slugs = new Set<string>();\n const claimed = new Set<string>();\n\n for (const section of resolved.sections) {\n if (section.at !== '') continue;\n const kept: PrefGroup['children'] = {};\n const held: Record<string, unknown> = {};\n for (const path of section.paths) {\n claimed.add(path);\n const child = resolved.group.children[path];\n if (!child || !isLeafVisible(resolved, path, config, showHidden)) continue;\n if (isLeaf(child)) {\n kept[path] = child;\n } else {\n const subtree = prune(resolved, child, path, config, showHidden);\n if (!subtree) continue;\n kept[path] = subtree;\n }\n held[path] = valueAtPath(config, path);\n }\n if (Object.keys(kept).length === 0) continue;\n const slug = slugOf(section.label, slugs);\n slugs.add(slug);\n children[slug] = { name: section.label, children: kept };\n values[slug] = held;\n }\n\n // Whatever no section claimed stays at the top level, where `prefRailItems`\n // gathers loose leaves into one item named for the root.\n for (const [key, child] of Object.entries(resolved.group.children)) {\n if (claimed.has(key) || !isLeafVisible(resolved, key, config, showHidden)) continue;\n if (isLeaf(child)) {\n children[key] = child;\n } else {\n const subtree = prune(resolved, child, key, config, showHidden);\n if (!subtree) continue;\n children[key] = subtree;\n }\n values[key] = valueAtPath(config, key);\n }\n\n return {\n group: { name: resolved.group.name, children },\n values,\n pathAt: (railPath) => {\n const cut = railPath.indexOf('.');\n const head = cut === -1 ? railPath : railPath.slice(0, cut);\n if (!slugs.has(head)) return railPath;\n return cut === -1 ? '' : railPath.slice(cut + 1);\n },\n };\n}\n","import { useContext, useMemo } from 'react';\nimport type { Instrument } from '../instrument/types';\nimport { LabContext } from '../lab/LabContext';\nimport { fromConfigFields } from './fromConfigField';\nimport { resolveConfigSchema } from './resolve';\nimport type { ResolvedConfig } from './types';\n\nconst NO_RULES: readonly [] = [];\n\n/**\n * An instrument's controls, resolved against the surrounding lab's rules.\n *\n * Resolution cannot happen in `defineInstrument` — rules are lab-scoped and\n * that runs at module load — so it happens here, at render. Both instrument\n * paths land in the same shape, so callers never branch on which was used.\n */\nexport function useConfigSchema(\n // biome-ignore lint/suspicious/noExplicitAny: instruments are stored contravariantly; see InstrumentList\n instrument: Instrument<any, any, any>, // eslint-disable-line @typescript-eslint/no-explicit-any\n): ResolvedConfig {\n const lab = useContext(LabContext);\n const rules = lab?.configRules ?? NO_RULES;\n return useMemo(\n () =>\n instrument.config\n ? resolveConfigSchema(instrument.config, rules)\n : fromConfigFields(instrument.configSchema?.() ?? []),\n [instrument, rules],\n );\n}\n"]}
@@ -375,6 +375,11 @@ function resolveEntry(entry, key, path, sink) {
375
375
  ...entry.kind === null ? {} : { kind: entry.kind }
376
376
  });
377
377
  const patch = applyRules(seed, { key, path, default: entry.default }, sink.chain);
378
+ if (patch.kind === void 0) {
379
+ throw new Error(
380
+ `[labkit] "${path}" is an f.value whose kind cannot be inferred from its default (${String(entry.default)}) \u2014 declare it with a typed builder, or f.custom(kind, default) for a value with no row`
381
+ );
382
+ }
378
383
  const { autoResolve, unpinned, manual } = entry.options;
379
384
  return {
380
385
  ...patch,
@@ -400,5 +405,5 @@ function useResolvedConfig(schema, config, autoPaths) {
400
405
  }
401
406
 
402
407
  export { BaseNode, BooleanNode, ColorNode, CustomNode, EnumNode, GroupNode, ListNode, NumberNode, StringNode, ValueNode, applyRules, builtinRules, f, isConfigBranch, resolveConfigSchema, titleCase, useResolvedConfig };
403
- //# sourceMappingURL=chunk-J5HJJDLF.js.map
404
- //# sourceMappingURL=chunk-J5HJJDLF.js.map
408
+ //# sourceMappingURL=chunk-T5OEUNCB.js.map
409
+ //# sourceMappingURL=chunk-T5OEUNCB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/builder.ts","../src/config/rules.ts","../src/config/resolve.ts","../src/config/useResolvedConfig.ts"],"names":[],"mappings":"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","file":"chunk-T5OEUNCB.js","sourcesContent":["import type { Display, InfinityText } from '@weasel-js/quantity';\nimport type { PrefLeaf, PrefNumberUnit } from '@weasel-js/ui';\nimport { type Auto, isAuto } from './auto';\nimport type {\n Annotations,\n BranchAnnotations,\n BranchOptions,\n ConfigBranch,\n ConfigEntry,\n ConfigNode,\n ConfigOption,\n ConfigSchema,\n ConfigShape,\n ControlRenderer,\n InDialogOptions,\n InferConfig,\n NodeOptions,\n SectionOption,\n} from './types';\n\n/** Whether a schema entry is a branch rather than a leaf. */\nexport function isConfigBranch(entry: ConfigEntry): entry is ConfigBranch {\n return 'children' in entry;\n}\n\n/** Shared chaining surface. Every method clones, so a node can be reused as a\n * base for several leaves without one bleeding into the next. */\nexport abstract class BaseNode<T> implements ConfigNode<T> {\n abstract readonly kind: string | null;\n\n // `default` is a reserved word in a parameter position, so the field is\n // declared rather than taken as a parameter property.\n readonly default: T;\n\n constructor(\n defaultValue: T,\n readonly annotations: Readonly<Annotations> = {},\n readonly options: Readonly<NodeOptions> = {},\n ) {\n this.default = defaultValue;\n }\n\n /** Clone into the same subclass. Subclasses with extra constructor\n * arguments override this. */\n protected with(annotations: Annotations, options: NodeOptions): this {\n const Ctor = this.constructor as new (d: T, a: Annotations, o: NodeOptions) => this;\n return new Ctor(this.default, annotations, options);\n }\n\n protected ann(patch: Annotations): this {\n return this.with({ ...this.annotations, ...patch }, this.options);\n }\n\n protected opt(patch: NodeOptions): this {\n return this.with(this.annotations, { ...this.options, ...patch });\n }\n\n /** Human-readable label. Defaults to the key, title-cased. */\n label(name: string): this {\n return this.ann({ name });\n }\n\n /** Longer help text, surfaced as a tooltip on the row label. */\n describe(description: string): this {\n return this.ann({ description });\n }\n\n /** Omit from the panel unless it is asked to show hidden leaves. */\n hidden(): this {\n return this.ann({ hidden: true });\n }\n\n /** Pair with a sibling sharing this id, side-by-side on one row. */\n pair(pair: string): this {\n return this.ann({ pair });\n }\n\n /** Render under a named section heading. `collapsed` opens the section\n * folded, and `layout` and `pack` lay out its rows — say each on any one of\n * the section's leaves. */\n section(label: string, opts: Omit<SectionOption, 'label'> = {}): this {\n return this.opt({ section: { label, ...opts } });\n }\n\n /** Show this row only while the predicate holds. Presentational — the value\n * stays in config and the instrument still reads it. */\n showIf(predicate: (config: Record<string, unknown>) => boolean): this {\n return this.opt({ showIf: predicate });\n }\n\n /** Draw this one row yourself, keeping the kind, default and validation. */\n render(renderer: ControlRenderer): this {\n return this.opt({ render: renderer, dialog: undefined });\n }\n\n /** Draw this row as a button that opens a dialog, with `body` as the\n * dialog's content — for an editor too big for the row. */\n dialog(body: ControlRenderer, opts?: InDialogOptions): this {\n return this.opt({ dialog: { ...opts, body }, render: undefined });\n }\n\n /**\n * Compute this leaf's value while it is auto, instead of leaving it\n * `undefined`. The resolver is given the config with every other auto path\n * already resolved.\n */\n auto(resolve: (config: Record<string, unknown>) => T): this {\n return this.opt({ autoResolve: resolve as (c: Record<string, unknown>) => unknown });\n }\n\n /**\n * Start this leaf auto rather than pinned at its default. Takes `auto` and\n * nothing else — the constructor argument already declares the value, and a\n * second way to say it would fight with the first.\n */\n initial(value: Auto): this {\n if (!isAuto(value)) throw new Error('[labkit] .initial() takes `auto` and nothing else');\n return this.opt({ unpinned: true });\n }\n\n /** Never auto. The row's label does not toggle. */\n manual(): this {\n return this.opt({ manual: true });\n }\n}\n\n/** A numeric leaf, built by `f.number`. */\nexport class NumberNode extends BaseNode<number> {\n readonly kind = 'number';\n\n /** Bound the value. A number with both bounds renders as a slider. */\n range(min: number, max: number): this {\n return this.ann({ min, max });\n }\n\n step(step: number): this {\n return this.ann({ step });\n }\n\n /** A display suffix for the value — `'px'`, `'ms'`, `'°'`. Presentation only:\n * it is never parsed, and the stored value stays a plain number. */\n suffix(suffix: string): this {\n return this.ann({ suffix });\n }\n\n /** Show, speak and read the value through a display — `compact()` reads\n * `2.00M`. Presentation only: the stored value stays a plain number. */\n display(display: Display): this {\n return this.ann({ display });\n }\n\n /**\n * Store the value in one unit and edit it in another — radians stored,\n * degrees typed. `min`, `max` and `step` are declared in the stored unit\n * alongside the value, and convert with it. `prefUnit` builds one from a\n * `UnitSystem`.\n */\n unit(unit: PrefNumberUnit): this {\n return this.ann({ unit });\n }\n\n /**\n * Make the top of the range mean infinity: the slider's `max` stop stores\n * `Infinity` and reads `word` — `.range(0, 5000).endless('never')`. A value\n * of `Infinity` sits at that stop. `at: 'min'` puts it at the bottom\n * instead, as `-Infinity`; `'both'` does both.\n */\n endless(word?: InfinityText, at: 'min' | 'max' | 'both' = 'max'): this {\n return this.ann(word === undefined ? { endless: at } : { endless: at, infinity: word });\n }\n\n /** Force a slider even without both bounds. */\n slider(): this {\n return this.ann({ control: 'slider' });\n }\n\n /** Force a typed input even when both bounds are set. */\n input(): this {\n return this.ann({ control: 'input' });\n }\n}\n\n/** A true/false leaf, built by `f.boolean`; a checkbox unless `.toggle()`. */\nexport class BooleanNode extends BaseNode<boolean> {\n readonly kind = 'boolean';\n\n /** Ask for a switch rather than a checkbox. */\n toggle(): this {\n return this.ann({ control: 'switch' });\n }\n}\n\n/** A text leaf, built by `f.string`. */\nexport class StringNode extends BaseNode<string> {\n readonly kind = 'string';\n\n placeholder(placeholder: string): this {\n return this.ann({ placeholder });\n }\n\n maxLength(maxLength: number): this {\n return this.ann({ maxLength });\n }\n\n /** Milliseconds to debounce live writes. 0 commits every keystroke. */\n debounce(debounceMs: number): this {\n return this.ann({ debounceMs });\n }\n}\n\n/** A string-list leaf, built by `f.list`. */\nexport class ListNode extends BaseNode<string[]> {\n readonly kind = 'list';\n\n /** Shown in an empty entry. */\n placeholder(placeholder: string): this {\n return this.ann({ placeholder });\n }\n}\n\n/** A color leaf holding a hex string, built by `f.color`. */\nexport class ColorNode extends BaseNode<string> {\n readonly kind = 'color';\n\n /** The value carries alpha as `#rrggbbaa`, and the row gets an opacity\n * track beside the swatch. */\n alpha(): this {\n return this.ann({ alpha: true });\n }\n}\n\n/** A fixed-choice leaf, built by `f.enum`; a select unless `.radio()`. */\nexport class EnumNode<T extends string> extends BaseNode<T> {\n readonly kind = 'enum';\n\n /** Render as a segmented control rather than a select. */\n radio(): this {\n return this.ann({ control: 'radio' });\n }\n\n /** Relabel the choices. Named `labels` rather than `options` because\n * `options` is the node's own extras bag. */\n labels(options: readonly ConfigOption[]): this {\n return this.ann({ options });\n }\n}\n\n/** A leaf whose kind the rule chain decides. */\nexport class ValueNode<T> extends BaseNode<T> {\n readonly kind = null;\n}\n\n/** A leaf of a kind labkit does not ship a row for. */\nexport class CustomNode<T> extends BaseNode<T> {\n constructor(\n readonly kind: string,\n defaultValue: T,\n annotations: Readonly<Annotations> = {},\n options: Readonly<NodeOptions> = {},\n ) {\n super(defaultValue, annotations, options);\n }\n\n protected override with(annotations: Annotations, options: NodeOptions): this {\n return new CustomNode(this.kind, this.default, annotations, options) as this;\n }\n}\n\n/**\n * A branch of the schema: its children nest under its key, so a leaf inside\n * one is read and written at a dotted path. Distinct from `.section()`, which\n * puts a heading over sibling leaves and leaves their paths alone.\n */\nexport class GroupNode<S extends ConfigShape> implements ConfigBranch<S> {\n constructor(\n readonly children: S,\n readonly annotations: Readonly<BranchAnnotations> = {},\n readonly options: Readonly<BranchOptions> = {},\n ) {}\n\n private with(annotations: BranchAnnotations, options: BranchOptions): GroupNode<S> {\n return new GroupNode(this.children, annotations, options);\n }\n\n /** Heading for the group's rows. Defaults to the key, title-cased. */\n label(name: string): GroupNode<S> {\n return this.with({ ...this.annotations, name }, this.options);\n }\n\n /** Longer help text. Carried on the resolved `PrefGroup`; both weasel-ui's\n * `PrefsForm` and `ControlPanel` draw it under the heading. */\n describe(description: string): GroupNode<S> {\n return this.with({ ...this.annotations, description }, this.options);\n }\n\n /** Render this whole group under a named section heading. */\n section(label: string, opts: Omit<SectionOption, 'label'> = {}): GroupNode<S> {\n return this.with(this.annotations, { ...this.options, section: { label, ...opts } });\n }\n\n /** Show this group only while the predicate holds. Presentational — every\n * value beneath it stays in config and the instrument still reads it. */\n showIf(predicate: (config: Record<string, unknown>) => boolean): GroupNode<S> {\n return this.with(this.annotations, { ...this.options, showIf: predicate });\n }\n\n /** Put a button on the group's heading that returns every value beneath it\n * to its default, leaving the rest of the config alone. */\n resettable(): GroupNode<S> {\n return this.with(this.annotations, { ...this.options, resettable: true });\n }\n}\n\n/** Every default in a shape, nested the way the shape is. */\nfunction shapeDefaults(shape: ConfigShape): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(shape)) {\n out[key] = isConfigBranch(entry) ? shapeDefaults(entry.children) : entry.default;\n }\n return out;\n}\n\nconst expand = <T extends string>(\n options: readonly T[] | readonly ConfigOption[],\n): readonly ConfigOption[] =>\n options.map((o) => (typeof o === 'string' ? { value: o, label: o } : o));\n\n/**\n * Builds an instrument's config: the values, their types, and how each is\n * edited, in one declaration.\n *\n * ```ts\n * const config = f.schema({\n * showGrid: f.boolean(true),\n * cellSize: f.number(20).range(5, 80).step(5).label('Grid spacing'),\n * // Nested: read and written at `grid.color`.\n * grid: f.group({ color: f.color('#ffffff') }),\n * })\n * ```\n */\nexport const f = {\n number: (def: number): NumberNode => new NumberNode(def),\n boolean: (def: boolean): BooleanNode => new BooleanNode(def),\n string: (def: string): StringNode => new StringNode(def),\n color: (def: string): ColorNode => new ColorNode(def),\n /** A list of strings. Its row summarizes the list, and opens a dialog to\n * edit it one entry per field. */\n list: (def: readonly string[]): ListNode => new ListNode([...def]),\n\n /** A fixed set of choices. The default's literal type flows into the config\n * type, so `f.enum('fast', ['fast', 'accurate'])` gives\n * `'fast' | 'accurate'` with no `as const`. */\n enum: <const T extends string>(\n def: T,\n options: readonly T[] | readonly ConfigOption[],\n ): EnumNode<T> => new EnumNode<T>(def, { options: expand(options) }),\n\n /** A leaf with no declared kind: the rule chain decides, which is how a lab\n * states a convention like \"every `*Color` key is a color picker\". */\n value: <T>(def: T): ValueNode<T> => new ValueNode(def),\n\n /** A branch: its children nest under this key, so `grid: f.group({ size })`\n * puts the value at `config.grid.size` and addresses it as `'grid.size'`.\n * Groups nest as deeply as the schema wants. */\n group: <const S extends ConfigShape>(children: S): GroupNode<S> => new GroupNode(children),\n\n /** A leaf of a kind a lab supplies the control for, through `controls`. */\n custom: <T>(\n kind: string,\n def: T,\n validate?: (leaf: PrefLeaf, config: Record<string, unknown>) => string[],\n ): CustomNode<T> => new CustomNode(kind, def, {}, validate ? { validate } : {}),\n\n /** Collect leaves and groups into an instrument's config. */\n schema<S extends ConfigShape>(nodes: S): ConfigSchema<InferConfig<S>> {\n return { nodes, defaults: () => shapeDefaults(nodes) as InferConfig<S> };\n },\n};\n","import type { ConfigRule, ConfigRuleContext, LeafPatch } from './types';\n\n/** `cellSize` -> `Cell size`. Splits camelCase without shattering acronyms. */\nexport function titleCase(key: string): string {\n const words = key\n .replace(/[_-]+/g, ' ')\n .split(/(?<=[a-z0-9])(?=[A-Z])/)\n .join(' ')\n .trim()\n .toLowerCase();\n return words.length === 0 ? words : words[0].toUpperCase() + words.slice(1);\n}\n\n/** An `f.value` leaf takes its kind from the shape of its default. */\nconst kindFromValue: ConfigRule = (ctx) => {\n const t = typeof ctx.default;\n if (t === 'boolean' || t === 'number' || t === 'string') return { kind: t };\n const d = ctx.default;\n if (Array.isArray(d) && d.every((v) => typeof v === 'string')) return { kind: 'list' };\n return null;\n};\n\nconst labelFromKey: ConfigRule = (ctx) => ({ name: titleCase(ctx.key) });\n\n/** A number bounded on both sides is a slider; one bound or none is an input. */\nconst sliderWhenBounded: ConfigRule = (ctx) => {\n if (ctx.leaf.kind !== 'number') return null;\n const bounded = ctx.leaf.min !== undefined && ctx.leaf.max !== undefined;\n return bounded ? { control: 'slider' } : null;\n};\n\n/** `PrefBase.description` is required, so every leaf must end up with one. */\nconst descriptionDefault: ConfigRule = () => ({ description: '' });\n\n/**\n * labkit's own inference, expressed in the same vocabulary a consumer\n * extends. Runs after the consumer's rules, so a lab always wins.\n *\n * If one of these ever needs something a `ConfigRule` cannot express, the\n * seam is too weak — that is the point of writing them this way.\n */\nexport const builtinRules: readonly ConfigRule[] = [\n kindFromValue,\n labelFromKey,\n sliderWhenBounded,\n descriptionDefault,\n];\n\n/**\n * Fold a rule chain into one leaf patch. Gap-filling: a property already\n * settled is never overwritten, so the accumulator seeded with the author's\n * annotations beats every rule, and earlier rules beat later ones.\n */\nexport function applyRules(\n seed: LeafPatch,\n ctx: Omit<ConfigRuleContext, 'leaf'>,\n rules: readonly ConfigRule[],\n): LeafPatch {\n const acc: LeafPatch = { ...seed };\n for (const rule of rules) {\n const patch = rule({ ...ctx, leaf: acc });\n if (patch === null) continue;\n for (const [k, v] of Object.entries(patch)) {\n if (v !== undefined && acc[k as keyof LeafPatch] === undefined) {\n (acc as Record<string, unknown>)[k] = v;\n }\n }\n }\n return acc;\n}\n","import type { PrefGroup, PrefLeaf } from '@weasel-js/ui';\nimport { isConfigBranch } from './builder';\nimport { applyRules, builtinRules, titleCase } from './rules';\nimport type {\n ConfigEntry,\n ConfigRule,\n ConfigSchema,\n ConfigShape,\n ControlRenderer,\n DialogSpec,\n LeafPatch,\n ResolvedConfig,\n SectionSpec,\n} from './types';\n\n/** Drop keys whose value is undefined, so an unset annotation stays a gap the\n * rule chain can fill rather than a settled `undefined`. */\nfunction defined(annotations: Record<string, unknown>): LeafPatch {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(annotations)) if (v !== undefined) out[k] = v;\n return out as LeafPatch;\n}\n\n/** What the walk collects on the side of the `PrefGroup` tree. */\ninterface Sink {\n sections: SectionSpec[];\n showIf: Map<string, (config: Record<string, unknown>) => boolean>;\n renderers: Record<string, ControlRenderer>;\n dialogs: Record<string, DialogSpec>;\n resettable: Set<string>;\n chain: readonly ConfigRule[];\n}\n\n/**\n * Resolve a schema into the vocabulary weasel-ui renders, running each leaf\n * through the consumer's rules and then labkit's own.\n *\n * The group mirrors the schema's own nesting: an `f.group` becomes a nested\n * `PrefGroup`, so a leaf's dotted path within the tree is the path its value\n * is written at, and both `ControlPanel` and `PrefsForm` address it\n * identically.\n */\nexport function resolveConfigSchema<TC>(\n schema: ConfigSchema<TC>,\n rules: readonly ConfigRule[] = [],\n): ResolvedConfig {\n const sink: Sink = {\n sections: [],\n showIf: new Map(),\n renderers: {},\n dialogs: {},\n resettable: new Set(),\n chain: [...rules, ...builtinRules],\n };\n const group = resolveShape(schema.nodes, '', '', sink);\n return {\n group,\n sections: sink.sections,\n showIf: sink.showIf,\n renderers: sink.renderers,\n dialogs: sink.dialogs,\n resettable: sink.resettable,\n };\n}\n\n/** A section while it is still being filled. */\ntype OpenSection = Omit<SectionSpec, 'paths'> & { paths: string[] };\n\nfunction resolveShape(shape: ConfigShape, at: string, name: string, sink: Sink): PrefGroup {\n const children: Record<string, PrefLeaf | PrefGroup> = {};\n const sections = new Map<string, OpenSection>();\n\n for (const [key, entry] of Object.entries(shape)) {\n const path = at === '' ? key : `${at}.${key}`;\n children[key] = resolveEntry(entry, key, path, sink);\n\n const { section, showIf: predicate } = entry.options;\n if (section !== undefined) {\n let spec = sections.get(section.label);\n if (!spec) {\n spec = { at, label: section.label, paths: [] };\n sections.set(section.label, spec);\n sink.sections.push(spec);\n }\n spec.paths.push(path);\n // One node saying `collapsed` settles the section, so a schema does not\n // have to repeat it on every node under the heading.\n if (section.collapsed !== undefined) {\n spec.collapsed = (spec.collapsed ?? false) || section.collapsed;\n }\n if (section.layout !== undefined && spec.layout === undefined) spec.layout = section.layout;\n if (section.pack !== undefined && spec.pack === undefined) spec.pack = section.pack;\n }\n if (predicate) sink.showIf.set(path, predicate);\n if (isConfigBranch(entry) && entry.options.resettable) sink.resettable.add(path);\n if (!isConfigBranch(entry) && entry.options.render) sink.renderers[path] = entry.options.render;\n if (!isConfigBranch(entry) && entry.options.dialog) sink.dialogs[path] = entry.options.dialog;\n }\n\n return { name, children };\n}\n\nfunction resolveEntry(\n entry: ConfigEntry,\n key: string,\n path: string,\n sink: Sink,\n): PrefLeaf | PrefGroup {\n if (isConfigBranch(entry)) {\n const group = resolveShape(\n entry.children,\n path,\n entry.annotations.name ?? titleCase(key),\n sink,\n );\n return entry.annotations.description === undefined\n ? group\n : { ...group, description: entry.annotations.description };\n }\n if (entry.options.manual && entry.options.unpinned) {\n throw new Error(\n `[labkit] \"${path}\" is both .manual() and .initial(auto) — a field that can never be auto cannot start auto`,\n );\n }\n const seed: LeafPatch = defined({\n ...entry.annotations,\n ...(entry.kind === null ? {} : { kind: entry.kind }),\n });\n const patch = applyRules(seed, { key, path, default: entry.default }, sink.chain);\n if (patch.kind === undefined) {\n throw new Error(\n `[labkit] \"${path}\" is an f.value whose kind cannot be inferred from its default (${String(entry.default)}) — declare it with a typed builder, or f.custom(kind, default) for a value with no row`,\n );\n }\n const { autoResolve, unpinned, manual } = entry.options;\n return {\n ...patch,\n default: entry.default,\n // Extra keys survive onto the leaf at runtime; `ControlPanel` reads them\n // back with its `extra<T>` helper, the same as `min`, `step` and the rest.\n ...(autoResolve === undefined ? {} : { autoResolve }),\n ...(unpinned === undefined ? {} : { unpinned }),\n ...(manual === undefined ? {} : { manual }),\n } as PrefLeaf;\n}\n","import { useMemo } from 'react';\nimport { resolveAutoConfig } from './autoConfig';\nimport type { ResolvedConfig } from './types';\n\nconst NONE: ReadonlySet<string> = new Set();\n\n/**\n * The config an instrument reads: the stored one with every unpinned path\n * resolved. The single place resolution happens, so `Trial` and\n * `useTrialState` cannot disagree about what an instrument is looking at.\n */\nexport function useResolvedConfig<TC>(\n schema: ResolvedConfig | undefined,\n config: TC,\n autoPaths: readonly string[] | undefined,\n): { config: TC; auto: ReadonlySet<string> } {\n const auto = useMemo(\n () => (autoPaths && autoPaths.length > 0 ? new Set(autoPaths) : NONE),\n [autoPaths],\n );\n const resolvedConfig = useMemo(\n () => (schema ? resolveAutoConfig(schema, config, auto) : config),\n [schema, config, auto],\n );\n return { config: resolvedConfig, auto };\n}\n"]}
@@ -50,5 +50,5 @@ function clearUndo(_stack) {
50
50
  }
51
51
 
52
52
  export { clearUndo, createEventBus, emptyStack, pushSnapshot, redo, undo };
53
- //# sourceMappingURL=chunk-RL2LOLNI.js.map
54
- //# sourceMappingURL=chunk-RL2LOLNI.js.map
53
+ //# sourceMappingURL=chunk-UX66USTN.js.map
54
+ //# sourceMappingURL=chunk-UX66USTN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/undo/eventBus.ts","../src/undo/undoStack.ts"],"names":[],"mappings":";AAYO,SAAS,cAAA,GAA2B;AACzC,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAgC;AACtD,EAAA,OAAO;AAAA,IACL,EAAA,CAAG,OAAO,QAAA,EAAU;AAClB,MAAA,IAAI,GAAA,GAAM,SAAA,CAAU,GAAA,CAAI,KAAK,CAAA;AAC7B,MAAA,IAAI,CAAC,GAAA,EAAK;AACR,QAAA,GAAA,uBAAU,GAAA,EAAI;AACd,QAAA,SAAA,CAAU,GAAA,CAAI,OAAO,GAAG,CAAA;AAAA,MAC1B;AACA,MAAA,GAAA,CAAI,IAAI,QAAQ,CAAA;AAChB,MAAA,OAAO,MAAM;AACX,QAAA,GAAA,EAAK,OAAO,QAAQ,CAAA;AAAA,MACtB,CAAA;AAAA,IACF,CAAA;AAAA,IACA,KAAK,KAAA,EAAO;AACV,MAAA,MAAM,GAAA,GAAM,SAAA,CAAU,GAAA,CAAI,KAAK,CAAA;AAC/B,MAAA,IAAI,CAAC,GAAA,EAAK;AACV,MAAA,KAAA,MAAW,EAAA,IAAM,CAAC,GAAG,GAAG,GAAG,EAAA,EAAG;AAAA,IAChC,CAAA;AAAA,IACA,KAAA,GAAQ;AACN,MAAA,SAAA,CAAU,KAAA,EAAM;AAAA,IAClB;AAAA,GACF;AACF;;;ACnBO,SAAS,UAAA,GAAwB;AACtC,EAAA,OAAO,EAAE,IAAA,EAAM,EAAC,EAAG,MAAA,EAAQ,EAAC,EAAE;AAChC;AAKO,SAAS,YAAA,CAAa,KAAA,EAAkB,QAAA,EAAmB,QAAA,EAA6B;AAC7F,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,IAAA,CAAK,MAAA,IAAU,QAAA,GAAW,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,GAAI,KAAA,CAAM,IAAA,CAAK,KAAA,EAAM;AACpF,EAAA,IAAA,CAAK,KAAK,QAAQ,CAAA;AAClB,EAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,EAAC,EAAE;AAC5B;AAMO,SAAS,IAAA,CACd,OACA,OAAA,EACgD;AAChD,EAAA,IAAI,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AACpC,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,IAAA,CAAK,KAAA,EAAM;AAC9B,EAAA,MAAM,QAAA,GAAW,KAAK,GAAA,EAAI;AAC1B,EAAA,OAAO,EAAE,KAAA,EAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,CAAC,GAAG,KAAA,CAAM,MAAA,EAAQ,OAAO,CAAA,EAAE,EAAG,QAAA,EAAS;AACzE;AAKO,SAAS,IAAA,CACd,OACA,OAAA,EACgD;AAChD,EAAA,IAAI,KAAA,CAAM,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AACtC,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,MAAA,CAAO,KAAA,EAAM;AAClC,EAAA,MAAM,QAAA,GAAW,OAAO,GAAA,EAAI;AAC5B,EAAA,OAAO,EAAE,KAAA,EAAO,EAAE,IAAA,EAAM,CAAC,GAAG,KAAA,CAAM,IAAA,EAAM,OAAO,CAAA,EAAG,MAAA,EAAO,EAAG,QAAA,EAAS;AACvE;AAIO,SAAS,UAAU,MAAA,EAA8B;AACtD,EAAA,OAAO,UAAA,EAAW;AACpB","file":"chunk-UX66USTN.js","sourcesContent":["/** A subscriber to a named event. */\nexport type EventListener = () => void;\n\n/** Named-event pub/sub, the channel an instrument's `emit` writes to and undo\n * snapshotting listens on. */\nexport interface EventBus {\n on(event: string, listener: EventListener): () => void;\n emit(event: string): void;\n clear(): void;\n}\n\n/** Build an empty event bus. */\nexport function createEventBus(): EventBus {\n const listeners = new Map<string, Set<EventListener>>();\n return {\n on(event, listener) {\n let set = listeners.get(event);\n if (!set) {\n set = new Set();\n listeners.set(event, set);\n }\n set.add(listener);\n return () => {\n set?.delete(listener);\n };\n },\n emit(event) {\n const set = listeners.get(event);\n if (!set) return;\n for (const fn of [...set]) fn();\n },\n clear() {\n listeners.clear();\n },\n };\n}\n","/**\n * @deprecated The whole module. A trial's undo runs on weasel-history\n * (`TrialRecord.history`); for an undo of your own, `createHistory` from\n * `@weasel-js/core`. Kept until sherpa's studio, its last user, moves off it.\n */\n\n/** Undo history as two stacks of state snapshots: what has been done, and\n * what has been undone. The present state is held elsewhere, not here.\n * @deprecated See the module comment. */\nexport interface UndoStack {\n past: unknown[];\n future: unknown[];\n}\n\n/** A fresh, empty history.\n * @deprecated See the module comment. */\nexport function emptyStack(): UndoStack {\n return { past: [], future: [] };\n}\n\n/** Record a new snapshot, discarding the redo branch and the oldest entry\n * once `maxDepth` is reached.\n * @deprecated See the module comment. */\nexport function pushSnapshot(stack: UndoStack, snapshot: unknown, maxDepth: number): UndoStack {\n const past = stack.past.length >= maxDepth ? stack.past.slice(1) : stack.past.slice();\n past.push(snapshot);\n return { past, future: [] };\n}\n\n/** Step back one snapshot, returning the new history and the state to restore.\n * `null` when there is nothing to undo. The caller supplies `current` so it\n * can be redone.\n * @deprecated See the module comment. */\nexport function undo(\n stack: UndoStack,\n current: unknown,\n): { stack: UndoStack; snapshot: unknown } | null {\n if (stack.past.length === 0) return null;\n const past = stack.past.slice();\n const snapshot = past.pop();\n return { stack: { past, future: [...stack.future, current] }, snapshot };\n}\n\n/** Step forward one snapshot, returning the new history and the state to\n * restore. `null` when there is nothing to redo.\n * @deprecated See the module comment. */\nexport function redo(\n stack: UndoStack,\n current: unknown,\n): { stack: UndoStack; snapshot: unknown } | null {\n if (stack.future.length === 0) return null;\n const future = stack.future.slice();\n const snapshot = future.pop();\n return { stack: { past: [...stack.past, current], future }, snapshot };\n}\n\n/** Discard the whole history.\n * @deprecated See the module comment. */\nexport function clearUndo(_stack: UndoStack): UndoStack {\n return emptyStack();\n}\n"]}