@weasel-js/core 1.0.3 → 1.1.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 (37) hide show
  1. package/CHANGELOG.md +251 -0
  2. package/dist/{chunk-SMCZRSYB.js → chunk-3B4QEB2G.js} +810 -228
  3. package/dist/chunk-3B4QEB2G.js.map +1 -0
  4. package/dist/{chunk-U45SODC2.js → chunk-4Z6J4IVH.js} +43 -27
  5. package/dist/chunk-4Z6J4IVH.js.map +1 -0
  6. package/dist/{chunk-3KGMJMOS.js → chunk-CSMF654J.js} +3 -3
  7. package/dist/{chunk-3KGMJMOS.js.map → chunk-CSMF654J.js.map} +1 -1
  8. package/dist/{chunk-TR3HZN34.js → chunk-J3YC3GVF.js} +5 -4
  9. package/dist/chunk-J3YC3GVF.js.map +1 -0
  10. package/dist/{chunk-3JYFAQIB.js → chunk-J7LUY47N.js} +3 -3
  11. package/dist/{chunk-3JYFAQIB.js.map → chunk-J7LUY47N.js.map} +1 -1
  12. package/dist/clipboard.d.ts +1 -1
  13. package/dist/clone.d.ts +2 -2
  14. package/dist/{geometry-5X6TrAkX.d.ts → geometry-C56YbnfD.d.ts} +1 -1
  15. package/dist/{grid-sfilv5HP.d.ts → grid-DqOi0Vgi.d.ts} +1 -1
  16. package/dist/index.d.ts +179 -59
  17. package/dist/index.js +5 -5
  18. package/dist/insert.d.ts +3 -3
  19. package/dist/insert.js +1 -1
  20. package/dist/move.d.ts +4 -4
  21. package/dist/move.js +3 -3
  22. package/dist/move.js.map +1 -1
  23. package/dist/{options-CbsIgQci.d.ts → options-C6HYCKP7.d.ts} +1 -1
  24. package/dist/{pointSnapToGrid-C3wowsQy.d.ts → pointSnapToGrid-BBtn5bus.d.ts} +2 -2
  25. package/dist/{registry-CxnkXbG8.d.ts → registry-DGBrjGbB.d.ts} +4 -4
  26. package/dist/renderer.d.ts +29 -13
  27. package/dist/renderer.js +5 -5
  28. package/dist/resize.d.ts +4 -4
  29. package/dist/resize.js +2 -2
  30. package/dist/routing.d.ts +4 -4
  31. package/dist/routing.js +1 -1
  32. package/dist/{types-ByZxoD6P.d.ts → types-BhrifbZ1.d.ts} +1 -1
  33. package/dist/{types-DpQ0cWYC.d.ts → types-DbysOO-2.d.ts} +10 -3
  34. package/package.json +6 -6
  35. package/dist/chunk-SMCZRSYB.js.map +0 -1
  36. package/dist/chunk-TR3HZN34.js.map +0 -1
  37. package/dist/chunk-U45SODC2.js.map +0 -1
@@ -1,7 +1,7 @@
1
- import { ActionDisabledReason, useOptionalDepRegistry, DepRegistryProvider, useActionsRegistry, ActionsProvider, useDepRegistry, reportRouteConflicts, buildDepsFromRequires, useDepSource, actionBindings } from './chunk-TR3HZN34.js';
2
- import { scratchKey, createTransformOp, snap, getScratch, createReparentOp, setScratch } from './chunk-3KGMJMOS.js';
3
- import { fixedCornerOf, DEFAULT_RESIZE_BEHAVIORS, CORNER_ANCHORS, cornerPoint } from './chunk-3JYFAQIB.js';
4
- import { AUTO_POSE_DESCRIPTOR, RECT_POSE_DESCRIPTOR, resolveUnit, pathPoseDescriptor, polygonFromPoints, starPath, regularPolygonPath, linePath, ellipsePath, rectPath, strokeHitTest, withinPxRadius, boundsOfPath, PATH_M, PATH_L, PATH_Z, PATH_Q, PATH_C, DEFAULT_FLATTEN_TOLERANCE, flattenQuadratic, flattenCubic, PathBuilder, pointInPath, transformPath, translatePath, pxExtent, remapRotatedLeaf, withinPxBox, flattenCubicWithArcLen, flattenQuadraticWithArcLen, PATH_CMD_LENGTHS, pointToGridCell } from './chunk-U45SODC2.js';
1
+ import { ActionDisabledReason, useOptionalDepRegistry, DepRegistryProvider, useActionsRegistry, ActionsProvider, useDepRegistry, reportRouteConflicts, buildDepsFromRequires, useDepSource, actionBindings } from './chunk-J3YC3GVF.js';
2
+ import { scratchKey, createTransformOp, snap, getScratch, createReparentOp, setScratch } from './chunk-CSMF654J.js';
3
+ import { fixedCornerOf, DEFAULT_RESIZE_BEHAVIORS, CORNER_ANCHORS, cornerPoint } from './chunk-J7LUY47N.js';
4
+ import { AUTO_POSE_DESCRIPTOR, RECT_POSE_DESCRIPTOR, resolveUnit, pathPoseDescriptor, polygonFromPoints, starPath, regularPolygonPath, linePath, ellipsePath, rectPath, strokeHitTest, withinPxRadius, boundsOfPath, PATH_M, PATH_L, PATH_Z, PATH_Q, PATH_C, DEFAULT_FLATTEN_TOLERANCE, flattenQuadratic, flattenCubic, PathBuilder, pointInPath, transformPath, translatePath, pxExtent, remapRotatedLeaf, withinPxBox, flattenCubicWithArcLen, flattenQuadraticWithArcLen, PATH_CMD_LENGTHS, pointToGridCell } from './chunk-4Z6J4IVH.js';
5
5
  import { dispatchApplyBatch, dlog, dwarn, usePointerContext, PointerContextProvider, applyOpsTo, extractWeaselClipboardFromSvg, parseWeaselClipboardText, materializePaste, WEASEL_CLIPBOARD_MIME, WEASEL_CLIPBOARD_MIME_WEB, sniffWeaselClipboardText } from './chunk-JOSUVS6C.js';
6
6
  import { createSetSelectionOp } from './chunk-BHVYVFGV.js';
7
7
  import { registerOpFactory, createDeleteOp, createInsertOp, rebuildOp } from './chunk-GVCNT7UH.js';
@@ -10,7 +10,7 @@ import { createContext, createElement, forwardRef, useMemo, useRef, useState, us
10
10
  import { subscribeGlyphReady, DEFAULT_BAKE_BUDGET, TEXT_VERT_SRC, TEXT_FRAG_SRC, TEXT_SDF_UNIFORMS, TEXT_SDF_ATTRIBUTES, TEXT_FRAG_R8_SRC, markAllFontsNotUploaded, resetBakeBudget, glyphGeneration, syncDynamicPageTexture, ensureFontTexture, dynamicPageTextureId, textureCacheKey, resolveFontVariant, resolveGlyphFallback, glyphOutline } from '@weasel-js/font';
11
11
  export { canQueryLocalFonts, canQueryLocalFonts as canQueryLocalFonts2, enableLocalFontOutlines, enableLocalFontOutlines as enableLocalFontOutlines2, hasFontOutlines, hasFontOutlines as hasFontOutlines2, isCanvasFont, isCanvasFont as isCanvasFont2, listFontOutlines, listFontOutlines as listFontOutlines2, outlineStatus, outlineStatus as outlineStatus2, registerCanvasFont, registerCanvasFont as registerCanvasFont2, registerFont, registerFont as registerFont2, registerFontOutlines, registerFontOutlines as registerFontOutlines2, subscribeGlyphReady, subscribeGlyphReady as subscribeGlyphReady2, unregisterCanvasFont, unregisterCanvasFont as unregisterCanvasFont2, unregisterFontOutlines, unregisterFontOutlines as unregisterFontOutlines2 } from '@weasel-js/font';
12
12
  import earcut from 'earcut';
13
- import { applyToPoint, rotateAboutPoint, boxToBox, pointInPolygon, segmentsCross } from '@weasel-js/geom';
13
+ import { applyToPoint, rotateAboutPoint, boxToBox, pointInPolygon, segmentsCross, pointSegmentDist2 } from '@weasel-js/geom';
14
14
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
15
15
  import polygonClipping from 'polygon-clipping';
16
16
  import { matchSpec, RESERVED_ID_PREFIXES, RESERVED_ID_NAMES, parseTargetSpec, mimeMatchesGlob } from '@weasel-js/gestures';
@@ -19,7 +19,7 @@ import { createHistory } from '@weasel-js/history';
19
19
  export * from '@weasel-js/history';
20
20
 
21
21
  // src/version.ts
22
- var VERSION = "1.0.3" ;
22
+ var VERSION = "1.1.0" ;
23
23
 
24
24
  // src/features/grid/roundToCell.ts
25
25
  function roundToCell(value, cellSize) {
@@ -524,13 +524,20 @@ var GLTextureCache = class {
524
524
  }
525
525
  gl;
526
526
  map = /* @__PURE__ */ new Map();
527
+ wraps = /* @__PURE__ */ new Map();
527
528
  has(id) {
528
529
  return this.map.has(id);
529
530
  }
530
- /** Uploads once per id; `wrap` therefore applies on first upload only.
531
- * An id is either a pattern tile or a shader texture, never both. */
531
+ /** Uploads the pixels once per id. `wrap` is re-applied whenever it differs
532
+ * from what the id was last uploaded under: the same registered image can
533
+ * be a clamped shader texture in one draw and a repeating pattern tile in
534
+ * the next, and first-upload-wins gave the second one the first one's mode. */
532
535
  upload(id, source, wrap = "clamp") {
533
- if (this.map.has(id)) return id;
536
+ const cached = this.map.get(id);
537
+ if (cached) {
538
+ if (this.wraps.get(id) !== wrap) this.applyWrap(id, cached, wrap);
539
+ return id;
540
+ }
534
541
  const gl = this.gl;
535
542
  const tex = gl.createTexture();
536
543
  if (!tex) throw new Error(`GLTextureCache: createTexture failed for id="${id}"`);
@@ -543,8 +550,18 @@ var GLTextureCache = class {
543
550
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapMode);
544
551
  gl.bindTexture(gl.TEXTURE_2D, null);
545
552
  this.map.set(id, tex);
553
+ this.wraps.set(id, wrap);
546
554
  return id;
547
555
  }
556
+ applyWrap(id, tex, wrap) {
557
+ const gl = this.gl;
558
+ const mode = wrap === "repeat" ? gl.REPEAT : gl.CLAMP_TO_EDGE;
559
+ gl.bindTexture(gl.TEXTURE_2D, tex);
560
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, mode);
561
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, mode);
562
+ gl.bindTexture(gl.TEXTURE_2D, null);
563
+ this.wraps.set(id, wrap);
564
+ }
548
565
  /** Create a single-channel R8 texture from raw bytes (full upload).
549
566
  * No-op if `id` already exists. Same LINEAR/CLAMP params as `upload`;
550
567
  * UNPACK_ALIGNMENT dropped to 1 for non-4-aligned row widths. */
@@ -563,6 +580,7 @@ var GLTextureCache = class {
563
580
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
564
581
  gl.bindTexture(gl.TEXTURE_2D, null);
565
582
  this.map.set(id, tex);
583
+ this.wraps.set(id, "clamp");
566
584
  }
567
585
  /** Patch a rect of an existing R8 texture with tightly-packed w×h bytes. */
568
586
  subImageR8(id, x, y, w, h, data) {
@@ -594,6 +612,7 @@ var GLTextureCache = class {
594
612
  gl.deleteTexture(tex);
595
613
  }
596
614
  this.map.clear();
615
+ this.wraps.clear();
597
616
  }
598
617
  };
599
618
 
@@ -1282,6 +1301,13 @@ var GroupState = class {
1282
1301
  this.alphaStack.push(nextAlpha);
1283
1302
  this.colorMatrixStack.push(nextCM);
1284
1303
  }
1304
+ /** Drop every pushed frame, leaving the root. A frame that throws part-way
1305
+ * down the tree never pops, and the next frame would draw under leftovers. */
1306
+ reset() {
1307
+ this.transformStack.length = 1;
1308
+ this.alphaStack.length = 1;
1309
+ this.colorMatrixStack.length = 1;
1310
+ }
1285
1311
  pop() {
1286
1312
  if (this.transformStack.length <= 1) {
1287
1313
  throw new Error("GroupState.pop: cannot pop root frame");
@@ -1935,16 +1961,28 @@ function verticalAlignOffset(align, boxHeight, textHeight) {
1935
1961
 
1936
1962
  // src/features/paths/pathFromD.ts
1937
1963
  var NUM_RE = /[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g;
1964
+ var NUM_RE_AT = /[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/y;
1965
+ function isCommandLetter(d, j) {
1966
+ const c = d[j];
1967
+ if (!/[A-Za-z]/.test(c)) return false;
1968
+ if (c !== "e" && c !== "E") return true;
1969
+ if (!/[\d.]/.test(d[j - 1] ?? "")) return true;
1970
+ const k = d[j + 1] === "+" || d[j + 1] === "-" ? j + 2 : j + 1;
1971
+ return !/\d/.test(d[k] ?? "");
1972
+ }
1938
1973
  function tokenize(d) {
1939
1974
  const out = [];
1940
1975
  let i = 0;
1941
1976
  while (i < d.length) {
1942
- const ch = d[i];
1943
- if (/[A-Za-z]/.test(ch)) {
1977
+ if (isCommandLetter(d, i)) {
1944
1978
  let j = i + 1;
1945
- while (j < d.length && !/[A-Za-z]/.test(d[j])) j++;
1979
+ while (j < d.length && !isCommandLetter(d, j)) j++;
1946
1980
  const argSlice = d.slice(i + 1, j);
1947
- out.push({ cmd: ch, args: readNumbers(argSlice) });
1981
+ const cmd = d[i];
1982
+ out.push({
1983
+ cmd,
1984
+ args: cmd === "A" || cmd === "a" ? readArcArgs(argSlice) : readNumbers(argSlice)
1985
+ });
1948
1986
  i = j;
1949
1987
  } else {
1950
1988
  i++;
@@ -1962,6 +2000,30 @@ function readNumbers(s) {
1962
2000
  }
1963
2001
  return out;
1964
2002
  }
2003
+ function readArcArgs(s) {
2004
+ const out = [];
2005
+ let i = 0;
2006
+ while (i < s.length) {
2007
+ const c = s[i];
2008
+ if (c === "," || c === " " || c === " " || c === "\n" || c === "\r") {
2009
+ i++;
2010
+ continue;
2011
+ }
2012
+ const slot = out.length % 7;
2013
+ if (slot === 3 || slot === 4) {
2014
+ if (c !== "0" && c !== "1") return out;
2015
+ out.push(c === "1" ? 1 : 0);
2016
+ i++;
2017
+ continue;
2018
+ }
2019
+ NUM_RE_AT.lastIndex = i;
2020
+ const m = NUM_RE_AT.exec(s);
2021
+ if (!m) return out;
2022
+ out.push(Number(m[0]));
2023
+ i = NUM_RE_AT.lastIndex;
2024
+ }
2025
+ return out;
2026
+ }
1965
2027
  var ARG_COUNTS = {
1966
2028
  M: 2,
1967
2029
  L: 2,
@@ -2879,42 +2941,42 @@ var MAX_VERTICES_PER_BATCH = 32768;
2879
2941
  var FLOATS_PER_VERTEX = 6;
2880
2942
  var INITIAL_VERTICES = 256;
2881
2943
  var INITIAL_INDICES = 384;
2944
+ var SOLID_RING_SIZE = 64;
2945
+ var SOLID_RING_SLOT_VERTICES = 1024;
2946
+ var SOLID_RING_SLOT_INDICES = 3072;
2947
+ var SOLID_LARGE_RING_SIZE = 4;
2948
+ function doubledTo(from, need) {
2949
+ let capacity = from;
2950
+ while (capacity < need) capacity *= 2;
2951
+ return capacity;
2952
+ }
2882
2953
  var SolidBatch = class {
2883
2954
  gl;
2884
- vao;
2885
- vbo;
2886
- ibo;
2955
+ aPos;
2956
+ aColor;
2957
+ /** Cycled per flush, and created on first use so a renderer that flushes
2958
+ * rarely allocates as few slots as it flushes. */
2959
+ ring = new Array(SOLID_RING_SIZE).fill(void 0);
2960
+ next = 0;
2961
+ /** The same, for flushes past a slot's capacity; these sets grow to fit. */
2962
+ largeRing = new Array(SOLID_LARGE_RING_SIZE).fill(void 0);
2963
+ nextLarge = 0;
2887
2964
  verts = new Float32Array(INITIAL_VERTICES * FLOATS_PER_VERTEX);
2888
2965
  idx = new Uint32Array(INITIAL_INDICES);
2889
2966
  nVerts = 0;
2890
2967
  nIdx = 0;
2891
- /** What the GPU buffers are currently sized for. */
2892
- vboVerts = 0;
2893
- iboIdx = 0;
2968
+ /** Whether the staged run is rects alone, so its indices are the canonical
2969
+ * quad pattern and a slot already holding that pattern needs no upload. */
2970
+ pureRects = true;
2894
2971
  constructor(gl, prog) {
2895
2972
  const aPos = prog.attribute("a_position");
2896
2973
  const aColor = prog.attribute("a_vertexColor");
2897
2974
  if (aPos === void 0 || aColor === void 0) {
2898
2975
  throw new Error("SolidBatch: vertex-color program is missing a_position / a_vertexColor");
2899
2976
  }
2900
- const vao = gl.createVertexArray();
2901
- const vbo = gl.createBuffer();
2902
- const ibo = gl.createBuffer();
2903
- if (!vao || !vbo || !ibo) throw new Error("SolidBatch: failed to create GL objects");
2904
2977
  this.gl = gl;
2905
- this.vao = vao;
2906
- this.vbo = vbo;
2907
- this.ibo = ibo;
2908
- const stride = FLOATS_PER_VERTEX * 4;
2909
- gl.bindVertexArray(vao);
2910
- gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
2911
- gl.enableVertexAttribArray(aPos);
2912
- gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, stride, 0);
2913
- gl.enableVertexAttribArray(aColor);
2914
- gl.vertexAttribPointer(aColor, 4, gl.FLOAT, false, stride, 8);
2915
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
2916
- gl.bindVertexArray(null);
2917
- this.growGpu(INITIAL_VERTICES, INITIAL_INDICES);
2978
+ this.aPos = aPos;
2979
+ this.aColor = aColor;
2918
2980
  }
2919
2981
  get length() {
2920
2982
  return this.nIdx;
@@ -2984,6 +3046,7 @@ var SolidBatch = class {
2984
3046
  const srcIdx = mesh.indices;
2985
3047
  const n = src.length >> 1;
2986
3048
  this.reserve(n, srcIdx.length);
3049
+ this.pureRects = false;
2987
3050
  const v = this.verts;
2988
3051
  let i = this.nVerts * FLOATS_PER_VERTEX;
2989
3052
  const ma = m[0], mb = m[1], mc = m[3], md = m[4], mtx = m[6], mty = m[7];
@@ -3004,67 +3067,109 @@ var SolidBatch = class {
3004
3067
  this.nVerts += n;
3005
3068
  this.nIdx += srcIdx.length;
3006
3069
  }
3007
- /** Upload the staged geometry and bind the batch VAO. Returns the index
3008
- * count for the caller's `drawElements`. */
3070
+ /** Upload the staged geometry into the next set of buffers and bind its VAO.
3071
+ * Returns the index count for the caller's `drawElements`. */
3009
3072
  uploadAndBind() {
3010
3073
  const gl = this.gl;
3011
- if (this.nVerts > this.vboVerts || this.nIdx > this.iboIdx) {
3012
- this.growGpu(this.nVerts, this.nIdx);
3013
- }
3014
- gl.bindVertexArray(this.vao);
3015
- gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
3074
+ const set = this.nVerts <= SOLID_RING_SLOT_VERTICES && this.nIdx <= SOLID_RING_SLOT_INDICES ? this.nextRingSlot() : this.nextLargeSlot();
3075
+ gl.bindVertexArray(set.vao);
3076
+ gl.bindBuffer(gl.ARRAY_BUFFER, set.vbo);
3016
3077
  gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verts, 0, this.nVerts * FLOATS_PER_VERTEX);
3017
- gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, this.idx, 0, this.nIdx);
3078
+ const rects = this.pureRects ? this.nVerts >> 2 : -1;
3079
+ if (rects < 0 || rects !== set.indexRects) {
3080
+ gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, this.idx, 0, this.nIdx);
3081
+ set.indexRects = rects;
3082
+ }
3018
3083
  return this.nIdx;
3019
3084
  }
3020
3085
  reset() {
3021
3086
  this.nVerts = 0;
3022
3087
  this.nIdx = 0;
3088
+ this.pureRects = true;
3023
3089
  }
3024
3090
  dispose() {
3025
- const gl = this.gl;
3026
- gl.deleteBuffer(this.vbo);
3027
- gl.deleteBuffer(this.ibo);
3028
- gl.deleteVertexArray(this.vao);
3091
+ for (const set of [...this.ring, ...this.largeRing]) {
3092
+ if (set) this.deleteSet(set);
3093
+ }
3094
+ this.ring.fill(void 0);
3095
+ this.largeRing.fill(void 0);
3029
3096
  }
3030
3097
  /** Grow the CPU arrays so `vertices` / `indices` more fit. */
3031
3098
  reserve(vertices, indices) {
3032
3099
  const needVerts = (this.nVerts + vertices) * FLOATS_PER_VERTEX;
3033
3100
  if (needVerts > this.verts.length) {
3034
- let len = this.verts.length;
3035
- while (len < needVerts) len *= 2;
3036
- const grown = new Float32Array(len);
3101
+ const grown = new Float32Array(doubledTo(this.verts.length, needVerts));
3037
3102
  grown.set(this.verts);
3038
3103
  this.verts = grown;
3039
3104
  }
3040
3105
  const needIdx = this.nIdx + indices;
3041
3106
  if (needIdx > this.idx.length) {
3042
- let len = this.idx.length;
3043
- while (len < needIdx) len *= 2;
3044
- const grown = new Uint32Array(len);
3107
+ const grown = new Uint32Array(doubledTo(this.idx.length, needIdx));
3045
3108
  grown.set(this.idx);
3046
3109
  this.idx = grown;
3047
3110
  }
3048
3111
  }
3049
- /** Size both GPU buffers for at least what is staged, doubling to amortize. */
3050
- growGpu(vertices, indices) {
3112
+ nextRingSlot() {
3113
+ const slot = this.next;
3114
+ this.next = (slot + 1) % SOLID_RING_SIZE;
3115
+ const existing = this.ring[slot];
3116
+ if (existing) return existing;
3117
+ const set = this.createSet(SOLID_RING_SLOT_VERTICES, SOLID_RING_SLOT_INDICES);
3118
+ this.ring[slot] = set;
3119
+ return set;
3120
+ }
3121
+ nextLargeSlot() {
3051
3122
  const gl = this.gl;
3052
- if (vertices > this.vboVerts) {
3053
- let capacity = Math.max(this.vboVerts, INITIAL_VERTICES);
3054
- while (capacity < vertices) capacity *= 2;
3055
- gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
3056
- gl.bufferData(gl.ARRAY_BUFFER, capacity * FLOATS_PER_VERTEX * 4, gl.DYNAMIC_DRAW);
3057
- this.vboVerts = capacity;
3058
- }
3059
- if (indices > this.iboIdx) {
3060
- let capacity = Math.max(this.iboIdx, INITIAL_INDICES);
3061
- while (capacity < indices) capacity *= 2;
3062
- gl.bindVertexArray(this.vao);
3063
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.ibo);
3064
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, capacity * 4, gl.DYNAMIC_DRAW);
3123
+ const slot = this.nextLarge;
3124
+ this.nextLarge = (slot + 1) % SOLID_LARGE_RING_SIZE;
3125
+ let set = this.largeRing[slot];
3126
+ if (!set) {
3127
+ set = this.createSet(
3128
+ doubledTo(SOLID_RING_SLOT_VERTICES, this.nVerts),
3129
+ doubledTo(SOLID_RING_SLOT_INDICES, this.nIdx)
3130
+ );
3131
+ this.largeRing[slot] = set;
3132
+ return set;
3133
+ }
3134
+ if (this.nVerts > set.vertexCapacity) {
3135
+ set.vertexCapacity = doubledTo(set.vertexCapacity, this.nVerts);
3136
+ gl.bindBuffer(gl.ARRAY_BUFFER, set.vbo);
3137
+ gl.bufferData(gl.ARRAY_BUFFER, set.vertexCapacity * FLOATS_PER_VERTEX * 4, gl.DYNAMIC_DRAW);
3138
+ }
3139
+ if (this.nIdx > set.indexCapacity) {
3140
+ set.indexCapacity = doubledTo(set.indexCapacity, this.nIdx);
3141
+ gl.bindVertexArray(set.vao);
3142
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, set.ibo);
3143
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, set.indexCapacity * 4, gl.DYNAMIC_DRAW);
3065
3144
  gl.bindVertexArray(null);
3066
- this.iboIdx = capacity;
3145
+ set.indexRects = -1;
3067
3146
  }
3147
+ return set;
3148
+ }
3149
+ createSet(vertices, indices) {
3150
+ const gl = this.gl;
3151
+ const vao = gl.createVertexArray();
3152
+ const vbo = gl.createBuffer();
3153
+ const ibo = gl.createBuffer();
3154
+ if (!vao || !vbo || !ibo) throw new Error("SolidBatch: failed to create GL objects");
3155
+ const stride = FLOATS_PER_VERTEX * 4;
3156
+ gl.bindVertexArray(vao);
3157
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
3158
+ gl.bufferData(gl.ARRAY_BUFFER, vertices * stride, gl.DYNAMIC_DRAW);
3159
+ gl.enableVertexAttribArray(this.aPos);
3160
+ gl.vertexAttribPointer(this.aPos, 2, gl.FLOAT, false, stride, 0);
3161
+ gl.enableVertexAttribArray(this.aColor);
3162
+ gl.vertexAttribPointer(this.aColor, 4, gl.FLOAT, false, stride, 8);
3163
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
3164
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices * 4, gl.DYNAMIC_DRAW);
3165
+ gl.bindVertexArray(null);
3166
+ return { vao, vbo, ibo, vertexCapacity: vertices, indexCapacity: indices, indexRects: -1 };
3167
+ }
3168
+ deleteSet(set) {
3169
+ const gl = this.gl;
3170
+ gl.deleteBuffer(set.vbo);
3171
+ gl.deleteBuffer(set.ibo);
3172
+ gl.deleteVertexArray(set.vao);
3068
3173
  }
3069
3174
  };
3070
3175
 
@@ -3089,6 +3194,25 @@ function sameValues(prev, next) {
3089
3194
  for (let i = 0; i < prev.length; i++) if (prev[i] !== next[i]) return false;
3090
3195
  return true;
3091
3196
  }
3197
+ function setColorUniform(ctx, prog, r, g, b, a) {
3198
+ const loc = prog.uniform("u_color");
3199
+ if (loc === void 0) return;
3200
+ const uploaded = uploadedFor(ctx, prog);
3201
+ const prev = uploaded.color;
3202
+ if (prev !== void 0 && prev[0] === r && prev[1] === g && prev[2] === b && prev[3] === a) {
3203
+ return;
3204
+ }
3205
+ ctx.gl.uniform4f(loc, r, g, b, a);
3206
+ uploaded.color = [r, g, b, a];
3207
+ }
3208
+ function setAlphaUniform(ctx, prog, alpha) {
3209
+ const loc = prog.uniform("u_alpha");
3210
+ if (loc === void 0) return;
3211
+ const uploaded = uploadedFor(ctx, prog);
3212
+ if (uploaded.alpha === alpha) return;
3213
+ ctx.gl.uniform1f(loc, alpha);
3214
+ uploaded.alpha = alpha;
3215
+ }
3092
3216
  var BATCH_MODEL = mat3.identity();
3093
3217
  function projFor(ctx) {
3094
3218
  let proj = FRAME_PROJ.get(ctx);
@@ -3381,8 +3505,8 @@ function flushSolids(ctx) {
3381
3505
  gl.useProgram(prog.handle);
3382
3506
  const indexCount = batch.uploadAndBind();
3383
3507
  setProjAndModel(ctx, prog, BATCH_MODEL);
3384
- gl.uniform4f(prog.uniform("u_color"), 1, 1, 1, 1);
3385
- gl.uniform1f(prog.uniform("u_alpha"), staged.alpha);
3508
+ setColorUniform(ctx, prog, 1, 1, 1, 1);
3509
+ setAlphaUniform(ctx, prog, staged.alpha);
3386
3510
  setColorMatrixUniforms(ctx, prog, staged.colorMatrix);
3387
3511
  applyClipTest(ctx, staged.clipDepth);
3388
3512
  gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_INT, 0);
@@ -3447,10 +3571,9 @@ function setProjAndModel(ctx, prog, model = ctx.state.transform) {
3447
3571
  }
3448
3572
  }
3449
3573
  function setSolidPaintUniforms(ctx, prog, color, opacity) {
3450
- const gl = ctx.gl;
3451
3574
  const [r, g, b, a] = resolveColor(color);
3452
- gl.uniform4f(prog.uniform("u_color"), r, g, b, a * (opacity ?? 1));
3453
- gl.uniform1f(prog.uniform("u_alpha"), ctx.state.alpha);
3575
+ setColorUniform(ctx, prog, r, g, b, a * (opacity ?? 1));
3576
+ setAlphaUniform(ctx, prog, ctx.state.alpha);
3454
3577
  }
3455
3578
  function drawPathFillByKind(ctx, fill, handle) {
3456
3579
  const kind = fill.fill ?? "solid";
@@ -3496,7 +3619,7 @@ function drawPathFillPattern(ctx, fill, handle) {
3496
3619
  ctx.textureCache.bind(tex.id, 0);
3497
3620
  gl.uniform1i(ctx.patternFill.uniform("u_sampler"), 0);
3498
3621
  gl.uniform1f(ctx.patternFill.uniform("u_opacity"), fill.opacity ?? 1);
3499
- gl.uniform1f(ctx.patternFill.uniform("u_alpha"), ctx.state.alpha);
3622
+ setAlphaUniform(ctx, ctx.patternFill, ctx.state.alpha);
3500
3623
  applyClipTest(ctx);
3501
3624
  gl.drawElements(gl.TRIANGLES, handle.indexCount, gl.UNSIGNED_INT, 0);
3502
3625
  gl.bindVertexArray(null);
@@ -3520,7 +3643,7 @@ function drawPathFillGradient(ctx, fill, handle) {
3520
3643
  gl.uniformMatrix3fv(ctx.gradFill.uniform("u_worldInv"), false, gradientSpaceInverse(ctx, fill.units));
3521
3644
  ctx.gradRampCache.bind(key, 0);
3522
3645
  gl.uniform1i(ctx.gradFill.uniform("u_ramp"), 0);
3523
- gl.uniform1f(ctx.gradFill.uniform("u_alpha"), ctx.state.alpha);
3646
+ setAlphaUniform(ctx, ctx.gradFill, ctx.state.alpha);
3524
3647
  gl.uniform1f(ctx.gradFill.uniform("u_opacity"), fill.opacity ?? 1);
3525
3648
  if (fill.fill === "linear-gradient") {
3526
3649
  gl.uniform1i(ctx.gradFill.uniform("u_gradKind"), 0);
@@ -3670,6 +3793,7 @@ function drawPathStrokeUnclipped(ctx, cmd) {
3670
3793
  gl.enableVertexAttribArray(aVColorLoc);
3671
3794
  gl.vertexAttribPointer(aVColorLoc, 4, gl.FLOAT, false, 0, 0);
3672
3795
  }
3796
+ applyClipTest(ctx);
3673
3797
  gl.drawElements(gl.TRIANGLES, handle.indexCount, gl.UNSIGNED_INT, 0);
3674
3798
  gl.bindVertexArray(null);
3675
3799
  gl.deleteBuffer(colorVbo);
@@ -3785,7 +3909,7 @@ function drawText(ctx, cmd) {
3785
3909
  preparedPrograms.add(prog);
3786
3910
  setProjAndModel(ctx, prog);
3787
3911
  setColorMatrixUniforms(ctx, prog);
3788
- gl.uniform1f(prog.uniform("u_alpha"), ctx.state.alpha);
3912
+ setAlphaUniform(ctx, prog, ctx.state.alpha);
3789
3913
  }
3790
3914
  drawTextGroup(ctx, group, prog, dx, dy);
3791
3915
  }
@@ -3903,8 +4027,8 @@ function drawTextDecorations(ctx, decorations, dx, dy) {
3903
4027
  if (!ibo) throw new Error("drawTextDecorations: createBuffer (IBO) returned null");
3904
4028
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
3905
4029
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.DYNAMIC_DRAW);
3906
- gl.uniform4f(prog.uniform("u_color"), rgba[0], rgba[1], rgba[2], rgba[3]);
3907
- gl.uniform1f(prog.uniform("u_alpha"), ctx.state.alpha);
4030
+ setColorUniform(ctx, prog, rgba[0], rgba[1], rgba[2], rgba[3]);
4031
+ setAlphaUniform(ctx, prog, ctx.state.alpha);
3908
4032
  gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_INT, 0);
3909
4033
  gl.bindVertexArray(null);
3910
4034
  gl.deleteVertexArray(vao);
@@ -3988,7 +4112,7 @@ function drawTextGroup(ctx, group, prog, dx, dy) {
3988
4112
  if ("color" in group.fill) {
3989
4113
  [r, g, b, a] = resolveColor(group.fill.color);
3990
4114
  }
3991
- gl.uniform4f(prog.uniform("u_color"), r, g, b, a);
4115
+ setColorUniform(ctx, prog, r, g, b, a);
3992
4116
  const synthBoldAmount = group.synthetic.bold ? 0.08 : 0;
3993
4117
  const uSynthBold = prog.uniform("u_synthBold");
3994
4118
  if (uSynthBold !== void 0) gl.uniform1f(uSynthBold, synthBoldAmount);
@@ -4092,7 +4216,7 @@ function drawImage(ctx, cmd) {
4092
4216
  );
4093
4217
  gl.uniform1i(prog.uniform("u_sampler"), 0);
4094
4218
  gl.uniform1f(prog.uniform("u_opacity"), cmd.opacity ?? 1);
4095
- gl.uniform1f(prog.uniform("u_alpha"), ctx.state.alpha);
4219
+ setAlphaUniform(ctx, prog, ctx.state.alpha);
4096
4220
  applyClipTest(ctx);
4097
4221
  gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, 0);
4098
4222
  gl.bindVertexArray(null);
@@ -4321,6 +4445,8 @@ var WeaselRenderer = class {
4321
4445
  const program = new ShaderProgram(this.gl, vertSrc, fragSrc);
4322
4446
  program.lookupUniforms([...CUSTOM_KIT_UNIFORMS, ...extractUniformNames(fragSrc)]);
4323
4447
  program.lookupAttributes(CUSTOM_ATTRIBUTES);
4448
+ const previous = this.programRegistry.get(handle.id);
4449
+ if (previous) this.gl.deleteProgram(previous.handle);
4324
4450
  this.programRegistry.set(handle.id, program);
4325
4451
  }
4326
4452
  applyViewport() {
@@ -4419,7 +4545,7 @@ var WeaselRenderer = class {
4419
4545
  }
4420
4546
  this.meshCache.freeTransient();
4421
4547
  this.meshCache.drainPendingDeletes();
4422
- for (const prog of [this.pathFill, this.pathFillVColor, this.textSdf, this.textSdfR8, this.imageFill, this.gradFill]) {
4548
+ for (const prog of [this.pathFill, this.pathFillVColor, this.textSdf, this.textSdfR8, this.imageFill, this.gradFill, this.patternFill]) {
4423
4549
  gl.deleteProgram(prog.handle);
4424
4550
  }
4425
4551
  for (const prog of this.programRegistry.values()) {
@@ -4446,6 +4572,7 @@ var WeaselRenderer = class {
4446
4572
  const gl = this.gl;
4447
4573
  this.meshCache.drainPendingDeletes();
4448
4574
  resetBakeBudget(this.bakeBudget);
4575
+ this.groupState.reset();
4449
4576
  gl.stencilMask(255);
4450
4577
  gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
4451
4578
  const ctx = {
@@ -5202,9 +5329,15 @@ function beginPointerDrag(payload, source, startX, startY, ghostFn) {
5202
5329
  function onUp(e) {
5203
5330
  if (!activeDrag) return;
5204
5331
  const zone = findZone(e.clientX, e.clientY, payload.kind);
5332
+ const dropped = activeDrag.payload;
5205
5333
  activeDrag.lastZone?.onOver?.(false);
5206
- if (zone) zone.onDrop(activeDrag.payload, e.clientX, e.clientY);
5207
- cleanup();
5334
+ try {
5335
+ if (zone) zone.onDrop(dropped, e.clientX, e.clientY);
5336
+ } catch (err) {
5337
+ console.error("weasel pointerDrag: drop zone onDrop threw", err);
5338
+ } finally {
5339
+ cleanup();
5340
+ }
5208
5341
  }
5209
5342
  function onCancel() {
5210
5343
  activeDrag?.lastZone?.onOver?.(false);
@@ -5225,6 +5358,10 @@ var DRAG_THRESHOLD_PX_SQ = 25;
5225
5358
  function useDragHandle(getPayload, options) {
5226
5359
  const optsRef = useRef(options);
5227
5360
  optsRef.current = options;
5361
+ const pendingTeardownRef = useRef(null);
5362
+ useEffect(() => () => {
5363
+ pendingTeardownRef.current?.();
5364
+ }, []);
5228
5365
  const onPointerDown = useCallback((e) => {
5229
5366
  if (e.pointerType === "mouse" && e.button !== 0) return;
5230
5367
  const target = e.currentTarget;
@@ -5260,10 +5397,12 @@ function useDragHandle(getPayload, options) {
5260
5397
  document.removeEventListener("pointermove", onMove);
5261
5398
  document.removeEventListener("pointerup", onEnd);
5262
5399
  document.removeEventListener("pointercancel", onEnd);
5400
+ pendingTeardownRef.current = null;
5263
5401
  }
5264
5402
  document.addEventListener("pointermove", onMove);
5265
5403
  document.addEventListener("pointerup", onEnd);
5266
5404
  document.addEventListener("pointercancel", onEnd);
5405
+ pendingTeardownRef.current = cleanup;
5267
5406
  }, [getPayload]);
5268
5407
  return { onPointerDown, style: { touchAction: "none" } };
5269
5408
  }
@@ -5817,7 +5956,7 @@ var IDENTITY_POSE_COMPOSITION = {
5817
5956
  compose: (_parent, child) => child,
5818
5957
  decompose: (_parent, world) => world
5819
5958
  };
5820
- function composeWorldPose(adapter, id, compose) {
5959
+ function composeWorldPose(adapter, id, compose2) {
5821
5960
  const chain = [id];
5822
5961
  const seen = /* @__PURE__ */ new Set([id]);
5823
5962
  let cursor = adapter.getParent(id);
@@ -5829,7 +5968,7 @@ function composeWorldPose(adapter, id, compose) {
5829
5968
  }
5830
5969
  let world = adapter.getPose(chain[chain.length - 1]);
5831
5970
  for (let i = chain.length - 2; i >= 0; i--) {
5832
- world = compose(world, adapter.getPose(chain[i]));
5971
+ world = compose2(world, adapter.getPose(chain[i]));
5833
5972
  }
5834
5973
  return world;
5835
5974
  }
@@ -5843,9 +5982,9 @@ function composeRectPose(parent, child) {
5843
5982
  function translateRectPose(pose, dx, dy) {
5844
5983
  return { ...pose, x: pose.x + dx, y: pose.y + dy };
5845
5984
  }
5846
- function rebaseLocalPose(adapter, worldPose, newParentId, compose, decompose) {
5985
+ function rebaseLocalPose(adapter, worldPose, newParentId, compose2, decompose) {
5847
5986
  if (newParentId === null) return worldPose;
5848
- const parentWorld = composeWorldPose(adapter, newParentId, compose);
5987
+ const parentWorld = composeWorldPose(adapter, newParentId, compose2);
5849
5988
  return decompose(parentWorld, worldPose);
5850
5989
  }
5851
5990
  function decomposeRectPose(parent, world) {
@@ -5855,10 +5994,10 @@ function decomposeRectPose(parent, world) {
5855
5994
  y: world.y - parent.y
5856
5995
  };
5857
5996
  }
5858
- function worldPoseLookup(adapter, compose) {
5997
+ function worldPoseLookup(adapter, compose2) {
5859
5998
  return (id) => {
5860
5999
  try {
5861
- return composeWorldPose(adapter, id, compose);
6000
+ return composeWorldPose(adapter, id, compose2);
5862
6001
  } catch {
5863
6002
  return null;
5864
6003
  }
@@ -8916,9 +9055,17 @@ function arraysEqual(a, b) {
8916
9055
  return true;
8917
9056
  }
8918
9057
  function createPartitionedReorderOp(args) {
8919
- const { ids, fn, label } = args;
8920
- let restore = null;
9058
+ const { ids, direction, fn, label, prevOrders } = args;
9059
+ const argsForSerial = {
9060
+ ids,
9061
+ direction,
9062
+ label,
9063
+ prevOrders: prevOrders ? prevOrders.map((e) => ({ ...e, before: e.before.slice() })) : void 0
9064
+ };
9065
+ let restore = prevOrders ? prevOrders.map((e) => ({ ...e, before: e.before.slice() })) : null;
8921
9066
  return {
9067
+ name: "reorder",
9068
+ args: argsForSerial,
8922
9069
  label,
8923
9070
  apply(adapter) {
8924
9071
  const a = adapter;
@@ -8941,6 +9088,7 @@ function createPartitionedReorderOp(args) {
8941
9088
  mutated = true;
8942
9089
  }
8943
9090
  restore = snapshots;
9091
+ argsForSerial.prevOrders = snapshots.map((e) => ({ ...e, before: e.before.slice() }));
8944
9092
  return mutated;
8945
9093
  },
8946
9094
  invert() {
@@ -8972,10 +9120,17 @@ var REORDER_DIRECTIONS = {
8972
9120
  back: { fn: sendToBack, defaultLabel: "Send to back" }
8973
9121
  };
8974
9122
  function createReorderOp(args) {
8975
- const { ids, direction, label } = args;
9123
+ const { ids, direction, label, prevOrders } = args;
8976
9124
  const { fn, defaultLabel } = REORDER_DIRECTIONS[direction];
8977
- return createPartitionedReorderOp({ ids, fn, label: label ?? defaultLabel });
9125
+ return createPartitionedReorderOp({
9126
+ ids,
9127
+ direction,
9128
+ fn,
9129
+ label: label ?? defaultLabel,
9130
+ prevOrders
9131
+ });
8978
9132
  }
9133
+ registerOpFactory("reorder", (args) => createReorderOp(args));
8979
9134
  function createMoveToIndexOp(args) {
8980
9135
  const { ids, parentId, index, label } = args;
8981
9136
  const argsForSerial = {
@@ -9102,7 +9257,7 @@ var reorderBackwardAction = {
9102
9257
  }
9103
9258
  ],
9104
9259
  eligible: { capability: "edits-page" },
9105
- requires: ["selection", "scene"],
9260
+ requires: ["selection", "scene", "applyOps"],
9106
9261
  invoker: {
9107
9262
  timing: "immediate",
9108
9263
  run: (deps, params) => {
@@ -9269,6 +9424,7 @@ function makeAlignAction(edge) {
9269
9424
  icon: ICON_FOR[edge],
9270
9425
  group: "align",
9271
9426
  eligible: { capability: "transforms-selection" },
9427
+ requires: ["selection", "scene"],
9272
9428
  // No default keybindings — six edges/centers don't fit a clean default
9273
9429
  // chord set. Wire bindings explicitly via the actions registry override map.
9274
9430
  invoker: {
@@ -9280,7 +9436,14 @@ function makeAlignAction(edge) {
9280
9436
  alignSelection(selection, scene, edge);
9281
9437
  }
9282
9438
  },
9283
- enabled: () => ActionDisabledReason.SelectionRequired
9439
+ // Deps-aware, matching `alignSelection`'s own `ids.length < 2` guard: a
9440
+ // constant disabled reason greys the entry out forever (see
9441
+ // `requiresSelection`).
9442
+ enabled: (deps) => {
9443
+ const selection = deps?.selection;
9444
+ const count = selection?.get().length ?? 0;
9445
+ return count >= 2 ? true : ActionDisabledReason.SelectionRequired;
9446
+ }
9284
9447
  };
9285
9448
  }
9286
9449
  var alignLeftAction = makeAlignAction("left");
@@ -9361,6 +9524,7 @@ function makeDistributeAction(axis) {
9361
9524
  icon: ICON_FOR2[axis],
9362
9525
  group: "distribute",
9363
9526
  eligible: { capability: "transforms-selection" },
9527
+ requires: ["selection", "scene"],
9364
9528
  // No default keybindings. Wire bindings explicitly via the actions registry.
9365
9529
  invoker: {
9366
9530
  timing: "immediate",
@@ -9371,7 +9535,14 @@ function makeDistributeAction(axis) {
9371
9535
  distributeSelection(selection, scene, axis);
9372
9536
  }
9373
9537
  },
9374
- enabled: () => ActionDisabledReason.SelectionRequired
9538
+ // Deps-aware, matching `distributeSelection`'s own `ids.length < 3` guard:
9539
+ // a constant disabled reason greys the entry out forever (see
9540
+ // `requiresSelection`).
9541
+ enabled: (deps) => {
9542
+ const selection = deps?.selection;
9543
+ const count = selection?.get().length ?? 0;
9544
+ return count >= 3 ? true : ActionDisabledReason.SelectionRequired;
9545
+ }
9375
9546
  };
9376
9547
  }
9377
9548
  var distributeHorizontalAction = makeDistributeAction("x");
@@ -9455,7 +9626,91 @@ function pathToMultiPolygon(path, opts = {}) {
9455
9626
  }
9456
9627
  finalizeCurrent();
9457
9628
  if (rings.length === 0) return [];
9458
- return rings.map((r) => [r]);
9629
+ return groupRings(rings, path.fillRule);
9630
+ }
9631
+ function ringSignedArea(ring) {
9632
+ let a = 0;
9633
+ for (let i = 0, n = ring.length - 1; i < n; i++) {
9634
+ a += ring[i][0] * ring[i + 1][1] - ring[i + 1][0] * ring[i][1];
9635
+ }
9636
+ return a * 0.5;
9637
+ }
9638
+ function ringBox(ring) {
9639
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
9640
+ for (const [x, y] of ring) {
9641
+ if (x < minX) minX = x;
9642
+ if (x > maxX) maxX = x;
9643
+ if (y < minY) minY = y;
9644
+ if (y > maxY) maxY = y;
9645
+ }
9646
+ return [minX, minY, maxX, maxY];
9647
+ }
9648
+ function pointInRing(ring, px, py) {
9649
+ let inside = false;
9650
+ for (let i = 0, n = ring.length - 1; i < n; i++) {
9651
+ const [ax, ay] = ring[i];
9652
+ const [bx, by] = ring[i + 1];
9653
+ if (ay > py !== by > py && px < (bx - ax) * (py - ay) / (by - ay) + ax) {
9654
+ inside = !inside;
9655
+ }
9656
+ }
9657
+ return inside;
9658
+ }
9659
+ function ringContains(outer, outerBox, inner, innerBox) {
9660
+ if (innerBox[0] < outerBox[0] || innerBox[1] < outerBox[1] || innerBox[2] > outerBox[2] || innerBox[3] > outerBox[3]) return false;
9661
+ const n = inner.length - 1;
9662
+ let votes = 0;
9663
+ for (const k of [0, Math.floor(n / 3), Math.floor(2 * n / 3)]) {
9664
+ if (pointInRing(outer, inner[k][0], inner[k][1])) votes++;
9665
+ }
9666
+ return votes >= 2;
9667
+ }
9668
+ function groupRings(rings, fillRule) {
9669
+ const n = rings.length;
9670
+ if (n === 1) return [[rings[0]]];
9671
+ const boxes = rings.map(ringBox);
9672
+ const orient = rings.map((r) => ringSignedArea(r) >= 0 ? 1 : -1);
9673
+ const parent = new Array(n).fill(-1);
9674
+ const children = rings.map(() => []);
9675
+ for (let i = 0; i < n; i++) {
9676
+ let best = -1;
9677
+ let bestArea = Infinity;
9678
+ for (let j = 0; j < n; j++) {
9679
+ if (i === j) continue;
9680
+ if (!ringContains(rings[j], boxes[j], rings[i], boxes[i])) continue;
9681
+ const area = (boxes[j][2] - boxes[j][0]) * (boxes[j][3] - boxes[j][1]);
9682
+ if (area < bestArea) {
9683
+ bestArea = area;
9684
+ best = j;
9685
+ }
9686
+ }
9687
+ parent[i] = best;
9688
+ }
9689
+ for (let i = 0; i < n; i++) if (parent[i] >= 0) children[parent[i]].push(i);
9690
+ const filled = new Array(n);
9691
+ for (let i = 0; i < n; i++) {
9692
+ let depth = 0;
9693
+ let winding = 0;
9694
+ for (let a = i; a >= 0; a = parent[a]) {
9695
+ winding += orient[a];
9696
+ if (a !== i) depth++;
9697
+ }
9698
+ filled[i] = fillRule === "evenodd" ? depth % 2 === 0 : winding !== 0;
9699
+ }
9700
+ const out = [];
9701
+ for (let i = 0; i < n; i++) {
9702
+ if (!filled[i]) continue;
9703
+ if (parent[i] >= 0 && filled[parent[i]]) continue;
9704
+ const polygon = [rings[i]];
9705
+ const queue = [...children[i]];
9706
+ while (queue.length > 0) {
9707
+ const c = queue.pop();
9708
+ if (filled[c]) queue.push(...children[c]);
9709
+ else polygon.push(rings[c]);
9710
+ }
9711
+ out.push(polygon);
9712
+ }
9713
+ return out;
9459
9714
  }
9460
9715
  function multiPolygonToPath(mp) {
9461
9716
  let nCmds = 0;
@@ -9772,6 +10027,7 @@ function makePathfinderAction(op) {
9772
10027
  icon: ICON_FOR3[op],
9773
10028
  group: "pathfinder",
9774
10029
  eligible: { capability: "creates-shapes" },
10030
+ requires: ["selection", "booleansAdapter"],
9775
10031
  // No default keybindings — there's no industry-standard chord set for
9776
10032
  // boolean ops. Wire bindings explicitly via the actions registry override map.
9777
10033
  invoker: {
@@ -12169,10 +12425,11 @@ function createDispatcher(opts) {
12169
12425
  uiOngoingByAction.set(actionId, gestureId);
12170
12426
  notify2();
12171
12427
  let ended = false;
12428
+ const live = () => !ended && inFlightHandles.get(gestureId) === handle;
12172
12429
  return {
12173
12430
  gestureId,
12174
12431
  update(nextParams) {
12175
- if (ended) return;
12432
+ if (!live()) return;
12176
12433
  if (!handle.onMove) return;
12177
12434
  try {
12178
12435
  handle.onMove(buildUiInvocationCtx(deps, nextParams));
@@ -12182,7 +12439,10 @@ function createDispatcher(opts) {
12182
12439
  notify2();
12183
12440
  },
12184
12441
  end(reason) {
12185
- if (ended) return;
12442
+ if (!live()) {
12443
+ ended = true;
12444
+ return;
12445
+ }
12186
12446
  ended = true;
12187
12447
  if (handle.onEnd) {
12188
12448
  try {
@@ -13509,6 +13769,7 @@ function useTools(opts) {
13509
13769
  );
13510
13770
  const active = contributions.focused;
13511
13771
  const getActiveOverlays = contributions.overlays;
13772
+ const registryKey = Object.keys(slotted.registry).sort().join("|") + "#" + slotted.ambient.map((t) => t.id).join("|");
13512
13773
  return useMemo(
13513
13774
  () => ({
13514
13775
  active,
@@ -13521,7 +13782,7 @@ function useTools(opts) {
13521
13782
  has,
13522
13783
  getActiveOverlays
13523
13784
  }),
13524
- [active, setActive, hotkeyEngaged, engageHotkey, disengageHotkey, has, getActiveOverlays]
13785
+ [active, setActive, hotkeyEngaged, engageHotkey, disengageHotkey, has, getActiveOverlays, registryKey]
13525
13786
  );
13526
13787
  }
13527
13788
 
@@ -15128,8 +15389,17 @@ function composeAffordanceLayer(id, label, affordances) {
15128
15389
  // `buildAffordanceAt` share one implementation rather than two that can
15129
15390
  // drift. This wrapper lifts the region's declared cursor and claim onto
15130
15391
  // the binding, the way `buildAffordanceAt` lifts them onto `AffordanceHit`.
15131
- hitTest: (wx, wy, state, view, _dims, isVisible) => {
15132
- const hit = hitAffordanceRegions(affordances, wx, wy, state, view, isVisible);
15392
+ hitTest: (wx, wy, data, view, _dims, isVisible) => {
15393
+ const state = asChromeState(data);
15394
+ if (!state) return null;
15395
+ const hit = hitAffordanceRegions(
15396
+ affordances,
15397
+ wx,
15398
+ wy,
15399
+ state,
15400
+ view,
15401
+ isVisible ?? asIsVisible(data) ?? void 0
15402
+ );
15133
15403
  if (!hit) return null;
15134
15404
  const { cursor, strength, claimedKinds } = hit.region;
15135
15405
  return {
@@ -15489,16 +15759,6 @@ function useRotateTool(adapter, options) {
15489
15759
  [overlay, handleHitRadius, rotationHandleDistance]
15490
15760
  );
15491
15761
  }
15492
-
15493
- // src/tools/builtin/hitExistingGate.ts
15494
- function applyHitExistingGate(ctx, hitExisting) {
15495
- if (!hitExisting) return false;
15496
- const hit = hitExisting({ x: ctx.worldX, y: ctx.worldY });
15497
- if (!hit) return false;
15498
- const ids = Array.isArray(hit) ? hit : [hit];
15499
- ctx.selection.set(ids);
15500
- return true;
15501
- }
15502
15762
  function useHandTool(opts = {}) {
15503
15763
  const inertia = opts.inertia === false ? false : opts.inertia;
15504
15764
  const axis = opts.axis ?? "both";
@@ -15750,6 +16010,10 @@ function usePenTool(options) {
15750
16010
  onEnd: (endCtx, reason) => {
15751
16011
  const se = s();
15752
16012
  if (reason === "cancel") {
16013
+ if (se.current && se.draggingHandleAt === se.current.anchors.length - 1) {
16014
+ se.current.anchors.pop();
16015
+ if (se.current.anchors.length === 0) se.current = null;
16016
+ }
15753
16017
  se.draggingHandleAt = null;
15754
16018
  forceRenderRef.current();
15755
16019
  return;
@@ -16699,7 +16963,7 @@ function composeSelectionPose(opts) {
16699
16963
  const visit = (nid) => {
16700
16964
  const kids = getChildren(nid);
16701
16965
  if (kids.length === 0) {
16702
- out.push(nid);
16966
+ if (!isContainer(nid)) out.push(nid);
16703
16967
  return;
16704
16968
  }
16705
16969
  for (const k of kids) visit(k);
@@ -16752,7 +17016,7 @@ function makeContainerAwareBoundsResolver(getPose, getBounds, getChildren, isCon
16752
17016
  const visit = (nid) => {
16753
17017
  const kids = getChildren(nid);
16754
17018
  if (kids.length === 0) {
16755
- out.push(nid);
17019
+ if (!isContainer(nid)) out.push(nid);
16756
17020
  return;
16757
17021
  }
16758
17022
  for (const k of kids) visit(k);
@@ -17541,7 +17805,6 @@ function CanvasInner(props, ref) {
17541
17805
  shaders,
17542
17806
  previewIdsExtra,
17543
17807
  previewPoseExtra,
17544
- previewBoundsExtra,
17545
17808
  gestureSource,
17546
17809
  viewport,
17547
17810
  backgroundFill,
@@ -17567,6 +17830,7 @@ function CanvasInner(props, ref) {
17567
17830
  debugSinkRefForCtx.current = debugSink;
17568
17831
  const adapter = adapterProp;
17569
17832
  const canvasRef = useRef(null);
17833
+ const helpersForLayersRef = useRef(null);
17570
17834
  const dimsRef = useRef({ width, height });
17571
17835
  dimsRef.current = { width, height };
17572
17836
  const getIsVisibleRef = useRef(getIsVisible);
@@ -17589,7 +17853,7 @@ function CanvasInner(props, ref) {
17589
17853
  const isVisible = getIsVisibleRef.current?.() ?? alwaysVisible;
17590
17854
  for (const layer of layers2) {
17591
17855
  if (!layer.hitTest) continue;
17592
- const hit = layer.hitTest(worldX, worldY, void 0, view, dims, isVisible);
17856
+ const hit = layer.hitTest(worldX, worldY, helpersForLayersRef.current, view, dims, isVisible);
17593
17857
  if (hit) return { layerId: layer.id, hit };
17594
17858
  }
17595
17859
  return null;
@@ -17744,8 +18008,8 @@ function CanvasInner(props, ref) {
17744
18008
  };
17745
18009
  const selectedIdsForWiring = effectiveSelection.current;
17746
18010
  const multiActive2 = selectedIdsForWiring.length > 1;
17747
- const previewExtraRef = useRef({ previewPoseExtra, previewBoundsExtra });
17748
- previewExtraRef.current = { previewPoseExtra, previewBoundsExtra };
18011
+ const previewExtraRef = useRef({ previewPoseExtra });
18012
+ previewExtraRef.current = { previewPoseExtra };
17749
18013
  const effectiveBoundsOf = useMemo(() => {
17750
18014
  return boundsOf2 ?? baseBoundsOf;
17751
18015
  }, [boundsOf2, baseBoundsOf]);
@@ -17764,10 +18028,6 @@ function CanvasInner(props, ref) {
17764
18028
  if (p != null) return geometry.getBounds(p);
17765
18029
  }
17766
18030
  const extras = previewExtraRef.current;
17767
- if (extras.previewBoundsExtra) {
17768
- const b = extras.previewBoundsExtra(id);
17769
- if (b) return b;
17770
- }
17771
18031
  if (extras.previewPoseExtra) {
17772
18032
  const p = extras.previewPoseExtra(id);
17773
18033
  if (p != null) return geometry.getBounds(p);
@@ -17808,10 +18068,6 @@ function CanvasInner(props, ref) {
17808
18068
  const p = firstPreviewPose(tools, id);
17809
18069
  if (p != null) return geometry.getBounds(p);
17810
18070
  }
17811
- if (previewBoundsExtra) {
17812
- const b = previewBoundsExtra(id);
17813
- if (b) return b;
17814
- }
17815
18071
  if (previewPoseExtra) {
17816
18072
  const p = previewPoseExtra(id);
17817
18073
  if (p != null) return geometry.getBounds(p);
@@ -17847,6 +18103,7 @@ function CanvasInner(props, ref) {
17847
18103
  getDebug: () => debugSink,
17848
18104
  getIsVisible: () => getIsVisibleRef.current?.() ?? alwaysVisible
17849
18105
  };
18106
+ helpersForLayersRef.current = helpersForLayers;
17850
18107
  if (helpersRef) helpersRef.current = helpersForLayers;
17851
18108
  const pointerDownRef = useRef(false);
17852
18109
  const handlePointerDown = (e) => {
@@ -18028,6 +18285,11 @@ function CanvasInner(props, ref) {
18028
18285
  );
18029
18286
  renderer.render(commands, viewToMat3(effectiveView));
18030
18287
  }, [layersWithDebug, width, height, effectiveView, debugSink, redrawNonce, dprProp]);
18288
+ useEffect(() => () => {
18289
+ glRendererRef.current?.dispose();
18290
+ glRendererRef.current = null;
18291
+ lastResizeRef.current = null;
18292
+ }, []);
18031
18293
  const shaderIdKey = shaders?.map((h) => h.id).join("|") ?? "";
18032
18294
  useEffect(() => {
18033
18295
  const renderer = glRendererRef.current;
@@ -19492,6 +19754,89 @@ function poseContainsRotated(pose, wx, wy, tolerance) {
19492
19754
  }
19493
19755
  return poseContains(pose, wx, wy, tolerance);
19494
19756
  }
19757
+ function hiddenLayerIds(layers) {
19758
+ const out = /* @__PURE__ */ new Set();
19759
+ for (const layer of layers ?? []) {
19760
+ if (layer.visible === false) out.add(layer.id);
19761
+ }
19762
+ return out;
19763
+ }
19764
+ function hitTestArea(scene, bounds) {
19765
+ const { x, y, width: w, height: h } = bounds;
19766
+ const area = [x, y, x + w, y, x + w, y + h, x, y + h];
19767
+ return hitTestAreaPolygon(scene, area, { x, y, width: w, height: h }, true);
19768
+ }
19769
+ function hitTestAreaPolygon(scene, area, areaBounds, areaIsRect = false) {
19770
+ const ab = areaBounds ?? boundsOf(area);
19771
+ if (!ab) return [];
19772
+ const hits = [];
19773
+ const hidden = hiddenLayerIds(scene.layers);
19774
+ const order = scene.renderOrderNodes();
19775
+ for (let i = 0; i < order.length; i++) {
19776
+ const node = order[i];
19777
+ if (node.kind === "container") continue;
19778
+ if (hidden.size > 0 && hidden.has(node.layer)) continue;
19779
+ const pose = node.pose;
19780
+ const silhouette = pose !== null && typeof pose === "object" && pose.kind === "polygon";
19781
+ const b = silhouette ? nodeMemo(node, "aabb", pose, () => aabbOfPose(pose)) : aabbOfPose(pose);
19782
+ if (!Number.isFinite(b.x) || !Number.isFinite(b.y) || !Number.isFinite(b.width) || !Number.isFinite(b.height)) {
19783
+ continue;
19784
+ }
19785
+ if (b.x >= ab.x + ab.width || b.x + b.width <= ab.x || b.y >= ab.y + ab.height || b.y + b.height <= ab.y) {
19786
+ continue;
19787
+ }
19788
+ if (areaIsRect && b.x >= ab.x && b.y >= ab.y && b.x + b.width <= ab.x + ab.width && b.y + b.height <= ab.y + ab.height) {
19789
+ hits.push(node.id);
19790
+ continue;
19791
+ }
19792
+ if (silhouette) {
19793
+ if (silhouetteOverlapsArea(pose.coords, area)) {
19794
+ hits.push(node.id);
19795
+ }
19796
+ continue;
19797
+ }
19798
+ const drawn = findShapeSilhouette(node, pose);
19799
+ if (drawn?.kind === "polygon") {
19800
+ if (silhouetteOverlapsArea(drawn.coords, area)) hits.push(node.id);
19801
+ continue;
19802
+ }
19803
+ hits.push(node.id);
19804
+ }
19805
+ return hits;
19806
+ }
19807
+ function silhouetteOverlapsArea(sil, area) {
19808
+ const ns = sil.length >> 1;
19809
+ const na = area.length >> 1;
19810
+ if (ns < 1 || na < 3) return false;
19811
+ for (let i = 0; i < ns; i++) {
19812
+ if (pointInPolygon(area, sil[i * 2], sil[i * 2 + 1])) return true;
19813
+ }
19814
+ for (let i = 0; i < na; i++) {
19815
+ if (pointInPolygon(sil, area[i * 2], area[i * 2 + 1])) return true;
19816
+ }
19817
+ for (let i = 0, j = ns - 1; i < ns; j = i++) {
19818
+ const ax = sil[j * 2], ay = sil[j * 2 + 1];
19819
+ const bx = sil[i * 2], by = sil[i * 2 + 1];
19820
+ for (let k = 0, l = na - 1; k < na; l = k++) {
19821
+ const cx = area[l * 2], cy = area[l * 2 + 1];
19822
+ const dx = area[k * 2], dy = area[k * 2 + 1];
19823
+ if (segmentsCross(ax, ay, bx, by, cx, cy, dx, dy)) return true;
19824
+ }
19825
+ }
19826
+ return false;
19827
+ }
19828
+ function boundsOf(coords) {
19829
+ if (coords.length < 2) return null;
19830
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
19831
+ for (let i = 0; i + 1 < coords.length; i += 2) {
19832
+ const x = coords[i], y = coords[i + 1];
19833
+ if (x < minX) minX = x;
19834
+ if (y < minY) minY = y;
19835
+ if (x > maxX) maxX = x;
19836
+ if (y > maxY) maxY = y;
19837
+ }
19838
+ return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
19839
+ }
19495
19840
 
19496
19841
  // src/canvas/SceneCanvas/useSceneSelectTool.ts
19497
19842
  var DEFAULT_PICK_TOLERANCE_PX = 4;
@@ -19591,7 +19936,9 @@ function useSceneSelectTool(args) {
19591
19936
  }
19592
19937
  const tolerance = pickTolerancePx / meanScale(getView?.()?.scale ?? { x: 1, y: 1 });
19593
19938
  const out = [];
19939
+ const hidden = hiddenLayerIds(scene.layers);
19594
19940
  for (const n of scene.renderOrderNodes()) {
19941
+ if (hidden.size > 0 && hidden.has(n.layer)) continue;
19595
19942
  if (!poseContainsRotated(n.pose, wx, wy, tolerance)) continue;
19596
19943
  if (shapePicking && !shapeCoversPoint(n, n.pose, wx, wy, { tolerance })) continue;
19597
19944
  out.push(n.id);
@@ -20494,8 +20841,10 @@ function createPathAnchorAffordances(getAnchorState, opts = {}) {
20494
20841
  const anchors = {
20495
20842
  id: PATH_ANCHOR_CHROME_ID,
20496
20843
  regions(state) {
20844
+ const editingId = getAnchorState()?.editingId ?? null;
20497
20845
  const out = [];
20498
20846
  for (const { id, path } of editablePaths(state)) {
20847
+ if (editingId !== null && id !== editingId) continue;
20499
20848
  for (const a of enumerateAnchors(path)) {
20500
20849
  out.push(point(
20501
20850
  `${id}:anchor:${a.anchorIndex}`,
@@ -20758,80 +21107,6 @@ function DepRegistryProviderIfRoot({ children }) {
20758
21107
  if (parent) return /* @__PURE__ */ jsx(Fragment, { children });
20759
21108
  return /* @__PURE__ */ jsx(DepRegistryProvider, { children });
20760
21109
  }
20761
- function hitTestArea(scene, bounds) {
20762
- const { x, y, width: w, height: h } = bounds;
20763
- const area = [x, y, x + w, y, x + w, y + h, x, y + h];
20764
- return hitTestAreaPolygon(scene, area, { x, y, width: w, height: h }, true);
20765
- }
20766
- function hitTestAreaPolygon(scene, area, areaBounds, areaIsRect = false) {
20767
- const ab = areaBounds ?? boundsOf(area);
20768
- if (!ab) return [];
20769
- const hits = [];
20770
- const order = scene.renderOrderNodes();
20771
- for (let i = 0; i < order.length; i++) {
20772
- const node = order[i];
20773
- if (node.kind === "container") continue;
20774
- const pose = node.pose;
20775
- const silhouette = pose !== null && typeof pose === "object" && pose.kind === "polygon";
20776
- const b = silhouette ? nodeMemo(node, "aabb", pose, () => aabbOfPose(pose)) : aabbOfPose(pose);
20777
- if (!Number.isFinite(b.x) || !Number.isFinite(b.y) || !Number.isFinite(b.width) || !Number.isFinite(b.height)) {
20778
- continue;
20779
- }
20780
- if (b.x >= ab.x + ab.width || b.x + b.width <= ab.x || b.y >= ab.y + ab.height || b.y + b.height <= ab.y) {
20781
- continue;
20782
- }
20783
- if (areaIsRect && b.x >= ab.x && b.y >= ab.y && b.x + b.width <= ab.x + ab.width && b.y + b.height <= ab.y + ab.height) {
20784
- hits.push(node.id);
20785
- continue;
20786
- }
20787
- if (silhouette) {
20788
- if (silhouetteOverlapsArea(pose.coords, area)) {
20789
- hits.push(node.id);
20790
- }
20791
- continue;
20792
- }
20793
- const drawn = findShapeSilhouette(node, pose);
20794
- if (drawn?.kind === "polygon") {
20795
- if (silhouetteOverlapsArea(drawn.coords, area)) hits.push(node.id);
20796
- continue;
20797
- }
20798
- hits.push(node.id);
20799
- }
20800
- return hits;
20801
- }
20802
- function silhouetteOverlapsArea(sil, area) {
20803
- const ns = sil.length >> 1;
20804
- const na = area.length >> 1;
20805
- if (ns < 1 || na < 3) return false;
20806
- for (let i = 0; i < ns; i++) {
20807
- if (pointInPolygon(area, sil[i * 2], sil[i * 2 + 1])) return true;
20808
- }
20809
- for (let i = 0; i < na; i++) {
20810
- if (pointInPolygon(sil, area[i * 2], area[i * 2 + 1])) return true;
20811
- }
20812
- for (let i = 0, j = ns - 1; i < ns; j = i++) {
20813
- const ax = sil[j * 2], ay = sil[j * 2 + 1];
20814
- const bx = sil[i * 2], by = sil[i * 2 + 1];
20815
- for (let k = 0, l = na - 1; k < na; l = k++) {
20816
- const cx = area[l * 2], cy = area[l * 2 + 1];
20817
- const dx = area[k * 2], dy = area[k * 2 + 1];
20818
- if (segmentsCross(ax, ay, bx, by, cx, cy, dx, dy)) return true;
20819
- }
20820
- }
20821
- return false;
20822
- }
20823
- function boundsOf(coords) {
20824
- if (coords.length < 2) return null;
20825
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
20826
- for (let i = 0; i + 1 < coords.length; i += 2) {
20827
- const x = coords[i], y = coords[i + 1];
20828
- if (x < minX) minX = x;
20829
- if (y < minY) minY = y;
20830
- if (x > maxX) maxX = x;
20831
- if (y > maxY) maxY = y;
20832
- }
20833
- return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
20834
- }
20835
21110
  function useViewDepSource(currentViewRef, onViewChange, recenter, hostSize) {
20836
21111
  const viewApiRef = useRef({
20837
21112
  get: () => currentViewRef.current,
@@ -21634,6 +21909,7 @@ function useActionsPropResolver(actions) {
21634
21909
  }
21635
21910
  if (!prop) return;
21636
21911
  const unregisters = [];
21912
+ const restores = [];
21637
21913
  const warnedIds = /* @__PURE__ */ new Set();
21638
21914
  for (const [slotId, entry] of Object.entries(prop)) {
21639
21915
  if (entry === null) {
@@ -21661,10 +21937,14 @@ function useActionsPropResolver(actions) {
21661
21937
  }
21662
21938
  const { id: _drop, ...rest } = entry;
21663
21939
  const merged = { ...existing, ...rest };
21940
+ restores.push(() => {
21941
+ reg.register(existing);
21942
+ });
21664
21943
  unregisters.push(reg.register(merged));
21665
21944
  }
21666
21945
  return () => {
21667
21946
  for (const u of unregisters) u();
21947
+ for (const r of restores) r();
21668
21948
  };
21669
21949
  }, [reg, actions]);
21670
21950
  }
@@ -24894,23 +25174,7 @@ function nestedHitTester(adapter, opts) {
24894
25174
  };
24895
25175
  return { pickOutermost, pickBest };
24896
25176
  }
24897
-
24898
- // src/features/paths/pathDistance.ts
24899
25177
  var BEZIER_SAMPLES = 16;
24900
- function pointSegDist2(px, py, ax, ay, bx, by) {
24901
- const dx = bx - ax, dy = by - ay;
24902
- const len2 = dx * dx + dy * dy;
24903
- if (len2 === 0) {
24904
- const ex2 = px - ax, ey2 = py - ay;
24905
- return ex2 * ex2 + ey2 * ey2;
24906
- }
24907
- let t = ((px - ax) * dx + (py - ay) * dy) / len2;
24908
- if (t < 0) t = 0;
24909
- else if (t > 1) t = 1;
24910
- const cx = ax + dx * t, cy = ay + dy * t;
24911
- const ex = px - cx, ey = py - cy;
24912
- return ex * ex + ey * ey;
24913
- }
24914
25178
  function pointCubicDist2(px, py, x0, y0, x1, y1, x2, y2, x3, y3) {
24915
25179
  let prevX = x0, prevY = y0;
24916
25180
  let best = Infinity;
@@ -24919,7 +25183,7 @@ function pointCubicDist2(px, py, x0, y0, x1, y1, x2, y2, x3, y3) {
24919
25183
  const u = 1 - t;
24920
25184
  const sx = u * u * u * x0 + 3 * u * u * t * x1 + 3 * u * t * t * x2 + t * t * t * x3;
24921
25185
  const sy = u * u * u * y0 + 3 * u * u * t * y1 + 3 * u * t * t * y2 + t * t * t * y3;
24922
- const d2 = pointSegDist2(px, py, prevX, prevY, sx, sy);
25186
+ const d2 = pointSegmentDist2(px, py, prevX, prevY, sx, sy);
24923
25187
  if (d2 < best) best = d2;
24924
25188
  prevX = sx;
24925
25189
  prevY = sy;
@@ -24956,7 +25220,7 @@ function polygonPathDistance(path, px, py) {
24956
25220
  ci += 2;
24957
25221
  } else if (cmd === PATH_L) {
24958
25222
  const x = coords[ci], y = coords[ci + 1];
24959
- const d2 = pointSegDist2(px, py, curX, curY, x, y);
25223
+ const d2 = pointSegmentDist2(px, py, curX, curY, x, y);
24960
25224
  if (d2 < best) best = d2;
24961
25225
  curX = x;
24962
25226
  curY = y;
@@ -24983,7 +25247,7 @@ function polygonPathDistance(path, px, py) {
24983
25247
  curY = y;
24984
25248
  ci += 4;
24985
25249
  } else if (cmd === PATH_Z) {
24986
- const d2 = pointSegDist2(px, py, curX, curY, startX, startY);
25250
+ const d2 = pointSegmentDist2(px, py, curX, curY, startX, startY);
24987
25251
  if (d2 < best) best = d2;
24988
25252
  curX = startX;
24989
25253
  curY = startY;
@@ -25836,7 +26100,10 @@ function arrayAdapter(config) {
25836
26100
  },
25837
26101
  getParent,
25838
26102
  setParent,
25839
- insertNode: (obj) => setItems((items) => [...items, obj]),
26103
+ insertNode: (obj, index) => setItems((items) => {
26104
+ if (index === void 0 || index < 0 || index >= items.length) return [...items, obj];
26105
+ return [...items.slice(0, index), obj, ...items.slice(index)];
26106
+ }),
25840
26107
  removeNode: (id) => setItems((items) => items.filter((o) => o.id !== id)),
25841
26108
  getSelection,
25842
26109
  setSelection,
@@ -27122,6 +27389,225 @@ function makeBuilder(animator, timers, createSupervisor, watchCompletion, items,
27122
27389
  };
27123
27390
  }
27124
27391
 
27392
+ // src/animation/timeline/sampleTrack.ts
27393
+ function floorKeyIndex(keys, t) {
27394
+ let lo = 0;
27395
+ let hi = keys.length - 1;
27396
+ let found = -1;
27397
+ while (lo <= hi) {
27398
+ const mid2 = lo + hi >> 1;
27399
+ if (keys[mid2].t <= t) {
27400
+ found = mid2;
27401
+ lo = mid2 + 1;
27402
+ } else {
27403
+ hi = mid2 - 1;
27404
+ }
27405
+ }
27406
+ return found;
27407
+ }
27408
+ function sampleTrack(track, t, segmentCache) {
27409
+ const { keys } = track;
27410
+ if (keys.length === 0) return void 0;
27411
+ const i = floorKeyIndex(keys, t);
27412
+ if (i < 0) return keys[0].value;
27413
+ if (i >= keys.length - 1) return keys[keys.length - 1].value;
27414
+ const a = keys[i];
27415
+ const b = keys[i + 1];
27416
+ const span = b.t - a.t;
27417
+ const raw = span <= 0 ? 1 : (t - a.t) / span;
27418
+ const u = b.easing ? b.easing(raw) : raw;
27419
+ if (track.interpolator) {
27420
+ let fn = segmentCache?.get(i + 1);
27421
+ if (!fn) {
27422
+ fn = track.interpolator(a.value, b.value);
27423
+ segmentCache?.set(i + 1, fn);
27424
+ }
27425
+ return fn(u);
27426
+ }
27427
+ if (track.interpolate) return track.interpolate(a.value, b.value, u);
27428
+ if (typeof a.value === "number" && typeof b.value === "number") {
27429
+ return a.value + (b.value - a.value) * u;
27430
+ }
27431
+ throw new Error("sampleTrack: interpolate or interpolator is required for non-numeric keyframe values");
27432
+ }
27433
+
27434
+ // src/animation/timeline/createTimeline.ts
27435
+ function firstAfter(events, t) {
27436
+ let lo = 0;
27437
+ let hi = events.length;
27438
+ while (lo < hi) {
27439
+ const mid2 = lo + hi >> 1;
27440
+ if (events[mid2].t > t) hi = mid2;
27441
+ else lo = mid2 + 1;
27442
+ }
27443
+ return lo;
27444
+ }
27445
+ function trackEnd(track) {
27446
+ switch (track.kind) {
27447
+ case "sampled":
27448
+ return track.keys.length ? track.keys[track.keys.length - 1].t : 0;
27449
+ case "event":
27450
+ return track.events.length ? track.events[track.events.length - 1].t : 0;
27451
+ case "timeline":
27452
+ return track.at + tracksEnd(track.timeline.tracks, track.timeline.duration);
27453
+ }
27454
+ }
27455
+ function tracksEnd(tracks, explicit) {
27456
+ if (explicit != null) return explicit;
27457
+ let max = 0;
27458
+ for (const t of tracks) max = Math.max(max, trackEnd(t));
27459
+ return max;
27460
+ }
27461
+ function createTimeline(register, id, opts) {
27462
+ let duration = tracksEnd(opts.tracks, opts.duration);
27463
+ let offset = 0;
27464
+ let lastVirtual = 0;
27465
+ let playhead = 0;
27466
+ let prevPlayhead = -Infinity;
27467
+ let done = false;
27468
+ let live = true;
27469
+ let cancelled = false;
27470
+ let wantPaused = opts.autoplay === false;
27471
+ let wantScale = 1;
27472
+ const subscribers2 = /* @__PURE__ */ new Set();
27473
+ const loopOpt = opts.loop ?? false;
27474
+ let loopsLeft = loopOpt === true ? Infinity : loopOpt === false ? 0 : loopOpt;
27475
+ let caches = /* @__PURE__ */ new WeakMap();
27476
+ const cacheFor = (track) => {
27477
+ let c = caches.get(track);
27478
+ if (!c) {
27479
+ c = /* @__PURE__ */ new Map();
27480
+ caches.set(track, c);
27481
+ }
27482
+ return c;
27483
+ };
27484
+ const applySampled = (tracks, t) => {
27485
+ for (const track of tracks) {
27486
+ if (track.kind === "sampled") {
27487
+ const st = track;
27488
+ const v = sampleTrack(st, t, cacheFor(st));
27489
+ if (v !== void 0) st.onTick(v);
27490
+ } else if (track.kind === "timeline") {
27491
+ applySampled(track.timeline.tracks, t - track.at);
27492
+ }
27493
+ }
27494
+ };
27495
+ const fireEvents = (tracks, from, to) => {
27496
+ for (const track of tracks) {
27497
+ if (track.kind === "event") {
27498
+ const end = firstAfter(track.events, to);
27499
+ for (let i = firstAfter(track.events, from); i < end; i += 1) track.events[i].fire();
27500
+ } else if (track.kind === "timeline") {
27501
+ fireEvents(track.timeline.tracks, from - track.at, to - track.at);
27502
+ }
27503
+ }
27504
+ };
27505
+ const onWrap = () => {
27506
+ fireEvents(opts.tracks, prevPlayhead, duration);
27507
+ prevPlayhead = -Infinity;
27508
+ };
27509
+ const tick = (virtualNow) => {
27510
+ lastVirtual = virtualNow;
27511
+ playhead = virtualNow + offset;
27512
+ if (duration > 0 && playhead >= duration) {
27513
+ if (loopsLeft === Infinity) {
27514
+ const laps = Math.floor(playhead / duration);
27515
+ offset -= laps * duration;
27516
+ playhead -= laps * duration;
27517
+ onWrap();
27518
+ } else {
27519
+ while (playhead >= duration && loopsLeft > 0) {
27520
+ loopsLeft -= 1;
27521
+ offset -= duration;
27522
+ playhead -= duration;
27523
+ onWrap();
27524
+ }
27525
+ }
27526
+ }
27527
+ const finished = playhead >= duration;
27528
+ playhead = Math.min(playhead, duration);
27529
+ applySampled(opts.tracks, playhead);
27530
+ fireEvents(opts.tracks, prevPlayhead, playhead);
27531
+ prevPlayhead = playhead;
27532
+ if (finished && !done) {
27533
+ done = true;
27534
+ opts.onDone?.();
27535
+ }
27536
+ if (playhead < duration) return false;
27537
+ live = false;
27538
+ return true;
27539
+ };
27540
+ const onCancel = () => {
27541
+ cancelled = true;
27542
+ live = false;
27543
+ };
27544
+ const base = register({ id, cancelKey: opts.cancelKey, tick, onCancel });
27545
+ if (opts.autoplay === false) base.pause();
27546
+ const rearm = () => {
27547
+ if (cancelled || playhead >= duration) return;
27548
+ done = false;
27549
+ if (live) return;
27550
+ lastVirtual = 0;
27551
+ offset = playhead;
27552
+ live = true;
27553
+ register({ id, cancelKey: opts.cancelKey, keepExisting: true, tick, onCancel });
27554
+ if (wantPaused) base.pause();
27555
+ base.setTimeScale(wantScale);
27556
+ };
27557
+ return {
27558
+ ...base,
27559
+ // Not just `onCancel`: cancelling a finished timeline never reaches the
27560
+ // animator, and must still keep a later seek from reviving it.
27561
+ cancel() {
27562
+ onCancel();
27563
+ base.cancel();
27564
+ },
27565
+ // Playback intent is tracked here, not read back from the entry: a revived
27566
+ // one is a fresh registration, and defaults to running at scale 1.
27567
+ pause() {
27568
+ wantPaused = true;
27569
+ base.pause();
27570
+ },
27571
+ resume() {
27572
+ wantPaused = false;
27573
+ base.resume();
27574
+ },
27575
+ setTimeScale(s) {
27576
+ wantScale = s;
27577
+ base.setTimeScale(s);
27578
+ },
27579
+ isPaused: () => live ? base.isPaused() : wantPaused,
27580
+ seek(t) {
27581
+ offset = t - lastVirtual;
27582
+ playhead = t;
27583
+ prevPlayhead = t;
27584
+ rearm();
27585
+ },
27586
+ time: () => playhead,
27587
+ duration: () => duration,
27588
+ tracks: () => opts.tracks,
27589
+ edit(fn) {
27590
+ fn();
27591
+ caches = /* @__PURE__ */ new WeakMap();
27592
+ duration = tracksEnd(opts.tracks, opts.duration);
27593
+ rearm();
27594
+ for (const cb of subscribers2) {
27595
+ try {
27596
+ cb();
27597
+ } catch (err) {
27598
+ console.error("timeline: subscriber threw", err);
27599
+ }
27600
+ }
27601
+ },
27602
+ subscribe(cb) {
27603
+ subscribers2.add(cb);
27604
+ return () => {
27605
+ subscribers2.delete(cb);
27606
+ };
27607
+ }
27608
+ };
27609
+ }
27610
+
27125
27611
  // src/animation/colorRegistry.ts
27126
27612
  var ColorOverrideRegistry = class {
27127
27613
  map = /* @__PURE__ */ new Map();
@@ -27276,7 +27762,7 @@ function useAnimator(opts = {}) {
27276
27762
  }
27277
27763
  };
27278
27764
  const register = (seed) => {
27279
- if (seed.cancelKey != null) cancelByKey(seed.cancelKey);
27765
+ if (seed.cancelKey != null && !seed.keepExisting) cancelByKey(seed.cancelKey);
27280
27766
  const anim = seed;
27281
27767
  anim.paused = false;
27282
27768
  anim.timeScale = 1;
@@ -27515,6 +28001,7 @@ function useAnimator(opts = {}) {
27515
28001
  factory,
27516
28002
  staggerOpts
27517
28003
  )),
28004
+ timeline: (o) => createTimeline(register, nextId.current++, o),
27518
28005
  colorOverrides: colorOverrides.current,
27519
28006
  onTick: (cb) => {
27520
28007
  tickSubscribers.current.add(cb);
@@ -27586,8 +28073,8 @@ function animateLifecycle(adapter, animator, opts) {
27586
28073
  const ms = opts.ms ?? 200;
27587
28074
  return {
27588
28075
  ...adapter,
27589
- insertNode(object) {
27590
- adapter.insertNode(object);
28076
+ insertNode(object, index) {
28077
+ adapter.insertNode(object, index);
27591
28078
  if (!opts.enterFrom) return;
27592
28079
  const final = adapter.getPose(object.id);
27593
28080
  const start = opts.enterFrom(final);
@@ -28050,6 +28537,101 @@ function hslToRgbUnit(h, s, l) {
28050
28537
  return hslToRgb(h, s, l);
28051
28538
  }
28052
28539
 
28540
+ // src/animation/rig/types.ts
28541
+ var IDENTITY_JOINT = {
28542
+ x: 0,
28543
+ y: 0,
28544
+ rotation: 0,
28545
+ scaleX: 1,
28546
+ scaleY: 1
28547
+ };
28548
+
28549
+ // src/animation/rig/blendPoses.ts
28550
+ var TAU = Math.PI * 2;
28551
+ function shortestDelta(a, b) {
28552
+ let d = (b - a) % TAU;
28553
+ if (d > Math.PI) d -= TAU;
28554
+ if (d < -Math.PI) d += TAU;
28555
+ return d;
28556
+ }
28557
+ var FIELDS = ["x", "y", "scaleX", "scaleY"];
28558
+ function blendPoses(poses, weights) {
28559
+ if (poses.length !== weights.length) {
28560
+ throw new Error("blendPoses: poses and weights must have the same length");
28561
+ }
28562
+ const total = weights.reduce((s, w) => s + w, 0);
28563
+ if (total === 0) return {};
28564
+ const norm = weights.map((w) => w / total);
28565
+ const names = /* @__PURE__ */ new Set();
28566
+ for (const p of poses) for (const k of Object.keys(p)) names.add(k);
28567
+ const out = {};
28568
+ for (const name of names) {
28569
+ const joint = {};
28570
+ for (const field of FIELDS) {
28571
+ let acc = 0;
28572
+ for (let i = 0; i < poses.length; i += 1) {
28573
+ const v = poses[i][name]?.[field];
28574
+ acc += (v ?? IDENTITY_JOINT[field]) * norm[i];
28575
+ }
28576
+ joint[field] = acc;
28577
+ }
28578
+ const base = poses[0][name]?.rotation ?? 0;
28579
+ let rot = 0;
28580
+ for (let i = 0; i < poses.length; i += 1) {
28581
+ const v = poses[i][name]?.rotation ?? 0;
28582
+ rot += shortestDelta(base, v) * norm[i];
28583
+ }
28584
+ joint.rotation = base + rot;
28585
+ out[name] = joint;
28586
+ }
28587
+ return out;
28588
+ }
28589
+
28590
+ // src/animation/rig/resolveSkeleton.ts
28591
+ function compose(bind, delta3) {
28592
+ if (!delta3) return bind;
28593
+ return {
28594
+ x: bind.x + (delta3.x ?? 0),
28595
+ y: bind.y + (delta3.y ?? 0),
28596
+ rotation: bind.rotation + (delta3.rotation ?? 0),
28597
+ scaleX: bind.scaleX * (delta3.scaleX ?? 1),
28598
+ scaleY: bind.scaleY * (delta3.scaleY ?? 1)
28599
+ };
28600
+ }
28601
+ function toMat3(t) {
28602
+ const c = Math.cos(t.rotation);
28603
+ const s = Math.sin(t.rotation);
28604
+ const m = new Float32Array(9);
28605
+ m[0] = c * t.scaleX;
28606
+ m[1] = s * t.scaleX;
28607
+ m[2] = 0;
28608
+ m[3] = -s * t.scaleY;
28609
+ m[4] = c * t.scaleY;
28610
+ m[5] = 0;
28611
+ m[6] = t.x;
28612
+ m[7] = t.y;
28613
+ m[8] = 1;
28614
+ return m;
28615
+ }
28616
+ function resolveSkeleton(skeleton, pose) {
28617
+ const out = /* @__PURE__ */ new Map();
28618
+ for (const joint of skeleton.joints) {
28619
+ const local = toMat3(compose(joint.bind, pose[joint.name]));
28620
+ if (joint.parent == null) {
28621
+ out.set(joint.name, local);
28622
+ continue;
28623
+ }
28624
+ const parent = out.get(joint.parent);
28625
+ if (!parent) {
28626
+ throw new Error(
28627
+ `resolveSkeleton: joint "${joint.name}" names parent "${joint.parent}", which has not been resolved. Skeleton.joints must be in topological order.`
28628
+ );
28629
+ }
28630
+ out.set(joint.name, mat3.multiply(parent, local));
28631
+ }
28632
+ return out;
28633
+ }
28634
+
28053
28635
  // src/layout/snaps.ts
28054
28636
  function dist22(a, b) {
28055
28637
  const dx = a.x - b.x;
@@ -28338,6 +28920,6 @@ function mergeContributions(...bundles) {
28338
28920
  return out;
28339
28921
  }
28340
28922
 
28341
- export { ALWAYS, ANCHOR_HIT_BASE_PX, ActionsProviderIfRoot, ActiveToolContextProvider, ActiveToolContextProviderIfRoot, BUNDLE_TOOLS, COARSE_TARGET_SCALE, CURVE_REPS, ColorOverrideRegistry, CropIcon, CursorCoordsHud, DEFAULT_ALPHA_DECAY, DEFAULT_ALPHA_MIN, DEFAULT_DEBUG_STROKES, DEFAULT_DEBUG_THEME, DEFAULT_DEVICE_PROFILE, DEFAULT_FILL_COLOR, DEFAULT_HANDLE_SIZE2 as DEFAULT_HANDLE_SIZE, DEFAULT_PALETTE, DEFAULT_ROTATION_HANDLE_DISTANCE, DEFAULT_STROKE_COLOR, DEFAULT_TEXT_STYLE, DEFAULT_VELOCITY_DECAY, DepRegistryProviderIfRoot, DeviceProfileProvider, DivideIcon, EASINGS, EllipseIcon, ExcludeIcon, EyedropperIcon, FALLBACK_FIT_VIEW, GHOST_STROKE, HANDLE_BASE_PX, HandIcon, IDENTITY_COLOR_MATRIX, IDENTITY_POSE_COMPOSITION, IntersectIcon, KIT_SHAPE_KINDS, LassoIcon, LineIcon, MIXED, MOVE_ANCHORS, MinimapCanvas, NEVER, OUTLINE_MIN_SCREEN_PX, PATH_ANCHOR_CHROME_ID, PenIcon, PencilIcon, PickHud, PointerProviderIfRoot, PolygonIcon, RECT_ALIGN_PROJECTION, ROTATION_HANDLE_BASE_PX, RectIcon, SPRING_PRESETS, SceneCanvas, SceneViewCanvas, SelectIcon, SelectionContextProvider, SelectionContextProviderIfRoot, ShaderCompileError, StarIcon, SubtractIcon, TextIcon, UnionIcon, UnknownIcon, VERSION, WeaselProvider, WeaselRenderer, aabbCenter, actionIs, alignDeltaFor, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, alignedStrokeRect, always, and, animateLifecycle, animateOnSetPose, annulusSemiAxes, applyBooleanOp, applyHitExistingGate, applyStyleToRange, areaSelectAction, arrayAdapter, asNodeId, bezierCubic, bezierQuadratic, buildChromeCtx, buildGradientRamp, buildRuleCtx, buildSceneViewCommands, canBringForward, canHover, canSendBackward, capabilityAll, capabilityIn, capabilityIs, capabilityNot, caretIndexAt, cellAt, charOffsetToDomPosition, clampView, clearSelectionAction, clientToCanvas, clipboardCopyAction, clipboardCutAction, cloneAction, coarsePointer, composeAffordanceLayer, composePath, composeRectPose, composeSelectionPose, composeWorldPose, computeFitView2 as computeFitView, computeIndicatorCommand, computeWheelAction, cond, constrainTo45, containedThenNearest, countPathAnchors, createCellHighlightLayer, createChildrenLayer, createCornerResizeAffordance, createDebugOverlayLayer, createDebugSink, createDispatcher, createGridLayer, createGuidesLayer, createHistory2 as createHistory, createMarkdownRenderer, createMoveToIndexOp, createNodeProperties, createNodeRouting, createParallaxLayer, createPathAnchorAffordances, createPathEditingOverlayLayer, createPathLayer, createPenPreviewLayer, createReorderOp, createRotationAffordance, createScene, createSelectionHandlesLayer, createSelectionOutlineLayer, createSelectionOverlayLayer, createSetDataOp, createSetLayerOp, createSetPathOp, createSetTextOp, createTextLayer, createViewportLayer, cycleVertexColors, decomposePath, decomposeRectPose, defaultCommitAdapter, defaultDrawOne, defaultLabelTextRenderer, defaultNodeProperties, defaultNodeRouting, defaultVisibilityRules, defineTool, defineViewportTool, deriveAlignmentGuides, deriveParallaxView, describeRule, domPositionToCharOffset, domToRuns, drawLayers, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic2 as easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorsAction, enterTextEditAction, enumerateAnchors, evaluate, fillInPoseFrame, fillToBoundsFrame, findNodeShape, findShapeInk, findShapeSilhouette, fitTextPose, fitToBounds, fitViewToBounds, fitZoom, focused, fontString, forEachCoalesced, freeform, gateLayer, gesturing, getAlpha01, getImageBitmap, getNodeShapes, getStylusData, gradientForBounds, gradientGeometry, hexToRgba, hitAffordanceRegions, hitAnchor, hitRotationHandle, hovering, hoveringSelected, imageStatus, inferredNodeProperties, inferredNodeRouting, insertAction, isEditableTarget2 as isEditableTarget, isPatternSpec, lassoSelectAction, layoutMarkdown, lerpColorArray, lerpOklab, lerpOklch, linear, liveScope, makeViewportZoomAction, markdownToRuns, mat3, matchAlignment, matchesKeyBinding, meanScale, measureText, measureTextBounds, measuredWidth, mergeAlphaFromPrev, mergeContributions, modeIn, modeIs, modeNot, modifierHeld, momentum, moveAction, multiActive, nearest, nearestWithin, nestedHitTester, never, nodeHasFlag, none, normalizeHex, not, nurbs, oklabToOklch, oklabToSrgbU8, oklchToOklab, openFilePicker, or, parseColor, parseColorToRgba255, parseDebugFlags, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathDistanceToPoint, pathDivide, pathExclude, pathFromD, pathInPoseFrame, pathInWorld, pathIntersect, pathIntersectsPolygon, pathIntersectsRect, pathOriginProjection, pathSubtract, pathToAnchors, pathUnion, pickTopMostHit, pinchZoomAction, planPixelRender, pointInRotatedRect, pointInTextPose, poseRotationOf, pressureToWidth, rainbowVertexColors, rebaseLocalPose, rectCorners, registerContentHandler, registerNodeShape, registerProgram, renderLabel, renderSceneToCanvas, renderSceneToPixels, resizeAction, resolveColor, resolveDeviceProfile, resolveFillPattern, resolvePatternSpec, resolveRuns, resolveTextStyle, resolveVisibility, rgbaToHex, rotateAction, rotateAroundAABBCenter, rotatePathAround, rotatePoint, rotatedRectCorners, rotationDegreesUnit, rotationHandle, roundToCell, runsToDom, runsToMarkdown, runsToPlainText, sampleGradientStops, sceneFromJSON, sceneNodeClientRect, sceneToAdapter, scopeBindings, screenToWorld, selectFromLasso, selectionAtLeast, selectionEmpty, selectionIs, setFlagOverRange, shapeCoversPoint, sliceAction, snapPoint, solidVertexColors, specificity, spiro, splitPathByLine, splitSubpaths, springPose, springVertexColors, srgbU8ToOklab, staggerVertexColors, startThresholdDrag, styleAtRange, subscribeImageReady, tessellate, tessellateStroke, textCommand, textLineBoxes, tileGrid, toHex8, toRuns, translatePoseViaDescriptor, translateRectPose, tweenPose, tweenVertexColors, unionBounds, unionBoundsPath, useActiveToolContext, useAlign, useAnimator, useArrayAdapter, useAutoCenter, useBooleans, useBooleansAdapter, useCanvasFocus, useCanvasSize, useDecayLoop, useDeviceProfile, useDistribute, useDragGesture, useDragHandle, useDragRadial, useDragRect, useDropZone, useEllipseTool, useEyedropperTool, useGestureDispatcher, useGridCellHover, useGuides, useHandTool, useHandleDrag, useHoverTracking, useImageTool, useKeybindings, useLassoTool, useLineTool, useOptionalActiveToolContext, usePenTool, usePencilTool, usePinchGesture, usePinchZoomTool, usePointerStylus, usePolygonTool, usePublishSelection, useRectTool, useResizePolicy, useRotateTool, useScene, useSceneAdapter, useSceneTextEdit, useSelectTool, useSelection, useSelectionContext, useSimulation, useSliceDep, useStandardActions, useStarTool, useTextEdit, useTextTool, useTools, useVelocityTracker, useViewAnimation, useViewTween, useZoom, verticalAlignOffset, viewToMat3, viewToTransform, viewportDragPanAction, viewportZoomAction, viewportsAt, when, withAlpha01, withCoord, withGradientKind, worldEditToStorage, worldPoseLookup, worldToScreen, zoomAt, zoomAtLeast };
28342
- //# sourceMappingURL=chunk-SMCZRSYB.js.map
28343
- //# sourceMappingURL=chunk-SMCZRSYB.js.map
28923
+ export { ALWAYS, ANCHOR_HIT_BASE_PX, ActionsProviderIfRoot, ActiveToolContextProvider, ActiveToolContextProviderIfRoot, BUNDLE_TOOLS, COARSE_TARGET_SCALE, CURVE_REPS, ColorOverrideRegistry, CropIcon, CursorCoordsHud, DEFAULT_ALPHA_DECAY, DEFAULT_ALPHA_MIN, DEFAULT_DEBUG_STROKES, DEFAULT_DEBUG_THEME, DEFAULT_DEVICE_PROFILE, DEFAULT_FILL_COLOR, DEFAULT_HANDLE_SIZE2 as DEFAULT_HANDLE_SIZE, DEFAULT_PALETTE, DEFAULT_ROTATION_HANDLE_DISTANCE, DEFAULT_STROKE_COLOR, DEFAULT_TEXT_STYLE, DEFAULT_VELOCITY_DECAY, DepRegistryProviderIfRoot, DeviceProfileProvider, DivideIcon, EASINGS, EllipseIcon, ExcludeIcon, EyedropperIcon, FALLBACK_FIT_VIEW, GHOST_STROKE, HANDLE_BASE_PX, HandIcon, IDENTITY_COLOR_MATRIX, IDENTITY_JOINT, IDENTITY_POSE_COMPOSITION, IntersectIcon, KIT_SHAPE_KINDS, LassoIcon, LineIcon, MIXED, MOVE_ANCHORS, MinimapCanvas, NEVER, OUTLINE_MIN_SCREEN_PX, PATH_ANCHOR_CHROME_ID, PenIcon, PencilIcon, PickHud, PointerProviderIfRoot, PolygonIcon, RECT_ALIGN_PROJECTION, ROTATION_HANDLE_BASE_PX, RectIcon, SPRING_PRESETS, SceneCanvas, SceneViewCanvas, SelectIcon, SelectionContextProvider, SelectionContextProviderIfRoot, ShaderCompileError, StarIcon, SubtractIcon, TextIcon, UnionIcon, UnknownIcon, VERSION, WeaselProvider, WeaselRenderer, aabbCenter, actionIs, alignDeltaFor, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, alignedStrokeRect, always, and, animateLifecycle, animateOnSetPose, annulusSemiAxes, applyBooleanOp, applyStyleToRange, areaSelectAction, arrayAdapter, asNodeId, bezierCubic, bezierQuadratic, blendPoses, buildChromeCtx, buildGradientRamp, buildRuleCtx, buildSceneViewCommands, canBringForward, canHover, canSendBackward, capabilityAll, capabilityIn, capabilityIs, capabilityNot, caretIndexAt, cellAt, charOffsetToDomPosition, clampView, clearSelectionAction, clientToCanvas, clipboardCopyAction, clipboardCutAction, cloneAction, coarsePointer, composeAffordanceLayer, composePath, composeRectPose, composeSelectionPose, composeWorldPose, computeFitView2 as computeFitView, computeIndicatorCommand, computeWheelAction, cond, constrainTo45, containedThenNearest, countPathAnchors, createCellHighlightLayer, createChildrenLayer, createCornerResizeAffordance, createDebugOverlayLayer, createDebugSink, createDispatcher, createGridLayer, createGuidesLayer, createHistory2 as createHistory, createMarkdownRenderer, createMoveToIndexOp, createNodeProperties, createNodeRouting, createParallaxLayer, createPathAnchorAffordances, createPathEditingOverlayLayer, createPathLayer, createPenPreviewLayer, createReorderOp, createRotationAffordance, createScene, createSelectionHandlesLayer, createSelectionOutlineLayer, createSelectionOverlayLayer, createSetDataOp, createSetLayerOp, createSetPathOp, createSetTextOp, createTextLayer, createViewportLayer, cycleVertexColors, decomposePath, decomposeRectPose, defaultCommitAdapter, defaultDrawOne, defaultLabelTextRenderer, defaultNodeProperties, defaultNodeRouting, defaultVisibilityRules, defineTool, defineViewportTool, deriveAlignmentGuides, deriveParallaxView, describeRule, domPositionToCharOffset, domToRuns, drawLayers, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic2 as easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorsAction, enterTextEditAction, enumerateAnchors, evaluate, fillInPoseFrame, fillToBoundsFrame, findNodeShape, findShapeInk, findShapeSilhouette, fitTextPose, fitToBounds, fitViewToBounds, fitZoom, focused, fontString, forEachCoalesced, freeform, gateLayer, gesturing, getAlpha01, getImageBitmap, getNodeShapes, getStylusData, gradientForBounds, gradientGeometry, hexToRgba, hitAffordanceRegions, hitAnchor, hitRotationHandle, hovering, hoveringSelected, imageStatus, inferredNodeProperties, inferredNodeRouting, insertAction, isEditableTarget2 as isEditableTarget, isPatternSpec, lassoSelectAction, layoutMarkdown, lerpColorArray, lerpOklab, lerpOklch, linear, liveScope, makeViewportZoomAction, markdownToRuns, mat3, matchAlignment, matchesKeyBinding, meanScale, measureText, measureTextBounds, measuredWidth, mergeAlphaFromPrev, mergeContributions, modeIn, modeIs, modeNot, modifierHeld, momentum, moveAction, multiActive, nearest, nearestWithin, nestedHitTester, never, nodeHasFlag, none, normalizeHex, not, nurbs, oklabToOklch, oklabToSrgbU8, oklchToOklab, openFilePicker, or, parseColor, parseColorToRgba255, parseDebugFlags, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathDistanceToPoint, pathDivide, pathExclude, pathFromD, pathInPoseFrame, pathInWorld, pathIntersect, pathIntersectsPolygon, pathIntersectsRect, pathOriginProjection, pathSubtract, pathToAnchors, pathUnion, pickTopMostHit, pinchZoomAction, planPixelRender, pointInRotatedRect, pointInTextPose, poseRotationOf, pressureToWidth, rainbowVertexColors, rebaseLocalPose, rectCorners, registerContentHandler, registerNodeShape, registerProgram, renderLabel, renderSceneToCanvas, renderSceneToPixels, resizeAction, resolveColor, resolveDeviceProfile, resolveFillPattern, resolvePatternSpec, resolveRuns, resolveSkeleton, resolveTextStyle, resolveVisibility, rgbaToHex, rotateAction, rotateAroundAABBCenter, rotatePathAround, rotatePoint, rotatedRectCorners, rotationDegreesUnit, rotationHandle, roundToCell, runsToDom, runsToMarkdown, runsToPlainText, sampleGradientStops, sampleTrack, sceneFromJSON, sceneNodeClientRect, sceneToAdapter, scopeBindings, screenToWorld, selectFromLasso, selectionAtLeast, selectionEmpty, selectionIs, setFlagOverRange, shapeCoversPoint, sliceAction, snapPoint, solidVertexColors, specificity, spiro, splitPathByLine, splitSubpaths, springPose, springVertexColors, srgbU8ToOklab, staggerVertexColors, startThresholdDrag, styleAtRange, subscribeImageReady, tessellate, tessellateStroke, textCommand, textLineBoxes, tileGrid, toHex8, toRuns, translatePoseViaDescriptor, translateRectPose, tweenPose, tweenVertexColors, unionBounds, unionBoundsPath, useActiveToolContext, useAlign, useAnimator, useArrayAdapter, useAutoCenter, useBooleans, useBooleansAdapter, useCanvasFocus, useCanvasSize, useDecayLoop, useDeviceProfile, useDistribute, useDragGesture, useDragHandle, useDragRadial, useDragRect, useDropZone, useEllipseTool, useEyedropperTool, useGestureDispatcher, useGridCellHover, useGuides, useHandTool, useHandleDrag, useHoverTracking, useImageTool, useKeybindings, useLassoTool, useLineTool, useOptionalActiveToolContext, usePenTool, usePencilTool, usePinchGesture, usePinchZoomTool, usePointerStylus, usePolygonTool, usePublishSelection, useRectTool, useResizePolicy, useRotateTool, useScene, useSceneAdapter, useSceneTextEdit, useSelectTool, useSelection, useSelectionContext, useSimulation, useSliceDep, useStandardActions, useStarTool, useTextEdit, useTextTool, useTools, useVelocityTracker, useViewAnimation, useViewTween, useZoom, verticalAlignOffset, viewToMat3, viewToTransform, viewportDragPanAction, viewportZoomAction, viewportsAt, when, withAlpha01, withCoord, withGradientKind, worldEditToStorage, worldPoseLookup, worldToScreen, zoomAt, zoomAtLeast };
28924
+ //# sourceMappingURL=chunk-3B4QEB2G.js.map
28925
+ //# sourceMappingURL=chunk-3B4QEB2G.js.map