@weasel-js/labkit 1.3.0 → 1.4.0-pre.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/dist/_dts/CanvasStackContext-DtAn_3O-.d.ts +42 -0
  2. package/dist/_dts/DrawCommand-B3bskUsC.d.ts +564 -0
  3. package/dist/_dts/{PrefsForm-BYa6cWnO.d.ts → PrefsForm-BkUJZx0A.d.ts} +4 -1
  4. package/dist/_dts/fitViewToBounds-dZ2UDB6e.d.ts +21 -0
  5. package/dist/_dts/frac-Cp3NivlC.d.ts +728 -0
  6. package/dist/_dts/index-BDVzvRzQ.d.ts +236 -0
  7. package/dist/_dts/shapeKinds-Cx_rxwsa.d.ts +87 -0
  8. package/dist/_dts/{DrawCommand-DBB45NfN.d.ts → types-C-gh9Ap-.d.ts} +1 -582
  9. package/dist/_dts/{types-DCGEcMO_.d.ts → types-D6s4b7if.d.ts} +1 -1
  10. package/dist/_dts/{types-1Sdxy_Pv.d.ts → types-DYMaEvM5.d.ts} +26 -1
  11. package/dist/_dts/{useTrialState-CsGMjhu9.d.ts → useTrialState-gmMvZqPc.d.ts} +7 -2
  12. package/dist/canvas/index.d.ts +43 -25
  13. package/dist/canvas/index.js +5 -4
  14. package/dist/chrome/index.d.ts +9 -6
  15. package/dist/chrome/index.js +6 -6
  16. package/dist/chunk-2AYGEN57.js +32 -0
  17. package/dist/chunk-2AYGEN57.js.map +1 -0
  18. package/dist/{chunk-QRYPXSGP.js → chunk-3P5TTTA7.js} +16 -8
  19. package/dist/chunk-3P5TTTA7.js.map +1 -0
  20. package/dist/{chunk-BDJXIBRZ.js → chunk-3ZELRRBV.js} +44 -27
  21. package/dist/chunk-3ZELRRBV.js.map +1 -0
  22. package/dist/{chunk-LGKRIUSW.js → chunk-4EY67BKJ.js} +32 -7
  23. package/dist/chunk-4EY67BKJ.js.map +1 -0
  24. package/dist/{chunk-3XXPU73K.js → chunk-5TCEVMPI.js} +9 -7
  25. package/dist/chunk-5TCEVMPI.js.map +1 -0
  26. package/dist/{chunk-JDROYQM3.js → chunk-6JMU56CU.js} +130 -131
  27. package/dist/chunk-6JMU56CU.js.map +1 -0
  28. package/dist/{chunk-CPUJ3QXL.js → chunk-CQLQPQ4P.js} +2 -2
  29. package/dist/chunk-CQLQPQ4P.js.map +1 -0
  30. package/dist/chunk-CTRKTLYZ.js +33 -0
  31. package/dist/chunk-CTRKTLYZ.js.map +1 -0
  32. package/dist/chunk-EQIB2PGC.js +458 -0
  33. package/dist/chunk-EQIB2PGC.js.map +1 -0
  34. package/dist/chunk-HEORBLVT.js +7128 -0
  35. package/dist/chunk-HEORBLVT.js.map +1 -0
  36. package/dist/{chunk-BMW4TDP5.js → chunk-JDDIZSLL.js} +3 -3
  37. package/dist/{chunk-BMW4TDP5.js.map → chunk-JDDIZSLL.js.map} +1 -1
  38. package/dist/chunk-LITE5YFB.js +669 -0
  39. package/dist/chunk-LITE5YFB.js.map +1 -0
  40. package/dist/{chunk-QCICIHEO.js → chunk-ROQDRXKW.js} +716 -311
  41. package/dist/chunk-ROQDRXKW.js.map +1 -0
  42. package/dist/{chunk-4TMMVIDM.js → chunk-XJ6N32QP.js} +50 -6
  43. package/dist/chunk-XJ6N32QP.js.map +1 -0
  44. package/dist/{chunk-WTI26YTM.js → chunk-YQJNT4XQ.js} +40 -13
  45. package/dist/chunk-YQJNT4XQ.js.map +1 -0
  46. package/dist/controls/index.d.ts +4 -28
  47. package/dist/controls/index.js +3 -3
  48. package/dist/dragdrop/index.d.ts +9 -5
  49. package/dist/dragdrop/index.js +3 -3
  50. package/dist/index.d.ts +518 -190
  51. package/dist/index.js +1753 -75
  52. package/dist/index.js.map +1 -1
  53. package/dist/layers/index.d.ts +6 -5
  54. package/dist/layers/index.js +3 -3
  55. package/dist/loupe/index.d.ts +187 -0
  56. package/dist/loupe/index.js +7 -0
  57. package/dist/loupe/index.js.map +1 -0
  58. package/dist/passthrough/weasel-canvas.d.ts +3 -1
  59. package/dist/passthrough/weasel-canvas.js +1 -1
  60. package/dist/passthrough/weasel-ui.d.ts +138 -91
  61. package/dist/passthrough/weasel-ui.js +2 -2
  62. package/dist/primitives/index.d.ts +3 -1
  63. package/dist/primitives/index.js +5 -5
  64. package/dist/state/index.d.ts +3 -3
  65. package/dist/state/index.js +2 -2
  66. package/dist/styles.css +229 -1
  67. package/dist/surface/index.d.ts +18 -2
  68. package/dist/surface/index.js +2 -2
  69. package/dist/ui/layers/index.js +2 -2
  70. package/dist/undo/index.d.ts +5 -4
  71. package/package.json +14 -7
  72. package/src/annotations/AnnotationOverlay.tsx +205 -0
  73. package/src/annotations/AnnotationTargets.tsx +37 -0
  74. package/src/annotations/Annotations.less +129 -0
  75. package/src/annotations/Annotations.meaning.test.tsx +111 -0
  76. package/src/annotations/Annotations.overlay.test.tsx +142 -0
  77. package/src/annotations/AnnotationsContext.ts +49 -0
  78. package/src/annotations/ExportMenu.test.tsx +86 -0
  79. package/src/annotations/ExportMenu.tsx +182 -0
  80. package/src/annotations/MarkList.tsx +75 -0
  81. package/src/annotations/capture.test.ts +140 -0
  82. package/src/annotations/capture.ts +237 -0
  83. package/src/annotations/drawOne.test.ts +56 -0
  84. package/src/annotations/drawOne.ts +47 -0
  85. package/src/annotations/frac.test.ts +59 -0
  86. package/src/annotations/frac.ts +66 -0
  87. package/src/annotations/history.test.ts +82 -0
  88. package/src/annotations/history.ts +118 -0
  89. package/src/annotations/index.ts +37 -0
  90. package/src/annotations/paint.test.ts +125 -0
  91. package/src/annotations/paint.ts +130 -0
  92. package/src/annotations/staleness.test.ts +56 -0
  93. package/src/annotations/staleness.ts +41 -0
  94. package/src/annotations/store.test.ts +175 -0
  95. package/src/annotations/store.ts +317 -0
  96. package/src/annotations/svgNodes.test.ts +77 -0
  97. package/src/annotations/svgNodes.ts +74 -0
  98. package/src/annotations/toolMap.test.ts +36 -0
  99. package/src/annotations/toolMap.ts +54 -0
  100. package/src/annotations/types.ts +222 -0
  101. package/src/annotations/view.test.ts +37 -0
  102. package/src/annotations/view.ts +40 -0
  103. package/src/canvas/AGENTS.md +55 -5
  104. package/src/canvas/CanvasStack.test.tsx +27 -1
  105. package/src/canvas/CanvasStack.tsx +29 -4
  106. package/src/canvas/CanvasStackContext.ts +23 -4
  107. package/src/canvas/camera.test.ts +78 -0
  108. package/src/canvas/camera.ts +62 -0
  109. package/src/canvas/canvasCoords.test.ts +26 -0
  110. package/src/canvas/canvasCoords.ts +20 -7
  111. package/src/canvas/index.ts +5 -1
  112. package/src/canvas/useLayerScheduler.ts +7 -3
  113. package/src/canvas/usePanZoom.test.ts +36 -5
  114. package/src/canvas/usePanZoom.ts +20 -15
  115. package/src/canvas/worldSpec.test.ts +63 -0
  116. package/src/canvas/worldSpec.ts +48 -0
  117. package/src/chrome/builtins.test.ts +31 -1
  118. package/src/chrome/builtins.tsx +63 -19
  119. package/src/chrome/regions/PaletteRegion.test.tsx +22 -1
  120. package/src/chrome/regions/PaletteRegion.tsx +4 -0
  121. package/src/chrome/regions/SidebarRegion.tsx +37 -13
  122. package/src/chrome/regions/ToolbarRegion.tsx +2 -1
  123. package/src/chrome/regions/ViewportRegion.tsx +9 -1
  124. package/src/chrome/regions/regions.test.tsx +6 -1
  125. package/src/chrome/types.ts +20 -0
  126. package/src/controls/ControlPanel.test.tsx +38 -0
  127. package/src/controls/ControlPanel.tsx +50 -3
  128. package/src/dragdrop/DragDropRuntime.tsx +8 -2
  129. package/src/dragdrop/dragDrop.test.tsx +29 -2
  130. package/src/index.test.ts +22 -0
  131. package/src/index.ts +87 -2
  132. package/src/instrument/SineWave.smoke.test.tsx +1 -0
  133. package/src/instrument/types.ts +24 -1
  134. package/src/lab/Lab.surface.test.tsx +162 -0
  135. package/src/lab/Lab.tsx +102 -17
  136. package/src/lab/Lab.undock.test.tsx +85 -0
  137. package/src/lab/LabFullChrome.stories.tsx +20 -0
  138. package/src/lab/LabShell.less +11 -0
  139. package/src/lab/Workspace.less +73 -0
  140. package/src/lab/Workspace.surface.test.tsx +2 -0
  141. package/src/lab/Workspace.tsx +97 -8
  142. package/src/lab/index.ts +1 -1
  143. package/src/lab/panelHost.ts +37 -0
  144. package/src/loupe/AGENTS.md +49 -0
  145. package/src/loupe/CanvasLoupe.tsx +83 -0
  146. package/src/loupe/DomLoupe.tsx +62 -0
  147. package/src/loupe/Loupe.less +38 -0
  148. package/src/loupe/LoupeBubble.tsx +32 -0
  149. package/src/loupe/TrialLoupe.tsx +93 -0
  150. package/src/loupe/canvasLens.test.ts +205 -0
  151. package/src/loupe/canvasLens.ts +141 -0
  152. package/src/loupe/index.ts +19 -0
  153. package/src/loupe/types.test.ts +29 -0
  154. package/src/loupe/types.ts +90 -0
  155. package/src/loupe/useHostSize.ts +30 -0
  156. package/src/loupe/useLoupe.test.tsx +197 -0
  157. package/src/loupe/useLoupe.ts +189 -0
  158. package/src/passthrough/weasel-ui.ts +3 -0
  159. package/src/primitives/FloatingPanel.test.tsx +76 -1
  160. package/src/primitives/FloatingPanel.tsx +19 -3
  161. package/src/primitives/ScaleIndicator.test.tsx +7 -2
  162. package/src/primitives/Sidebar.less +18 -0
  163. package/src/primitives/Toolbar.less +8 -0
  164. package/src/primitives/Toolbar.tsx +5 -0
  165. package/src/primitives/useRovingTabIndex.test.ts +21 -0
  166. package/src/primitives/useRovingTabIndex.ts +26 -17
  167. package/src/state/document.test.ts +23 -0
  168. package/src/state/document.ts +14 -2
  169. package/src/state/index.ts +2 -0
  170. package/src/state/store.test.ts +24 -0
  171. package/src/state/store.ts +35 -1
  172. package/src/state/types.ts +11 -0
  173. package/src/state/undock.test.ts +40 -0
  174. package/src/state/undock.ts +39 -0
  175. package/src/styles.less +2 -0
  176. package/src/surface/SurfaceContext.ts +4 -0
  177. package/src/surface/index.ts +8 -3
  178. package/src/surface/useSurfaceTile.test.tsx +2 -0
  179. package/src/surface/useSurfaceTile.ts +7 -1
  180. package/src/surface/useTiledSurface.test.tsx +58 -0
  181. package/src/surface/useTiledSurface.ts +38 -3
  182. package/src/trial/Trial.annotations.persist.test.tsx +166 -0
  183. package/src/trial/Trial.annotations.test.tsx +121 -0
  184. package/src/trial/Trial.canvas.test.tsx +76 -1
  185. package/src/trial/Trial.less +19 -0
  186. package/src/trial/Trial.loupe.test.tsx +128 -0
  187. package/src/trial/Trial.stories.tsx +1 -0
  188. package/src/trial/Trial.test.tsx +12 -0
  189. package/src/trial/Trial.trialId.test.tsx +36 -0
  190. package/src/trial/Trial.tsx +215 -40
  191. package/src/trial/TrialChrome.tsx +35 -0
  192. package/src/trial/UndockedSections.tsx +65 -0
  193. package/src/trial/trialOps.test.ts +21 -0
  194. package/src/trial/trialOps.ts +5 -3
  195. package/dist/_dts/types-BttTIent.d.ts +0 -314
  196. package/dist/chunk-3XXPU73K.js.map +0 -1
  197. package/dist/chunk-4TMMVIDM.js.map +0 -1
  198. package/dist/chunk-6X5RBC6G.js +0 -366
  199. package/dist/chunk-6X5RBC6G.js.map +0 -1
  200. package/dist/chunk-BDJXIBRZ.js.map +0 -1
  201. package/dist/chunk-CPUJ3QXL.js.map +0 -1
  202. package/dist/chunk-JDROYQM3.js.map +0 -1
  203. package/dist/chunk-LGKRIUSW.js.map +0 -1
  204. package/dist/chunk-QCICIHEO.js.map +0 -1
  205. package/dist/chunk-QRYPXSGP.js.map +0 -1
  206. package/dist/chunk-RGT4EJIB.js +0 -6961
  207. package/dist/chunk-RGT4EJIB.js.map +0 -1
  208. package/dist/chunk-T66HU3HX.js +0 -17
  209. package/dist/chunk-T66HU3HX.js.map +0 -1
  210. package/dist/chunk-WTI26YTM.js.map +0 -1
@@ -3,7 +3,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
3
  import earcut from 'earcut';
4
4
  import polygonClipping from 'polygon-clipping';
5
5
 
6
- // ../core/dist/chunk-3ZV53ZW2.js
6
+ // ../core/dist/chunk-H6E6KPTU.js
7
7
 
8
8
  // ../gestures/dist/index.js
9
9
  var GESTURE_DESCRIPTORS = [
@@ -287,7 +287,7 @@ function matchSpec(e, spec, isMac, phaseCtx) {
287
287
  }
288
288
  var EMPTY_ENGAGED = /* @__PURE__ */ new Set();
289
289
 
290
- // ../core/dist/chunk-3ZV53ZW2.js
290
+ // ../core/dist/chunk-H6E6KPTU.js
291
291
  var DepRegistryContext = createContext(null);
292
292
  function DepRegistryProvider({ children }) {
293
293
  const sourcesRef = useRef(/* @__PURE__ */ new Map());
@@ -428,6 +428,12 @@ function validateActionDefaultBinding(action) {
428
428
  }
429
429
  }
430
430
  }
431
+ function warnSharedScope() {
432
+ if (!IS_DEV) return;
433
+ console.warn(
434
+ "weasel: a second dispatcher claimed this <ActionsProvider>, so only the newest <SceneCanvas> under it will respond to input. Give each canvas its own scope with <WeaselProvider isolate>."
435
+ );
436
+ }
431
437
  var ActionsContext = createContext(null);
432
438
  function ActionsProvider({ children }) {
433
439
  const actionsRef = useRef(/* @__PURE__ */ new Map());
@@ -438,6 +444,7 @@ function ActionsProvider({ children }) {
438
444
  const depReg = useOptionalDepRegistry();
439
445
  const depRegRef = useRef(depReg);
440
446
  depRegRef.current = depReg;
447
+ const warnedRef = useRef(false);
441
448
  const wiredDepRegRef = useRef(null);
442
449
  const dispatcherRef = useRef(null);
443
450
  const registry4 = useMemo(() => {
@@ -510,10 +517,20 @@ function ActionsProvider({ children }) {
510
517
  };
511
518
  },
512
519
  setDispatcher: (d) => {
520
+ if (d && dispatcherRef.current && dispatcherRef.current !== d && !warnedRef.current) {
521
+ warnedRef.current = true;
522
+ warnSharedScope();
523
+ }
513
524
  dispatcherRef.current = d;
525
+ return () => {
526
+ if (dispatcherRef.current === d) dispatcherRef.current = null;
527
+ };
514
528
  },
515
529
  setDepRegistry: (r) => {
516
530
  wiredDepRegRef.current = r;
531
+ return () => {
532
+ if (wiredDepRegRef.current === r) wiredDepRegRef.current = null;
533
+ };
517
534
  },
518
535
  begin: (id, params) => {
519
536
  const disp = dispatcherRef.current;
@@ -535,7 +552,7 @@ function ActionsProvider({ children }) {
535
552
  }, []);
536
553
  return /* @__PURE__ */ jsx(ActionsContext.Provider, { value: registry4, children });
537
554
  }
538
- (() => {
555
+ var IS_DEV = (() => {
539
556
  try {
540
557
  return Boolean(import.meta.env?.DEV);
541
558
  } catch {
@@ -879,8 +896,19 @@ function transformCoords(coords, m) {
879
896
  }
880
897
  return out;
881
898
  }
899
+ function clampAxis(start, size, low, extent, padding) {
900
+ const min = low + padding;
901
+ return Math.max(min, Math.min(start, min + extent - padding * 2 - size));
902
+ }
903
+ function clampRectWithin(rect, boundary, padding = 0) {
904
+ return {
905
+ ...rect,
906
+ x: clampAxis(rect.x, rect.width, boundary.x, boundary.width, padding),
907
+ y: clampAxis(rect.y, rect.height, boundary.y, boundary.height, padding)
908
+ };
909
+ }
882
910
 
883
- // ../core/dist/chunk-F74NVEHT.js
911
+ // ../core/dist/chunk-SBFC6J3C.js
884
912
  function resolveUnit(v, unitSystem) {
885
913
  if (typeof v === "number") return v;
886
914
  if (!unitSystem) {
@@ -1315,6 +1343,17 @@ var PathBuilder = class _PathBuilder {
1315
1343
  function rectPath(x, y, width, height) {
1316
1344
  return { kind: "rect", x, y, width, height };
1317
1345
  }
1346
+ function polylineFromPoints(points) {
1347
+ const b = new PathBuilder();
1348
+ const first = points[0];
1349
+ if (!first) return rectPath(0, 0, 0, 0);
1350
+ b.moveTo(first.x, first.y);
1351
+ for (let i = 1; i < points.length; i++) {
1352
+ const p = points[i];
1353
+ if (p) b.lineTo(p.x, p.y);
1354
+ }
1355
+ return b.build();
1356
+ }
1318
1357
  function polygonFromPoints(points, opts = {}) {
1319
1358
  if (points.length === 0) {
1320
1359
  return {
@@ -1690,7 +1729,7 @@ function createDeleteOp(args) {
1690
1729
  }
1691
1730
  registerOpFactory("delete", (args) => createDeleteOp(args));
1692
1731
 
1693
- // ../core/dist/chunk-AQS4JGRA.js
1732
+ // ../core/dist/chunk-Y5KS66G7.js
1694
1733
  function createTransformOp(args) {
1695
1734
  const { id, from, to, label, coalesceKey = `transform:${id}` } = args;
1696
1735
  return {
@@ -1814,7 +1853,7 @@ function snap(strategy, opts = {}) {
1814
1853
  };
1815
1854
  }
1816
1855
 
1817
- // ../core/dist/chunk-3FB44DBC.js
1856
+ // ../core/dist/chunk-BLJNRMKB.js
1818
1857
  function lockAspectWithModifier(opts = {}) {
1819
1858
  const { key = "shift" } = opts;
1820
1859
  return {
@@ -4224,7 +4263,7 @@ function serialToEntry(se, custom, logger) {
4224
4263
  };
4225
4264
  }
4226
4265
 
4227
- // ../core/dist/chunk-N3T7D6AS.js
4266
+ // ../core/dist/chunk-IMFXEINV.js
4228
4267
  function worldToScreen(worldX, worldY, view) {
4229
4268
  return [view.panX + worldX * view.zoom.x, view.panY + worldY * view.zoom.y];
4230
4269
  }
@@ -5551,6 +5590,9 @@ function flattenPolygon(p, tolerance) {
5551
5590
  let coordIdx = 0;
5552
5591
  let prevX = 0;
5553
5592
  let prevY = 0;
5593
+ let startX = 0;
5594
+ let startY = 0;
5595
+ let startAnchor = -1;
5554
5596
  let prevAnchor = -1;
5555
5597
  let anchorCounter = 0;
5556
5598
  for (let cmdIdx = 0; cmdIdx < commands.length; cmdIdx++) {
@@ -5560,6 +5602,9 @@ function flattenPolygon(p, tolerance) {
5560
5602
  contourStarts.push(out.length / 2);
5561
5603
  prevX = coords[coordIdx];
5562
5604
  prevY = coords[coordIdx + 1];
5605
+ startX = prevX;
5606
+ startY = prevY;
5607
+ startAnchor = anchorCounter;
5563
5608
  out.push(prevX, prevY);
5564
5609
  anchorA.push(anchorCounter);
5565
5610
  anchorB.push(anchorCounter);
@@ -5629,6 +5674,9 @@ function flattenPolygon(p, tolerance) {
5629
5674
  break;
5630
5675
  }
5631
5676
  case PATH_Z: {
5677
+ prevX = startX;
5678
+ prevY = startY;
5679
+ prevAnchor = startAnchor;
5632
5680
  break;
5633
5681
  }
5634
5682
  default:
@@ -8444,6 +8492,7 @@ var WeaselRenderer = class {
8444
8492
  heightCss;
8445
8493
  dpr;
8446
8494
  canvas = null;
8495
+ target = null;
8447
8496
  imageMinification;
8448
8497
  flattenTolerance;
8449
8498
  bakeBudget;
@@ -8461,6 +8510,12 @@ var WeaselRenderer = class {
8461
8510
  const gl = opts.gl ?? opts.canvas.getContext("webgl2", { stencil: true });
8462
8511
  if (!gl) throw new Error("WeaselRenderer: WebGL2 not available");
8463
8512
  this.gl = gl;
8513
+ const attrs = typeof this.gl.getContextAttributes === "function" ? this.gl.getContextAttributes() : null;
8514
+ if (attrs && attrs.stencil === false) {
8515
+ throw new Error(
8516
+ "WeaselRenderer: the supplied WebGL2 context has no stencil buffer. Create it with { stencil: true } \u2014 clips and even-odd fills need one."
8517
+ );
8518
+ }
8464
8519
  this.widthCss = opts.width;
8465
8520
  this.heightCss = opts.height;
8466
8521
  this.dpr = opts.dpr;
@@ -8479,12 +8534,8 @@ var WeaselRenderer = class {
8479
8534
  this.canvas.addEventListener("webglcontextlost", this.boundOnLost);
8480
8535
  this.canvas.addEventListener("webglcontextrestored", this.boundOnRestored);
8481
8536
  }
8482
- this.gl.enable(this.gl.BLEND);
8483
- this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
8484
- this.gl.disable(this.gl.DEPTH_TEST);
8485
- this.gl.disable(this.gl.CULL_FACE);
8486
- this.gl.clearColor(0, 0, 0, 0);
8487
- this.applyViewport();
8537
+ this.applyGlState();
8538
+ this.applyTarget();
8488
8539
  this.pathFill = new ShaderProgram(this.gl, VERT_SRC, FRAG_SRC);
8489
8540
  this.pathFill.lookupUniforms(PATH_FILL_UNIFORMS);
8490
8541
  this.pathFill.lookupAttributes(PATH_FILL_ATTRIBUTES);
@@ -8574,8 +8625,42 @@ var WeaselRenderer = class {
8574
8625
  }
8575
8626
  return this.programRegistry.get(id) ?? null;
8576
8627
  }
8577
- applyViewport() {
8578
- this.gl.viewport(0, 0, this.widthCss * this.dpr, this.heightCss * this.dpr);
8628
+ /** The GL state every frame assumes. Applied per `render()` rather than once
8629
+ * at construction because a co-tenant sharing this context moves all of it
8630
+ * between our frames. */
8631
+ applyGlState() {
8632
+ const gl = this.gl;
8633
+ gl.enable(gl.BLEND);
8634
+ gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
8635
+ gl.disable(gl.DEPTH_TEST);
8636
+ gl.disable(gl.CULL_FACE);
8637
+ gl.clearColor(0, 0, 0, 0);
8638
+ }
8639
+ /** Viewport and scissor for this frame. Re-applied inside `render()` because
8640
+ * a co-tenant on the same context moves both between our frames. */
8641
+ applyTarget() {
8642
+ const gl = this.gl;
8643
+ const w = Math.round(this.widthCss * this.dpr);
8644
+ const h = Math.round(this.heightCss * this.dpr);
8645
+ if (!this.target) {
8646
+ gl.disable(gl.SCISSOR_TEST);
8647
+ gl.viewport(0, 0, w, h);
8648
+ return;
8649
+ }
8650
+ const x = Math.round(this.target.origin.x * this.dpr);
8651
+ const y = gl.drawingBufferHeight - Math.round(this.target.origin.y * this.dpr) - h;
8652
+ gl.viewport(x, y, w, h);
8653
+ gl.scissor(x, y, w, h);
8654
+ gl.enable(gl.SCISSOR_TEST);
8655
+ }
8656
+ /** Confine this renderer to a rect of its drawing buffer, or pass null to
8657
+ * give it the whole buffer back. Takes effect from the next `render()`. */
8658
+ setTarget(target) {
8659
+ this.target = target;
8660
+ this.applyTarget();
8661
+ }
8662
+ getTarget() {
8663
+ return this.target;
8579
8664
  }
8580
8665
  isContextLost() {
8581
8666
  return this.contextLost;
@@ -8586,12 +8671,8 @@ var WeaselRenderer = class {
8586
8671
  }
8587
8672
  onContextRestored() {
8588
8673
  this.contextLost = false;
8589
- this.gl.enable(this.gl.BLEND);
8590
- this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
8591
- this.gl.disable(this.gl.DEPTH_TEST);
8592
- this.gl.disable(this.gl.CULL_FACE);
8593
- this.gl.clearColor(0, 0, 0, 0);
8594
- this.applyViewport();
8674
+ this.applyGlState();
8675
+ this.applyTarget();
8595
8676
  this.pathFill = new ShaderProgram(this.gl, VERT_SRC, FRAG_SRC);
8596
8677
  this.pathFill.lookupUniforms(PATH_FILL_UNIFORMS);
8597
8678
  this.pathFill.lookupAttributes(PATH_FILL_ATTRIBUTES);
@@ -8698,8 +8779,12 @@ var WeaselRenderer = class {
8698
8779
  this.meshCache.drainPendingDeletes();
8699
8780
  resetBakeBudget(this.bakeBudget);
8700
8781
  this.groupState.reset();
8701
- gl.stencilMask(255);
8702
- gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
8782
+ this.applyGlState();
8783
+ this.applyTarget();
8784
+ if (this.target?.clear !== false) {
8785
+ gl.stencilMask(255);
8786
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
8787
+ }
8703
8788
  const ctx = {
8704
8789
  gl,
8705
8790
  pathFill: this.pathFill,
@@ -8742,7 +8827,7 @@ var WeaselRenderer = class {
8742
8827
  this.canvas.style.height = `${dims.height}px`;
8743
8828
  }
8744
8829
  }
8745
- this.applyViewport();
8830
+ this.applyTarget();
8746
8831
  }
8747
8832
  /** @internal */
8748
8833
  _gl() {
@@ -9382,10 +9467,10 @@ function targetSizesPx(targetScale = 1) {
9382
9467
  rotationDistance: BASE_PX.rotationDistance * targetScale
9383
9468
  };
9384
9469
  }
9385
- var SYSTEM_MAX_SCALE = 10;
9386
- var SYSTEM_MIN_SCALE = 0.1;
9470
+ var DEFAULT_MIN_ZOOM = 0.1;
9471
+ var DEFAULT_MAX_ZOOM = 8;
9387
9472
  function fitViewToBounds(bounds, viewportDims, currentView, opts = {}) {
9388
- const { padding = 16, maxScale = SYSTEM_MAX_SCALE, minScale = SYSTEM_MIN_SCALE } = opts;
9473
+ const { padding = 16, maxScale = DEFAULT_MAX_ZOOM, minScale = DEFAULT_MIN_ZOOM } = opts;
9389
9474
  if (bounds.width <= 0 || bounds.height <= 0) {
9390
9475
  console.warn("[weasel] fitViewToBounds: bounds has zero or negative area; returning currentView unchanged.", bounds);
9391
9476
  return currentView;
@@ -9429,15 +9514,19 @@ function fitViewToBounds(bounds, viewportDims, currentView, opts = {}) {
9429
9514
  scale: { x: scaleX, y: scaleY }
9430
9515
  };
9431
9516
  }
9517
+ function clampScale(scale2, min, max) {
9518
+ const magnitude = Math.min(max, Math.max(min, Math.abs(scale2)));
9519
+ return scale2 < 0 ? -magnitude : magnitude;
9520
+ }
9432
9521
  function zoomAt(view, anchor, factor, opts) {
9433
9522
  const fx = typeof factor === "number" ? factor : factor.x;
9434
9523
  const fy = typeof factor === "number" ? factor : factor.y;
9435
- const minX = typeof opts?.min === "number" ? opts.min : opts?.min?.x ?? 0.1;
9436
- const minY = typeof opts?.min === "number" ? opts.min : opts?.min?.y ?? 0.1;
9437
- const maxX = typeof opts?.max === "number" ? opts.max : opts?.max?.x ?? 8;
9438
- const maxY = typeof opts?.max === "number" ? opts.max : opts?.max?.y ?? 8;
9439
- const nextX = Math.min(maxX, Math.max(minX, view.scale.x * fx));
9440
- const nextY = Math.min(maxY, Math.max(minY, view.scale.y * fy));
9524
+ const minX = typeof opts?.min === "number" ? opts.min : opts?.min?.x ?? DEFAULT_MIN_ZOOM;
9525
+ const minY = typeof opts?.min === "number" ? opts.min : opts?.min?.y ?? DEFAULT_MIN_ZOOM;
9526
+ const maxX = typeof opts?.max === "number" ? opts.max : opts?.max?.x ?? DEFAULT_MAX_ZOOM;
9527
+ const maxY = typeof opts?.max === "number" ? opts.max : opts?.max?.y ?? DEFAULT_MAX_ZOOM;
9528
+ const nextX = clampScale(view.scale.x * fx, minX, maxX);
9529
+ const nextY = clampScale(view.scale.y * fy, minY, maxY);
9441
9530
  const worldX = anchor.x / view.scale.x + view.x;
9442
9531
  const worldY = anchor.y / view.scale.y + view.y;
9443
9532
  return {
@@ -9447,22 +9536,24 @@ function zoomAt(view, anchor, factor, opts) {
9447
9536
  };
9448
9537
  }
9449
9538
  function clampView(view, bounds, canvas) {
9450
- const visW = canvas.width / view.scale.x;
9451
- const visH = canvas.height / view.scale.y;
9539
+ const visW = Math.abs(canvas.width / view.scale.x);
9540
+ const visH = Math.abs(canvas.height / view.scale.y);
9541
+ const flipX = view.scale.x < 0;
9542
+ const flipY = view.scale.y < 0;
9452
9543
  let x;
9453
9544
  if (visW >= bounds.width) {
9454
- x = bounds.x + (bounds.width - visW) / 2;
9545
+ x = bounds.x + (bounds.width - visW) / 2 + (flipX ? visW : 0);
9455
9546
  } else {
9456
- const minX = bounds.x;
9457
- const maxX = bounds.x + bounds.width - visW;
9547
+ const minX = flipX ? bounds.x + visW : bounds.x;
9548
+ const maxX = flipX ? bounds.x + bounds.width : bounds.x + bounds.width - visW;
9458
9549
  x = view.x < minX ? minX : view.x > maxX ? maxX : view.x;
9459
9550
  }
9460
9551
  let y;
9461
9552
  if (visH >= bounds.height) {
9462
- y = bounds.y + (bounds.height - visH) / 2;
9553
+ y = bounds.y + (bounds.height - visH) / 2 + (flipY ? visH : 0);
9463
9554
  } else {
9464
- const minY = bounds.y;
9465
- const maxY = bounds.y + bounds.height - visH;
9555
+ const minY = flipY ? bounds.y + visH : bounds.y;
9556
+ const maxY = flipY ? bounds.y + bounds.height : bounds.y + bounds.height - visH;
9466
9557
  y = view.y < minY ? minY : view.y > maxY ? maxY : view.y;
9467
9558
  }
9468
9559
  return x === view.x && y === view.y ? view : { x, y, scale: view.scale };
@@ -11165,7 +11256,7 @@ function keyAnchor(view) {
11165
11256
  }
11166
11257
  var WHEEL_STEP = 1.1;
11167
11258
  function makeViewportZoomAction(opts = {}) {
11168
- const clamp2 = { min: opts.min ?? 0.1, max: opts.max ?? 8 };
11259
+ const clamp2 = { min: opts.min ?? DEFAULT_MIN_ZOOM, max: opts.max ?? DEFAULT_MAX_ZOOM };
11169
11260
  const wheelSpec = opts.wheel === "plain" ? { kind: "wheel" } : { kind: "wheel", mods: { mod: true } };
11170
11261
  const rawAnimate = opts.animate === true ? {} : opts.animate || null;
11171
11262
  const { resetMs, ...animateRest } = rawAnimate ?? {};
@@ -11814,7 +11905,7 @@ var lassoSelectAction = {
11814
11905
  enabled: () => true
11815
11906
  };
11816
11907
  function makePinchZoomAction(opts = {}) {
11817
- const clamp2 = { min: opts.min ?? 0.1, max: opts.max ?? 8 };
11908
+ const clamp2 = { min: opts.min ?? DEFAULT_MIN_ZOOM, max: opts.max ?? DEFAULT_MAX_ZOOM };
11818
11909
  return {
11819
11910
  id: "viewport.pinchZoom",
11820
11911
  label: "Pinch Zoom",
@@ -16349,154 +16440,6 @@ function useGestureDispatcher(opts) {
16349
16440
  };
16350
16441
  }, [enabled, keyboard, canvasRef]);
16351
16442
  }
16352
- function useVelocityTracker() {
16353
- const samplesRef = useRef([]);
16354
- return useMemo(() => ({
16355
- record(dx, dy, t) {
16356
- samplesRef.current.push({ dx, dy, t });
16357
- const cutoff = t - 100;
16358
- samplesRef.current = samplesRef.current.filter((s) => s.t >= cutoff);
16359
- },
16360
- getVelocity() {
16361
- const s = samplesRef.current;
16362
- if (s.length < 2) return { vx: 0, vy: 0 };
16363
- const dt = s[s.length - 1].t - s[0].t;
16364
- if (dt === 0) return { vx: 0, vy: 0 };
16365
- const totalDx = s.slice(1).reduce((acc, p) => acc + p.dx, 0);
16366
- const totalDy = s.slice(1).reduce((acc, p) => acc + p.dy, 0);
16367
- return { vx: totalDx / dt, vy: totalDy / dt };
16368
- },
16369
- reset() {
16370
- samplesRef.current = [];
16371
- }
16372
- }), []);
16373
- }
16374
- var SPRING_DAMPING = 0.5;
16375
- function useDecayLoop() {
16376
- const stateRef = useRef(null);
16377
- const frameLoop = useVisibleRaf(
16378
- (now) => {
16379
- tick(now);
16380
- },
16381
- {
16382
- // Coasting does not continue while suspended: dropping `lastTime` makes
16383
- // the resuming frame seed a fresh interval instead of applying an hour of
16384
- // friction at once, which would end the decay before it was seen.
16385
- onResume: useCallback(() => {
16386
- if (stateRef.current) stateRef.current.lastTime = null;
16387
- }, [])
16388
- }
16389
- );
16390
- const cancel = useCallback(() => {
16391
- frameLoop.cancel();
16392
- stateRef.current = null;
16393
- }, [frameLoop]);
16394
- const tick = useCallback((now) => {
16395
- const s = stateRef.current;
16396
- if (!s) return;
16397
- if (s.lastTime === null) {
16398
- s.lastTime = now;
16399
- frameLoop.request();
16400
- return;
16401
- }
16402
- const dt = Math.min(now - s.lastTime, 64);
16403
- s.lastTime = now;
16404
- const f = Math.pow(s.friction, dt / 16.67);
16405
- s.vx *= f;
16406
- s.vy *= f;
16407
- const speed = Math.sqrt(s.vx * s.vx + s.vy * s.vy);
16408
- if (speed < s.minSpeed) {
16409
- stateRef.current = null;
16410
- frameLoop.cancel();
16411
- s.onEnd?.();
16412
- return;
16413
- }
16414
- let dx = s.vx * dt;
16415
- let dy = s.vy * dt;
16416
- if (s.viewBounds && s.boundary) {
16417
- const { minX, maxX, minY, maxY } = s.viewBounds;
16418
- const newX = s.posX + dx;
16419
- const newY = s.posY + dy;
16420
- if (s.boundary === "stop") {
16421
- if (minX !== void 0 && newX < minX) {
16422
- dx = minX - s.posX;
16423
- s.vx = 0;
16424
- } else if (maxX !== void 0 && newX > maxX) {
16425
- dx = maxX - s.posX;
16426
- s.vx = 0;
16427
- }
16428
- if (minY !== void 0 && newY < minY) {
16429
- dy = minY - s.posY;
16430
- s.vy = 0;
16431
- } else if (maxY !== void 0 && newY > maxY) {
16432
- dy = maxY - s.posY;
16433
- s.vy = 0;
16434
- }
16435
- } else if (s.boundary === "spring") {
16436
- if (minX !== void 0 && newX < minX) {
16437
- dx = minX - s.posX;
16438
- s.vx = Math.abs(s.vx) * SPRING_DAMPING;
16439
- } else if (maxX !== void 0 && newX > maxX) {
16440
- dx = maxX - s.posX;
16441
- s.vx = -Math.abs(s.vx) * SPRING_DAMPING;
16442
- }
16443
- if (minY !== void 0 && newY < minY) {
16444
- dy = minY - s.posY;
16445
- s.vy = Math.abs(s.vy) * SPRING_DAMPING;
16446
- } else if (maxY !== void 0 && newY > maxY) {
16447
- dy = maxY - s.posY;
16448
- s.vy = -Math.abs(s.vy) * SPRING_DAMPING;
16449
- }
16450
- } else {
16451
- if (minX !== void 0 && newX < minX) {
16452
- dx = minX - s.posX;
16453
- s.vx = Math.abs(s.vx);
16454
- } else if (maxX !== void 0 && newX > maxX) {
16455
- dx = maxX - s.posX;
16456
- s.vx = -Math.abs(s.vx);
16457
- }
16458
- if (minY !== void 0 && newY < minY) {
16459
- dy = minY - s.posY;
16460
- s.vy = Math.abs(s.vy);
16461
- } else if (maxY !== void 0 && newY > maxY) {
16462
- dy = maxY - s.posY;
16463
- s.vy = -Math.abs(s.vy);
16464
- }
16465
- }
16466
- s.posX += dx;
16467
- s.posY += dy;
16468
- }
16469
- s.onTick(dx, dy);
16470
- frameLoop.request();
16471
- }, [frameLoop]);
16472
- const start = useCallback((config) => {
16473
- cancel();
16474
- const { velocity, friction = 0.92, minSpeed = 0.01, viewBounds, boundary, initialPosition, onTick, onEnd } = config;
16475
- const speed = Math.sqrt(velocity.vx ** 2 + velocity.vy ** 2);
16476
- if (speed < minSpeed) {
16477
- onEnd?.();
16478
- return;
16479
- }
16480
- stateRef.current = {
16481
- vx: velocity.vx,
16482
- vy: velocity.vy,
16483
- friction,
16484
- minSpeed,
16485
- lastTime: null,
16486
- posX: initialPosition?.x ?? 0,
16487
- posY: initialPosition?.y ?? 0,
16488
- viewBounds,
16489
- boundary,
16490
- onTick,
16491
- onEnd
16492
- };
16493
- frameLoop.request();
16494
- }, [cancel, frameLoop]);
16495
- useEffect(() => () => {
16496
- cancel();
16497
- }, [cancel]);
16498
- return { start, cancel };
16499
- }
16500
16443
  function dist(a, b) {
16501
16444
  return Math.hypot(b.x - a.x, b.y - a.y);
16502
16445
  }
@@ -16575,15 +16518,180 @@ function interpolateView(from, to) {
16575
16518
  return { x: a.x, y: b.x, scale: { x: a.s, y: b.s } };
16576
16519
  };
16577
16520
  }
16521
+ var linear = (t) => t;
16522
+ var easeInQuad = (t) => t * t;
16578
16523
  var easeOutQuad = (t) => 1 - (1 - t) * (1 - t);
16524
+ var easeInOutQuad = (t) => t < 0.5 ? 2 * t * t : 1 - 2 * (1 - t) * (1 - t);
16525
+ var easeInCubic = (t) => t * t * t;
16579
16526
  var easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
16527
+ var easeInOutCubic = (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
16528
+ var easeInQuart = (t) => t * t * t * t;
16529
+ var easeOutQuart = (t) => 1 - Math.pow(1 - t, 4);
16530
+ var easeInOutQuart = (t) => t < 0.5 ? 8 * t * t * t * t : 1 - Math.pow(-2 * t + 2, 4) / 2;
16531
+ var easeInQuint = (t) => t * t * t * t * t;
16532
+ var easeOutQuint = (t) => 1 - Math.pow(1 - t, 5);
16533
+ var easeInOutQuint = (t) => t < 0.5 ? 16 * t * t * t * t * t : 1 - Math.pow(-2 * t + 2, 5) / 2;
16534
+ var easeInSine = (t) => 1 - Math.cos(t * Math.PI / 2);
16535
+ var easeOutSine = (t) => Math.sin(t * Math.PI / 2);
16536
+ var easeInOutSine = (t) => -(Math.cos(Math.PI * t) - 1) / 2;
16537
+ var easeInExpo = (t) => t === 0 ? 0 : Math.pow(2, 10 * t - 10);
16538
+ var easeOutExpo = (t) => t === 1 ? 1 : 1 - Math.pow(2, -10 * t);
16539
+ var easeInOutExpo = (t) => {
16540
+ if (t === 0) return 0;
16541
+ if (t === 1) return 1;
16542
+ return t < 0.5 ? Math.pow(2, 20 * t - 10) / 2 : (2 - Math.pow(2, -20 * t + 10)) / 2;
16543
+ };
16544
+ var easeInCirc = (t) => 1 - Math.sqrt(1 - t * t);
16545
+ var easeOutCirc = (t) => Math.sqrt(1 - Math.pow(t - 1, 2));
16546
+ var easeInOutCirc = (t) => t < 0.5 ? (1 - Math.sqrt(1 - Math.pow(2 * t, 2))) / 2 : (Math.sqrt(1 - Math.pow(-2 * t + 2, 2)) + 1) / 2;
16547
+ var C1 = 1.70158;
16548
+ var C2 = C1 * 1.525;
16549
+ var C3 = C1 + 1;
16550
+ var C4 = 2 * Math.PI / 3;
16551
+ var C5 = 2 * Math.PI / 4.5;
16552
+ var easeInBack = (t) => C3 * t * t * t - C1 * t * t;
16553
+ var easeOutBack = (t) => 1 + C3 * Math.pow(t - 1, 3) + C1 * Math.pow(t - 1, 2);
16554
+ var easeInOutBack = (t) => t < 0.5 ? Math.pow(2 * t, 2) * ((C2 + 1) * 2 * t - C2) / 2 : (Math.pow(2 * t - 2, 2) * ((C2 + 1) * (2 * t - 2) + C2) + 2) / 2;
16555
+ var easeInElastic = (t) => {
16556
+ if (t === 0) return 0;
16557
+ if (t === 1) return 1;
16558
+ return -Math.pow(2, 10 * t - 10) * Math.sin((t * 10 - 10.75) * C4);
16559
+ };
16560
+ var easeOutElastic = (t) => {
16561
+ if (t === 0) return 0;
16562
+ if (t === 1) return 1;
16563
+ return Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * C4) + 1;
16564
+ };
16565
+ var easeInOutElastic = (t) => {
16566
+ if (t === 0) return 0;
16567
+ if (t === 1) return 1;
16568
+ return t < 0.5 ? -(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * C5)) / 2 : Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * C5) / 2 + 1;
16569
+ };
16570
+ var easeOutBounce = (t) => {
16571
+ const n1 = 7.5625;
16572
+ const d1 = 2.75;
16573
+ if (t < 1 / d1) return n1 * t * t;
16574
+ if (t < 2 / d1) {
16575
+ t -= 1.5 / d1;
16576
+ return n1 * t * t + 0.75;
16577
+ }
16578
+ if (t < 2.5 / d1) {
16579
+ t -= 2.25 / d1;
16580
+ return n1 * t * t + 0.9375;
16581
+ }
16582
+ t -= 2.625 / d1;
16583
+ return n1 * t * t + 0.984375;
16584
+ };
16585
+ var easeInBounce = (t) => 1 - easeOutBounce(1 - t);
16586
+ var easeInOutBounce = (t) => t < 0.5 ? (1 - easeOutBounce(1 - 2 * t)) / 2 : (1 + easeOutBounce(2 * t - 1)) / 2;
16580
16587
  var easeOut = easeOutQuad;
16588
+ var EASINGS = {
16589
+ linear,
16590
+ easeInQuad,
16591
+ easeOutQuad,
16592
+ easeInOutQuad,
16593
+ easeInCubic,
16594
+ easeOutCubic,
16595
+ easeInOutCubic,
16596
+ easeInQuart,
16597
+ easeOutQuart,
16598
+ easeInOutQuart,
16599
+ easeInQuint,
16600
+ easeOutQuint,
16601
+ easeInOutQuint,
16602
+ easeInSine,
16603
+ easeOutSine,
16604
+ easeInOutSine,
16605
+ easeInExpo,
16606
+ easeOutExpo,
16607
+ easeInOutExpo,
16608
+ easeInCirc,
16609
+ easeOutCirc,
16610
+ easeInOutCirc,
16611
+ easeInBack,
16612
+ easeOutBack,
16613
+ easeInOutBack,
16614
+ easeInElastic,
16615
+ easeOutElastic,
16616
+ easeInOutElastic,
16617
+ easeInBounce,
16618
+ easeOutBounce,
16619
+ easeInOutBounce
16620
+ };
16581
16621
  var SPRING_PRESETS = {
16582
16622
  gentle: { stiffness: 120, damping: 14, mass: 1 },
16583
16623
  wobbly: { stiffness: 180, damping: 12, mass: 1 },
16584
16624
  stiff: { stiffness: 210, damping: 20, mass: 1 },
16585
16625
  slow: { stiffness: 80, damping: 20, mass: 1 }
16586
16626
  };
16627
+ var NEWTON_ITERATIONS = 8;
16628
+ var NEWTON_MIN_SLOPE = 1e-3;
16629
+ var SUBDIVISION_EPSILON = 1e-7;
16630
+ var SUBDIVISION_MAX = 12;
16631
+ function bezierAt(t, a1, a2) {
16632
+ const c = 3 * a1;
16633
+ const b = 3 * (a2 - a1) - c;
16634
+ const a = 1 - c - b;
16635
+ return ((a * t + b) * t + c) * t;
16636
+ }
16637
+ function bezierSlope(t, a1, a2) {
16638
+ const c = 3 * a1;
16639
+ const b = 3 * (a2 - a1) - c;
16640
+ const a = 1 - c - b;
16641
+ return (3 * a * t + 2 * b) * t + c;
16642
+ }
16643
+ function solveForX(x, x1, x2) {
16644
+ let t = x;
16645
+ for (let i = 0; i < NEWTON_ITERATIONS; i++) {
16646
+ const slope = bezierSlope(t, x1, x2);
16647
+ if (slope < NEWTON_MIN_SLOPE) break;
16648
+ t -= (bezierAt(t, x1, x2) - x) / slope;
16649
+ }
16650
+ let lo = 0;
16651
+ let hi = 1;
16652
+ t = Math.min(1, Math.max(0, t));
16653
+ for (let i = 0; i < SUBDIVISION_MAX; i++) {
16654
+ const err = bezierAt(t, x1, x2) - x;
16655
+ if (Math.abs(err) < SUBDIVISION_EPSILON) return t;
16656
+ if (err > 0) hi = t;
16657
+ else lo = t;
16658
+ t = (lo + hi) / 2;
16659
+ }
16660
+ return t;
16661
+ }
16662
+ function cubicBezierEasing(x1, y1, x2, y2) {
16663
+ const cx1 = Math.min(1, Math.max(0, x1));
16664
+ const cx2 = Math.min(1, Math.max(0, x2));
16665
+ return (t) => {
16666
+ if (t <= 0) return 0;
16667
+ if (t >= 1) return 1;
16668
+ return bezierAt(solveForX(t, cx1, cx2), y1, y2);
16669
+ };
16670
+ }
16671
+ var bezierCache = /* @__PURE__ */ new Map();
16672
+ function isBezier(spec) {
16673
+ return typeof spec === "object" && spec !== null && "bezier" in spec;
16674
+ }
16675
+ function resolveEasing(spec) {
16676
+ if (spec === void 0) return EASINGS.linear;
16677
+ if (typeof spec === "function") return spec;
16678
+ if (typeof spec === "string") {
16679
+ const fn = EASINGS[spec];
16680
+ if (!fn) throw new Error(`resolveEasing: unknown easing name "${spec}"`);
16681
+ return fn;
16682
+ }
16683
+ if (isBezier(spec)) {
16684
+ const [x1, y1, x2, y2] = spec.bezier;
16685
+ const key = `${x1},${y1},${x2},${y2}`;
16686
+ let fn = bezierCache.get(key);
16687
+ if (!fn) {
16688
+ fn = cubicBezierEasing(x1, y1, x2, y2);
16689
+ bezierCache.set(key, fn);
16690
+ }
16691
+ return fn;
16692
+ }
16693
+ throw new Error("resolveEasing: unrecognized easing spec");
16694
+ }
16587
16695
  function createLoop(createSupervisor, factory, opts = {}) {
16588
16696
  const max = opts.count ?? Infinity;
16589
16697
  let iteration = 0;
@@ -16623,6 +16731,7 @@ function createLoop(createSupervisor, factory, opts = {}) {
16623
16731
  supervisor.setTimeScale(s);
16624
16732
  current?.setTimeScale(s);
16625
16733
  },
16734
+ timeScale: () => supervisor.timeScale(),
16626
16735
  isPaused: () => supervisor.isPaused()
16627
16736
  };
16628
16737
  }
@@ -16782,6 +16891,9 @@ var CompositeHandle = class {
16782
16891
  this.timeScale_ = s;
16783
16892
  for (const c of this.children) c.setTimeScale(s);
16784
16893
  }
16894
+ timeScale() {
16895
+ return this.timeScale_;
16896
+ }
16785
16897
  isPaused() {
16786
16898
  return this.paused_;
16787
16899
  }
@@ -16889,7 +17001,7 @@ function sampleTrack(track, t, segmentCache) {
16889
17001
  const b = keys[i + 1];
16890
17002
  const span = b.t - a.t;
16891
17003
  const raw = span <= 0 ? 1 : (t - a.t) / span;
16892
- const u = b.easing ? b.easing(raw) : raw;
17004
+ const u = b.easing ? resolveEasing(b.easing)(raw) : raw;
16893
17005
  if (track.interpolator) {
16894
17006
  let fn = segmentCache?.get(i + 1);
16895
17007
  if (!fn) {
@@ -16968,7 +17080,10 @@ function createTimeline(register, id, opts) {
16968
17080
  for (const track of tracks) {
16969
17081
  if (track.kind === "event") {
16970
17082
  const end = firstAfter(track.events, to);
16971
- for (let i = firstAfter(track.events, from); i < end; i += 1) track.events[i].fire();
17083
+ for (let i = firstAfter(track.events, from); i < end; i += 1) {
17084
+ const ev = track.events[i];
17085
+ ev.fire(to - ev.t);
17086
+ }
16972
17087
  } else if (track.kind === "timeline") {
16973
17088
  fireEvents(track.timeline.tracks, from - track.at, to - track.at);
16974
17089
  }
@@ -17048,6 +17163,7 @@ function createTimeline(register, id, opts) {
17048
17163
  wantScale = s;
17049
17164
  base.setTimeScale(s);
17050
17165
  },
17166
+ timeScale: () => live ? base.timeScale() : wantScale,
17051
17167
  isPaused: () => live ? base.isPaused() : wantPaused,
17052
17168
  seek(t) {
17053
17169
  offset = t - lastVirtual;
@@ -17058,6 +17174,10 @@ function createTimeline(register, id, opts) {
17058
17174
  time: () => playhead,
17059
17175
  duration: () => duration,
17060
17176
  tracks: () => opts.tracks,
17177
+ setLoop(loop) {
17178
+ loopsLeft = loop === true ? Infinity : loop === false ? 0 : loop;
17179
+ },
17180
+ loop: () => loopsLeft === Infinity ? true : loopsLeft === 0 ? false : loopsLeft,
17061
17181
  edit(fn) {
17062
17182
  fn();
17063
17183
  caches = /* @__PURE__ */ new WeakMap();
@@ -17272,6 +17392,7 @@ function useAnimator(opts = {}) {
17272
17392
  const a = animations.current.get(anim.id);
17273
17393
  if (a) a.timeScale = s;
17274
17394
  },
17395
+ timeScale: () => animations.current.get(anim.id)?.timeScale ?? 1,
17275
17396
  isPaused: () => animations.current.get(anim.id)?.paused ?? false
17276
17397
  };
17277
17398
  };
@@ -17293,6 +17414,7 @@ function useAnimator(opts = {}) {
17293
17414
  pause: base.pause,
17294
17415
  resume: base.resume,
17295
17416
  setTimeScale: base.setTimeScale,
17417
+ timeScale: base.timeScale,
17296
17418
  isPaused: base.isPaused,
17297
17419
  setOnCancel: (cb) => {
17298
17420
  onCancelCb = cb;
@@ -17303,7 +17425,7 @@ function useAnimator(opts = {}) {
17303
17425
  const tween = (o) => {
17304
17426
  const id = nextId.current++;
17305
17427
  const start = 0;
17306
- const easing = o.easing ?? easeOut;
17428
+ const easing = resolveEasing(o.easing ?? easeOut);
17307
17429
  const factoryFn = o.interpolator ? o.interpolator(o.from, o.to) : null;
17308
17430
  const perTickInterp = factoryFn ? null : o.interpolate ?? ((a, b, t) => {
17309
17431
  if (typeof a === "number" && typeof b === "number") {
@@ -17458,6 +17580,7 @@ function useAnimator(opts = {}) {
17458
17580
  setTimeScale: (s) => {
17459
17581
  globalTimeScale.current = s;
17460
17582
  },
17583
+ timeScale: () => globalTimeScale.current,
17461
17584
  pauseKey: (key) => {
17462
17585
  for (const a of animations.current.values()) if (a.cancelKey === key) a.paused = true;
17463
17586
  },
@@ -18055,6 +18178,21 @@ function LineIcon({ className, size = 20 }) {
18055
18178
  }
18056
18179
  );
18057
18180
  }
18181
+ function ArrowIcon({ className, size = 20 }) {
18182
+ return /* @__PURE__ */ jsxs(
18183
+ "svg",
18184
+ {
18185
+ ...ICON_SVG_BASE,
18186
+ className,
18187
+ width: size,
18188
+ height: size,
18189
+ children: [
18190
+ /* @__PURE__ */ jsx("path", { d: "M3.5 16.5 12.9 7.1", strokeLinecap: "round" }),
18191
+ /* @__PURE__ */ jsx("path", { d: "M16.5 3.5 11.06 5.98 14.03 8.95Z", fill: "currentColor", strokeLinejoin: "round" })
18192
+ ]
18193
+ }
18194
+ );
18195
+ }
18058
18196
  function PolygonIcon({ className, size = 20 }) {
18059
18197
  return /* @__PURE__ */ jsx(
18060
18198
  "svg",
@@ -18632,6 +18770,14 @@ var TEXT_PAINTER = {
18632
18770
  );
18633
18771
  if (boxes.length === 0) return null;
18634
18772
  return rectsToPath(boxes);
18773
+ },
18774
+ // `filled` is unconditional: a text node's `data.fill: null` still resolves
18775
+ // to the default in `ResolvedRun`, so there is no outline-only text to
18776
+ // report. The reach is the point — a heavily outlined glyph was unpickable
18777
+ // across the width of its own outline.
18778
+ ink: (node, _pose, ctx) => {
18779
+ const d = node.data;
18780
+ return { filled: true, ...inkReach(resolveNodeStroke(d.stroke), ctx?.scale) };
18635
18781
  }
18636
18782
  };
18637
18783
  function rectsToPath(rects) {
@@ -18897,10 +19043,10 @@ var RECT_FALLBACK_PAINTER = {
18897
19043
  };
18898
19044
  function registerBuiltInShapePainters() {
18899
19045
  registerNodeShape(TEXT_PAINTER);
19046
+ registerNodeShape(DERIVED_PAINTER);
18900
19047
  registerNodeShape(PATH_PAINTER);
18901
19048
  registerNodeShape(SHAPE_PAINTER);
18902
19049
  registerNodeShape(IMAGE_PAINTER);
18903
- registerNodeShape(DERIVED_PAINTER);
18904
19050
  registerNodeShape(RECT_FALLBACK_PAINTER);
18905
19051
  }
18906
19052
  registerBuiltInShapePainters();
@@ -19909,11 +20055,7 @@ function useRotateTool(adapter, options) {
19909
20055
  [overlay]
19910
20056
  );
19911
20057
  }
19912
- function useHandTool(opts = {}) {
19913
- const inertia = opts.inertia === false ? false : opts.inertia;
19914
- const axis = opts.axis ?? "both";
19915
- const tracker = useVelocityTracker();
19916
- const decay = useDecayLoop();
20058
+ function useHandTool(_opts = {}) {
19917
20059
  return useMemo(
19918
20060
  () => Object.assign(defineViewportTool({
19919
20061
  id: "hand",
@@ -19933,8 +20075,7 @@ function useHandTool(opts = {}) {
19933
20075
  // phase entry.
19934
20076
  bindings: [{ spec: { kind: "drag" }, actionId: "viewport.dragPan" }]
19935
20077
  }),
19936
- // eslint-disable-next-line react-hooks/exhaustive-deps
19937
- [inertia, axis, tracker, decay]
20078
+ []
19938
20079
  );
19939
20080
  }
19940
20081
  var PRESENTATION = {
@@ -19963,7 +20104,7 @@ function usePinchZoomTool(canvasRef, getView, setView, opts = {}) {
19963
20104
  getViewRef.current = getView;
19964
20105
  const setViewRef = useRef(setView);
19965
20106
  setViewRef.current = setView;
19966
- const { min = 0.1, max = 8, enabled = true } = opts;
20107
+ const { min = DEFAULT_MIN_ZOOM, max = DEFAULT_MAX_ZOOM, enabled = true } = opts;
19967
20108
  const resolveTargetRef = useRef(opts.resolveTarget);
19968
20109
  resolveTargetRef.current = opts.resolveTarget;
19969
20110
  usePinchGesture(canvasRef, (clientAnchor, scaleFactor) => {
@@ -21616,14 +21757,89 @@ function wrapNodeOutput(cmds, pose, alpha) {
21616
21757
  }
21617
21758
  return rotated;
21618
21759
  }
21619
- function CursorCoordsHud({ canvasRef, viewRef, offset }) {
21760
+ function insetAlong(start, extent, size, align, by) {
21761
+ if (align === "start") return start + by;
21762
+ if (align === "end") return start + extent - size - by;
21763
+ return start + (extent - size) / 2 + by;
21764
+ }
21765
+ function hostAnchorRect(input) {
21766
+ const { host, panel, viewport, align, offset, padding = 0 } = input;
21767
+ const desired = {
21768
+ x: insetAlong(host.x, host.width, panel.width, align.x, offset.x),
21769
+ y: insetAlong(host.y, host.height, panel.height, align.y, offset.y),
21770
+ width: panel.width,
21771
+ height: panel.height
21772
+ };
21773
+ return clampRectWithin(desired, { x: 0, y: 0, width: viewport.width, height: viewport.height }, padding);
21774
+ }
21775
+ function hostAnchorCss(rect, align, viewport) {
21776
+ return {
21777
+ ...align.y === "end" ? { bottom: viewport.height - rect.y - rect.height } : { top: rect.y },
21778
+ ...align.x === "end" ? { right: viewport.width - rect.x - rect.width } : { left: rect.x }
21779
+ };
21780
+ }
21781
+ function useHostAnchor(resolveHost, options) {
21782
+ const [panel, setPanel] = useState(null);
21783
+ const [style, setStyle] = useState(null);
21784
+ const ref = useCallback((el) => setPanel(el), []);
21785
+ const resolve = useRef(resolveHost);
21786
+ resolve.current = resolveHost;
21787
+ const { align, offset, padding = 0 } = options;
21788
+ const { x: alignX, y: alignY } = align;
21789
+ const { x: offsetX, y: offsetY } = offset;
21790
+ const recompute = useCallback(() => {
21791
+ const hostEl = resolve.current();
21792
+ if (!hostEl) {
21793
+ setStyle(null);
21794
+ return;
21795
+ }
21796
+ const h = hostEl.getBoundingClientRect();
21797
+ const p = panel?.getBoundingClientRect();
21798
+ const viewport = { width: window.innerWidth, height: window.innerHeight };
21799
+ const align2 = { x: alignX, y: alignY };
21800
+ const rect = hostAnchorRect({
21801
+ host: { x: h.left, y: h.top, width: h.width, height: h.height },
21802
+ panel: { width: p?.width ?? 0, height: p?.height ?? 0 },
21803
+ viewport,
21804
+ align: align2,
21805
+ offset: { x: offsetX, y: offsetY },
21806
+ padding
21807
+ });
21808
+ setStyle(hostAnchorCss(rect, align2, viewport));
21809
+ }, [panel, alignX, alignY, offsetX, offsetY, padding]);
21810
+ const raf = useVisibleRaf(recompute);
21811
+ useEffect(() => {
21812
+ recompute();
21813
+ const schedule = () => raf.request();
21814
+ window.addEventListener("scroll", schedule, true);
21815
+ window.addEventListener("resize", schedule);
21816
+ const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(schedule) : null;
21817
+ if (panel) observer?.observe(panel);
21818
+ const hostEl = resolve.current();
21819
+ if (hostEl) observer?.observe(hostEl);
21820
+ return () => {
21821
+ window.removeEventListener("scroll", schedule, true);
21822
+ window.removeEventListener("resize", schedule);
21823
+ raf.cancel();
21824
+ observer?.disconnect();
21825
+ };
21826
+ }, [recompute, raf, panel]);
21827
+ return { ref, style };
21828
+ }
21829
+ function CursorCoordsHud({ canvasRef, anchorRef, viewRef, offset }) {
21620
21830
  const [state, setState] = useState({
21621
21831
  client: { x: 0, y: 0 },
21622
21832
  world: null,
21623
- inCanvas: false,
21624
- anchor: null
21833
+ inCanvas: false
21625
21834
  });
21626
21835
  const [fps, setFps] = useState(0);
21836
+ const { ref, style: anchorStyle } = useHostAnchor(
21837
+ () => anchorRef?.current ?? canvasRef.current?.parentElement ?? canvasRef.current,
21838
+ {
21839
+ align: { x: "end", y: "start" },
21840
+ offset: { x: offset?.right ?? 8, y: offset?.top ?? 8 }
21841
+ }
21842
+ );
21627
21843
  const framesRef = useRef(0);
21628
21844
  const lastFlushRef = useRef(0);
21629
21845
  const fpsLoop = useVisibleRaf(
@@ -21653,49 +21869,33 @@ function CursorCoordsHud({ canvasRef, viewRef, offset }) {
21653
21869
  return () => fpsLoop.cancel();
21654
21870
  }, [fpsLoop]);
21655
21871
  useEffect(() => {
21656
- const readAnchor = () => {
21657
- const canvas = canvasRef.current;
21658
- if (!canvas) return null;
21659
- const host = canvas.parentElement ?? canvas;
21660
- const rect = host.getBoundingClientRect();
21661
- return { top: rect.top, right: window.innerWidth - rect.right };
21662
- };
21663
21872
  const onMove = (e) => {
21664
21873
  const canvas = canvasRef.current;
21665
21874
  const view = viewRef.current;
21666
21875
  const client = { x: e.clientX, y: e.clientY };
21667
- const anchor = readAnchor();
21668
21876
  if (!canvas || !view) {
21669
- setState({ client, world: null, inCanvas: false, anchor });
21877
+ setState({ client, world: null, inCanvas: false });
21670
21878
  return;
21671
21879
  }
21672
21880
  const rect = canvas.getBoundingClientRect();
21673
21881
  const inCanvas = e.clientX >= rect.left && e.clientX <= rect.right && e.clientY >= rect.top && e.clientY <= rect.bottom;
21674
21882
  const [wx, wy] = clientToWorld(e.clientX, e.clientY, rect, view);
21675
21883
  const world = { x: wx, y: wy };
21676
- setState({ client, world, inCanvas, anchor });
21884
+ setState({ client, world, inCanvas });
21677
21885
  };
21678
- const onLayout = () => setState((s) => ({ ...s, anchor: readAnchor() }));
21679
21886
  document.addEventListener("pointermove", onMove);
21680
- window.addEventListener("scroll", onLayout, true);
21681
- window.addEventListener("resize", onLayout);
21682
- setState((s) => ({ ...s, anchor: readAnchor() }));
21683
21887
  return () => {
21684
21888
  document.removeEventListener("pointermove", onMove);
21685
- window.removeEventListener("scroll", onLayout, true);
21686
- window.removeEventListener("resize", onLayout);
21687
21889
  };
21688
21890
  }, [canvasRef, viewRef]);
21689
- if (!state.anchor) return null;
21690
- const top = state.anchor.top + (offset?.top ?? 8);
21691
- const right = state.anchor.right + (offset?.right ?? 8);
21891
+ if (!anchorStyle) return null;
21692
21892
  return /* @__PURE__ */ jsxs(
21693
21893
  "div",
21694
21894
  {
21895
+ ref,
21695
21896
  style: {
21696
21897
  position: "fixed",
21697
- top,
21698
- right,
21898
+ ...anchorStyle,
21699
21899
  zIndex: 1e4,
21700
21900
  background: "rgba(0,0,0,0.7)",
21701
21901
  color: "#e8e8e8",
@@ -21718,33 +21918,31 @@ function CursorCoordsHud({ canvasRef, viewRef, offset }) {
21718
21918
  );
21719
21919
  }
21720
21920
  var PickHud_default = {};
21721
- function PickHud({ canvasRef, viewRef, pickEvery, pickBest, offset }) {
21921
+ function PickHud({ canvasRef, anchorRef, viewRef, pickEvery, pickBest, offset }) {
21722
21922
  const [state, setState] = useState({
21723
21923
  ids: [],
21724
21924
  best: null,
21725
- inCanvas: false,
21726
- anchor: null
21925
+ inCanvas: false
21727
21926
  });
21927
+ const { ref, style } = useHostAnchor(
21928
+ () => anchorRef?.current ?? canvasRef.current?.parentElement ?? canvasRef.current,
21929
+ {
21930
+ align: { x: "end", y: "start" },
21931
+ offset: { x: offset?.right ?? 8, y: offset?.top ?? 76 }
21932
+ }
21933
+ );
21728
21934
  useEffect(() => {
21729
- const readAnchor = () => {
21730
- const canvas = canvasRef.current;
21731
- if (!canvas) return null;
21732
- const host = canvas.parentElement ?? canvas;
21733
- const rect = host.getBoundingClientRect();
21734
- return { top: rect.top, right: window.innerWidth - rect.right };
21735
- };
21736
21935
  const onMove = (e) => {
21737
21936
  const canvas = canvasRef.current;
21738
21937
  const view = viewRef.current;
21739
- const anchor = readAnchor();
21740
21938
  if (!canvas || !view) {
21741
- setState({ ids: [], best: null, inCanvas: false, anchor });
21939
+ setState({ ids: [], best: null, inCanvas: false });
21742
21940
  return;
21743
21941
  }
21744
21942
  const rect = canvas.getBoundingClientRect();
21745
21943
  const inCanvas = e.clientX >= rect.left && e.clientX <= rect.right && e.clientY >= rect.top && e.clientY <= rect.bottom;
21746
21944
  if (!inCanvas) {
21747
- setState({ ids: [], best: null, inCanvas: false, anchor });
21945
+ setState({ ids: [], best: null, inCanvas: false });
21748
21946
  return;
21749
21947
  }
21750
21948
  const worldX = (e.clientX - rect.left) / view.scale.x + view.x;
@@ -21763,24 +21961,15 @@ function PickHud({ canvasRef, viewRef, pickEvery, pickBest, offset }) {
21763
21961
  best = null;
21764
21962
  }
21765
21963
  }
21766
- setState({ ids, best, inCanvas: true, anchor });
21964
+ setState({ ids, best, inCanvas: true });
21767
21965
  };
21768
- const onLayout = () => setState((s) => ({ ...s, anchor: readAnchor() }));
21769
21966
  document.addEventListener("pointermove", onMove);
21770
- window.addEventListener("scroll", onLayout, true);
21771
- window.addEventListener("resize", onLayout);
21772
- setState((s) => ({ ...s, anchor: readAnchor() }));
21773
21967
  return () => {
21774
21968
  document.removeEventListener("pointermove", onMove);
21775
- window.removeEventListener("scroll", onLayout, true);
21776
- window.removeEventListener("resize", onLayout);
21777
21969
  };
21778
21970
  }, [canvasRef, viewRef, pickEvery, pickBest]);
21779
- if (!state.anchor) return null;
21780
- const top = state.anchor.top + (offset?.top ?? 76);
21781
- const right = state.anchor.right + (offset?.right ?? 8);
21782
- const style = { top, right };
21783
- return /* @__PURE__ */ jsxs("div", { className: PickHud_default.hud, style, children: [
21971
+ if (!style) return null;
21972
+ return /* @__PURE__ */ jsxs("div", { ref, className: PickHud_default.hud, style, children: [
21784
21973
  /* @__PURE__ */ jsxs("div", { className: PickHud_default.header, children: [
21785
21974
  "pickEvery (",
21786
21975
  state.ids.length,
@@ -21797,34 +21986,20 @@ function PickHud({ canvasRef, viewRef, pickEvery, pickBest, offset }) {
21797
21986
  ] });
21798
21987
  }
21799
21988
  var ModalityHud_default = {};
21800
- function ModalityHud({ canvasRef, modeId, offset }) {
21989
+ function ModalityHud({ canvasRef, anchorRef, modeId, offset }) {
21801
21990
  const toolCtx = useOptionalActiveToolContext();
21802
- const [state, setState] = useState({ anchor: null });
21803
- useEffect(() => {
21804
- const readAnchor = () => {
21805
- const canvas = canvasRef.current;
21806
- if (!canvas) return null;
21807
- const host = canvas.parentElement ?? canvas;
21808
- const rect = host.getBoundingClientRect();
21809
- return { top: rect.top, right: window.innerWidth - rect.right };
21810
- };
21811
- const onLayout = () => setState({ anchor: readAnchor() });
21812
- onLayout();
21813
- window.addEventListener("scroll", onLayout, true);
21814
- window.addEventListener("resize", onLayout);
21815
- return () => {
21816
- window.removeEventListener("scroll", onLayout, true);
21817
- window.removeEventListener("resize", onLayout);
21818
- };
21819
- }, [canvasRef]);
21820
- if (!state.anchor) return null;
21821
- const top = state.anchor.top + (offset?.top ?? 220);
21822
- const right = state.anchor.right + (offset?.right ?? 8);
21823
- const style = { top, right };
21991
+ const { ref, style } = useHostAnchor(
21992
+ () => anchorRef?.current ?? canvasRef.current?.parentElement ?? canvasRef.current,
21993
+ {
21994
+ align: { x: "end", y: "start" },
21995
+ offset: { x: offset?.right ?? 8, y: offset?.top ?? 220 }
21996
+ }
21997
+ );
21998
+ if (!style) return null;
21824
21999
  const active = toolCtx?.active ?? null;
21825
22000
  const hotkeys = toolCtx?.hotkeyStack ?? [];
21826
22001
  const hotkeyTop = hotkeys.length > 0 ? hotkeys[hotkeys.length - 1] : null;
21827
- return /* @__PURE__ */ jsxs("div", { className: ModalityHud_default.hud, style, children: [
22002
+ return /* @__PURE__ */ jsxs("div", { ref, className: ModalityHud_default.hud, style, children: [
21828
22003
  /* @__PURE__ */ jsxs("div", { className: ModalityHud_default.row, children: [
21829
22004
  /* @__PURE__ */ jsx("span", { className: ModalityHud_default.label, children: "mode" }),
21830
22005
  /* @__PURE__ */ jsx("span", { className: modeId ? ModalityHud_default.value : ModalityHud_default.valueMuted, children: modeId ?? "\u2014" })
@@ -21955,6 +22130,8 @@ function CanvasInner(props, ref) {
21955
22130
  boundsOf: boundsOf2,
21956
22131
  pickEvery,
21957
22132
  clientToWorld: clientToWorld2,
22133
+ paintInto,
22134
+ inputElement,
21958
22135
  geometry = AUTO_POSE_DESCRIPTOR,
21959
22136
  className,
21960
22137
  style,
@@ -22000,6 +22177,15 @@ function CanvasInner(props, ref) {
22000
22177
  debugSinkRefForCtx.current = debugSink;
22001
22178
  const adapter = adapterProp;
22002
22179
  const canvasRef = useRef(null);
22180
+ const ownCanvasRef = useRef(null);
22181
+ const detached = !!paintInto;
22182
+ if (detached) {
22183
+ canvasRef.current = inputElement ?? null;
22184
+ }
22185
+ const paintTargetRef = useRef(null);
22186
+ paintTargetRef.current = detached ? paintInto.canvas ?? null : null;
22187
+ const paintRectRef = useRef(null);
22188
+ paintRectRef.current = detached ? { x: paintInto.x, y: paintInto.y } : null;
22003
22189
  const helpersForLayersRef = useRef(null);
22004
22190
  const dimsRef = useRef({ width, height });
22005
22191
  dimsRef.current = { width, height };
@@ -22087,7 +22273,10 @@ function CanvasInner(props, ref) {
22087
22273
  };
22088
22274
  }, []);
22089
22275
  useImperativeHandle(ref, () => ({
22090
- element: canvasRef.current,
22276
+ // Named rather than read off `canvasRef` so the handle rebuilds when a
22277
+ // detached surface's input element arrives, which is a render later.
22278
+ element: detached ? inputElement ?? null : canvasRef.current,
22279
+ surface: detached ? paintInto?.canvas ?? null : ownCanvasRef.current,
22091
22280
  requestRedraw,
22092
22281
  subscribeFrame,
22093
22282
  registerLayer,
@@ -22098,6 +22287,10 @@ function CanvasInner(props, ref) {
22098
22287
  getPaintedVersion
22099
22288
  }), [
22100
22289
  canvasRef,
22290
+ ownCanvasRef,
22291
+ detached,
22292
+ inputElement,
22293
+ paintInto?.canvas,
22101
22294
  requestRedraw,
22102
22295
  subscribeFrame,
22103
22296
  registerLayer,
@@ -22393,8 +22586,9 @@ function CanvasInner(props, ref) {
22393
22586
  shaders
22394
22587
  };
22395
22588
  const paint = useCallback(() => {
22396
- const c = canvasRef.current;
22589
+ const c = paintTargetRef.current ?? ownCanvasRef.current;
22397
22590
  if (!c) return false;
22591
+ const rect = paintRectRef.current;
22398
22592
  const {
22399
22593
  layers: paintLayers,
22400
22594
  width: w,
@@ -22423,7 +22617,9 @@ function CanvasInner(props, ref) {
22423
22617
  try {
22424
22618
  renderer = new WeaselRenderer({
22425
22619
  gl,
22426
- canvas: c,
22620
+ // A shared buffer is the caller's to size. Handing the renderer the
22621
+ // element would have every co-tenant resize it to its own pane.
22622
+ ...rect ? {} : { canvas: c },
22427
22623
  width: w,
22428
22624
  height: h,
22429
22625
  dpr
@@ -22442,6 +22638,7 @@ function CanvasInner(props, ref) {
22442
22638
  lastResizeRef.current = { w, h, dpr };
22443
22639
  }
22444
22640
  }
22641
+ renderer.setTarget(rect ? { origin: { x: rect.x, y: rect.y } } : null);
22445
22642
  const view = viewRef.current;
22446
22643
  const commands = drawLayers(
22447
22644
  paintLayers,
@@ -22490,31 +22687,69 @@ function CanvasInner(props, ref) {
22490
22687
  }, [shaderIdKey]);
22491
22688
  const toolsCursor = tools ? resolveToolsCursor(tools, toolsCtxBase) : void 0;
22492
22689
  const effectiveStyle = toolsCursor ? { ...style, cursor: toolsCursor } : style;
22493
- return /* @__PURE__ */ jsxs(Fragment, { children: [
22494
- /* @__PURE__ */ jsx(
22495
- "canvas",
22690
+ const layersForMoveRef = useRef(layersWithDebug);
22691
+ layersForMoveRef.current = layersWithDebug;
22692
+ useEffect(() => {
22693
+ if (!detached || !inputElement) return;
22694
+ const el = inputElement;
22695
+ const onDown = () => {
22696
+ if (autoFocusOnPointerDown) el.focus();
22697
+ pointerDownRef.current = true;
22698
+ };
22699
+ const onMove = (e) => {
22700
+ if (pointerDownRef.current) return;
22701
+ const view = viewRef.current;
22702
+ const [worldX, worldY] = toWorld(
22703
+ el,
22704
+ e.clientX,
22705
+ e.clientY,
22706
+ view,
22707
+ clientToWorldRef.current
22708
+ );
22709
+ const dims = dimsRef.current;
22710
+ for (const layer of layersForMoveRef.current) {
22711
+ layer.onUncapturedMove?.(worldX, worldY, e, view, dims);
22712
+ }
22713
+ };
22714
+ const onUp = () => {
22715
+ pointerDownRef.current = false;
22716
+ };
22717
+ const onLeave = () => {
22718
+ pointerDownRef.current = false;
22719
+ for (const layer of layersForMoveRef.current) layer.onUncapturedLeave?.();
22720
+ };
22721
+ const onMenu = (e) => e.preventDefault();
22722
+ el.addEventListener("pointerdown", onDown);
22723
+ el.addEventListener("pointermove", onMove);
22724
+ el.addEventListener("pointerup", onUp);
22725
+ el.addEventListener("pointerleave", onLeave);
22726
+ el.addEventListener("contextmenu", onMenu);
22727
+ return () => {
22728
+ el.removeEventListener("pointerdown", onDown);
22729
+ el.removeEventListener("pointermove", onMove);
22730
+ el.removeEventListener("pointerup", onUp);
22731
+ el.removeEventListener("pointerleave", onLeave);
22732
+ el.removeEventListener("contextmenu", onMenu);
22733
+ };
22734
+ }, [detached, inputElement, autoFocusOnPointerDown]);
22735
+ useEffect(() => {
22736
+ if (!detached || !inputElement) return;
22737
+ inputElement.style.cursor = toolsCursor ?? "";
22738
+ }, [detached, inputElement, toolsCursor]);
22739
+ const huds = /* @__PURE__ */ jsxs(Fragment, { children: [
22740
+ cursorCoordsHud && /* @__PURE__ */ jsx(
22741
+ CursorCoordsHud,
22496
22742
  {
22497
- ref: canvasRef,
22498
- width,
22499
- height,
22500
- tabIndex,
22501
- className,
22502
- style: effectiveStyle,
22503
- onFocus,
22504
- onBlur,
22505
- onPointerDown: handlePointerDown,
22506
- onPointerMove: handlePointerMove,
22507
- onPointerUp: handlePointerUp,
22508
- onPointerCancel: handlePointerCancel,
22509
- onPointerLeave: handlePointerLeave,
22510
- onContextMenu: (e) => e.preventDefault()
22743
+ canvasRef,
22744
+ ...detached ? { anchorRef: canvasRef } : {},
22745
+ viewRef
22511
22746
  }
22512
22747
  ),
22513
- cursorCoordsHud && /* @__PURE__ */ jsx(CursorCoordsHud, { canvasRef, viewRef }),
22514
22748
  pickHud && /* @__PURE__ */ jsx(
22515
22749
  PickHud,
22516
22750
  {
22517
22751
  canvasRef,
22752
+ ...detached ? { anchorRef: canvasRef } : {},
22518
22753
  viewRef,
22519
22754
  pickEvery: stablePickEveryForHud,
22520
22755
  pickBest: stablePickBestForHud
@@ -22524,10 +22759,37 @@ function CanvasInner(props, ref) {
22524
22759
  ModalityHud,
22525
22760
  {
22526
22761
  canvasRef,
22762
+ ...detached ? { anchorRef: canvasRef } : {},
22527
22763
  modeId: typeof modalityHud === "object" ? modalityHud.modeId : void 0
22528
22764
  }
22529
22765
  )
22530
22766
  ] });
22767
+ if (detached) return inputElement ? huds : null;
22768
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
22769
+ /* @__PURE__ */ jsx(
22770
+ "canvas",
22771
+ {
22772
+ ref: (el) => {
22773
+ ownCanvasRef.current = el;
22774
+ canvasRef.current = el;
22775
+ },
22776
+ width,
22777
+ height,
22778
+ tabIndex,
22779
+ className,
22780
+ style: effectiveStyle,
22781
+ onFocus,
22782
+ onBlur,
22783
+ onPointerDown: handlePointerDown,
22784
+ onPointerMove: handlePointerMove,
22785
+ onPointerUp: handlePointerUp,
22786
+ onPointerCancel: handlePointerCancel,
22787
+ onPointerLeave: handlePointerLeave,
22788
+ onContextMenu: (e) => e.preventDefault()
22789
+ }
22790
+ ),
22791
+ huds
22792
+ ] });
22531
22793
  }
22532
22794
  var Canvas = forwardRef(CanvasInner);
22533
22795
  function composeAlphaFor(scene, alphaFor) {
@@ -23656,6 +23918,32 @@ function sceneFromJSON(json, options) {
23656
23918
  });
23657
23919
  }
23658
23920
  var SelectionContext = createContext(null);
23921
+ function SelectionContextProvider({ children }) {
23922
+ const [state, setState] = useState({ selection: [], kinds: void 0 });
23923
+ const lastSerializedRef = useRef("");
23924
+ const publishSelection = useCallback((ids, kinds) => {
23925
+ const next = JSON.stringify([ids, kinds]);
23926
+ if (next === lastSerializedRef.current) return;
23927
+ lastSerializedRef.current = next;
23928
+ setState({ selection: ids, kinds });
23929
+ }, []);
23930
+ const value = useMemo(
23931
+ () => ({
23932
+ selection: state.selection,
23933
+ ...state.kinds !== void 0 ? { kinds: state.kinds } : {},
23934
+ publishSelection
23935
+ }),
23936
+ [state, publishSelection]
23937
+ );
23938
+ return /* @__PURE__ */ jsx(SelectionContext.Provider, { value, children });
23939
+ }
23940
+ function SelectionContextProviderIfRoot({
23941
+ children
23942
+ }) {
23943
+ const parent = useContext(SelectionContext);
23944
+ if (parent !== null) return /* @__PURE__ */ jsx(Fragment, { children });
23945
+ return /* @__PURE__ */ jsx(SelectionContextProvider, { children });
23946
+ }
23659
23947
  function usePublishSelection(ids, kinds) {
23660
23948
  const ctx = useContext(SelectionContext);
23661
23949
  const idsRef = useRef(ids);
@@ -25113,7 +25401,7 @@ function useDispatcherOverlayLayer(args) {
25113
25401
  break;
25114
25402
  case "pencil": {
25115
25403
  if (geom.samples.length < 2) break;
25116
- pathCmd = polygonFromPoints(projectPoints(geom.samples));
25404
+ pathCmd = polylineFromPoints(projectPoints(geom.samples));
25117
25405
  break;
25118
25406
  }
25119
25407
  }
@@ -25125,7 +25413,7 @@ function useDispatcherOverlayLayer(args) {
25125
25413
  stroke: { paint: { color: cfg.stroke }, width: cfg.lineWidth, dash: cfg.dash }
25126
25414
  };
25127
25415
  out.push(cmd);
25128
- if (ov.anchorPoint) {
25416
+ if (ov.anchorPoint && ov.shape !== "pencil") {
25129
25417
  const anchorScreen = projectPoint(ov.anchorPoint.x, ov.anchorPoint.y);
25130
25418
  out.push({
25131
25419
  kind: "path",
@@ -26994,7 +27282,7 @@ function SceneCanvasInner(props, ref) {
26994
27282
  boundary: inertiaObj.boundary,
26995
27283
  bounds: inertiaObj.bounds
26996
27284
  } : void 0;
26997
- const handTool = useHandTool(handToolInertia ? { inertia: handToolInertia } : {});
27285
+ const handTool = useHandTool(handToolInertia ? {} : {});
26998
27286
  const viewportAmbient = [];
26999
27287
  const baseRequestedTools = defaultTools ?? (toolBundle ? BUNDLE_TOOLS[toolBundle] : null) ?? (viewportRegistered ? ["select", "rotate", "hand"] : ["select", "rotate"]);
27000
27288
  const KNOWN_BUILTIN_IDS = /* @__PURE__ */ new Set([
@@ -27719,14 +28007,12 @@ function StandardActionsRegistrar({
27719
28007
  }, [registry22, actionsRegistryRef]);
27720
28008
  useEffect(() => {
27721
28009
  if (!registry22) return;
27722
- registry22.setDispatcher(dispatcher);
27723
- return () => registry22.setDispatcher(null);
28010
+ return registry22.setDispatcher(dispatcher);
27724
28011
  }, [registry22, dispatcher]);
27725
28012
  const wiredDepRegistry = useDepRegistry();
27726
28013
  useEffect(() => {
27727
28014
  if (!registry22) return;
27728
- registry22.setDepRegistry(wiredDepRegistry);
27729
- return () => registry22.setDepRegistry(null);
28015
+ return registry22.setDepRegistry(wiredDepRegistry);
27730
28016
  }, [registry22, wiredDepRegistry]);
27731
28017
  useEffect(() => {
27732
28018
  if (!registry22) return;
@@ -27787,7 +28073,7 @@ var SceneCanvasInnerForwardRef = forwardRef(SceneCanvasInner);
27787
28073
  function SceneCanvasWrapper(props, ref) {
27788
28074
  return /* @__PURE__ */ jsx(ActiveToolContextProviderIfRoot, { children: /* @__PURE__ */ jsx(ViewRegistryProvider, { children: /* @__PURE__ */ jsx(SceneCanvasInnerForwardRef, { ...props, ref }) }) });
27789
28075
  }
27790
- forwardRef(SceneCanvasWrapper);
28076
+ var SceneCanvas = forwardRef(SceneCanvasWrapper);
27791
28077
  var RAD_TO_DEG = 180 / Math.PI;
27792
28078
  var rotationDegreesUnit = {
27793
28079
  toDisplay: (rad) => Math.round(rad * RAD_TO_DEG * 10) / 10,
@@ -27919,9 +28205,9 @@ function shapeSchema(opts = {}) {
27919
28205
  fontWeight: { kind: "number", name: "Weight", description: "Font weight, 100\u2013900.", default: 400, min: 100, max: 900, step: 100, pair: "Size / weight" },
27920
28206
  fontStyle: { kind: "enum", name: "Style", description: "Upright or italic.", default: "normal", options: [{ value: "normal", label: "Normal" }, { value: "italic", label: "Italic" }] },
27921
28207
  letterSpacing: { kind: "number", name: "Tracking", description: "Extra advance per glyph, world units.", default: 0, step: 0.1 },
27922
- underline: { kind: "boolean", name: "Underline", description: "Underline the text.", default: false },
27923
- strikethrough: { kind: "boolean", name: "Strikethrough", description: "Strike through the text.", default: false },
27924
- overline: { kind: "boolean", name: "Overline", description: "Rule the text above its ascent.", default: false }
28208
+ underline: { kind: "boolean", name: "Underline", description: "Underline the text.", default: false, control: "toggle", short: "U", pair: "Decoration" },
28209
+ strikethrough: { kind: "boolean", name: "Strikethrough", description: "Strike through the text.", default: false, control: "toggle", short: "S", pair: "Decoration" },
28210
+ overline: { kind: "boolean", name: "Overline", description: "Rule the text above its ascent.", default: false, control: "toggle", short: "O", pair: "Decoration" }
27925
28211
  }
27926
28212
  },
27927
28213
  paragraph: {
@@ -28274,6 +28560,125 @@ function MinimapCanvasInner(props) {
28274
28560
  );
28275
28561
  }
28276
28562
  var MinimapCanvas = MinimapCanvasInner;
28563
+ function planPixelRender(args) {
28564
+ const { sourceRect, scale: scale2 } = args;
28565
+ for (const [label, v] of [
28566
+ ["sourceRect.width", sourceRect.width],
28567
+ ["sourceRect.height", sourceRect.height],
28568
+ ["scale.x", scale2.x],
28569
+ ["scale.y", scale2.y]
28570
+ ]) {
28571
+ if (!Number.isFinite(v) || v <= 0) {
28572
+ throw new Error(`renderSceneToPixels: ${label} must be a positive finite number, got ${v}`);
28573
+ }
28574
+ }
28575
+ for (const [label, v] of [
28576
+ ["sourceRect.x", sourceRect.x],
28577
+ ["sourceRect.y", sourceRect.y]
28578
+ ]) {
28579
+ if (!Number.isFinite(v)) {
28580
+ throw new Error(`renderSceneToPixels: ${label} must be a finite number, got ${v}`);
28581
+ }
28582
+ }
28583
+ const width = Math.max(1, Math.round(sourceRect.width * scale2.x));
28584
+ const height = Math.max(1, Math.round(sourceRect.height * scale2.y));
28585
+ const view = { x: sourceRect.x, y: sourceRect.y, scale: { x: scale2.x, y: scale2.y } };
28586
+ const resolveImage = args.resolveImage;
28587
+ const drawOne = args.drawOne ?? ((node, pose, view2, ctx) => defaultDrawOne(node, pose, view2, { ...ctx, resolveImage }));
28588
+ const commands = [];
28589
+ if (args.background !== void 0) {
28590
+ commands.push({
28591
+ kind: "path",
28592
+ path: { kind: "rect", x: 0, y: 0, width, height },
28593
+ fill: { fill: "solid", color: args.background }
28594
+ });
28595
+ }
28596
+ commands.push(...buildSceneViewCommands(args.scene, view, drawOne, void 0, args.alphaFor));
28597
+ return { width, height, view, commands };
28598
+ }
28599
+ function defaultCreateCanvas(width, height) {
28600
+ if (typeof OffscreenCanvas !== "undefined") {
28601
+ return new OffscreenCanvas(width, height);
28602
+ }
28603
+ if (typeof document !== "undefined") {
28604
+ const c = document.createElement("canvas");
28605
+ c.width = width;
28606
+ c.height = height;
28607
+ return c;
28608
+ }
28609
+ throw new Error("renderSceneToPixels: no canvas source in this environment \u2014 supply `gl` or `createCanvas`");
28610
+ }
28611
+ function flipRows(raw, width, height) {
28612
+ const rowBytes = width * 4;
28613
+ const out = new Uint8ClampedArray(raw.length);
28614
+ for (let y = 0; y < height; y++) {
28615
+ out.set(raw.subarray((height - 1 - y) * rowBytes, (height - y) * rowBytes), y * rowBytes);
28616
+ }
28617
+ return out;
28618
+ }
28619
+ function unpremultiply(data) {
28620
+ for (let i = 0; i < data.length; i += 4) {
28621
+ const a = data[i + 3];
28622
+ if (a === 0 || a === 255) continue;
28623
+ const inv = 255 / a;
28624
+ data[i] = data[i] * inv;
28625
+ data[i + 1] = data[i + 1] * inv;
28626
+ data[i + 2] = data[i + 2] * inv;
28627
+ }
28628
+ }
28629
+ function renderSceneToPixels(args) {
28630
+ if (args.gl && args.createCanvas) {
28631
+ throw new Error("renderSceneToPixels: `gl` and `createCanvas` are mutually exclusive");
28632
+ }
28633
+ const plan = planPixelRender(args);
28634
+ const { width, height } = plan;
28635
+ let gl = args.gl;
28636
+ if (!gl) {
28637
+ const canvas = (args.createCanvas ?? defaultCreateCanvas)(width, height);
28638
+ canvas.width = width;
28639
+ canvas.height = height;
28640
+ gl = canvas.getContext("webgl2", { preserveDrawingBuffer: true, stencil: true }) ?? void 0;
28641
+ }
28642
+ if (!gl || typeof gl.enable !== "function") {
28643
+ throw new Error("renderSceneToPixels: WebGL2 is unavailable \u2014 supply `gl` or a WebGL2-capable `createCanvas`");
28644
+ }
28645
+ if (typeof gl.isContextLost === "function" && gl.isContextLost()) {
28646
+ throw new Error("renderSceneToPixels: the supplied WebGL2 context is lost");
28647
+ }
28648
+ const flattenTolerancePx = args.flattenTolerancePx ?? 0.25;
28649
+ const renderer = new WeaselRenderer({
28650
+ gl,
28651
+ width,
28652
+ height,
28653
+ dpr: 1,
28654
+ imageMinification: "mipmap",
28655
+ flattenTolerance: flattenTolerancePx / Math.max(args.scale.x, args.scale.y),
28656
+ // Dynamic canvas-SDF glyphs must all bake inline — this path is
28657
+ // synchronous with no notify-and-redraw, and print must be complete.
28658
+ bakeBudget: Infinity
28659
+ });
28660
+ try {
28661
+ renderer.render(plan.commands, viewToMat3(plan.view));
28662
+ if (typeof gl.isContextLost === "function" && gl.isContextLost()) {
28663
+ throw new Error("renderSceneToPixels: WebGL2 context was lost during render");
28664
+ }
28665
+ const raw = new Uint8Array(width * height * 4);
28666
+ gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, raw);
28667
+ const data = flipRows(raw, width, height);
28668
+ unpremultiply(data);
28669
+ return { width, height, data };
28670
+ } finally {
28671
+ renderer.dispose();
28672
+ }
28673
+ }
28674
+ function WeaselProvider({ children, isolate = false }) {
28675
+ const Deps = isolate ? DepRegistryProvider : DepRegistryProviderIfRoot;
28676
+ const Actions = isolate ? ActionsProvider : ActionsProviderIfRoot;
28677
+ const ActiveTool = isolate ? ActiveToolContextProvider : ActiveToolContextProviderIfRoot;
28678
+ const Selection = isolate ? SelectionContextProvider : SelectionContextProviderIfRoot;
28679
+ const Pointer = isolate ? PointerContextProvider : PointerProviderIfRoot;
28680
+ return /* @__PURE__ */ jsx(Deps, { children: /* @__PURE__ */ jsx(Actions, { children: /* @__PURE__ */ jsx(ActiveTool, { children: /* @__PURE__ */ jsx(Selection, { children: /* @__PURE__ */ jsx(Pointer, { children }) }) }) }) });
28681
+ }
28277
28682
  function createSetTextOp(args) {
28278
28683
  const { id, from, to, label, coalesceKey = `setText:${id}` } = args;
28279
28684
  return {
@@ -28410,6 +28815,6 @@ function oklchToOklab(L, C, h) {
28410
28815
  return [L, C * Math.cos(h), C * Math.sin(h)];
28411
28816
  }
28412
28817
 
28413
- export { FALLBACK_FIT_VIEW, MinimapCanvas, SceneViewCanvas, UnknownIcon, buildSceneViewCommands, computeFitView, computeIndicatorCommand, eligibleContribution, evaluateEnabled, isBuiltinToolPref, oklabToSrgbU8, oklchToOklab, renderSceneToCanvas, rgbaToHex, useActionsRegistry, useOptionalDepRegistry, useVisibleRaf };
28414
- //# sourceMappingURL=chunk-QCICIHEO.js.map
28415
- //# sourceMappingURL=chunk-QCICIHEO.js.map
28818
+ export { ArrowIcon, EllipseIcon, FALLBACK_FIT_VIEW, LineIcon, MinimapCanvas, PATH_C, PATH_L, PATH_M, PATH_Q, PATH_Z, PathBuilder, PencilIcon, RectIcon, SCRIPT_METRICS, SceneCanvas, SceneViewCanvas, SelectIcon, TextIcon, UnknownIcon, WeaselProvider, asNodeId, boundsOfPath, buildSceneViewCommands, computeFitView, computeIndicatorCommand, createScene, eligibleContribution, ellipsePath, evaluateEnabled, getMarker, getPaintKind, isBuiltinToolPref, linePath, markerDrawCommands, oklabToSrgbU8, oklchToOklab, rectPath, renderSceneToCanvas, renderSceneToPixels, resolveStrokeWidth, rgbaToHex, sceneFromJSON, textCommand, tileGeometry, useActionsRegistry, useActiveToolContext, useOptionalDepRegistry, useVisibleRaf };
28819
+ //# sourceMappingURL=chunk-ROQDRXKW.js.map
28820
+ //# sourceMappingURL=chunk-ROQDRXKW.js.map