@pixerate/editor 0.38.0 → 0.40.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.
@@ -36,7 +36,9 @@ var _chunkQKLRAA4Tcjs = require('../chunk-QKLRAA4T.cjs');
36
36
 
37
37
 
38
38
 
39
- var _chunk7EDLVBOOcjs = require('../chunk-7EDLVBOO.cjs');
39
+
40
+
41
+ var _chunkVNYRI265cjs = require('../chunk-VNYRI265.cjs');
40
42
 
41
43
  // src/vector/types.ts
42
44
  var VECTOR_CORNER_KINDS = ["round", "inverted", "chamfer"];
@@ -80,7 +82,7 @@ var Side = class {
80
82
  constructor(a, h1, h2, b) {
81
83
  this.straight = onAnchor(a, h1) && onAnchor(b, h2);
82
84
  this.curve = this.straight ? [pt(a), pt(b)] : [pt(a), pt(_nullishCoalesce(h1, () => ( a))), pt(_nullishCoalesce(h2, () => ( b))), pt(b)];
83
- this.length = this.straight ? Math.hypot(b.x - a.x, b.y - a.y) : _chunk7EDLVBOOcjs.segmentLength.call(void 0, this.curve);
85
+ this.length = this.straight ? Math.hypot(b.x - a.x, b.y - a.y) : _chunkVNYRI265cjs.segmentLength.call(void 0, this.curve);
84
86
  }
85
87
  /** Unit direction the side leaves the corner in (towards its first control point off the corner). */
86
88
  direction() {
@@ -98,10 +100,10 @@ var Side = class {
98
100
  const t2 = this.length > EPS ? Math.min(1, Math.max(0, s / this.length)) : 0;
99
101
  return { t: t2, p: add(vec(this.curve[0]), mul(u, s)), dir: u };
100
102
  }
101
- const t = _chunk7EDLVBOOcjs.segmentParamAtLength.call(void 0, this.curve, s, this.length);
102
- const d = vec(_chunk7EDLVBOOcjs.segmentDerivative.call(void 0, this.curve, t));
103
+ const t = _chunkVNYRI265cjs.segmentParamAtLength.call(void 0, this.curve, s, this.length);
104
+ const d = vec(_chunkVNYRI265cjs.segmentDerivative.call(void 0, this.curve, t));
103
105
  const l = len(d);
104
- return { t, p: vec(_chunk7EDLVBOOcjs.segmentPoint.call(void 0, this.curve, t)), dir: l > EPS ? mul(d, 1 / l) : u };
106
+ return { t, p: vec(_chunkVNYRI265cjs.segmentPoint.call(void 0, this.curve, t)), dir: l > EPS ? mul(d, 1 / l) : u };
105
107
  }
106
108
  };
107
109
  function cornerOf(sp, s, i) {
@@ -174,7 +176,7 @@ function cutCorner(c, radius, kind) {
174
176
  return { tu: a.t, tv: b.t, start: a.p, out: add(a.p, mul(d1, k)), in: add(b.p, mul(d2, k)), end: b.p };
175
177
  }
176
178
  var radiusOf = (a) => {
177
- const r = _optionalChain([a, 'access', _3 => _3.corner, 'optionalAccess', _4 => _4.radius]);
179
+ const r = _optionalChain([a, 'access', _2 => _2.corner, 'optionalAccess', _3 => _3.radius]);
178
180
  return typeof r === "number" && r > 0 && Number.isFinite(r) ? r : 0;
179
181
  };
180
182
  function hasVectorCorners(subpaths) {
@@ -188,7 +190,7 @@ function expandVectorCornersOf(sp, subpath = 0) {
188
190
  const c = cornerOf(sp, subpath, i);
189
191
  if (!c) return null;
190
192
  const fitted = fitCornerRadius(c, r);
191
- return fitted > EPS ? cutCorner(c, fitted, _nullishCoalesce(_optionalChain([a, 'access', _5 => _5.corner, 'optionalAccess', _6 => _6.kind]), () => ( "round"))) : null;
193
+ return fitted > EPS ? cutCorner(c, fitted, _nullishCoalesce(_optionalChain([a, 'access', _4 => _4.corner, 'optionalAccess', _5 => _5.kind]), () => ( "round"))) : null;
192
194
  });
193
195
  if (!cuts.some(Boolean)) return { subpath: { closed: sp.closed, anchors: sp.anchors.map(plain) }, sources: sp.anchors.map((_, i) => i) };
194
196
  const trimmed = sp.anchors.map((a, j) => {
@@ -202,7 +204,7 @@ function expandVectorCornersOf(sp, subpath = 0) {
202
204
  const s0 = from ? from.tv : 0;
203
205
  const s1 = to ? 1 - to.tu : 1;
204
206
  const seg = [pt(a), pt(_nullishCoalesce(a.out, () => ( a))), pt(_nullishCoalesce(b.in, () => ( b))), pt(b)];
205
- const part = _chunk7EDLVBOOcjs.subSegment.call(void 0, seg, s0, Math.max(s0, s1));
207
+ const part = _chunkVNYRI265cjs.subSegment.call(void 0, seg, s0, Math.max(s0, s1));
206
208
  return { out: vec(part[1]), in: vec(part[2]) };
207
209
  });
208
210
  const anchors = [];
@@ -256,7 +258,7 @@ function expandVectorCorners(subpaths) {
256
258
  function setAnchorCorner(a, radius, kind) {
257
259
  const { corner, ...rest } = a;
258
260
  const r = Number.isFinite(radius) ? Math.max(0, radius) : 0;
259
- const k = _nullishCoalesce(kind, () => ( _optionalChain([corner, 'optionalAccess', _7 => _7.kind])));
261
+ const k = _nullishCoalesce(kind, () => ( _optionalChain([corner, 'optionalAccess', _6 => _6.kind])));
260
262
  if (r <= 0 && !kind) return rest;
261
263
  return { ...rest, corner: { radius: r, ...k && k !== "round" ? { kind: k } : {} } };
262
264
  }
@@ -305,7 +307,7 @@ function sanitizeWidthProfile(points) {
305
307
  return out.map((p, i) => [p, i]).sort((a, b) => a[0].t - b[0].t || a[1] - b[1]).map(([p]) => p);
306
308
  }
307
309
  function isUniformWidthProfile(points) {
308
- return !_optionalChain([points, 'optionalAccess', _8 => _8.length]) || points.every((p) => p.left === 1 && p.right === 1);
310
+ return !_optionalChain([points, 'optionalAccess', _7 => _7.length]) || points.every((p) => p.left === 1 && p.right === 1);
309
311
  }
310
312
  function secant(pts, i, k) {
311
313
  const a = pts[i];
@@ -338,11 +340,11 @@ function interp(pts, t, k) {
338
340
  return Math.max(0, w);
339
341
  }
340
342
  function widthProfileAt(points, t) {
341
- if (!_optionalChain([points, 'optionalAccess', _9 => _9.length])) return { left: 1, right: 1 };
343
+ if (!_optionalChain([points, 'optionalAccess', _8 => _8.length])) return { left: 1, right: 1 };
342
344
  return { left: interp(points, t, "left"), right: interp(points, t, "right") };
343
345
  }
344
346
  function widthProfileMaxScale(points) {
345
- if (!_optionalChain([points, 'optionalAccess', _10 => _10.length])) return 1;
347
+ if (!_optionalChain([points, 'optionalAccess', _9 => _9.length])) return 1;
346
348
  return points.reduce((m, p) => Math.max(m, p.left, p.right), 0);
347
349
  }
348
350
  function widthProfilePreset(id) {
@@ -352,7 +354,7 @@ function widthProfilePreset(id) {
352
354
  function widthProfilePresetId(points) {
353
355
  if (isUniformWidthProfile(points)) return "uniform";
354
356
  const same = (a, b) => a.length === b.length && a.every((p, i) => Math.abs(p.t - b[i].t) < 1e-9 && Math.abs(p.left - b[i].left) < 1e-9 && Math.abs(p.right - b[i].right) < 1e-9);
355
- return _nullishCoalesce(_optionalChain([VECTOR_WIDTH_PRESETS, 'access', _11 => _11.find, 'call', _12 => _12((p) => p.points.length && same(points, p.points)), 'optionalAccess', _13 => _13.id]), () => ( "custom"));
357
+ return _nullishCoalesce(_optionalChain([VECTOR_WIDTH_PRESETS, 'access', _10 => _10.find, 'call', _11 => _11((p) => p.points.length && same(points, p.points)), 'optionalAccess', _12 => _12.id]), () => ( "custom"));
356
358
  }
357
359
  function reverseWidthProfile(points) {
358
360
  return points.map((p) => ({ t: 1 - p.t, left: p.right, right: p.left })).reverse();
@@ -361,12 +363,12 @@ function mirrorWidthProfile(points) {
361
363
  return points.map((p) => ({ t: p.t, left: p.right, right: p.left }));
362
364
  }
363
365
  function editable(points) {
364
- return _optionalChain([points, 'optionalAccess', _14 => _14.length]) ? points.map((p) => ({ ...p })) : [sym(0, 1), sym(1, 1)];
366
+ return _optionalChain([points, 'optionalAccess', _13 => _13.length]) ? points.map((p) => ({ ...p })) : [sym(0, 1), sym(1, 1)];
365
367
  }
366
368
  function insertWidthPoint(points, t, sides2 = {}, options = {}) {
367
369
  const pts = editable(points);
368
370
  const tt = Math.min(Math.max(Number.isFinite(t) ? t : 0, 0), 1);
369
- const here = widthProfileAt(_optionalChain([points, 'optionalAccess', _15 => _15.length]) ? points : null, tt);
371
+ const here = widthProfileAt(_optionalChain([points, 'optionalAccess', _14 => _14.length]) ? points : null, tt);
370
372
  const p = {
371
373
  t: tt,
372
374
  left: _nullishCoalesce(clean(sides2.left, VECTOR_WIDTH_PROFILE_MAX_SCALE), () => ( here.left)),
@@ -437,7 +439,7 @@ function generateWidthProfile(options) {
437
439
  const n = Math.max(8, Math.min(VECTOR_WIDTH_PROFILE_MAX_POINTS * 4, _nullishCoalesce(options.samples, () => ( Math.ceil(32 + cycles * 16)))));
438
440
  const raw = [];
439
441
  for (let i = 0; i <= n; i++) raw.push([i / n, taperWaveFactor(i / n, options.taper, options.wave)]);
440
- const ts = [_optionalChain([options, 'access', _16 => _16.taper, 'optionalAccess', _17 => _17.startLength]), _optionalChain([options, 'access', _18 => _18.taper, 'optionalAccess', _19 => _19.endLength]) !== void 0 ? 1 - options.taper.endLength : void 0];
442
+ const ts = [_optionalChain([options, 'access', _15 => _15.taper, 'optionalAccess', _16 => _16.startLength]), _optionalChain([options, 'access', _17 => _17.taper, 'optionalAccess', _18 => _18.endLength]) !== void 0 ? 1 - options.taper.endLength : void 0];
441
443
  for (const t of ts) if (typeof t === "number" && t > 0 && t < 1) raw.push([t, taperWaveFactor(t, options.taper, options.wave)]);
442
444
  raw.sort((a, b) => a[0] - b[0]);
443
445
  const kept = simplifySamples(raw, _nullishCoalesce(options.tolerance, () => ( 0.01)), VECTOR_WIDTH_PROFILE_MAX_POINTS);
@@ -505,7 +507,7 @@ function flatten(segments, closed, tol) {
505
507
  corner.push(isCorner);
506
508
  };
507
509
  for (const seg of segments) {
508
- const flat = _chunk7EDLVBOOcjs.flattenChain.call(void 0, [seg], tol);
510
+ const flat = _chunkVNYRI265cjs.flattenChain.call(void 0, [seg], tol);
509
511
  if (dist(seg[0], seg[seg.length - 1]) <= 1e-12 && flat.length > 1) flat.push(seg[seg.length - 1]);
510
512
  flat.forEach((p, i) => push(p, i === 0 || i === flat.length - 1));
511
513
  }
@@ -595,7 +597,7 @@ function variableStrokeRings(path, options) {
595
597
  const last = out[out.length - 1];
596
598
  if (!last || dist(last, p) > 1e-12) out.push(p);
597
599
  };
598
- for (const contour of _chunk7EDLVBOOcjs.pathToContours.call(void 0, path)) {
600
+ for (const contour of _chunkVNYRI265cjs.pathToContours.call(void 0, path)) {
599
601
  if (!contour.segments.length) continue;
600
602
  const poly = flatten(contour.segments, contour.closed, Math.min(tol, half) / 4);
601
603
  if (!poly) continue;
@@ -604,7 +606,7 @@ function variableStrokeRings(path, options) {
604
606
  const w = widthProfileAt(options.profile, s / len2);
605
607
  return [w.left * half, w.right * half];
606
608
  };
607
- const intervals = _optionalChain([options, 'access', _20 => _20.dash, 'optionalAccess', _21 => _21.some, 'call', _22 => _22((v2) => v2 > 0)]) ? dashIntervals(options.dash, _nullishCoalesce(options.dashOffset, () => ( 0)), len2) : [[0, len2]];
609
+ const intervals = _optionalChain([options, 'access', _19 => _19.dash, 'optionalAccess', _20 => _20.some, 'call', _21 => _21((v2) => v2 > 0)]) ? dashIntervals(options.dash, _nullishCoalesce(options.dashOffset, () => ( 0)), len2) : [[0, len2]];
608
610
  const whole = intervals.length === 1 && intervals[0][0] <= 0 && intervals[0][1] >= len2;
609
611
  for (const [a, b] of intervals) {
610
612
  const [pa, ia] = at(poly, a);
@@ -721,7 +723,7 @@ function variableStrokeOutline(path, options) {
721
723
  const tol = options.tolerance && options.tolerance > 0 ? options.tolerance : 0.05;
722
724
  let polygons;
723
725
  try {
724
- polygons = _chunk7EDLVBOOcjs.polygonBoolean.call(void 0, "union", [{ rings: pieces, fillRule: "nonzero" }]);
726
+ polygons = _chunkVNYRI265cjs.polygonBoolean.call(void 0, "union", [{ rings: pieces, fillRule: "nonzero" }]);
725
727
  } catch (e2) {
726
728
  return [];
727
729
  }
@@ -729,11 +731,11 @@ function variableStrokeOutline(path, options) {
729
731
  for (const polygon2 of polygons) {
730
732
  for (const ring of polygon2) {
731
733
  if (ring.length < 3) continue;
732
- const cubics = _chunk7EDLVBOOcjs.fitCubics.call(void 0, ring, tol, { closed: true, cornerAngleDeg: 25 });
734
+ const cubics = _chunkVNYRI265cjs.fitCubics.call(void 0, ring, tol, { closed: true, cornerAngleDeg: 25 });
733
735
  if (!cubics.length) continue;
734
736
  out.push({ type: "M", x: cubics[0].p0[0], y: cubics[0].p0[1] });
735
737
  cubics.forEach((c, i) => {
736
- if (_chunk7EDLVBOOcjs.isStraightCubic.call(void 0, c, tol * 0.02)) {
738
+ if (_chunkVNYRI265cjs.isStraightCubic.call(void 0, c, tol * 0.02)) {
737
739
  if (i < cubics.length - 1) out.push({ type: "L", x: c.p3[0], y: c.p3[1] });
738
740
  } else out.push({ type: "C", x1: c.p1[0], y1: c.p1[1], x2: c.p2[0], y2: c.p2[1], x: c.p3[0], y: c.p3[1] });
739
741
  });
@@ -744,17 +746,17 @@ function variableStrokeOutline(path, options) {
744
746
  }
745
747
  function nearestStrokePosition(path, p, only) {
746
748
  let best = null;
747
- _chunk7EDLVBOOcjs.pathToContours.call(void 0, path).forEach((c, ci) => {
749
+ _chunkVNYRI265cjs.pathToContours.call(void 0, path).forEach((c, ci) => {
748
750
  if (!c.segments.length || only !== void 0 && ci !== only) return;
749
- const lengths = c.segments.map((s) => _chunk7EDLVBOOcjs.segmentLength.call(void 0, s));
751
+ const lengths = c.segments.map((s) => _chunkVNYRI265cjs.segmentLength.call(void 0, s));
750
752
  const total = lengths.reduce((a, b) => a + b, 0);
751
753
  if (!(total > 0)) return;
752
754
  let acc = 0;
753
755
  c.segments.forEach((seg, si) => {
754
- const near2 = _chunk7EDLVBOOcjs.segmentNearest.call(void 0, seg, p);
756
+ const near2 = _chunkVNYRI265cjs.segmentNearest.call(void 0, seg, p);
755
757
  if (!best || near2.distance < best.distance) {
756
- const s = acc + _chunk7EDLVBOOcjs.segmentLength.call(void 0, seg, 0, near2.t);
757
- const tan = _chunk7EDLVBOOcjs.segmentTangent.call(void 0, seg, near2.t);
758
+ const s = acc + _chunkVNYRI265cjs.segmentLength.call(void 0, seg, 0, near2.t);
759
+ const tan = _chunkVNYRI265cjs.segmentTangent.call(void 0, seg, near2.t);
758
760
  const pt2 = seg.length === 2 ? lerp(seg[0], seg[1], near2.t) : evalSeg(seg, near2.t);
759
761
  const n = leftNormal([tan[0], tan[1]]);
760
762
  const side = (p[0] - pt2[0]) * n[0] + (p[1] - pt2[1]) * n[1] >= 0 ? "left" : "right";
@@ -771,9 +773,9 @@ function evalSeg(seg, t) {
771
773
  return pts[0];
772
774
  }
773
775
  function strokePositionPoint(path, index, t) {
774
- const c = _chunk7EDLVBOOcjs.pathToContours.call(void 0, path)[index];
775
- if (!_optionalChain([c, 'optionalAccess', _23 => _23.segments, 'access', _24 => _24.length])) return null;
776
- const lengths = c.segments.map((s) => _chunk7EDLVBOOcjs.segmentLength.call(void 0, s));
776
+ const c = _chunkVNYRI265cjs.pathToContours.call(void 0, path)[index];
777
+ if (!_optionalChain([c, 'optionalAccess', _22 => _22.segments, 'access', _23 => _23.length])) return null;
778
+ const lengths = c.segments.map((s) => _chunkVNYRI265cjs.segmentLength.call(void 0, s));
777
779
  const total = lengths.reduce((a, b) => a + b, 0);
778
780
  let want = Math.min(Math.max(t, 0), 1) * total;
779
781
  for (let i = 0; i < c.segments.length; i++) {
@@ -784,11 +786,11 @@ function strokePositionPoint(path, index, t) {
784
786
  const target = Math.min(want, lengths[i]);
785
787
  for (let k = 0; k < 40; k++) {
786
788
  const mid = (lo + hi) / 2;
787
- if (_chunk7EDLVBOOcjs.segmentLength.call(void 0, seg, 0, mid) < target) lo = mid;
789
+ if (_chunkVNYRI265cjs.segmentLength.call(void 0, seg, 0, mid) < target) lo = mid;
788
790
  else hi = mid;
789
791
  }
790
792
  const u = (lo + hi) / 2;
791
- const tan = _chunk7EDLVBOOcjs.segmentTangent.call(void 0, seg, u);
793
+ const tan = _chunkVNYRI265cjs.segmentTangent.call(void 0, seg, u);
792
794
  return { point: evalSeg(seg, u), tangent: [tan[0], tan[1]] };
793
795
  }
794
796
  want -= lengths[i];
@@ -800,7 +802,7 @@ function strokePositionPoint(path, index, t) {
800
802
  var KAPPA = 0.5522847498307936;
801
803
  var NOTCH = 0.7;
802
804
  function isClosedBezierShape(path) {
803
- const contours = _chunk7EDLVBOOcjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
805
+ const contours = _chunkVNYRI265cjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
804
806
  return contours.length > 0 && contours.every((c) => c.closed);
805
807
  }
806
808
  function effectiveStrokeAlign(style, closedShape) {
@@ -808,7 +810,7 @@ function effectiveStrokeAlign(style, closedShape) {
808
810
  return closedShape ? a : "center";
809
811
  }
810
812
  function strokeWidthProfileOf(style) {
811
- if (!_optionalChain([style, 'access', _25 => _25.strokeWidthProfile, 'optionalAccess', _26 => _26.length])) return null;
813
+ if (!_optionalChain([style, 'access', _24 => _24.strokeWidthProfile, 'optionalAccess', _25 => _25.length])) return null;
812
814
  const p = sanitizeWidthProfile(style.strokeWidthProfile);
813
815
  return isUniformWidthProfile(p) ? null : p;
814
816
  }
@@ -817,17 +819,21 @@ function hasStrokeExtras(style) {
817
819
  }
818
820
  function vectorStrokeLineArea(parts, style, tolerance) {
819
821
  const profile = strokeWidthProfileOf(style);
820
- const dash = _optionalChain([style, 'access', _27 => _27.strokeDasharray, 'optionalAccess', _28 => _28.length]) ? { dash: style.strokeDasharray } : {};
822
+ const dash = _optionalChain([style, 'access', _26 => _26.strokeDasharray, 'optionalAccess', _27 => _27.length]) ? { dash: style.strokeDasharray } : {};
821
823
  const join = _nullishCoalesce(style.strokeLinejoin, () => ( "miter"));
822
824
  const cap = _nullishCoalesce(style.strokeLinecap, () => ( "butt"));
823
825
  if (profile) return variableStrokeOutline(parts.line, { width: parts.drawWidth, profile, join, cap, ...dash, ...tolerance ? { tolerance } : {} });
824
- return _chunk7EDLVBOOcjs.outlineStroke.call(void 0, parts.line, { width: parts.drawWidth, join, cap, ...dash, ...tolerance ? { tolerance } : {} });
826
+ return _chunkVNYRI265cjs.outlineStroke.call(void 0, parts.line, { width: parts.drawWidth, join, cap, ...dash, ...tolerance ? { tolerance } : {} });
825
827
  }
826
828
  var capExtent = (style, width) => style.strokeLinecap === "round" || style.strokeLinecap === "square" ? width / 2 : 0;
827
829
  function arrowheadWeight(style, end) {
828
830
  const scale = end === "start" ? style.startArrowScale : style.endArrowScale;
829
831
  const s = typeof scale === "number" && scale > 0 && Number.isFinite(scale) ? scale : 1;
830
- return Math.max(0.25, (_nullishCoalesce(style.strokeWidth, () => ( 1))) * s);
832
+ const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
833
+ const profile = strokeWidthProfileOf(style);
834
+ const at2 = profile ? widthProfileAt(profile, end === "start" ? 0 : 1) : null;
835
+ const here = at2 ? Math.max(width, width * (at2.left + at2.right) / 2) : width;
836
+ return Math.max(0.25, here * s);
831
837
  }
832
838
  function arrowheadInset(kind, hw, cap) {
833
839
  const l = 4 * hw;
@@ -898,16 +904,16 @@ function arrowheadPath(kind, tip, dir, hw) {
898
904
  }
899
905
  }
900
906
  var toVec = (p) => ({ x: p[0], y: p[1] });
901
- var segLen = (s) => _chunk7EDLVBOOcjs.segmentLength.call(void 0, s);
907
+ var segLen = (s) => _chunkVNYRI265cjs.segmentLength.call(void 0, s);
902
908
  function endOf(segs, atStart) {
903
909
  const long = (s2) => segLen(s2) > 1e-9;
904
910
  if (atStart) {
905
911
  const s2 = _nullishCoalesce(segs.find(long), () => ( segs[0]));
906
- const t2 = _chunk7EDLVBOOcjs.segmentTangent.call(void 0, s2, 0);
912
+ const t2 = _chunkVNYRI265cjs.segmentTangent.call(void 0, s2, 0);
907
913
  return { p: toVec(segs[0][0]), out: v(-t2[0], -t2[1]) };
908
914
  }
909
915
  const s = _nullishCoalesce([...segs].reverse().find(long), () => ( segs[segs.length - 1]));
910
- const t = _chunk7EDLVBOOcjs.segmentTangent.call(void 0, s, 1);
916
+ const t = _chunkVNYRI265cjs.segmentTangent.call(void 0, s, 1);
911
917
  const last = segs[segs.length - 1];
912
918
  return { p: toVec(last[last.length - 1]), out: v(t[0], t[1]) };
913
919
  }
@@ -918,7 +924,7 @@ function pointAlong(segs, fromStart, dist4) {
918
924
  const l = segLen(s);
919
925
  if (acc + l >= dist4) {
920
926
  const want = fromStart ? dist4 - acc : l - (dist4 - acc);
921
- const pts = _chunk7EDLVBOOcjs.trimSegments.call(void 0, [s], 0, want);
927
+ const pts = _chunkVNYRI265cjs.trimSegments.call(void 0, [s], 0, want);
922
928
  const p = pts.length ? pts[0][pts[0].length - 1] : s[0];
923
929
  return toVec(p);
924
930
  }
@@ -928,7 +934,7 @@ function pointAlong(segs, fromStart, dist4) {
928
934
  }
929
935
  function vectorStrokeParts(path, style) {
930
936
  const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
931
- const contours = _chunk7EDLVBOOcjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
937
+ const contours = _chunkVNYRI265cjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
932
938
  const closedShape = contours.length > 0 && contours.every((c) => c.closed);
933
939
  const align = effectiveStrokeAlign(style, closedShape);
934
940
  const drawWidth = align === "center" ? width : width * 2;
@@ -968,26 +974,26 @@ function vectorStrokeParts(path, style) {
968
974
  if (!s0 && !s1) return c;
969
975
  const lengths = c.segments.map(segLen);
970
976
  const total = lengths.reduce((a, b) => a + b, 0);
971
- return { closed: false, segments: total - s1 > s0 ? _chunk7EDLVBOOcjs.trimSegments.call(void 0, c.segments, s0, total - s1, lengths) : [] };
977
+ return { closed: false, segments: total - s1 > s0 ? _chunkVNYRI265cjs.trimSegments.call(void 0, c.segments, s0, total - s1, lengths) : [] };
972
978
  });
973
- return { align, line: _chunk7EDLVBOOcjs.contoursToPath.call(void 0, trimmed), drawWidth, heads };
979
+ return { align, line: _chunkVNYRI265cjs.contoursToPath.call(void 0, trimmed), drawWidth, heads };
974
980
  }
975
981
  function vectorStrokeOutlinePath(path, style) {
976
982
  const parts = vectorStrokeParts(path, style);
977
983
  const stroke = vectorStrokeLineArea(parts, style);
978
984
  const fill = { path, fillRule: _nullishCoalesce(style.fillRule, () => ( "nonzero")) };
979
985
  let area = stroke;
980
- if (parts.align === "inside") area = _chunk7EDLVBOOcjs.pathBoolean.call(void 0, "intersection", [stroke, fill]).path;
981
- else if (parts.align === "outside") area = _chunk7EDLVBOOcjs.pathBoolean.call(void 0, "difference", [stroke, fill]).path;
986
+ if (parts.align === "inside") area = _chunkVNYRI265cjs.pathBoolean.call(void 0, "intersection", [stroke, fill]).path;
987
+ else if (parts.align === "outside") area = _chunkVNYRI265cjs.pathBoolean.call(void 0, "difference", [stroke, fill]).path;
982
988
  if (!parts.heads.length) return area;
983
- return _chunk7EDLVBOOcjs.pathBoolean.call(void 0, "union", [area, ...parts.heads.map((h) => h.path)]).path;
989
+ return _chunkVNYRI265cjs.pathBoolean.call(void 0, "union", [area, ...parts.heads.map((h) => h.path)]).path;
984
990
  }
985
991
  function hitTestVectorStroke(path, p, style, tolerance = 0) {
986
992
  const parts = vectorStrokeParts(path, style);
987
993
  const point = [p.x, p.y];
988
994
  const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
989
995
  for (const h of parts.heads) {
990
- if (_chunk7EDLVBOOcjs.pointInBezierPath.call(void 0, h.path, point) || tolerance > 0 && _chunk7EDLVBOOcjs.distanceToBezierPath.call(void 0, h.path, point) <= tolerance) return true;
996
+ if (_chunkVNYRI265cjs.pointInBezierPath.call(void 0, h.path, point) || tolerance > 0 && _chunkVNYRI265cjs.distanceToBezierPath.call(void 0, h.path, point) <= tolerance) return true;
991
997
  }
992
998
  if (!parts.line.length) return false;
993
999
  const profile = strokeWidthProfileOf(style);
@@ -1005,13 +1011,13 @@ function hitTestVectorStroke(path, p, style, tolerance = 0) {
1005
1011
  reach = Math.max(reach, width / 2 * (w.left + w.right) / 2);
1006
1012
  }
1007
1013
  } else {
1008
- d = _chunk7EDLVBOOcjs.distanceToBezierPath.call(void 0, parts.line, point);
1014
+ d = _chunkVNYRI265cjs.distanceToBezierPath.call(void 0, parts.line, point);
1009
1015
  if (parts.align !== "center") reach = width;
1010
1016
  }
1011
1017
  if (parts.align === "center") return d <= reach + tolerance;
1012
1018
  if (d > reach + tolerance) return false;
1013
1019
  if (d <= tolerance) return true;
1014
- const inside = _chunk7EDLVBOOcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(style.fillRule, () => ( "nonzero")));
1020
+ const inside = _chunkVNYRI265cjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(style.fillRule, () => ( "nonzero")));
1015
1021
  return parts.align === "inside" ? inside : !inside;
1016
1022
  }
1017
1023
  function vectorStrokeReach(style) {
@@ -1087,7 +1093,7 @@ function vectorNodeToBezier(node) {
1087
1093
  }
1088
1094
  function vectorPathData(input, precision = 3) {
1089
1095
  const subpaths = Array.isArray(input) ? input : input.subpaths;
1090
- return _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, subpathsToBezier(subpaths), { precision });
1096
+ return _chunkVNYRI265cjs.bezierPathToSvg.call(void 0, subpathsToBezier(subpaths), { precision });
1091
1097
  }
1092
1098
  function inferAnchorType(a) {
1093
1099
  if (isOnAnchor(a, a.in) || isOnAnchor(a, a.out)) return "corner";
@@ -1103,7 +1109,7 @@ function inferAnchorType(a) {
1103
1109
  return Math.abs(li - lo) <= 1e-3 * Math.max(li, lo) ? "symmetric" : "smooth";
1104
1110
  }
1105
1111
  function bezierToSubpaths(input) {
1106
- const path = _chunk7EDLVBOOcjs.toBezierPath.call(void 0, input);
1112
+ const path = _chunkVNYRI265cjs.toBezierPath.call(void 0, input);
1107
1113
  const out = [];
1108
1114
  let current = null;
1109
1115
  const last = () => current.anchors[current.anchors.length - 1];
@@ -1200,14 +1206,14 @@ function nodeMatrix(node) {
1200
1206
  function vectorPathWorldBezier(node, parent = IDENTITY_MATRIX) {
1201
1207
  const m = multiplyMatrices(nodeMatrix(node), parent);
1202
1208
  const path = subpathsToBezier(node.subpaths);
1203
- return isIdentityMatrix(m) ? path : _chunk7EDLVBOOcjs.transformBezierPath.call(void 0, path, m);
1209
+ return isIdentityMatrix(m) ? path : _chunkVNYRI265cjs.transformBezierPath.call(void 0, path, m);
1204
1210
  }
1205
1211
  function vectorNodeBounds(node, parent = IDENTITY_MATRIX) {
1206
1212
  if (node.type === "group") {
1207
1213
  const m = multiplyMatrices(nodeMatrix(node), parent);
1208
1214
  return unionBounds(node.children.map((c) => vectorNodeBounds(c, m)));
1209
1215
  }
1210
- const b = _chunk7EDLVBOOcjs.bezierPathBounds.call(void 0, vectorPathWorldBezier(node, parent));
1216
+ const b = _chunkVNYRI265cjs.bezierPathBounds.call(void 0, vectorPathWorldBezier(node, parent));
1211
1217
  if (!b) return null;
1212
1218
  return { x: b.minX, y: b.minY, width: b.maxX - b.minX, height: b.maxY - b.minY };
1213
1219
  }
@@ -1241,9 +1247,9 @@ function hitTestVectorNode(node, p, tolerance = 0, parent = IDENTITY_MATRIX) {
1241
1247
  const path = vectorPathWorldBezier(node, parent);
1242
1248
  if (path.length === 0) return false;
1243
1249
  const point = [p.x, p.y];
1244
- if (hasPaint(node.fill) && _chunk7EDLVBOOcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(node.fillRule, () => ( "nonzero")))) return true;
1250
+ if (hasPaint(node.fill) && _chunkVNYRI265cjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(node.fillRule, () => ( "nonzero")))) return true;
1245
1251
  if (hasPaint(node.stroke)) return hitTestVectorStroke(path, p, node, tolerance);
1246
- return _chunk7EDLVBOOcjs.distanceToBezierPath.call(void 0, path, point) <= tolerance;
1252
+ return _chunkVNYRI265cjs.distanceToBezierPath.call(void 0, path, point) <= tolerance;
1247
1253
  }
1248
1254
  function vectorStrokeOutline(node, parent = IDENTITY_MATRIX) {
1249
1255
  if (!hasPaint(node.stroke) || !((_nullishCoalesce(node.strokeWidth, () => ( 1))) > 0)) return [];
@@ -1263,9 +1269,9 @@ function nearestSegment(node, p) {
1263
1269
  const count = subpathSegmentCount(sp);
1264
1270
  for (let i = 0; i < count; i++) {
1265
1271
  const seg = subpathSegment(sp, i);
1266
- const hit = _chunk7EDLVBOOcjs.segmentNearest.call(void 0, seg, [p.x, p.y]);
1272
+ const hit = _chunkVNYRI265cjs.segmentNearest.call(void 0, seg, [p.x, p.y]);
1267
1273
  if (!best || hit.distance < best.distance) {
1268
- const pt2 = _chunk7EDLVBOOcjs.segmentPoint.call(void 0, seg, hit.t);
1274
+ const pt2 = _chunkVNYRI265cjs.segmentPoint.call(void 0, seg, hit.t);
1269
1275
  best = { subpath: s, segment: i, t: hit.t, point: { x: pt2[0], y: pt2[1] }, distance: hit.distance };
1270
1276
  }
1271
1277
  }
@@ -1325,7 +1331,7 @@ function bakeIntoGeometry(node, m) {
1325
1331
  if (isIdentityMatrix(m)) return node;
1326
1332
  if (node.type === "group") return { ...node, children: node.children.map((c) => transformVectorNode(c, m)) };
1327
1333
  const path = node;
1328
- const mirrored = m[0] * m[3] - m[1] * m[2] < 0 && _optionalChain([path, 'access', _29 => _29.strokeWidthProfile, 'optionalAccess', _30 => _30.length]);
1334
+ const mirrored = m[0] * m[3] - m[1] * m[2] < 0 && _optionalChain([path, 'access', _28 => _28.strokeWidthProfile, 'optionalAccess', _29 => _29.length]);
1329
1335
  return {
1330
1336
  ...path,
1331
1337
  ...mirrored ? { strokeWidthProfile: mirrorWidthProfile(path.strokeWidthProfile) } : {},
@@ -1439,7 +1445,7 @@ function insertAnchor(sp, segment, t) {
1439
1445
  const ia = segment;
1440
1446
  const ib = (segment + 1) % n;
1441
1447
  const seg = subpathSegment(sp, segment);
1442
- const [left, right] = _chunk7EDLVBOOcjs.splitSegment.call(void 0, seg, t);
1448
+ const [left, right] = _chunkVNYRI265cjs.splitSegment.call(void 0, seg, t);
1443
1449
  const mid = left[left.length - 1];
1444
1450
  const anchor = { x: mid[0], y: mid[1] };
1445
1451
  if (seg.length === 4) {
@@ -1535,7 +1541,7 @@ function vectorCornersAttribute(subpaths, precision = 3) {
1535
1541
  subpaths.forEach(
1536
1542
  (sp, s) => sp.anchors.forEach((a, i) => {
1537
1543
  if (!a.corner || !(a.corner.radius > 0)) return;
1538
- out.push(`${s}.${i}:${_chunk7EDLVBOOcjs.formatNumber.call(void 0, a.corner.radius, precision)}${a.corner.kind && a.corner.kind !== "round" ? `:${a.corner.kind}` : ""}`);
1544
+ out.push(`${s}.${i}:${_chunkVNYRI265cjs.formatNumber.call(void 0, a.corner.radius, precision)}${a.corner.kind && a.corner.kind !== "round" ? `:${a.corner.kind}` : ""}`);
1539
1545
  })
1540
1546
  );
1541
1547
  return out.join(" ");
@@ -1544,7 +1550,7 @@ function applyVectorCornersAttribute(subpaths, value) {
1544
1550
  for (const item of value.trim().split(/\s+/)) {
1545
1551
  const m = /^(\d+)\.(\d+):([\d.eE+-]+)(?::([a-z]+))?$/.exec(item);
1546
1552
  if (!m) continue;
1547
- const a = _optionalChain([subpaths, 'access', _31 => _31[+m[1]], 'optionalAccess', _32 => _32.anchors, 'access', _33 => _33[+m[2]]]);
1553
+ const a = _optionalChain([subpaths, 'access', _30 => _30[+m[1]], 'optionalAccess', _31 => _31.anchors, 'access', _32 => _32[+m[2]]]);
1548
1554
  const radius = parseFloat(m[3]);
1549
1555
  if (!a || !(radius > 0)) continue;
1550
1556
  const kind = VECTOR_CORNER_KINDS.includes(m[4]) ? m[4] : void 0;
@@ -1556,7 +1562,7 @@ var escapeAttr = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;")
1556
1562
  function vectorPaintToSvg(paint, gradientId) {
1557
1563
  if (!hasPaint(paint)) return { value: "none", gradient: null };
1558
1564
  if (typeof paint === "string") return { value: paint, gradient: null };
1559
- return { value: `url(#${gradientId})`, gradient: _chunk7EDLVBOOcjs.gradientToSvgDef.call(void 0, paint, gradientId) };
1565
+ return { value: `url(#${gradientId})`, gradient: _chunkVNYRI265cjs.gradientToSvgDef.call(void 0, paint, gradientId) };
1560
1566
  }
1561
1567
  var partsCache = /* @__PURE__ */ new WeakMap();
1562
1568
  function vectorStrokeSvgParts(node, clipId, precision = 3) {
@@ -1574,22 +1580,22 @@ function computeStrokeSvgParts(node, clipId, precision) {
1574
1580
  if (parts.align === "center" && !parts.heads.length && !profiled) return void 0;
1575
1581
  let clip = null;
1576
1582
  if (parts.align !== "center") {
1577
- const b = _chunk7EDLVBOOcjs.bezierPathBounds.call(void 0, path);
1583
+ const b = _chunkVNYRI265cjs.bezierPathBounds.call(void 0, path);
1578
1584
  const pad = vectorStrokeReach(node) + 1;
1579
1585
  const bounds = b ? { x: round2(b.minX - pad), y: round2(b.minY - pad), width: round2(b.maxX - b.minX + 2 * pad), height: round2(b.maxY - b.minY + 2 * pad) } : { x: 0, y: 0, width: 0, height: 0 };
1580
1586
  clip = { kind: parts.align === "inside" ? "clip" : "mask", id: clipId, bounds };
1581
1587
  }
1582
1588
  return {
1583
1589
  align: parts.align,
1584
- d: _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, parts.line, { precision }),
1590
+ d: _chunkVNYRI265cjs.bezierPathToSvg.call(void 0, parts.line, { precision }),
1585
1591
  width: parts.drawWidth,
1586
- ...profiled && (_nullishCoalesce(node.strokeWidth, () => ( 1))) > 0 ? { outline: _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, vectorStrokeLineArea(parts, node), { precision }) } : {},
1592
+ ...profiled && (_nullishCoalesce(node.strokeWidth, () => ( 1))) > 0 ? { outline: _chunkVNYRI265cjs.bezierPathToSvg.call(void 0, vectorStrokeLineArea(parts, node), { precision }) } : {},
1587
1593
  clip,
1588
- heads: parts.heads.map((h) => _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, h.path, { precision }))
1594
+ heads: parts.heads.map((h) => _chunkVNYRI265cjs.bezierPathToSvg.call(void 0, h.path, { precision }))
1589
1595
  };
1590
1596
  }
1591
1597
  function widthProfileToAttribute(points, precision = 4) {
1592
- return points.map((p) => [p.t, p.left, p.right].map((n) => _chunk7EDLVBOOcjs.formatNumber.call(void 0, n, precision)).join(",")).join(";");
1598
+ return points.map((p) => [p.t, p.left, p.right].map((n) => _chunkVNYRI265cjs.formatNumber.call(void 0, n, precision)).join(",")).join(";");
1593
1599
  }
1594
1600
  function widthProfileFromAttribute(value) {
1595
1601
  if (!value) return [];
@@ -1613,11 +1619,11 @@ function vectorDocumentToSvg(doc, options = {}) {
1613
1619
  const pretty = options.pretty !== false;
1614
1620
  const defs = [];
1615
1621
  const lines = [];
1616
- const num = (n) => _chunk7EDLVBOOcjs.formatNumber.call(void 0, n, precision);
1622
+ const num = (n) => _chunkVNYRI265cjs.formatNumber.call(void 0, n, precision);
1617
1623
  const indent = (depth) => pretty ? " ".repeat(depth) : "";
1618
1624
  const common = (node) => {
1619
1625
  let out2 = ` id="${escapeAttr(node.id)}"`;
1620
- if (node.transform) out2 += ` transform="matrix(${node.transform.map((n) => _chunk7EDLVBOOcjs.formatNumber.call(void 0, n, 6)).join(" ")})"`;
1626
+ if (node.transform) out2 += ` transform="matrix(${node.transform.map((n) => _chunkVNYRI265cjs.formatNumber.call(void 0, n, 6)).join(" ")})"`;
1621
1627
  if (node.opacity !== void 0 && node.opacity < 1) out2 += ` opacity="${num(node.opacity)}"`;
1622
1628
  if (node.visible === false) out2 += ` display="none"`;
1623
1629
  return out2;
@@ -1636,13 +1642,13 @@ function vectorDocumentToSvg(doc, options = {}) {
1636
1642
  if (stroke.gradient) defs.push(gradientMarkup(stroke.gradient));
1637
1643
  const d = vectorPathData(node, precision);
1638
1644
  const corners2 = vectorCornersAttribute(node.subpaths, precision);
1639
- const live = corners2 ? ` data-vector-d="${_chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, subpathsToBezier(node.subpaths, { corners: false }), { precision })}" data-vector-corners="${corners2}"` : "";
1645
+ const live = corners2 ? ` data-vector-d="${_chunkVNYRI265cjs.bezierPathToSvg.call(void 0, subpathsToBezier(node.subpaths, { corners: false }), { precision })}" data-vector-corners="${corners2}"` : "";
1640
1646
  const fillAttrs = ` fill="${escapeAttr(fill.value)}"${node.fillRule === "evenodd" ? ` fill-rule="evenodd"` : ""}`;
1641
1647
  const lineAttrs = (width) => {
1642
1648
  let out2 = ` stroke="${escapeAttr(stroke.value)}" stroke-width="${num(width)}"`;
1643
1649
  if (node.strokeLinecap && node.strokeLinecap !== "butt") out2 += ` stroke-linecap="${node.strokeLinecap}"`;
1644
1650
  if (node.strokeLinejoin && node.strokeLinejoin !== "miter") out2 += ` stroke-linejoin="${node.strokeLinejoin}"`;
1645
- if (_optionalChain([node, 'access', _34 => _34.strokeDasharray, 'optionalAccess', _35 => _35.length])) out2 += ` stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
1651
+ if (_optionalChain([node, 'access', _33 => _33.strokeDasharray, 'optionalAccess', _34 => _34.length])) out2 += ` stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
1646
1652
  return out2;
1647
1653
  };
1648
1654
  const parts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${prefix}clip-${node.id}`, precision) : void 0;
@@ -1662,12 +1668,12 @@ function vectorDocumentToSvg(doc, options = {}) {
1662
1668
  data += ` data-vector-width-profile="${widthProfileToAttribute(profile)}"`;
1663
1669
  if (node.strokeLinecap && node.strokeLinecap !== "butt") data += ` data-vector-stroke-linecap="${node.strokeLinecap}"`;
1664
1670
  if (node.strokeLinejoin && node.strokeLinejoin !== "miter") data += ` data-vector-stroke-linejoin="${node.strokeLinejoin}"`;
1665
- if (_optionalChain([node, 'access', _36 => _36.strokeDasharray, 'optionalAccess', _37 => _37.length])) data += ` data-vector-stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
1671
+ if (_optionalChain([node, 'access', _35 => _35.strokeDasharray, 'optionalAccess', _36 => _36.length])) data += ` data-vector-stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
1666
1672
  }
1667
1673
  lines.push(`${indent(depth)}<g${common(node)}${data}>`);
1668
1674
  lines.push(`${indent(depth + 1)}<path data-vector-part="fill" d="${d}"${fillAttrs}${live}/>`);
1669
1675
  if (options.strokes === "outline") {
1670
- const area = _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, vectorStrokeOutlinePath(subpathsToBezier(node.subpaths), node), { precision });
1676
+ const area = _chunkVNYRI265cjs.bezierPathToSvg.call(void 0, vectorStrokeOutlinePath(subpathsToBezier(node.subpaths), node), { precision });
1671
1677
  lines.push(`${indent(depth + 1)}<path data-vector-part="stroke-outline" d="${area}" fill="${escapeAttr(stroke.value)}"/>`);
1672
1678
  } else {
1673
1679
  let clip = "";
@@ -1831,7 +1837,7 @@ function resolveGradient(id, raw, seen = /* @__PURE__ */ new Set()) {
1831
1837
  seen.add(id);
1832
1838
  let stops = readStops(el);
1833
1839
  const href = _nullishCoalesce(el.attrs.href, () => ( el.attrs["xlink:href"]));
1834
- if (!stops.length && _optionalChain([href, 'optionalAccess', _38 => _38.startsWith, 'call', _39 => _39("#")])) stops = _nullishCoalesce(_optionalChain([resolveGradient, 'call', _40 => _40(href.slice(1), raw, seen), 'optionalAccess', _41 => _41.stops]), () => ( []));
1840
+ if (!stops.length && _optionalChain([href, 'optionalAccess', _37 => _37.startsWith, 'call', _38 => _38("#")])) stops = _nullishCoalesce(_optionalChain([resolveGradient, 'call', _39 => _39(href.slice(1), raw, seen), 'optionalAccess', _40 => _40.stops]), () => ( []));
1835
1841
  if (!stops.length) return null;
1836
1842
  if (el.name === "radialGradient") {
1837
1843
  return { type: "radial", cx: fraction(el.attrs.cx, 0.5), cy: fraction(el.attrs.cy, 0.5), r: fraction(el.attrs.r, 0.5), stops };
@@ -1935,7 +1941,7 @@ function parseVectorSvg(svg, options = {}) {
1935
1941
  const part = (name) => g.children.find((c) => c.name === "path" && c.attrs["data-vector-part"] === name);
1936
1942
  const fillEl = part("fill");
1937
1943
  const subpaths = fillEl ? shapeOf(fillEl) : null;
1938
- if (!fillEl || !_optionalChain([subpaths, 'optionalAccess', _42 => _42.length])) return null;
1944
+ if (!fillEl || !_optionalChain([subpaths, 'optionalAccess', _41 => _41.length])) return null;
1939
1945
  const a = g.attrs;
1940
1946
  const fs = readStyle(fillEl, style);
1941
1947
  const strokeEl = part("stroke");
@@ -1972,7 +1978,7 @@ function parseVectorSvg(svg, options = {}) {
1972
1978
  ...profile.length ? { strokeWidthProfile: profile } : {},
1973
1979
  ...keptCap ? { strokeLinecap: keptCap } : {},
1974
1980
  ...keptJoin ? { strokeLinejoin: keptJoin } : {},
1975
- ..._optionalChain([keptDash, 'optionalAccess', _43 => _43.length]) ? { strokeDasharray: keptDash } : {}
1981
+ ..._optionalChain([keptDash, 'optionalAccess', _42 => _42.length]) ? { strokeDasharray: keptDash } : {}
1976
1982
  };
1977
1983
  return placed(node, m);
1978
1984
  };
@@ -2026,7 +2032,7 @@ function parseVectorSvg(svg, options = {}) {
2026
2032
  // src/vector/snap.ts
2027
2033
  var VECTOR_CONSTRUCTION_ANGLES = [0, 45, 90, 135];
2028
2034
  function constrainToLeave(leave, p) {
2029
- if (!_optionalChain([leave, 'optionalAccess', _44 => _44.constrain])) return p;
2035
+ if (!_optionalChain([leave, 'optionalAccess', _43 => _43.constrain])) return p;
2030
2036
  const dx = p.x - leave.at.x;
2031
2037
  const dy = p.y - leave.at.y;
2032
2038
  switch (leave.constrain) {
@@ -2137,7 +2143,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2137
2143
  moving.set(r.nodeId, set);
2138
2144
  }
2139
2145
  const t = _VectorSnapTargets.collect(doc, new Set(moving.keys()), "segments", false);
2140
- t.pushPaths(doc, new Set(moving.keys()), (id, s, i) => !!_optionalChain([moving, 'access', _45 => _45.get, 'call', _46 => _46(id), 'optionalAccess', _47 => _47.has, 'call', _48 => _48(`${s}:${i}`)]), true);
2146
+ t.pushPaths(doc, new Set(moving.keys()), (id, s, i) => !!_optionalChain([moving, 'access', _44 => _44.get, 'call', _45 => _45(id), 'optionalAccess', _46 => _46.has, 'call', _47 => _47(`${s}:${i}`)]), true);
2141
2147
  return t.index();
2142
2148
  }
2143
2149
  /**
@@ -2255,7 +2261,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2255
2261
  }
2256
2262
  const b = vectorNodeBounds(n, parent);
2257
2263
  const f = nodeFrame(n, parent, b);
2258
- if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess', _49 => _49.centre]), () => ( null)));
2264
+ if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess', _48 => _48.centre]), () => ( null)));
2259
2265
  }
2260
2266
  };
2261
2267
  visit(doc.nodes, IDENTITY_MATRIX);
@@ -2300,7 +2306,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2300
2306
  lines: lines2,
2301
2307
  labels: [this.label(q, kind)]
2302
2308
  });
2303
- if (_optionalChain([leave, 'optionalAccess', _50 => _50.constrain])) {
2309
+ if (_optionalChain([leave, 'optionalAccess', _49 => _49.constrain])) {
2304
2310
  const q = constrainToLeave(leave, p);
2305
2311
  const hit = this.slide(leave.at, { x: q.x - leave.at.x, y: q.y - leave.at.y }, q, tol, true);
2306
2312
  return hit ? { point: hit.point, x: true, y: true, ...this.linedUp(hit.point, hit.from) } : none(q);
@@ -2316,7 +2322,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2316
2322
  }
2317
2323
  const on = this.onSegment(near2);
2318
2324
  if (on) return at2(on, "path");
2319
- if (_optionalChain([leave, 'optionalAccess', _51 => _51.angles, 'optionalAccess', _52 => _52.length])) {
2325
+ if (_optionalChain([leave, 'optionalAccess', _50 => _50.angles, 'optionalAccess', _51 => _51.length])) {
2320
2326
  const c = construction(leave, p, tol);
2321
2327
  if (c) {
2322
2328
  const hit = this.slide(leave.at, c.dir, c.point, tol, false);
@@ -2392,13 +2398,13 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2392
2398
  const pt2 = toPoint(p);
2393
2399
  for (const s of this.segments) {
2394
2400
  if (p.x < s.minX - tol || p.x > s.maxX + tol || p.y < s.minY - tol || p.y > s.maxY + tol) continue;
2395
- const hit = _chunk7EDLVBOOcjs.segmentNearest.call(void 0, s.seg, pt2);
2401
+ const hit = _chunkVNYRI265cjs.segmentNearest.call(void 0, s.seg, pt2);
2396
2402
  if (hit.distance <= tol) out.push({ seg: s.seg, t: hit.t, distance: hit.distance });
2397
2403
  }
2398
2404
  return out.sort((a, b) => a.distance - b.distance);
2399
2405
  }
2400
2406
  onSegment(near2) {
2401
- return near2.length ? toVec2(_chunk7EDLVBOOcjs.segmentPoint.call(void 0, near2[0].seg, near2[0].t)) : null;
2407
+ return near2.length ? toVec2(_chunkVNYRI265cjs.segmentPoint.call(void 0, near2[0].seg, near2[0].t)) : null;
2402
2408
  }
2403
2409
  /** Where two of the target segments near `p` cross, the crossing nearest `p` within `tol`. */
2404
2410
  crossing(near2, p, tol) {
@@ -2407,7 +2413,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2407
2413
  let bestD = Infinity;
2408
2414
  for (let i = 0; i < list.length; i++) {
2409
2415
  for (let j = i + 1; j < list.length; j++) {
2410
- for (const hit of _chunk7EDLVBOOcjs.segmentIntersections.call(void 0, list[i].seg, list[j].seg)) {
2416
+ for (const hit of _chunkVNYRI265cjs.segmentIntersections.call(void 0, list[i].seg, list[j].seg)) {
2411
2417
  const q = toVec2(hit.point);
2412
2418
  const d = distance(q, p);
2413
2419
  if (d <= tol && d < bestD) {
@@ -2446,7 +2452,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2446
2452
  * with nothing spaces the box evenly in its row or column. Returns the correction and guides.
2447
2453
  */
2448
2454
  snapRect(r, tol, options = {}) {
2449
- const axes = { x: _optionalChain([options, 'access', _53 => _53.axes, 'optionalAccess', _54 => _54.x]) !== false, y: _optionalChain([options, 'access', _55 => _55.axes, 'optionalAccess', _56 => _56.y]) !== false };
2455
+ const axes = { x: _optionalChain([options, 'access', _52 => _52.axes, 'optionalAccess', _53 => _53.x]) !== false, y: _optionalChain([options, 'access', _54 => _54.axes, 'optionalAccess', _55 => _55.y]) !== false };
2450
2456
  const nearest = (lines, values) => {
2451
2457
  let best = null;
2452
2458
  for (const v2 of values) for (const l of linesNear(lines, v2, tol)) if (!best || Math.abs(l.v - v2) < Math.abs(best.d)) best = { d: l.v - v2, from: l.from };
@@ -2454,7 +2460,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2454
2460
  };
2455
2461
  const bx = axes.x ? nearest(this.xs, [r.x, r.x + r.width / 2, r.x + r.width]) : null;
2456
2462
  const by = axes.y ? nearest(this.ys, [r.y, r.y + r.height / 2, r.y + r.height]) : null;
2457
- const out = { dx: _nullishCoalesce(_optionalChain([bx, 'optionalAccess', _57 => _57.d]), () => ( 0)), dy: _nullishCoalesce(_optionalChain([by, 'optionalAccess', _58 => _58.d]), () => ( 0)), x: !!bx, y: !!by, lines: [], labels: [] };
2463
+ const out = { dx: _nullishCoalesce(_optionalChain([bx, 'optionalAccess', _56 => _56.d]), () => ( 0)), dy: _nullishCoalesce(_optionalChain([by, 'optionalAccess', _57 => _57.d]), () => ( 0)), x: !!bx, y: !!by, lines: [], labels: [] };
2458
2464
  const done = { ...r, x: r.x + out.dx, y: r.y + out.dy };
2459
2465
  if (bx) {
2460
2466
  const x = bx.from.x;
@@ -2577,9 +2583,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2577
2583
  const on = this.nearestPoint(p, tol);
2578
2584
  const tries = [];
2579
2585
  if (on) tries.push({ tx: on.p.x, ty: on.p.y, s: { hx: null, hy: null, on } });
2580
- tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _59 => _59.v]), () => ( null)), ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _60 => _60.v]), () => ( null)), s: { hx, hy, on: null } });
2581
- tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _61 => _61.v]), () => ( null)), ty: null, s: { hx, hy: null, on: null } });
2582
- tries.push({ tx: null, ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _62 => _62.v]), () => ( null)), s: { hx: null, hy, on: null } });
2586
+ tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _58 => _58.v]), () => ( null)), ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _59 => _59.v]), () => ( null)), s: { hx, hy, on: null } });
2587
+ tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _60 => _60.v]), () => ( null)), ty: null, s: { hx, hy: null, on: null } });
2588
+ tries.push({ tx: null, ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _61 => _61.v]), () => ( null)), s: { hx: null, hy, on: null } });
2583
2589
  for (const { tx, ty, s } of tries) {
2584
2590
  if (tx === null && ty === null) continue;
2585
2591
  const q = applyMatrix(toLocal, { x: _nullishCoalesce(tx, () => ( p.x)), y: _nullishCoalesce(ty, () => ( p.y)) });
@@ -2729,15 +2735,15 @@ var crossCondition = (r, d) => r[0] * d[1] - r[1] * d[0];
2729
2735
  function solveOnSegment(seg, base, cond) {
2730
2736
  const b = [base.x, base.y];
2731
2737
  const g = (t) => {
2732
- const p = _chunk7EDLVBOOcjs.segmentPoint.call(void 0, seg, t);
2733
- return cond([p[0] - b[0], p[1] - b[1]], _chunk7EDLVBOOcjs.segmentDerivative.call(void 0, seg, t));
2738
+ const p = _chunkVNYRI265cjs.segmentPoint.call(void 0, seg, t);
2739
+ return cond([p[0] - b[0], p[1] - b[1]], _chunkVNYRI265cjs.segmentDerivative.call(void 0, seg, t));
2734
2740
  };
2735
2741
  const vals = [];
2736
2742
  let scale = 0;
2737
2743
  for (let i = 0; i <= CURVE_SAMPLES; i++) {
2738
2744
  const t = i / CURVE_SAMPLES;
2739
- const p = _chunk7EDLVBOOcjs.segmentPoint.call(void 0, seg, t);
2740
- const d = _chunk7EDLVBOOcjs.segmentDerivative.call(void 0, seg, t);
2745
+ const p = _chunkVNYRI265cjs.segmentPoint.call(void 0, seg, t);
2746
+ const d = _chunkVNYRI265cjs.segmentDerivative.call(void 0, seg, t);
2741
2747
  const r = [p[0] - b[0], p[1] - b[1]];
2742
2748
  vals.push(cond(r, d));
2743
2749
  scale = Math.max(scale, Math.hypot(r[0], r[1]) * Math.hypot(d[0], d[1]));
@@ -2764,8 +2770,8 @@ function solveOnSegment(seg, base, cond) {
2764
2770
  } else hi = m;
2765
2771
  }
2766
2772
  const t = (lo + hi) / 2;
2767
- const p = _chunk7EDLVBOOcjs.segmentPoint.call(void 0, seg, t);
2768
- const dv = _chunk7EDLVBOOcjs.segmentDerivative.call(void 0, seg, t);
2773
+ const p = _chunkVNYRI265cjs.segmentPoint.call(void 0, seg, t);
2774
+ const dv = _chunkVNYRI265cjs.segmentDerivative.call(void 0, seg, t);
2769
2775
  const r = Math.hypot(p[0] - b[0], p[1] - b[1]);
2770
2776
  const dl = Math.hypot(dv[0], dv[1]);
2771
2777
  if (!(r > 1e-9 && dl > 0) || Math.abs(cond([p[0] - b[0], p[1] - b[1]], dv)) > 1e-6 * r * dl) continue;
@@ -2802,7 +2808,7 @@ function endTangent(c) {
2802
2808
  return [1, 0];
2803
2809
  }
2804
2810
  function sample(c, n, out, withStart) {
2805
- for (let i = withStart ? 0 : 1; i <= n; i++) out.push(_chunk7EDLVBOOcjs.segmentPoint.call(void 0, c, i / n));
2811
+ for (let i = withStart ? 0 : 1; i <= n; i++) out.push(_chunkVNYRI265cjs.segmentPoint.call(void 0, c, i / n));
2806
2812
  }
2807
2813
  var onAnchor2 = (a, h) => !h || Math.abs(h.x - a.x) < EPS3 && Math.abs(h.y - a.y) < EPS3;
2808
2814
  function tidy(a, keepType = true) {
@@ -2848,7 +2854,7 @@ function fitAcross(pts, N, t0, t1) {
2848
2854
  const u = [];
2849
2855
  for (let i = 0; i <= N; i++) u.push(k * i / N);
2850
2856
  for (let i = 1; i <= N; i++) u.push(k + (1 - k) * i / N);
2851
- return _chunk7EDLVBOOcjs.fitCubicWithTangents.call(void 0, pts, u, t0, t1);
2857
+ return _chunkVNYRI265cjs.fitCubicWithTangents.call(void 0, pts, u, t0, t1);
2852
2858
  };
2853
2859
  return goldenMin(fit, 0, 1);
2854
2860
  }
@@ -2908,7 +2914,7 @@ function bendSegment(sp, segment, t, p) {
2908
2914
  return { anchors, closed: sp.closed };
2909
2915
  }
2910
2916
  function subpathPointAt(sp, segment, t) {
2911
- const [x, y] = _chunk7EDLVBOOcjs.segmentPoint.call(void 0, subpathSegment(sp, segment), t);
2917
+ const [x, y] = _chunkVNYRI265cjs.segmentPoint.call(void 0, subpathSegment(sp, segment), t);
2912
2918
  return { x, y };
2913
2919
  }
2914
2920
  function curvatureMoveAnchor(sp, index, q) {
@@ -3169,12 +3175,12 @@ function removeRedundantPoints(sp, tolerance) {
3169
3175
  }
3170
3176
 
3171
3177
  // src/vector/pathCut.ts
3172
- var loopArea = (l) => _chunk7EDLVBOOcjs.contourSignedArea.call(void 0, l.segs);
3178
+ var loopArea = (l) => _chunkVNYRI265cjs.contourSignedArea.call(void 0, l.segs);
3173
3179
  function loopSample(l) {
3174
3180
  const k = l.chord.findIndex((c) => !c);
3175
- return _chunk7EDLVBOOcjs.segmentPoint.call(void 0, l.segs[k >= 0 ? k : 0], 0.5);
3181
+ return _chunkVNYRI265cjs.segmentPoint.call(void 0, l.segs[k >= 0 ? k : 0], 0.5);
3176
3182
  }
3177
- var contains2 = (l, p) => _chunk7EDLVBOOcjs.windingNumber.call(void 0, [l.segs], p) !== 0;
3183
+ var contains2 = (l, p) => _chunkVNYRI265cjs.windingNumber.call(void 0, [l.segs], p) !== 0;
3178
3184
  function groupFaces(loops) {
3179
3185
  const usable2 = loops.filter((l) => l.segs.length && Math.abs(loopArea(l)) > 1e-12);
3180
3186
  if (!usable2.length) return [];
@@ -3190,12 +3196,12 @@ function groupFaces(loops) {
3190
3196
  return outers.reverse().map((o) => faces.get(o));
3191
3197
  }
3192
3198
  function faceToPath(face) {
3193
- return _chunk7EDLVBOOcjs.contoursToPath.call(void 0, face.loops.map((l) => ({ segments: l.segs, closed: true })));
3199
+ return _chunkVNYRI265cjs.contoursToPath.call(void 0, face.loops.map((l) => ({ segments: l.segs, closed: true })));
3194
3200
  }
3195
3201
  function facesOf(path, fillRule) {
3196
- const normalized = _chunk7EDLVBOOcjs.normalizeBezierPath.call(void 0, path, fillRule);
3197
- const loops = _chunk7EDLVBOOcjs.pathToContours.call(void 0, normalized).map((c) => {
3198
- const segs = _chunk7EDLVBOOcjs.closeContour.call(void 0, c, true).map((s) => s.slice());
3202
+ const normalized = _chunkVNYRI265cjs.normalizeBezierPath.call(void 0, path, fillRule);
3203
+ const loops = _chunkVNYRI265cjs.pathToContours.call(void 0, normalized).map((c) => {
3204
+ const segs = _chunkVNYRI265cjs.closeContour.call(void 0, c, true).map((s) => s.slice());
3199
3205
  return { segs, chord: segs.map(() => false) };
3200
3206
  });
3201
3207
  return groupFaces(loops);
@@ -3265,7 +3271,7 @@ function lineHits(p, q, seg) {
3265
3271
  const scale = Math.sqrt(len2) * Math.max(1, ...seg.map((pt2) => Math.abs(pt2[0]) + Math.abs(pt2[1])));
3266
3272
  const out = [];
3267
3273
  for (const t of rootsInUnit(...coeffs, scale)) {
3268
- const point = _chunk7EDLVBOOcjs.segmentPoint.call(void 0, seg, t);
3274
+ const point = _chunkVNYRI265cjs.segmentPoint.call(void 0, seg, t);
3269
3275
  const s = ((point[0] - p[0]) * dx + (point[1] - p[1]) * dy) / len2;
3270
3276
  if (s >= -1e-9 && s <= 1 + 1e-9) out.push({ s: Math.min(1, Math.max(0, s)), t, point });
3271
3277
  }
@@ -3278,7 +3284,7 @@ function arc(l, a, b, eps) {
3278
3284
  const segs = [];
3279
3285
  const chord = [];
3280
3286
  const push = (k, t0, t1) => {
3281
- const piece = t0 <= 0 && t1 >= 1 ? l.segs[k].slice() : _chunk7EDLVBOOcjs.subSegment.call(void 0, l.segs[k], t0, t1);
3287
+ const piece = t0 <= 0 && t1 >= 1 ? l.segs[k].slice() : _chunkVNYRI265cjs.subSegment.call(void 0, l.segs[k], t0, t1);
3282
3288
  if (!degenerate(piece, eps)) {
3283
3289
  segs.push(piece);
3284
3290
  chord.push(l.chord[k]);
@@ -3303,7 +3309,7 @@ function knifePath(path, fillRule, knife) {
3303
3309
  if (pts.length < 2) return null;
3304
3310
  let faces = facesOf(path, fillRule);
3305
3311
  if (!faces.length) return null;
3306
- const b = _chunk7EDLVBOOcjs.bezierPathBounds.call(void 0, path);
3312
+ const b = _chunkVNYRI265cjs.bezierPathBounds.call(void 0, path);
3307
3313
  const size = b ? Math.max(b.maxX - b.minX, b.maxY - b.minY, 1) : 1;
3308
3314
  const eps = size * 1e-9;
3309
3315
  let cursor = -Infinity;
@@ -3317,7 +3323,7 @@ function knifePath(path, fillRule, knife) {
3317
3323
  const kb = { minX: Math.min(pts[m][0], pts[m + 1][0]), maxX: Math.max(pts[m][0], pts[m + 1][0]), minY: Math.min(pts[m][1], pts[m + 1][1]), maxY: Math.max(pts[m][1], pts[m + 1][1]) };
3318
3324
  face2.loops.forEach(
3319
3325
  (l, li) => l.segs.forEach((seg, si) => {
3320
- const sb = _chunk7EDLVBOOcjs.segmentBounds.call(void 0, seg);
3326
+ const sb = _chunkVNYRI265cjs.segmentBounds.call(void 0, seg);
3321
3327
  if (sb.maxX < kb.minX - eps || sb.minX > kb.maxX + eps || sb.maxY < kb.minY - eps || sb.minY > kb.maxY + eps) return;
3322
3328
  for (const h of lineHits(pts[m], pts[m + 1], seg)) {
3323
3329
  const s = m + h.s;
@@ -3332,7 +3338,7 @@ function knifePath(path, fillRule, knife) {
3332
3338
  const m = Math.min(pts.length - 2, Math.floor(s));
3333
3339
  const u = s - m;
3334
3340
  const q = [pts[m][0] + (pts[m + 1][0] - pts[m][0]) * u, pts[m][1] + (pts[m + 1][1] - pts[m][1]) * u];
3335
- return _chunk7EDLVBOOcjs.windingNumber.call(void 0, face2.loops.map((l) => l.segs), q) !== 0;
3341
+ return _chunkVNYRI265cjs.windingNumber.call(void 0, face2.loops.map((l) => l.segs), q) !== 0;
3336
3342
  };
3337
3343
  for (let i = 0; i < unique.length - 1; i++) {
3338
3344
  const from2 = unique[i];
@@ -3392,33 +3398,33 @@ function eraserRegion(points, width) {
3392
3398
  ];
3393
3399
  }
3394
3400
  const line2 = [{ type: "M", x: pts[0][0], y: pts[0][1] }, ...pts.slice(1).map((p) => ({ type: "L", x: p[0], y: p[1] }))];
3395
- return _chunk7EDLVBOOcjs.outlineStroke.call(void 0, line2, { width, cap: "round", join: "round" });
3401
+ return _chunkVNYRI265cjs.outlineStroke.call(void 0, line2, { width, cap: "round", join: "round" });
3396
3402
  }
3397
3403
  function eraseFromFill(path, fillRule, region) {
3398
- const overlap = _chunk7EDLVBOOcjs.pathBoolean.call(void 0, "intersection", [{ path, fillRule }, { path: region }]).path;
3399
- if (!_chunk7EDLVBOOcjs.pathToContours.call(void 0, overlap).some((c) => Math.abs(_chunk7EDLVBOOcjs.contourSignedArea.call(void 0, _chunk7EDLVBOOcjs.closeContour.call(void 0, c, true))) > 1e-9)) return null;
3400
- const rest = _chunk7EDLVBOOcjs.pathBoolean.call(void 0, "difference", [{ path, fillRule }, { path: region }]).path;
3404
+ const overlap = _chunkVNYRI265cjs.pathBoolean.call(void 0, "intersection", [{ path, fillRule }, { path: region }]).path;
3405
+ if (!_chunkVNYRI265cjs.pathToContours.call(void 0, overlap).some((c) => Math.abs(_chunkVNYRI265cjs.contourSignedArea.call(void 0, _chunkVNYRI265cjs.closeContour.call(void 0, c, true))) > 1e-9)) return null;
3406
+ const rest = _chunkVNYRI265cjs.pathBoolean.call(void 0, "difference", [{ path, fillRule }, { path: region }]).path;
3401
3407
  return splitIntoFaces(rest);
3402
3408
  }
3403
3409
  function eraseFromOutline(sp, region) {
3404
- const regionSegs = _chunk7EDLVBOOcjs.pathToContours.call(void 0, region).flatMap((c) => _chunk7EDLVBOOcjs.closeContour.call(void 0, c, true));
3410
+ const regionSegs = _chunkVNYRI265cjs.pathToContours.call(void 0, region).flatMap((c) => _chunkVNYRI265cjs.closeContour.call(void 0, c, true));
3405
3411
  if (!regionSegs.length) return null;
3406
- const rb = _chunk7EDLVBOOcjs.bezierPathBounds.call(void 0, region);
3412
+ const rb = _chunkVNYRI265cjs.bezierPathBounds.call(void 0, region);
3407
3413
  const count = subpathSegmentCount(sp);
3408
3414
  if (!rb || count === 0) return null;
3409
- const removed = (p) => _chunk7EDLVBOOcjs.pointInBezierPath.call(void 0, region, p, "nonzero");
3415
+ const removed = (p) => _chunkVNYRI265cjs.pointInBezierPath.call(void 0, region, p, "nonzero");
3410
3416
  const runs = [];
3411
3417
  let cur = [];
3412
3418
  let any = false;
3413
3419
  for (let i = 0; i < count; i++) {
3414
3420
  const seg = subpathSegment(sp, i);
3415
- const sb = _chunk7EDLVBOOcjs.segmentBounds.call(void 0, seg);
3421
+ const sb = _chunkVNYRI265cjs.segmentBounds.call(void 0, seg);
3416
3422
  const ts = [0, 1];
3417
3423
  if (!(sb.maxX < rb.minX || sb.minX > rb.maxX || sb.maxY < rb.minY || sb.minY > rb.maxY)) {
3418
3424
  for (const r of regionSegs) {
3419
- const b = _chunk7EDLVBOOcjs.segmentBounds.call(void 0, r);
3425
+ const b = _chunkVNYRI265cjs.segmentBounds.call(void 0, r);
3420
3426
  if (b.maxX < sb.minX || b.minX > sb.maxX || b.maxY < sb.minY || b.minY > sb.maxY) continue;
3421
- for (const hit of _chunk7EDLVBOOcjs.segmentIntersections.call(void 0, seg, r)) if (hit.ta > 1e-9 && hit.ta < 1 - 1e-9) ts.push(hit.ta);
3427
+ for (const hit of _chunkVNYRI265cjs.segmentIntersections.call(void 0, seg, r)) if (hit.ta > 1e-9 && hit.ta < 1 - 1e-9) ts.push(hit.ta);
3422
3428
  }
3423
3429
  }
3424
3430
  ts.sort((a, b) => a - b);
@@ -3426,7 +3432,7 @@ function eraseFromOutline(sp, region) {
3426
3432
  const t0 = ts[k];
3427
3433
  const t1 = ts[k + 1];
3428
3434
  if (t1 - t0 < 1e-9) continue;
3429
- if (removed(_chunk7EDLVBOOcjs.segmentPoint.call(void 0, seg, (t0 + t1) / 2))) {
3435
+ if (removed(_chunkVNYRI265cjs.segmentPoint.call(void 0, seg, (t0 + t1) / 2))) {
3430
3436
  any = true;
3431
3437
  if (cur.length) runs.push(cur);
3432
3438
  cur = [];
@@ -3445,7 +3451,7 @@ function eraseFromOutline(sp, region) {
3445
3451
  for (const run of runs) {
3446
3452
  const anchors = [];
3447
3453
  for (const { seg, t0, t1 } of run) {
3448
- const c = _chunk7EDLVBOOcjs.subSegment.call(void 0, subpathSegment(sp, seg), t0, t1);
3454
+ const c = _chunkVNYRI265cjs.subSegment.call(void 0, subpathSegment(sp, seg), t0, t1);
3449
3455
  const line2 = c.length === 2;
3450
3456
  const start = c[0];
3451
3457
  const end = c[c.length - 1];
@@ -3753,14 +3759,14 @@ function worldOf(entry, subpaths) {
3753
3759
  return vectorPathWorldBezier({ ...entry.node, subpaths }, entry.parent);
3754
3760
  }
3755
3761
  function subpathEnclosesArea(sp) {
3756
- const contour = _chunk7EDLVBOOcjs.pathToContours.call(void 0, subpathsToBezier([{ ...sp, closed: true }]))[0];
3762
+ const contour = _chunkVNYRI265cjs.pathToContours.call(void 0, subpathsToBezier([{ ...sp, closed: true }]))[0];
3757
3763
  if (!contour) return false;
3758
- const segs = _chunk7EDLVBOOcjs.closeContour.call(void 0, contour, true);
3759
- const b = _chunk7EDLVBOOcjs.bezierPathBounds.call(void 0, _chunk7EDLVBOOcjs.contoursToPath.call(void 0, [{ segments: segs, closed: true }]));
3764
+ const segs = _chunkVNYRI265cjs.closeContour.call(void 0, contour, true);
3765
+ const b = _chunkVNYRI265cjs.bezierPathBounds.call(void 0, _chunkVNYRI265cjs.contoursToPath.call(void 0, [{ segments: segs, closed: true }]));
3760
3766
  if (!b) return false;
3761
3767
  const w = b.maxX - b.minX;
3762
3768
  const h = b.maxY - b.minY;
3763
- return Math.abs(_chunk7EDLVBOOcjs.contourSignedArea.call(void 0, segs)) > 1e-9 * (w * w + h * h);
3769
+ return Math.abs(_chunkVNYRI265cjs.contourSignedArea.call(void 0, segs)) > 1e-9 * (w * w + h * h);
3764
3770
  }
3765
3771
  var paintKey = (paint) => typeof paint === "string" ? paint.trim().toLowerCase() : JSON.stringify(_nullishCoalesce(paint, () => ( null)));
3766
3772
  function vectorPathfinderShape(entry, op) {
@@ -3813,31 +3819,50 @@ function vectorShapeBuilderMap(entries) {
3813
3819
  lines.push(!subpaths.some((s) => s.closed));
3814
3820
  return { path: worldOf(entry, subpaths), fillRule: _nullishCoalesce(entry.node.fillRule, () => ( "nonzero")) };
3815
3821
  });
3816
- const map = _chunk7EDLVBOOcjs.PlanarMap.build(inputs);
3822
+ const map = _chunkVNYRI265cjs.PlanarMap.build(inputs);
3817
3823
  if (!map) return null;
3818
- const pieces = map.chains().filter((c) => lines[c.source] && !c.closed).map((c) => {
3819
- const path = _chunk7EDLVBOOcjs.contoursToPath.call(void 0, [{ segments: c.segments, closed: false }]);
3820
- return { path, segments: c.segments, source: c.source, bounds: _chunk7EDLVBOOcjs.bezierPathBounds.call(void 0, path) };
3824
+ let chains = null;
3825
+ const runs = (line2) => (chains ??= map.chains()).filter((c) => lines[c.source] === line2 && !(line2 && c.closed)).map((c) => {
3826
+ const path = _chunkVNYRI265cjs.contoursToPath.call(void 0, [{ segments: c.segments, closed: c.closed }]);
3827
+ return { path, segments: c.segments, closed: c.closed, source: c.source, bounds: _chunkVNYRI265cjs.bezierPathBounds.call(void 0, path) };
3821
3828
  });
3822
- return { entries: [...entries], map, lines, pieces };
3829
+ const pieces = lines.some(Boolean) ? runs(true) : [];
3830
+ let edges = null;
3831
+ let size = 0;
3832
+ for (const input of inputs) {
3833
+ const b = _chunkVNYRI265cjs.bezierPathBounds.call(void 0, input.path);
3834
+ if (b) size = Math.max(size, b.maxX - b.minX, b.maxY - b.minY);
3835
+ }
3836
+ return {
3837
+ entries: [...entries],
3838
+ map,
3839
+ lines,
3840
+ pieces,
3841
+ get edges() {
3842
+ return edges ??= runs(false);
3843
+ },
3844
+ tolerance: 0.2 * Math.max(size / 2e3, 0.25)
3845
+ };
3823
3846
  }
3824
- function vectorShapeBuilderHit(m, p, lineTolerance = null) {
3847
+ function pieceNear(pieces, p, tol) {
3825
3848
  const point = [p.x, p.y];
3849
+ let best = null;
3850
+ pieces.forEach((piece, index) => {
3851
+ const b = piece.bounds;
3852
+ if (p.x < b.minX - tol || p.x > b.maxX + tol || p.y < b.minY - tol || p.y > b.maxY + tol) return;
3853
+ const d = _chunkVNYRI265cjs.distanceToBezierPath.call(void 0, piece.path, point);
3854
+ if (d <= tol && (!best || d < best.d)) best = { index, d };
3855
+ });
3856
+ return best;
3857
+ }
3858
+ function vectorShapeBuilderHit(m, p, lineTolerance = null) {
3826
3859
  if (lineTolerance !== null && lineTolerance > 0) {
3827
- let best = -1;
3828
- let bestD = lineTolerance;
3829
- m.pieces.forEach((piece, i) => {
3830
- const b = piece.bounds;
3831
- if (p.x < b.minX - bestD || p.x > b.maxX + bestD || p.y < b.minY - bestD || p.y > b.maxY + bestD) return;
3832
- const d = _chunk7EDLVBOOcjs.distanceToBezierPath.call(void 0, piece.path, point);
3833
- if (d <= bestD) {
3834
- best = i;
3835
- bestD = d;
3836
- }
3837
- });
3838
- if (best >= 0) return { line: best };
3860
+ const line2 = pieceNear(m.pieces, p, lineTolerance);
3861
+ const edge = pieceNear(m.edges, p, lineTolerance);
3862
+ if (edge && (!line2 || edge.d < line2.d)) return { edge: edge.index };
3863
+ if (line2) return { line: line2.index };
3839
3864
  }
3840
- const face = m.map.faceAt(point);
3865
+ const face = m.map.faceAt([p.x, p.y]);
3841
3866
  return face >= 0 ? { face } : null;
3842
3867
  }
3843
3868
  function samplePolyline(points, step) {
@@ -3855,25 +3880,31 @@ function samplePolyline(points, step) {
3855
3880
  }
3856
3881
  function addBuilderHit(touched, hit) {
3857
3882
  if (!hit) return false;
3858
- const [list, i] = "face" in hit ? [touched.faces, hit.face] : [touched.lines, hit.line];
3883
+ const [list, i] = "face" in hit ? [touched.faces, hit.face] : "line" in hit ? [touched.lines, hit.line] : [touched.edges, hit.edge];
3859
3884
  if (list.includes(i)) return false;
3860
3885
  list.push(i);
3861
3886
  return true;
3862
3887
  }
3863
3888
  function sides(m, piece) {
3864
3889
  const seg = piece.segments[Math.floor(piece.segments.length / 2)];
3865
- const mid = _chunk7EDLVBOOcjs.segmentPoint.call(void 0, seg, 0.5);
3866
- const t = _chunk7EDLVBOOcjs.segmentTangent.call(void 0, seg, 0.5);
3890
+ const mid = _chunkVNYRI265cjs.segmentPoint.call(void 0, seg, 0.5);
3891
+ const t = _chunkVNYRI265cjs.segmentTangent.call(void 0, seg, 0.5);
3867
3892
  const chord = Math.hypot(seg[seg.length - 1][0] - seg[0][0], seg[seg.length - 1][1] - seg[0][1]);
3868
3893
  const off = Math.max(1e-6, Math.min(0.05, 0.05 * chord));
3869
3894
  return [m.map.faceAt([mid[0] - t[1] * off, mid[1] + t[0] * off]), m.map.faceAt([mid[0] + t[1] * off, mid[1] - t[0] * off])];
3870
3895
  }
3896
+ function vectorBuilderErasedEdges(m, touched) {
3897
+ if (!touched.edges.length) return [];
3898
+ const faces = new Set(touched.faces);
3899
+ return touched.edges.filter((k) => m.edges[k] && !sides(m, m.edges[k]).some((f) => f >= 0 && faces.has(f)));
3900
+ }
3871
3901
  function planShapeBuilder(m, touched, erase) {
3872
3902
  const replace = /* @__PURE__ */ new Map();
3873
3903
  const picked = new Set(touched.faces);
3874
3904
  const faces = m.map.faces;
3875
3905
  const sourcesOf = faces.map((_, i) => m.map.faceSources(i));
3876
- m.entries.forEach((_, i) => {
3906
+ const cuts = erase ? vectorBuilderErasedEdges(m, touched).map((k) => m.edges[k]) : [];
3907
+ m.entries.forEach((entry, i) => {
3877
3908
  if (m.lines[i]) {
3878
3909
  const own = m.pieces.flatMap((piece, k) => piece.source === i ? [k] : []);
3879
3910
  const keep = own.filter((k) => !touched.lines.includes(k) && !sides(m, m.pieces[k]).every((f) => f >= 0 && picked.has(f)));
@@ -3881,10 +3912,24 @@ function planShapeBuilder(m, touched, erase) {
3881
3912
  replace.set(i, keep.length ? { kind: "pieces", paths: keep.map((k) => m.pieces[k].path) } : null);
3882
3913
  return;
3883
3914
  }
3884
- if (!touched.faces.some((f) => sourcesOf[f].includes(i))) return;
3885
- const kept = faces.flatMap((_2, f) => sourcesOf[f].includes(i) && !picked.has(f) ? [f] : []);
3886
- const rest = kept.length ? m.map.merge(kept) : [];
3887
- replace.set(i, rest.length ? { kind: "fill", path: rest } : null);
3915
+ const inUnion = touched.faces.some((f) => sourcesOf[f].includes(i));
3916
+ if (!inUnion && !cuts.length) return;
3917
+ let rest;
3918
+ if (inUnion) {
3919
+ const kept = faces.flatMap((_, f) => sourcesOf[f].includes(i) && !picked.has(f) ? [f] : []);
3920
+ rest = kept.length ? m.map.merge(kept) : [];
3921
+ } else rest = worldOf(entry, entry.node.subpaths.filter(usable));
3922
+ const fillCloses = hasPaint(entry.node.fill);
3923
+ let cut = false;
3924
+ for (const piece of cuts) {
3925
+ const r = rest.length ? _chunkVNYRI265cjs.cutOutPiece.call(void 0, rest, piece, fillCloses, m.tolerance) : null;
3926
+ if (r) {
3927
+ rest = r;
3928
+ cut = true;
3929
+ }
3930
+ }
3931
+ if (!inUnion && !cut) return;
3932
+ replace.set(i, rest.length ? { kind: inUnion ? "fill" : "outline", path: rest } : null);
3888
3933
  });
3889
3934
  let merged = null;
3890
3935
  if (!erase && touched.faces.length) {
@@ -3898,7 +3943,7 @@ function planShapeBuilder(m, touched, erase) {
3898
3943
  }
3899
3944
  function vectorFillMap(doc, gap = 0) {
3900
3945
  const entries = vectorPathEntries(doc, null, { locked: true });
3901
- return _chunk7EDLVBOOcjs.PlanarMap.build(
3946
+ return _chunkVNYRI265cjs.PlanarMap.build(
3902
3947
  entries.map((e) => ({ path: worldOf(e, e.node.subpaths.filter(usable)), fills: false })),
3903
3948
  { gap, maxSegments: 2e4 }
3904
3949
  );
@@ -3960,7 +4005,7 @@ function getEditablePathIds(state) {
3960
4005
  }
3961
4006
  }
3962
4007
  }
3963
- return [...ids].filter((id) => _optionalChain([findVectorNode, 'call', _63 => _63(state.document.nodes, id), 'optionalAccess', _64 => _64.type]) === "path");
4008
+ return [...ids].filter((id) => _optionalChain([findVectorNode, 'call', _62 => _62(state.document.nodes, id), 'optionalAccess', _63 => _63.type]) === "path");
3964
4009
  }
3965
4010
  function getVisibleAnchors(state) {
3966
4011
  const out = [];
@@ -3975,7 +4020,7 @@ function getVisibleHandles(state) {
3975
4020
  const seen = /* @__PURE__ */ new Set();
3976
4021
  const push = (node, s, i, handle) => {
3977
4022
  const sp = node.subpaths[s];
3978
- const a = _optionalChain([sp, 'optionalAccess', _65 => _65.anchors, 'access', _66 => _66[i]]);
4023
+ const a = _optionalChain([sp, 'optionalAccess', _64 => _64.anchors, 'access', _65 => _65[i]]);
3979
4024
  if (!a || !a[handle]) return;
3980
4025
  const key = `${node.id}:${s}:${i}:${handle}`;
3981
4026
  if (seen.has(key)) return;
@@ -3984,8 +4029,8 @@ function getVisibleHandles(state) {
3984
4029
  };
3985
4030
  if (state.tool === "pen" && state.pen) {
3986
4031
  const node = findVectorNode(state.document.nodes, state.pen.nodeId);
3987
- const sp = _optionalChain([node, 'optionalAccess', _67 => _67.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
3988
- if (_optionalChain([node, 'optionalAccess', _68 => _68.type]) === "path" && sp) {
4032
+ const sp = _optionalChain([node, 'optionalAccess', _66 => _66.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
4033
+ if (_optionalChain([node, 'optionalAccess', _67 => _67.type]) === "path" && sp) {
3989
4034
  const last = sp.anchors.length - 1;
3990
4035
  push(node, state.pen.subpath, last, "in");
3991
4036
  push(node, state.pen.subpath, last, "out");
@@ -4023,11 +4068,11 @@ function getVectorCornerWidgets(state) {
4023
4068
  const side = Math.min(c.lu, c.lv);
4024
4069
  if (side * zoom < 3 * VECTOR_CORNER_WIDGET_INSET) continue;
4025
4070
  const a = node.subpaths[c.subpath].anchors[c.index];
4026
- const radius = _nullishCoalesce(_optionalChain([a, 'access', _69 => _69.corner, 'optionalAccess', _70 => _70.radius]), () => ( 0));
4071
+ const radius = _nullishCoalesce(_optionalChain([a, 'access', _68 => _68.corner, 'optionalAccess', _69 => _69.radius]), () => ( 0));
4027
4072
  const drawn = fitCornerRadius(c, radius);
4028
4073
  const k = Math.min(Math.max(drawn / c.sin, VECTOR_CORNER_WIDGET_INSET / (zoom * Math.sqrt(1 + c.cos))), side / 2);
4029
4074
  const p = applyMatrix(m, cornerBisectorPoint(c, k));
4030
- out.push({ ref: { nodeId: id, subpath: c.subpath, index: c.index }, x: p.x, y: p.y, radius, drawnRadius: drawn, kind: _nullishCoalesce(_optionalChain([a, 'access', _71 => _71.corner, 'optionalAccess', _72 => _72.kind]), () => ( "round")), corner: c });
4075
+ out.push({ ref: { nodeId: id, subpath: c.subpath, index: c.index }, x: p.x, y: p.y, radius, drawnRadius: drawn, kind: _nullishCoalesce(_optionalChain([a, 'access', _70 => _70.corner, 'optionalAccess', _71 => _71.kind]), () => ( "round")), corner: c });
4031
4076
  }
4032
4077
  }
4033
4078
  return out;
@@ -4037,6 +4082,12 @@ function vectorStrokeWidthGeometry(node) {
4037
4082
  const parts = vectorStrokeParts(subpathsToBezier(node.subpaths), node);
4038
4083
  return parts.line.length ? { line: parts.line, unit: parts.drawWidth / 2 } : null;
4039
4084
  }
4085
+ function shiftWidthPoints(profile, indices, dt) {
4086
+ const moving = new Set(indices);
4087
+ const tagged = profile.map((p, i) => ({ p: moving.has(i) ? { ...p, t: Math.min(Math.max(p.t + dt, 0), 1) } : { ...p }, i }));
4088
+ tagged.sort((a, b) => a.p.t - b.p.t || a.i - b.i);
4089
+ return { points: tagged.map((x) => x.p), renumber: new Map(tagged.map((x, k) => [x.i, k])) };
4090
+ }
4040
4091
  function getVectorWidthHandles(state) {
4041
4092
  if (state.tool !== "width") return [];
4042
4093
  const ids = /* @__PURE__ */ new Set();
@@ -4044,13 +4095,13 @@ function getVectorWidthHandles(state) {
4044
4095
  const n = findVectorNode(state.document.nodes, id);
4045
4096
  if (n) for (const p of collectPathNodes(n)) ids.add(p.id);
4046
4097
  }
4047
- if (_optionalChain([state, 'access', _73 => _73.hover, 'optionalAccess', _74 => _74.nodeId])) ids.add(state.hover.nodeId);
4048
- if (_optionalChain([state, 'access', _75 => _75.interaction, 'optionalAccess', _76 => _76.type]) === "width") ids.add(state.interaction.ref.nodeId);
4049
- if (state.selectedWidthPoint) ids.add(state.selectedWidthPoint.nodeId);
4098
+ if (_optionalChain([state, 'access', _72 => _72.hover, 'optionalAccess', _73 => _73.nodeId])) ids.add(state.hover.nodeId);
4099
+ if (_optionalChain([state, 'access', _74 => _74.interaction, 'optionalAccess', _75 => _75.type]) === "width") ids.add(state.interaction.ref.nodeId);
4100
+ for (const r of state.selectedWidthPoints) ids.add(r.nodeId);
4050
4101
  const out = [];
4051
4102
  for (const id of ids) {
4052
4103
  const node = findVectorNode(state.document.nodes, id);
4053
- if (_optionalChain([node, 'optionalAccess', _77 => _77.type]) !== "path" || node.visible === false || !_optionalChain([node, 'access', _78 => _78.strokeWidthProfile, 'optionalAccess', _79 => _79.length])) continue;
4104
+ if (_optionalChain([node, 'optionalAccess', _76 => _76.type]) !== "path" || node.visible === false || !_optionalChain([node, 'access', _77 => _77.strokeWidthProfile, 'optionalAccess', _78 => _78.length])) continue;
4054
4105
  const g = vectorStrokeWidthGeometry(node);
4055
4106
  if (!g) continue;
4056
4107
  const profile = sanitizeWidthProfile(node.strokeWidthProfile);
@@ -4065,7 +4116,6 @@ function getVectorWidthHandles(state) {
4065
4116
  const p = applyMatrix(m, c);
4066
4117
  const l = applyMatrix(m, { x: c.x + n.x * w.left * g.unit, y: c.y + n.y * w.left * g.unit });
4067
4118
  const r = applyMatrix(m, { x: c.x - n.x * w.right * g.unit, y: c.y - n.y * w.right * g.unit });
4068
- const sel = state.selectedWidthPoint;
4069
4119
  out.push({
4070
4120
  ref: { nodeId: id, index, subpath: sp },
4071
4121
  x: p.x,
@@ -4075,7 +4125,7 @@ function getVectorWidthHandles(state) {
4075
4125
  t: w.t,
4076
4126
  leftScale: w.left,
4077
4127
  rightScale: w.right,
4078
- selected: !!sel && sel.nodeId === id && sel.index === index
4128
+ selected: state.selectedWidthPoints.some((r2) => r2.nodeId === id && r2.index === index)
4079
4129
  });
4080
4130
  });
4081
4131
  }
@@ -4084,7 +4134,7 @@ function getVectorWidthHandles(state) {
4084
4134
  }
4085
4135
  function getAnchor(doc, ref) {
4086
4136
  const node = findVectorNode(doc.nodes, ref.nodeId);
4087
- return _optionalChain([node, 'optionalAccess', _80 => _80.type]) === "path" ? _optionalChain([node, 'access', _81 => _81.subpaths, 'access', _82 => _82[ref.subpath], 'optionalAccess', _83 => _83.anchors, 'access', _84 => _84[ref.index]]) : void 0;
4137
+ return _optionalChain([node, 'optionalAccess', _79 => _79.type]) === "path" ? _optionalChain([node, 'access', _80 => _80.subpaths, 'access', _81 => _81[ref.subpath], 'optionalAccess', _82 => _82.anchors, 'access', _83 => _83[ref.index]]) : void 0;
4088
4138
  }
4089
4139
  function getAnchorWorld(doc, ref) {
4090
4140
  const a = getAnchor(doc, ref);
@@ -4271,43 +4321,44 @@ var VectorEditorController = (_class3 = class {
4271
4321
 
4272
4322
  __init9() {this.selection = []}
4273
4323
  __init10() {this.anchors = []}
4274
- __init11() {this.widthPoint = null}
4275
- __init12() {this.editingId = null}
4276
- __init13() {this.pen = null}
4324
+ __init11() {this.widthPoints = []}
4325
+ __init12() {this.widthEdit = null}
4326
+ __init13() {this.editingId = null}
4327
+ __init14() {this.pen = null}
4277
4328
 
4278
4329
 
4279
4330
 
4280
4331
 
4281
4332
 
4282
4333
 
4283
- __init14() {this.hover = null}
4284
- __init15() {this.pointer = null}
4285
- __init16() {this.drag = null}
4286
- __init17() {this.interaction = null}
4287
- __init18() {this.guides = null}
4334
+ __init15() {this.hover = null}
4335
+ __init16() {this.pointer = null}
4336
+ __init17() {this.drag = null}
4337
+ __init18() {this.interaction = null}
4338
+ __init19() {this.guides = null}
4288
4339
  /** Whether the last `snapDrawn` landed on an object (guides may be hidden). */
4289
- __init19() {this.snappedToObject = false}
4340
+ __init20() {this.snappedToObject = false}
4290
4341
  /** Drawing targets for hovering the pen and shape tools, kept while the document and the excluded path stay the same. */
4291
- __init20() {this.hoverTargets = null}
4292
- __init21() {this.spaceHeld = false}
4293
- __init22() {this.clipboard = null}
4342
+ __init21() {this.hoverTargets = null}
4343
+ __init22() {this.spaceHeld = false}
4344
+ __init23() {this.clipboard = null}
4294
4345
  /** Shape Builder map of the selection, kept while the document and the selection stay the same. */
4295
- __init23() {this.builderCache = null}
4346
+ __init24() {this.builderCache = null}
4296
4347
  /** Paint bucket map of the visible outlines. */
4297
- __init24() {this.fillCache = null}
4298
- __init25() {this.builderPreview = null}
4348
+ __init25() {this.fillCache = null}
4349
+ __init26() {this.builderPreview = null}
4299
4350
  /** What the highlight shows, to skip notifying when the pointer stays over the same face. */
4300
- __init26() {this.builderHoverKey = null}
4351
+ __init27() {this.builderHoverKey = null}
4301
4352
 
4302
4353
 
4303
- __init27() {this.listeners = /* @__PURE__ */ new Set()}
4304
- __init28() {this.cleanups = []}
4305
- __init29() {this.applying = false}
4354
+ __init28() {this.listeners = /* @__PURE__ */ new Set()}
4355
+ __init29() {this.cleanups = []}
4356
+ __init30() {this.applying = false}
4306
4357
  /** Set while a commit records a change that was already applied, so `restore` still reports it. */
4307
- __init30() {this.committing = false}
4308
- __init31() {this.revision = 0}
4358
+ __init31() {this.committing = false}
4359
+ __init32() {this.revision = 0}
4309
4360
 
4310
- constructor(options = {}) {;_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this);_class3.prototype.__init16.call(this);_class3.prototype.__init17.call(this);_class3.prototype.__init18.call(this);_class3.prototype.__init19.call(this);_class3.prototype.__init20.call(this);_class3.prototype.__init21.call(this);_class3.prototype.__init22.call(this);_class3.prototype.__init23.call(this);_class3.prototype.__init24.call(this);_class3.prototype.__init25.call(this);_class3.prototype.__init26.call(this);_class3.prototype.__init27.call(this);_class3.prototype.__init28.call(this);_class3.prototype.__init29.call(this);_class3.prototype.__init30.call(this);_class3.prototype.__init31.call(this);_class3.prototype.__init32.call(this);_class3.prototype.__init33.call(this);
4361
+ constructor(options = {}) {;_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this);_class3.prototype.__init16.call(this);_class3.prototype.__init17.call(this);_class3.prototype.__init18.call(this);_class3.prototype.__init19.call(this);_class3.prototype.__init20.call(this);_class3.prototype.__init21.call(this);_class3.prototype.__init22.call(this);_class3.prototype.__init23.call(this);_class3.prototype.__init24.call(this);_class3.prototype.__init25.call(this);_class3.prototype.__init26.call(this);_class3.prototype.__init27.call(this);_class3.prototype.__init28.call(this);_class3.prototype.__init29.call(this);_class3.prototype.__init30.call(this);_class3.prototype.__init31.call(this);_class3.prototype.__init32.call(this);_class3.prototype.__init33.call(this);_class3.prototype.__init34.call(this);
4311
4362
  this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkQKLRAA4Tcjs.HistoryManager)()));
4312
4363
  this.doc = options.document ? cloneVectorDocument(options.document) : { width: 800, height: 600, nodes: [] };
4313
4364
  this.tool = _nullishCoalesce(options.tool, () => ( "select"));
@@ -4336,9 +4387,9 @@ var VectorEditorController = (_class3 = class {
4336
4387
  // State
4337
4388
  // -------------------------------------------------------------------------
4338
4389
  /** The current snapshot (stable until the next change). */
4339
- __init32() {this.getState = () => this.state}
4390
+ __init33() {this.getState = () => this.state}
4340
4391
  /** Calls `listener` after every change. Returns an unsubscribe function. */
4341
- __init33() {this.subscribe = (listener) => {
4392
+ __init34() {this.subscribe = (listener) => {
4342
4393
  this.listeners.add(listener);
4343
4394
  return () => {
4344
4395
  this.listeners.delete(listener);
@@ -4357,7 +4408,8 @@ var VectorEditorController = (_class3 = class {
4357
4408
  tool: this.tool,
4358
4409
  selection: this.selection,
4359
4410
  selectedAnchors: this.anchors,
4360
- selectedWidthPoint: this.widthPoint,
4411
+ selectedWidthPoints: this.widthPoints,
4412
+ widthPointEdit: this.widthEdit,
4361
4413
  editingId: this.editingId,
4362
4414
  selectionBounds: this.boundsOf(this.selection),
4363
4415
  selectionFrame: this.frameOf(this.selectedRoots()),
@@ -4383,7 +4435,7 @@ var VectorEditorController = (_class3 = class {
4383
4435
  notify(docChanged = false) {
4384
4436
  this.revision++;
4385
4437
  this.state = this.computeState();
4386
- if (docChanged) _optionalChain([this, 'access', _85 => _85.onChange, 'optionalCall', _86 => _86(this.doc)]);
4438
+ if (docChanged) _optionalChain([this, 'access', _84 => _84.onChange, 'optionalCall', _85 => _85(this.doc)]);
4387
4439
  const state = this.state;
4388
4440
  this.listeners.forEach((listener) => {
4389
4441
  try {
@@ -4413,26 +4465,26 @@ var VectorEditorController = (_class3 = class {
4413
4465
  return applyMatrix(invertMatrix(this.worldOf(id, doc)), p);
4414
4466
  }
4415
4467
  cursorFor() {
4416
- if (_optionalChain([this, 'access', _87 => _87.drag, 'optionalAccess', _88 => _88.kind]) === "pan" || this.spaceHeld) return this.drag ? "grabbing" : "grab";
4417
- if (_optionalChain([this, 'access', _89 => _89.drag, 'optionalAccess', _90 => _90.kind]) === "rotate") return "grabbing";
4418
- if (_optionalChain([this, 'access', _91 => _91.drag, 'optionalAccess', _92 => _92.kind]) === "move") return "move";
4468
+ if (_optionalChain([this, 'access', _86 => _86.drag, 'optionalAccess', _87 => _87.kind]) === "pan" || this.spaceHeld) return this.drag ? "grabbing" : "grab";
4469
+ if (_optionalChain([this, 'access', _88 => _88.drag, 'optionalAccess', _89 => _89.kind]) === "rotate") return "grabbing";
4470
+ if (_optionalChain([this, 'access', _90 => _90.drag, 'optionalAccess', _91 => _91.kind]) === "move") return "move";
4419
4471
  const h = this.hover;
4420
4472
  switch (this.tool) {
4421
4473
  case "select":
4422
- if (_optionalChain([h, 'optionalAccess', _93 => _93.transformHandle])) return vectorResizeCursor(h.transformHandle, _nullishCoalesce(_optionalChain([this, 'access', _94 => _94.frameOf, 'call', _95 => _95(this.selectedRoots()), 'optionalAccess', _96 => _96.angle]), () => ( 0)));
4423
- return _optionalChain([h, 'optionalAccess', _97 => _97.nodeId]) ? "move" : "default";
4474
+ if (_optionalChain([h, 'optionalAccess', _92 => _92.transformHandle])) return vectorResizeCursor(h.transformHandle, _nullishCoalesce(_optionalChain([this, 'access', _93 => _93.frameOf, 'call', _94 => _94(this.selectedRoots()), 'optionalAccess', _95 => _95.angle]), () => ( 0)));
4475
+ return _optionalChain([h, 'optionalAccess', _96 => _96.nodeId]) ? "move" : "default";
4424
4476
  case "direct":
4425
- if (_optionalChain([h, 'optionalAccess', _98 => _98.anchor]) || _optionalChain([h, 'optionalAccess', _99 => _99.handle]) || _optionalChain([h, 'optionalAccess', _100 => _100.corner])) return "pointer";
4426
- if (_optionalChain([h, 'optionalAccess', _101 => _101.segment])) return "copy";
4427
- return _optionalChain([h, 'optionalAccess', _102 => _102.nodeId]) ? "move" : "default";
4477
+ if (_optionalChain([h, 'optionalAccess', _97 => _97.anchor]) || _optionalChain([h, 'optionalAccess', _98 => _98.handle]) || _optionalChain([h, 'optionalAccess', _99 => _99.corner])) return "pointer";
4478
+ if (_optionalChain([h, 'optionalAccess', _100 => _100.segment])) return "copy";
4479
+ return _optionalChain([h, 'optionalAccess', _101 => _101.nodeId]) ? "move" : "default";
4428
4480
  case "pen":
4429
- return _optionalChain([h, 'optionalAccess', _103 => _103.anchor]) ? "pointer" : "crosshair";
4481
+ return _optionalChain([h, 'optionalAccess', _102 => _102.anchor]) ? "pointer" : "crosshair";
4430
4482
  case "width":
4431
- if (_optionalChain([this, 'access', _104 => _104.drag, 'optionalAccess', _105 => _105.kind]) === "width") return this.drag.mode === "move" ? "grabbing" : "ew-resize";
4432
- if (_optionalChain([h, 'optionalAccess', _106 => _106.widthPoint])) return h.widthPoint.part === "center" ? "grab" : "ew-resize";
4433
- return _optionalChain([h, 'optionalAccess', _107 => _107.strokeSpot]) ? "copy" : "default";
4483
+ if (_optionalChain([this, 'access', _103 => _103.drag, 'optionalAccess', _104 => _104.kind]) === "width") return this.drag.mode === "width" ? "ew-resize" : "grabbing";
4484
+ if (_optionalChain([h, 'optionalAccess', _105 => _105.widthPoint])) return h.widthPoint.part === "center" ? "grab" : "ew-resize";
4485
+ return _optionalChain([h, 'optionalAccess', _106 => _106.strokeSpot]) ? "copy" : "default";
4434
4486
  case "shapeBuilder":
4435
- if (!_optionalChain([this, 'access', _108 => _108.builderPreview, 'optionalAccess', _109 => _109.faces, 'access', _110 => _110.length]) && !_optionalChain([this, 'access', _111 => _111.builderPreview, 'optionalAccess', _112 => _112.lines, 'access', _113 => _113.length])) return "crosshair";
4487
+ if (!_optionalChain([this, 'access', _107 => _107.builderPreview, 'optionalAccess', _108 => _108.faces, 'access', _109 => _109.length]) && !_optionalChain([this, 'access', _110 => _110.builderPreview, 'optionalAccess', _111 => _111.lines, 'access', _112 => _112.length])) return "crosshair";
4436
4488
  return this.builderPreview.mode === "erase" ? "not-allowed" : "cell";
4437
4489
  case "paintBucket":
4438
4490
  return this.builderPreview ? "cell" : "crosshair";
@@ -4448,6 +4500,7 @@ var VectorEditorController = (_class3 = class {
4448
4500
  }
4449
4501
  restore(s) {
4450
4502
  const docChanged = s.doc !== this.doc || this.committing;
4503
+ if (!this.committing) this.widthEdit = null;
4451
4504
  this.committing = false;
4452
4505
  this.applying = true;
4453
4506
  try {
@@ -4455,7 +4508,14 @@ var VectorEditorController = (_class3 = class {
4455
4508
  this.selection = s.selection.filter((id) => this.getNode(id));
4456
4509
  this.anchors = s.anchors.filter((r) => getAnchor(this.doc, r));
4457
4510
  this.editingId = s.editingId && this.getNode(s.editingId) ? s.editingId : null;
4458
- this.pen = s.pen && _optionalChain([this, 'access', _114 => _114.getNode, 'call', _115 => _115(s.pen.nodeId), 'optionalAccess', _116 => _116.subpaths, 'optionalAccess', _117 => _117[s.pen.subpath]]) ? s.pen : null;
4511
+ this.pen = s.pen && _optionalChain([this, 'access', _113 => _113.getNode, 'call', _114 => _114(s.pen.nodeId), 'optionalAccess', _115 => _115.subpaths, 'optionalAccess', _116 => _116[s.pen.subpath]]) ? s.pen : null;
4512
+ if (this.widthPoints.length) {
4513
+ const kept = this.widthPoints.filter((r) => {
4514
+ const n = this.getNode(r.nodeId);
4515
+ return _optionalChain([n, 'optionalAccess', _117 => _117.type]) === "path" && !!_optionalChain([n, 'access', _118 => _118.strokeWidthProfile, 'optionalAccess', _119 => _119[r.index]]);
4516
+ });
4517
+ if (kept.length !== this.widthPoints.length) this.widthPoints = kept;
4518
+ }
4459
4519
  } finally {
4460
4520
  this.applying = false;
4461
4521
  }
@@ -4561,7 +4621,8 @@ var VectorEditorController = (_class3 = class {
4561
4621
  this.tool = tool;
4562
4622
  this.hover = null;
4563
4623
  this.guides = null;
4564
- this.widthPoint = null;
4624
+ this.widthPoints = [];
4625
+ this.widthEdit = null;
4565
4626
  this.builderPreview = null;
4566
4627
  this.builderHoverKey = null;
4567
4628
  if (tool !== "direct") {
@@ -4622,7 +4683,7 @@ var VectorEditorController = (_class3 = class {
4622
4683
  if (res.kind) {
4623
4684
  this.snappedToObject = true;
4624
4685
  this.setGuides(res);
4625
- return _optionalChain([leave, 'optionalAccess', _118 => _118.constrain]) ? res.point : { x: res.x ? res.point.x : this.gridValue(res.point.x), y: res.y ? res.point.y : this.gridValue(res.point.y) };
4686
+ return _optionalChain([leave, 'optionalAccess', _120 => _120.constrain]) ? res.point : { x: res.x ? res.point.x : this.gridValue(res.point.x), y: res.y ? res.point.y : this.gridValue(res.point.y) };
4626
4687
  }
4627
4688
  }
4628
4689
  this.setGuides(null);
@@ -4640,14 +4701,14 @@ var VectorEditorController = (_class3 = class {
4640
4701
  /** Where the pointer at `p` would place a point with the pen or a shape tool (and the guides showing why). */
4641
4702
  hoverSnap(p, input) {
4642
4703
  if (this.tool === "pen") {
4643
- const sp = this.pen ? _optionalChain([this, 'access', _119 => _119.getNode, 'call', _120 => _120(this.pen.nodeId), 'optionalAccess', _121 => _121.subpaths, 'access', _122 => _122[this.pen.subpath]]) : void 0;
4644
- if (this.pen && sp && _optionalChain([this, 'access', _123 => _123.hover, 'optionalAccess', _124 => _124.anchor])) {
4704
+ const sp = this.pen ? _optionalChain([this, 'access', _121 => _121.getNode, 'call', _122 => _122(this.pen.nodeId), 'optionalAccess', _123 => _123.subpaths, 'access', _124 => _124[this.pen.subpath]]) : void 0;
4705
+ if (this.pen && sp && _optionalChain([this, 'access', _125 => _125.hover, 'optionalAccess', _126 => _126.anchor])) {
4645
4706
  this.setGuides(null);
4646
4707
  return p;
4647
4708
  }
4648
4709
  const last = this.pen && sp ? getAnchorWorld(this.doc, { nodeId: this.pen.nodeId, subpath: this.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
4649
4710
  const leave = last ? { at: { x: last.x, y: last.y }, constrain: input.shift ? "angle" : null } : null;
4650
- return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access', _125 => _125.pen, 'optionalAccess', _126 => _126.nodeId]), () => ( null))));
4711
+ return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access', _127 => _127.pen, 'optionalAccess', _128 => _128.nodeId]), () => ( null))));
4651
4712
  }
4652
4713
  if (SHAPE_TOOLS.has(this.tool)) return this.snapDrawn(p, null, input, () => this.drawingTargets(null));
4653
4714
  this.setGuides(null);
@@ -4795,7 +4856,7 @@ var VectorEditorController = (_class3 = class {
4795
4856
  if (d.fillRule) out.fillRule = d.fillRule;
4796
4857
  if (d.strokeAlign && d.strokeAlign !== "center") out.strokeAlign = d.strokeAlign;
4797
4858
  for (const key of ["startArrow", "endArrow", "arrowAlign", "startArrowScale", "endArrowScale"]) if (d[key]) out[key] = d[key];
4798
- if (_optionalChain([d, 'access', _127 => _127.strokeWidthProfile, 'optionalAccess', _128 => _128.length])) out.strokeWidthProfile = d.strokeWidthProfile.map((p) => ({ ...p }));
4859
+ if (_optionalChain([d, 'access', _129 => _129.strokeWidthProfile, 'optionalAccess', _130 => _130.length])) out.strokeWidthProfile = d.strokeWidthProfile.map((p) => ({ ...p }));
4799
4860
  return out;
4800
4861
  }
4801
4862
  /** Merges `patch` into a node (paths take style and geometry; groups name / visibility / opacity). */
@@ -4837,18 +4898,143 @@ var VectorEditorController = (_class3 = class {
4837
4898
  const p = profile;
4838
4899
  points = p.taper || p.wave ? generateWidthProfile(p) : [];
4839
4900
  }
4840
- this.widthPoint = null;
4901
+ this.widthPoints = [];
4902
+ this.widthEdit = null;
4841
4903
  return this.setStyle({ strokeWidthProfile: points.length ? points : null }, _nullishCoalesce(options.ids, () => ( this.selection)), { label: points.length ? "Width Profile" : "Uniform Width" });
4842
4904
  }
4843
- /** Removes one width point of a path's profile (the width tool's Delete). */
4905
+ /** Removes one width point of a path's profile. */
4844
4906
  removeWidthPoint(ref) {
4845
- const node = this.getNode(ref.nodeId);
4846
- if (_optionalChain([node, 'optionalAccess', _129 => _129.type]) !== "path" || !_optionalChain([node, 'access', _130 => _130.strokeWidthProfile, 'optionalAccess', _131 => _131[ref.index]])) return { ok: false, message: "There is no such width point." };
4847
- const rest = removeWidthPoints(sanitizeWidthProfile(node.strokeWidthProfile), [ref.index]);
4848
- this.widthPoint = null;
4849
- this.change("Delete Width Point", () => this.replaceNode(node.id, withStyle(node, { strokeWidthProfile: rest.length ? rest : null })));
4907
+ return this.removeWidthPoints([ref]);
4908
+ }
4909
+ /** Removes width points (default: the selected ones — the width tool's Delete), as one undo step. */
4910
+ removeWidthPoints(refs = this.widthPoints) {
4911
+ const byNode = /* @__PURE__ */ new Map();
4912
+ for (const r of refs) {
4913
+ const node = this.getNode(r.nodeId);
4914
+ if (_optionalChain([node, 'optionalAccess', _131 => _131.type]) !== "path" || !_optionalChain([node, 'access', _132 => _132.strokeWidthProfile, 'optionalAccess', _133 => _133[r.index]])) continue;
4915
+ byNode.set(r.nodeId, [..._nullishCoalesce(byNode.get(r.nodeId), () => ( [])), r.index]);
4916
+ }
4917
+ if (!byNode.size) return { ok: false, message: "There is no such width point." };
4918
+ const count = [...byNode.values()].reduce((n, l) => n + new Set(l).size, 0);
4919
+ this.widthPoints = [];
4920
+ this.widthEdit = null;
4921
+ this.change(count > 1 ? "Delete Width Points" : "Delete Width Point", () => {
4922
+ for (const [id, indices] of byNode) {
4923
+ const node = this.getNode(id);
4924
+ const rest = removeWidthPoints(sanitizeWidthProfile(node.strokeWidthProfile), indices);
4925
+ this.replaceNode(id, withStyle(node, { strokeWidthProfile: rest.length ? rest : null }));
4926
+ }
4927
+ });
4850
4928
  return { ok: true };
4851
4929
  }
4930
+ /** Selects width points (width tool); points on other paths than the first are dropped. */
4931
+ selectWidthPoints(refs) {
4932
+ const first = _optionalChain([refs, 'access', _134 => _134[0], 'optionalAccess', _135 => _135.nodeId]);
4933
+ const next = [];
4934
+ for (const r of refs) {
4935
+ if (r.nodeId !== first || next.some((q) => q.index === r.index)) continue;
4936
+ const n = this.getNode(r.nodeId);
4937
+ if (_optionalChain([n, 'optionalAccess', _136 => _136.type]) === "path" && _optionalChain([n, 'access', _137 => _137.strokeWidthProfile, 'optionalAccess', _138 => _138[r.index]])) next.push({ nodeId: r.nodeId, index: r.index, subpath: _nullishCoalesce(r.subpath, () => ( 0)) });
4938
+ }
4939
+ this.widthPoints = next;
4940
+ this.notify();
4941
+ }
4942
+ /**
4943
+ * Sets one width point numerically: `index` names an existing point of path `nodeId`, null
4944
+ * (or left out) adds one at `values.t`. `left` / `right` are side widths in document units
4945
+ * (how far the stroke reaches on each side of the path), `t` the position along each subpath
4946
+ * (0–1). A point moved onto another one's `t` makes a step there. Selects the point and
4947
+ * refreshes an open width-point editor; one undo step. Ported from ArtCraft vectorcraft's
4948
+ * Width Point Edit dialog (`crates/tools/src/distort/width.rs`, MIT OR Apache-2.0).
4949
+ */
4950
+ setWidthPoint(target, values) {
4951
+ const node = this.getNode(target.nodeId);
4952
+ if (_optionalChain([node, 'optionalAccess', _139 => _139.type]) !== "path") return { ok: false, message: "There is no such path." };
4953
+ const g = vectorStrokeWidthGeometry(node);
4954
+ if (!g || !(g.unit > 0)) return { ok: false, message: "The path has no visible stroke." };
4955
+ const num = (v2) => typeof v2 === "number" && Number.isFinite(v2) ? v2 : void 0;
4956
+ const t = num(values.t);
4957
+ const left = num(values.left);
4958
+ const right = num(values.right);
4959
+ const scale = (w) => w === void 0 ? void 0 : Math.max(0, w) / g.unit;
4960
+ const profile = sanitizeWidthProfile(node.strokeWidthProfile);
4961
+ let points;
4962
+ let index;
4963
+ const existing = _nullishCoalesce(target.index, () => ( null));
4964
+ if (existing === null) {
4965
+ if (t === void 0) return { ok: false, message: "A new width point needs a position t." };
4966
+ if (profile.length >= VECTOR_WIDTH_PROFILE_MAX_POINTS) return { ok: false, message: "The profile has too many width points." };
4967
+ ({ points, index } = insertWidthPoint(profile.length ? profile : null, t, { left: scale(left), right: scale(right) }, { step: true }));
4968
+ } else {
4969
+ const p = profile[existing];
4970
+ if (!p) return { ok: false, message: "There is no such width point." };
4971
+ const sides2 = { left: _nullishCoalesce(scale(left), () => ( p.left)), right: _nullishCoalesce(scale(right), () => ( p.right)) };
4972
+ const tt = t === void 0 ? p.t : Math.min(Math.max(t, 0), 1);
4973
+ if (Math.abs(tt - p.t) <= VECTOR_WIDTH_SAME_T) {
4974
+ points = setWidthPoint(profile, existing, sides2);
4975
+ index = existing;
4976
+ } else {
4977
+ const rest = removeWidthPoints(profile, [existing]);
4978
+ if (rest.length) ({ points, index } = insertWidthPoint(rest, tt, sides2, { step: true }));
4979
+ else {
4980
+ points = [{ t: tt, ...sides2 }];
4981
+ index = 0;
4982
+ }
4983
+ }
4984
+ }
4985
+ const subpath = _optionalChain([this, 'access', _140 => _140.widthEdit, 'optionalAccess', _141 => _141.nodeId]) === node.id ? this.widthEdit.subpath : _nullishCoalesce(_optionalChain([this, 'access', _142 => _142.widthPoints, 'access', _143 => _143.find, 'call', _144 => _144((r) => r.nodeId === node.id), 'optionalAccess', _145 => _145.subpath]), () => ( 0));
4986
+ const edit = this.widthEdit;
4987
+ this.change("Width Point", () => this.replaceNode(node.id, withStyle(node, { strokeWidthProfile: points })));
4988
+ this.widthPoints = [{ nodeId: node.id, index, subpath }];
4989
+ if (_optionalChain([edit, 'optionalAccess', _146 => _146.nodeId]) === node.id && (edit.index === existing || existing === null)) this.widthEdit = this.widthEditFor(node.id, subpath, index, null);
4990
+ this.notify();
4991
+ return { ok: true };
4992
+ }
4993
+ /**
4994
+ * Opens the width-point editor (what a double-click with the width tool does) on a width
4995
+ * point, or on a spot of a stroke (`t` without `index`: applying it adds a point there).
4996
+ * Returns false when the path has no visible stroke.
4997
+ */
4998
+ openWidthPointEdit(target) {
4999
+ const edit = this.widthEditFor(target.nodeId, _nullishCoalesce(target.subpath, () => ( 0)), _nullishCoalesce(target.index, () => ( null)), _nullishCoalesce(target.t, () => ( null)));
5000
+ if (!edit) return false;
5001
+ this.widthEdit = edit;
5002
+ this.widthPoints = edit.index === null ? [] : [{ nodeId: edit.nodeId, index: edit.index, subpath: edit.subpath }];
5003
+ this.notify();
5004
+ return true;
5005
+ }
5006
+ /** Closes the width-point editor without changing anything. */
5007
+ closeWidthPointEdit() {
5008
+ if (!this.widthEdit) return;
5009
+ this.widthEdit = null;
5010
+ this.notify();
5011
+ }
5012
+ /** Applies `values` to the width point being edited (see `setWidthPoint`) and closes the editor. */
5013
+ commitWidthPointEdit(values) {
5014
+ const edit = this.widthEdit;
5015
+ if (!edit) return { ok: false, message: "No width point is being edited." };
5016
+ const r = this.setWidthPoint({ nodeId: edit.nodeId, index: edit.index }, edit.index === null ? { t: edit.t, ...values } : values);
5017
+ if (r.ok) this.closeWidthPointEdit();
5018
+ return r;
5019
+ }
5020
+ /** The editor's view of point `index` (or of the stroke at `t` when index is null) of `nodeId`. */
5021
+ widthEditFor(nodeId, subpath, index, t) {
5022
+ const node = this.getNode(nodeId);
5023
+ if (_optionalChain([node, 'optionalAccess', _147 => _147.type]) !== "path") return null;
5024
+ const g = vectorStrokeWidthGeometry(node);
5025
+ if (!g) return null;
5026
+ const profile = sanitizeWidthProfile(node.strokeWidthProfile);
5027
+ const p = index === null ? null : profile[index];
5028
+ if (index !== null && !p) return null;
5029
+ const tt = p ? p.t : Math.min(Math.max(_nullishCoalesce(t, () => ( 0)), 0), 1);
5030
+ const sides2 = _nullishCoalesce(p, () => ( widthProfileAt(profile.length ? profile : null, tt)));
5031
+ const sp = Math.min(Math.max(0, subpath), Math.max(0, node.subpaths.length - 1));
5032
+ const at2 = strokePositionPoint(g.line, sp, tt);
5033
+ const round = (v2) => Math.round(v2 * 1e6) / 1e6;
5034
+ const q = at2 ? applyMatrix(this.worldOf(nodeId), { x: at2.point[0], y: at2.point[1] }) : { x: 0, y: 0 };
5035
+ const point = { x: round(q.x), y: round(q.y) };
5036
+ return { nodeId, subpath: sp, index, t: round(tt), left: round(sides2.left * g.unit), right: round(sides2.right * g.unit), point };
5037
+ }
4852
5038
  /**
4853
5039
  * Sets the live corner radius (and optionally the kind) of anchors: `refs`, else the selected
4854
5040
  * anchors (direct tool), else every anchor of the `ids` / selected paths. Radius 0 makes the
@@ -4868,7 +5054,7 @@ var VectorEditorController = (_class3 = class {
4868
5054
  const refs = this.cornerTargets(options);
4869
5055
  if (!refs.length) return { ok: false, message: "Nothing is selected." };
4870
5056
  this.change("Corner Kind", () => {
4871
- this.doc = this.updateAnchors(this.doc, refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _132 => _132.corner, 'optionalAccess', _133 => _133.radius]), () => ( 0)), kind));
5057
+ this.doc = this.updateAnchors(this.doc, refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _148 => _148.corner, 'optionalAccess', _149 => _149.radius]), () => ( 0)), kind));
4872
5058
  });
4873
5059
  return { ok: true };
4874
5060
  }
@@ -5007,10 +5193,10 @@ var VectorEditorController = (_class3 = class {
5007
5193
  return this.selection.filter((id) => {
5008
5194
  let covered = false;
5009
5195
  const loc = locateVectorNode(this.doc.nodes, id);
5010
- let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _134 => _134.parent]), () => ( null));
5196
+ let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _150 => _150.parent]), () => ( null));
5011
5197
  while (parent) {
5012
5198
  if (this.selection.includes(parent.id)) covered = true;
5013
- parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _135 => _135(this.doc.nodes, parent.id), 'optionalAccess', _136 => _136.parent]), () => ( null));
5199
+ parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _151 => _151(this.doc.nodes, parent.id), 'optionalAccess', _152 => _152.parent]), () => ( null));
5014
5200
  }
5015
5201
  return !covered;
5016
5202
  });
@@ -5193,8 +5379,8 @@ var VectorEditorController = (_class3 = class {
5193
5379
  if (!ids.length) return null;
5194
5380
  const locs = ids.map((id2) => locateVectorNode(this.doc.nodes, id2));
5195
5381
  if (locs.some((l) => !l)) return null;
5196
- const parent = _nullishCoalesce(_optionalChain([locs, 'access', _137 => _137[0], 'access', _138 => _138.parent, 'optionalAccess', _139 => _139.id]), () => ( null));
5197
- if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access', _140 => _140.parent, 'optionalAccess', _141 => _141.id]), () => ( null))) !== parent)) return null;
5382
+ const parent = _nullishCoalesce(_optionalChain([locs, 'access', _153 => _153[0], 'access', _154 => _154.parent, 'optionalAccess', _155 => _155.id]), () => ( null));
5383
+ if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access', _156 => _156.parent, 'optionalAccess', _157 => _157.id]), () => ( null))) !== parent)) return null;
5198
5384
  const id = this.newId();
5199
5385
  this.change("Group", () => {
5200
5386
  const set = new Set(ids);
@@ -5217,7 +5403,7 @@ var VectorEditorController = (_class3 = class {
5217
5403
  }
5218
5404
  /** Replaces selected groups with their children. */
5219
5405
  ungroup(options = {}) {
5220
- const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _142 => _142.type]) === "group");
5406
+ const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _158 => _158.type]) === "group");
5221
5407
  if (!groups.length) return { ok: false, message: "Select a group to ungroup." };
5222
5408
  const set = new Set(groups.map((g) => g.id));
5223
5409
  this.change("Ungroup", () => {
@@ -5277,7 +5463,7 @@ var VectorEditorController = (_class3 = class {
5277
5463
  });
5278
5464
  const paths = [...new Set(order)].map((id2) => this.getNode(id2));
5279
5465
  if (paths.length < 2) return null;
5280
- const result = _chunk7EDLVBOOcjs.pathBoolean.call(void 0,
5466
+ const result = _chunkVNYRI265cjs.pathBoolean.call(void 0,
5281
5467
  BOOLEAN_OPS[op],
5282
5468
  paths.map((p) => ({
5283
5469
  path: vectorPathWorldBezier(
@@ -5327,11 +5513,11 @@ var VectorEditorController = (_class3 = class {
5327
5513
  /** A document-space path mapped into the parent space of node `id`. */
5328
5514
  intoParentSpace(id, path) {
5329
5515
  const parent = _nullishCoalesce(vectorParentMatrix(this.doc.nodes, id), () => ( IDENTITY_MATRIX));
5330
- return isRoot(parent) ? path : _chunk7EDLVBOOcjs.transformBezierPath.call(void 0, path, invertMatrix(parent));
5516
+ return isRoot(parent) ? path : _chunkVNYRI265cjs.transformBezierPath.call(void 0, path, invertMatrix(parent));
5331
5517
  }
5332
5518
  /** Splits selected compound paths into one path per subpath. */
5333
5519
  breakApart(options = {}) {
5334
- const paths = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _143 => _143.type]) === "path" && n.subpaths.length > 1);
5520
+ const paths = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _159 => _159.type]) === "path" && n.subpaths.length > 1);
5335
5521
  if (!paths.length) return { ok: false, message: "Select a compound path to break apart." };
5336
5522
  const created = [];
5337
5523
  const set = new Set(paths.map((p) => p.id));
@@ -5366,7 +5552,7 @@ var VectorEditorController = (_class3 = class {
5366
5552
  const entries = vectorPathEntries(this.doc, roots);
5367
5553
  if (entries.length < (op === "divide" || op === "outline" ? 1 : 2)) return [];
5368
5554
  const shapes = entries.map((e) => vectorPathfinderShape(e, op));
5369
- const result = _chunk7EDLVBOOcjs.pathfinder.call(void 0, op, shapes);
5555
+ const result = _chunkVNYRI265cjs.pathfinder.call(void 0, op, shapes);
5370
5556
  if (result.error || !result.pieces.length) return [];
5371
5557
  const last = entries[entries.length - 1].node.id;
5372
5558
  const topRoot = _nullishCoalesce([...roots].reverse().find((r) => {
@@ -5397,7 +5583,7 @@ var VectorEditorController = (_class3 = class {
5397
5583
  shapeBuilderMap(ids) {
5398
5584
  const roots = _nullishCoalesce(ids, () => ( this.selectedRoots()));
5399
5585
  const key = roots.join("\0");
5400
- if (_optionalChain([this, 'access', _144 => _144.builderCache, 'optionalAccess', _145 => _145.doc]) === this.doc && this.builderCache.key === key) return this.builderCache.map;
5586
+ if (_optionalChain([this, 'access', _160 => _160.builderCache, 'optionalAccess', _161 => _161.doc]) === this.doc && this.builderCache.key === key) return this.builderCache.map;
5401
5587
  const entries = roots.length ? vectorPathEntries(this.doc, roots) : [];
5402
5588
  const map = entries.length ? vectorShapeBuilderMap(entries) : null;
5403
5589
  this.builderCache = { doc: this.doc, key, map };
@@ -5411,7 +5597,7 @@ var VectorEditorController = (_class3 = class {
5411
5597
  const p = m.map.faceInterior(index);
5412
5598
  return {
5413
5599
  index,
5414
- d: _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, f.path, { precision: 3 }),
5600
+ d: _chunkVNYRI265cjs.bezierPathToSvg.call(void 0, f.path, { precision: 3 }),
5415
5601
  area: f.area,
5416
5602
  sources: m.map.faceSources(index).map((i) => m.entries[i].node.id),
5417
5603
  point: p ? { x: p[0], y: p[1] } : null
@@ -5426,20 +5612,23 @@ var VectorEditorController = (_class3 = class {
5426
5612
  * Shape Builder gesture along `points` (document units; one point is a click) over the faces
5427
5613
  * the selected (or given) paths make. Merges the faces touched into one shape styled like the
5428
5614
  * top-most shape over the first face (the current fill for a face only outlines enclose);
5429
- * with `erase` deletes them, and the pieces of unfilled open paths within `tolerance`. Every
5430
- * shape covering a touched face loses that area. One undo step.
5615
+ * with `erase` deletes them, the pieces of unfilled open paths and the edges of other outlines
5616
+ * (cut where paths meet) within `tolerance`: a deleted edge opens a closed subpath there,
5617
+ * splits an open one or takes a subpath it runs all the way round, and edges bounding a face
5618
+ * deleted in the same gesture go with the face. Every shape covering a touched face loses
5619
+ * that area; paths keep their own transforms. One undo step.
5431
5620
  */
5432
5621
  shapeBuilder(points, options = {}) {
5433
- const none = (message) => ({ ok: false, message, merged: null, faces: 0, lines: 0 });
5622
+ const none = (message) => ({ ok: false, message, merged: null, faces: 0, lines: 0, edges: 0 });
5434
5623
  if (!points.length || !points.every((p) => Number.isFinite(p.x) && Number.isFinite(p.y))) return none("Give the points of the gesture.");
5435
5624
  const m = this.shapeBuilderMap(options.ids);
5436
5625
  if (!m) return none("Select paths that make faces.");
5437
5626
  const erase = !!options.erase;
5438
5627
  const tol = erase ? _nullishCoalesce(options.tolerance, () => ( this.builderLineTolerance())) : null;
5439
- const touched = { faces: [], lines: [] };
5628
+ const touched = { faces: [], lines: [], edges: [] };
5440
5629
  const size = m.map.faces.reduce((s, f) => Math.max(s, f.bounds.maxX - f.bounds.minX, f.bounds.maxY - f.bounds.minY), 1);
5441
5630
  for (const p of samplePolyline(points, Math.max(size / 2e3, 0.25 / this.viewport.zoom))) addBuilderHit(touched, vectorShapeBuilderHit(m, p, tol));
5442
- if (!touched.faces.length && !touched.lines.length) return none("There is no face under the pointer.");
5631
+ if (!touched.faces.length && !touched.lines.length && !touched.edges.length) return none("There is no face under the pointer.");
5443
5632
  return this.applyShapeBuilder(m, touched, erase);
5444
5633
  }
5445
5634
  applyShapeBuilder(m, touched, erase) {
@@ -5450,12 +5639,13 @@ var VectorEditorController = (_class3 = class {
5450
5639
  let nodes = this.doc.nodes;
5451
5640
  m.entries.forEach((entry, i) => {
5452
5641
  const node = entry.node;
5453
- const toLocal = (path) => _chunk7EDLVBOOcjs.transformBezierPath.call(void 0, path, invertMatrix(multiplyMatrices(nodeMatrix(node), entry.parent)));
5642
+ const toLocal = (path) => _chunkVNYRI265cjs.transformBezierPath.call(void 0, path, invertMatrix(multiplyMatrices(nodeMatrix(node), entry.parent)));
5454
5643
  const list = [];
5455
5644
  const r = plan.replace.get(i);
5456
5645
  if (r === void 0) list.push(node);
5457
- else if (_optionalChain([r, 'optionalAccess', _146 => _146.kind]) === "fill") list.push({ ...node, fillRule: "nonzero", subpaths: bezierToSubpaths(toLocal(r.path)) });
5458
- else if (_optionalChain([r, 'optionalAccess', _147 => _147.kind]) === "pieces") {
5646
+ else if (_optionalChain([r, 'optionalAccess', _162 => _162.kind]) === "fill") list.push({ ...node, fillRule: "nonzero", subpaths: bezierToSubpaths(toLocal(r.path)) });
5647
+ else if (_optionalChain([r, 'optionalAccess', _163 => _163.kind]) === "outline") list.push({ ...node, subpaths: bezierToSubpaths(toLocal(r.path)) });
5648
+ else if (_optionalChain([r, 'optionalAccess', _164 => _164.kind]) === "pieces") {
5459
5649
  r.paths.forEach(
5460
5650
  (path, k) => list.push({ ...node, id: k === 0 ? node.id : this.newId(), subpaths: bezierToSubpaths(toLocal(path)).map((sp) => ({ ...sp, closed: false })) })
5461
5651
  );
@@ -5485,11 +5675,11 @@ var VectorEditorController = (_class3 = class {
5485
5675
  this.editingId = null;
5486
5676
  });
5487
5677
  this.builderCache = null;
5488
- return { ok: true, merged: mergedId, faces: touched.faces.length, lines: touched.lines.length };
5678
+ return { ok: true, merged: mergedId, faces: touched.faces.length, lines: touched.lines.length, edges: erase ? vectorBuilderErasedEdges(m, touched).length : 0 };
5489
5679
  }
5490
5680
  /** The paint bucket's map of the visible outlines, cached per document and gap. */
5491
5681
  fillMap(gap) {
5492
- if (_optionalChain([this, 'access', _148 => _148.fillCache, 'optionalAccess', _149 => _149.doc]) === this.doc && this.fillCache.gap === gap) return this.fillCache.map;
5682
+ if (_optionalChain([this, 'access', _165 => _165.fillCache, 'optionalAccess', _166 => _166.doc]) === this.doc && this.fillCache.gap === gap) return this.fillCache.map;
5493
5683
  const map = vectorFillMap(this.doc, gap);
5494
5684
  this.fillCache = { doc: this.doc, gap, map };
5495
5685
  return map;
@@ -5503,8 +5693,8 @@ var VectorEditorController = (_class3 = class {
5503
5693
  */
5504
5694
  fillRegion(point, options = {}) {
5505
5695
  if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) return null;
5506
- const region = _optionalChain([this, 'access', _150 => _150.fillMap, 'call', _151 => _151(_nullishCoalesce(options.gap, () => ( this.editing.fillGap))), 'optionalAccess', _152 => _152.regionAt, 'call', _153 => _153([point.x, point.y])]);
5507
- if (!_optionalChain([region, 'optionalAccess', _154 => _154.length])) return null;
5696
+ const region = _optionalChain([this, 'access', _167 => _167.fillMap, 'call', _168 => _168(_nullishCoalesce(options.gap, () => ( this.editing.fillGap))), 'optionalAccess', _169 => _169.regionAt, 'call', _170 => _170([point.x, point.y])]);
5697
+ if (!_optionalChain([region, 'optionalAccess', _171 => _171.length])) return null;
5508
5698
  let below = null;
5509
5699
  const roots = new Set(this.selectedRoots());
5510
5700
  walkVectorNodes(this.doc.nodes, (n) => {
@@ -5530,8 +5720,8 @@ var VectorEditorController = (_class3 = class {
5530
5720
  /** Updates the Shape Builder / paint bucket highlight for the pointer at `p`; true when it changed. */
5531
5721
  hoverBuilder(p, alt) {
5532
5722
  if (this.tool === "paintBucket") {
5533
- const region = _nullishCoalesce(_optionalChain([this, 'access', _155 => _155.fillMap, 'call', _156 => _156(this.editing.fillGap), 'optionalAccess', _157 => _157.regionAt, 'call', _158 => _158([p.x, p.y])]), () => ( null));
5534
- const key2 = region ? _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, region, { precision: 2 }) : null;
5723
+ const region = _nullishCoalesce(_optionalChain([this, 'access', _172 => _172.fillMap, 'call', _173 => _173(this.editing.fillGap), 'optionalAccess', _174 => _174.regionAt, 'call', _175 => _175([p.x, p.y])]), () => ( null));
5724
+ const key2 = region ? _chunkVNYRI265cjs.bezierPathToSvg.call(void 0, region, { precision: 2 }) : null;
5535
5725
  if (key2 === this.builderHoverKey) return false;
5536
5726
  this.builderHoverKey = key2;
5537
5727
  this.builderPreview = region ? { mode: "fill", faces: [region], lines: [], trail: null } : null;
@@ -5542,7 +5732,9 @@ var VectorEditorController = (_class3 = class {
5542
5732
  const key = hit ? `${alt ? "erase" : "merge"}:${JSON.stringify(hit)}` : null;
5543
5733
  if (key === this.builderHoverKey) return false;
5544
5734
  this.builderHoverKey = key;
5545
- this.builderPreview = hit && m ? this.builderPreviewOf(m, "face" in hit ? { faces: [hit.face], lines: [] } : { faces: [], lines: [hit.line] }, alt, null) : null;
5735
+ const touched = { faces: [], lines: [], edges: [] };
5736
+ addBuilderHit(touched, hit);
5737
+ this.builderPreview = hit && m ? this.builderPreviewOf(m, touched, alt, null) : null;
5546
5738
  return true;
5547
5739
  }
5548
5740
  builderPreviewOf(m, touched, erase, trail) {
@@ -5550,7 +5742,8 @@ var VectorEditorController = (_class3 = class {
5550
5742
  return {
5551
5743
  mode: erase ? "erase" : "merge",
5552
5744
  faces: touched.faces.map((f) => faces[f].path),
5553
- lines: touched.lines.map((k) => m.pieces[k].path),
5745
+ // Edges bounding a face touched too go with the face, so they are not shown apart.
5746
+ lines: [...touched.lines.map((k) => m.pieces[k].path), ...erase ? vectorBuilderErasedEdges(m, touched).map((k) => m.edges[k].path) : []],
5554
5747
  trail
5555
5748
  };
5556
5749
  }
@@ -5566,7 +5759,7 @@ var VectorEditorController = (_class3 = class {
5566
5759
  const m = this.selectedRoots().length ? this.shapeBuilderMap() : null;
5567
5760
  if (!m) return;
5568
5761
  const erase = !!input.alt;
5569
- const touched = { faces: [], lines: [] };
5762
+ const touched = { faces: [], lines: [], edges: [] };
5570
5763
  addBuilderHit(touched, vectorShapeBuilderHit(m, p, erase ? this.builderLineTolerance() : null));
5571
5764
  this.drag = { kind: "builder", erase, points: [p], touched, map: m };
5572
5765
  this.interaction = { type: "builder", mode: erase ? "erase" : "merge", points: [p] };
@@ -5586,7 +5779,7 @@ var VectorEditorController = (_class3 = class {
5586
5779
  upBuilder(drag) {
5587
5780
  this.builderPreview = null;
5588
5781
  this.builderHoverKey = null;
5589
- if (!drag.touched.faces.length && !drag.touched.lines.length) {
5782
+ if (!drag.touched.faces.length && !drag.touched.lines.length && !drag.touched.edges.length) {
5590
5783
  this.notify();
5591
5784
  return;
5592
5785
  }
@@ -5635,7 +5828,7 @@ var VectorEditorController = (_class3 = class {
5635
5828
  /** `doc` with anchor `ref` moved to document point `to` by a Curvature-tool move. */
5636
5829
  curvatureMoved(doc, ref, to) {
5637
5830
  const node = findVectorNode(doc.nodes, ref.nodeId);
5638
- if (_optionalChain([node, 'optionalAccess', _159 => _159.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
5831
+ if (_optionalChain([node, 'optionalAccess', _176 => _176.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
5639
5832
  const q = this.toLocal(ref.nodeId, to, doc);
5640
5833
  const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? curvatureMoveAnchor(sp, ref.index, q) : sp);
5641
5834
  const nodes = mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n);
@@ -5644,7 +5837,7 @@ var VectorEditorController = (_class3 = class {
5644
5837
  /** `doc` with segment `ref` bent through document point `p` at parameter `t`. */
5645
5838
  bent(doc, ref, t, p) {
5646
5839
  const node = findVectorNode(doc.nodes, ref.nodeId);
5647
- if (_optionalChain([node, 'optionalAccess', _160 => _160.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
5840
+ if (_optionalChain([node, 'optionalAccess', _177 => _177.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
5648
5841
  const q = this.toLocal(ref.nodeId, p, doc);
5649
5842
  const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? bendSegment(sp, ref.segment, t, q) : sp);
5650
5843
  return { ...doc, nodes: mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n) };
@@ -5655,7 +5848,7 @@ var VectorEditorController = (_class3 = class {
5655
5848
  */
5656
5849
  bendSegment(ref, to, t = 0.5) {
5657
5850
  const node = this.getNode(ref.nodeId);
5658
- const sp = _optionalChain([node, 'optionalAccess', _161 => _161.type]) === "path" ? node.subpaths[ref.subpath] : void 0;
5851
+ const sp = _optionalChain([node, 'optionalAccess', _178 => _178.type]) === "path" ? node.subpaths[ref.subpath] : void 0;
5659
5852
  if (!sp || ref.segment < 0 || ref.segment >= (sp.closed ? sp.anchors.length : sp.anchors.length - 1) || sp.anchors.length < 2) return { ok: false, message: "There is no such segment." };
5660
5853
  if (node.locked) return { ok: false, message: "The path is locked." };
5661
5854
  this.change("Bend Segment", () => {
@@ -5690,7 +5883,7 @@ var VectorEditorController = (_class3 = class {
5690
5883
  /** Adds an anchor on the nearest segment of `nodeId` to document point `point` (shape unchanged). */
5691
5884
  insertAnchorAt(nodeId, point) {
5692
5885
  const node = this.getNode(nodeId);
5693
- if (_optionalChain([node, 'optionalAccess', _162 => _162.type]) !== "path") return null;
5886
+ if (_optionalChain([node, 'optionalAccess', _179 => _179.type]) !== "path") return null;
5694
5887
  const hit = nearestSegment(node, this.toLocal(nodeId, point));
5695
5888
  if (!hit) return null;
5696
5889
  const { subpath, index } = insertAnchor(node.subpaths[hit.subpath], hit.segment, hit.t);
@@ -5734,7 +5927,7 @@ var VectorEditorController = (_class3 = class {
5734
5927
  setClosed(closed, nodeId) {
5735
5928
  const targets = nodeId ? (() => {
5736
5929
  const n = this.getNode(nodeId);
5737
- return _optionalChain([n, 'optionalAccess', _163 => _163.type]) === "path" ? n.subpaths.map((_, s) => ({ nodeId, subpath: s })) : [];
5930
+ return _optionalChain([n, 'optionalAccess', _180 => _180.type]) === "path" ? n.subpaths.map((_, s) => ({ nodeId, subpath: s })) : [];
5738
5931
  })() : this.anchors.map((r) => ({ nodeId: r.nodeId, subpath: r.subpath }));
5739
5932
  if (!targets.length) return { ok: false, message: "Select a path or points." };
5740
5933
  this.change(closed ? "Close Path" : "Open Path", () => {
@@ -5755,7 +5948,7 @@ var VectorEditorController = (_class3 = class {
5755
5948
  this.setDoc(
5756
5949
  mapTree(
5757
5950
  this.doc.nodes,
5758
- (n) => n.type === "path" && set.has(n.id) ? { ...n, subpaths: n.subpaths.map(reverseSubpath), ..._optionalChain([n, 'access', _164 => _164.strokeWidthProfile, 'optionalAccess', _165 => _165.length]) ? { strokeWidthProfile: reverseWidthProfile(n.strokeWidthProfile) } : {} } : n
5951
+ (n) => n.type === "path" && set.has(n.id) ? { ...n, subpaths: n.subpaths.map(reverseSubpath), ..._optionalChain([n, 'access', _181 => _181.strokeWidthProfile, 'optionalAccess', _182 => _182.length]) ? { strokeWidthProfile: reverseWidthProfile(n.strokeWidthProfile) } : {} } : n
5759
5952
  )
5760
5953
  );
5761
5954
  this.anchors = [];
@@ -5893,7 +6086,7 @@ var VectorEditorController = (_class3 = class {
5893
6086
  */
5894
6087
  cutPathAt(nodeId, point) {
5895
6088
  const node = this.getNode(nodeId);
5896
- if (_optionalChain([node, 'optionalAccess', _166 => _166.type]) !== "path" || node.locked) return null;
6089
+ if (_optionalChain([node, 'optionalAccess', _183 => _183.type]) !== "path" || node.locked) return null;
5897
6090
  const local = this.toLocal(nodeId, point);
5898
6091
  const hit = nearestSegment(node, local);
5899
6092
  if (!hit) return null;
@@ -5943,7 +6136,7 @@ var VectorEditorController = (_class3 = class {
5943
6136
  if (n.visible === false || n.locked || parent && blocked.has(parent.id)) blocked.add(n.id);
5944
6137
  });
5945
6138
  const visible = (p) => !blocked.has(p.id);
5946
- if (_optionalChain([ids, 'optionalAccess', _167 => _167.length]) || this.selection.length) return this.pathTargets(_nullishCoalesce(ids, () => ( this.selection))).filter(visible);
6139
+ if (_optionalChain([ids, 'optionalAccess', _184 => _184.length]) || this.selection.length) return this.pathTargets(_nullishCoalesce(ids, () => ( this.selection))).filter(visible);
5947
6140
  const box = polylineBounds(points.map(toPoint2), pad);
5948
6141
  if (!box) return [];
5949
6142
  const out = [];
@@ -5999,10 +6192,10 @@ var VectorEditorController = (_class3 = class {
5999
6192
  const region = eraserRegion(pts, width);
6000
6193
  if (!region.length) return [];
6001
6194
  const plan = [];
6002
- const rb = _chunk7EDLVBOOcjs.bezierPathBounds.call(void 0, region);
6195
+ const rb = _chunkVNYRI265cjs.bezierPathBounds.call(void 0, region);
6003
6196
  const reach = rb ? [{ x: rb.minX, y: rb.minY }, { x: rb.maxX, y: rb.maxY }] : points;
6004
6197
  for (const node of this.strokeTargets(reach, options.ids, 0)) {
6005
- const local = _chunk7EDLVBOOcjs.transformBezierPath.call(void 0, region, invertMatrix(this.worldOf(node.id)));
6198
+ const local = _chunkVNYRI265cjs.transformBezierPath.call(void 0, region, invertMatrix(this.worldOf(node.id)));
6006
6199
  const live = node.subpaths.filter((sp) => sp.anchors.length > 1);
6007
6200
  if (!live.length) continue;
6008
6201
  if (hasPaint(node.fill) && live.every((sp) => sp.closed)) {
@@ -6029,7 +6222,7 @@ var VectorEditorController = (_class3 = class {
6029
6222
  finishPath() {
6030
6223
  if (!this.pen) return;
6031
6224
  const node = this.getNode(this.pen.nodeId);
6032
- const sp = _optionalChain([node, 'optionalAccess', _168 => _168.subpaths, 'access', _169 => _169[this.pen.subpath]]);
6225
+ const sp = _optionalChain([node, 'optionalAccess', _185 => _185.subpaths, 'access', _186 => _186[this.pen.subpath]]);
6033
6226
  const pen = this.pen;
6034
6227
  this.pen = null;
6035
6228
  this.pointer = null;
@@ -6073,7 +6266,7 @@ var VectorEditorController = (_class3 = class {
6073
6266
  if (trimmed.startsWith("{")) {
6074
6267
  try {
6075
6268
  const data = JSON.parse(trimmed);
6076
- if (_optionalChain([data, 'optionalAccess', _170 => _170.type]) === VECTOR_CLIPBOARD_TYPE && Array.isArray(data.nodes)) nodes = data.nodes;
6269
+ if (_optionalChain([data, 'optionalAccess', _187 => _187.type]) === VECTOR_CLIPBOARD_TYPE && Array.isArray(data.nodes)) nodes = data.nodes;
6077
6270
  } catch (e3) {
6078
6271
  nodes = null;
6079
6272
  }
@@ -6085,7 +6278,7 @@ var VectorEditorController = (_class3 = class {
6085
6278
  }
6086
6279
  }
6087
6280
  } else nodes = this.clipboard;
6088
- if (!_optionalChain([nodes, 'optionalAccess', _171 => _171.length])) return [];
6281
+ if (!_optionalChain([nodes, 'optionalAccess', _188 => _188.length])) return [];
6089
6282
  const offset = _nullishCoalesce(options.offset, () => ( (text && !text.trim().startsWith("{") ? { x: 0, y: 0 } : { x: 10, y: 10 })));
6090
6283
  const copies = nodes.map((n) => transformVectorNode(this.freshIds(n), translateMatrix(offset.x, offset.y)));
6091
6284
  this.change("Paste", () => {
@@ -6136,7 +6329,7 @@ var VectorEditorController = (_class3 = class {
6136
6329
  hitHandle(screen) {
6137
6330
  for (const ref of getVisibleHandles(this.view()).reverse()) {
6138
6331
  const a = getAnchorWorld(this.doc, ref);
6139
- const h = _optionalChain([a, 'optionalAccess', _172 => _172[ref.handle]]);
6332
+ const h = _optionalChain([a, 'optionalAccess', _189 => _189[ref.handle]]);
6140
6333
  if (!h) continue;
6141
6334
  const s = this.documentToScreen(h);
6142
6335
  if (Math.hypot(s.x - screen.x, s.y - screen.y) <= this.hitRadius) return ref;
@@ -6250,13 +6443,19 @@ var VectorEditorController = (_class3 = class {
6250
6443
  }
6251
6444
  }
6252
6445
  widthView() {
6253
- return { document: this.doc, selection: this.selection, tool: this.tool, hover: this.hover, interaction: this.interaction, selectedWidthPoint: this.widthPoint };
6446
+ return { document: this.doc, selection: this.selection, tool: this.tool, hover: this.hover, interaction: this.interaction, selectedWidthPoints: this.widthPoints };
6254
6447
  }
6255
6448
  /** The width point centre or side end under `screen` (width tool). */
6256
6449
  hitWidthHandle(screen) {
6450
+ const shown = this.hitWidthHandleIn(screen, this.widthView());
6451
+ if (shown) return shown;
6452
+ const spot = this.strokeSpotAt(this.screenToDocument(screen));
6453
+ return spot && spot.nodeId !== _optionalChain([this, 'access', _190 => _190.hover, 'optionalAccess', _191 => _191.nodeId]) ? this.hitWidthHandleIn(screen, { ...this.widthView(), hover: { nodeId: spot.nodeId } }) : null;
6454
+ }
6455
+ hitWidthHandleIn(screen, view) {
6257
6456
  let best = null;
6258
6457
  let bestD = Infinity;
6259
- for (const h of getVectorWidthHandles(this.widthView())) {
6458
+ for (const h of getVectorWidthHandles(view)) {
6260
6459
  for (const [part, q] of [["center", h], ["left", h.left], ["right", h.right]]) {
6261
6460
  const s = this.documentToScreen(q);
6262
6461
  const d = Math.hypot(s.x - screen.x, s.y - screen.y);
@@ -6306,14 +6505,13 @@ var VectorEditorController = (_class3 = class {
6306
6505
  downWidth(screen, p, input) {
6307
6506
  const hit = this.hitWidthHandle(screen);
6308
6507
  const spot = hit ? null : this.strokeSpotAt(p);
6309
- const nodeId = _nullishCoalesce(_optionalChain([hit, 'optionalAccess', _173 => _173.ref, 'access', _174 => _174.nodeId]), () => ( _optionalChain([spot, 'optionalAccess', _175 => _175.nodeId])));
6508
+ const nodeId = _nullishCoalesce(_optionalChain([hit, 'optionalAccess', _192 => _192.ref, 'access', _193 => _193.nodeId]), () => ( _optionalChain([spot, 'optionalAccess', _194 => _194.nodeId])));
6310
6509
  const node = nodeId ? this.getNode(nodeId) : void 0;
6311
- const g = _optionalChain([node, 'optionalAccess', _176 => _176.type]) === "path" ? vectorStrokeWidthGeometry(node) : null;
6510
+ const g = _optionalChain([node, 'optionalAccess', _195 => _195.type]) === "path" ? vectorStrokeWidthGeometry(node) : null;
6511
+ if (this.widthEdit) this.widthEdit = null;
6312
6512
  if (!node || node.type !== "path" || !g) {
6313
- if (this.widthPoint) {
6314
- this.widthPoint = null;
6315
- this.notify();
6316
- }
6513
+ if (!input.shift) this.widthPoints = [];
6514
+ this.notify();
6317
6515
  return;
6318
6516
  }
6319
6517
  const before = this.snapshot();
@@ -6322,15 +6520,29 @@ var VectorEditorController = (_class3 = class {
6322
6520
  let ref;
6323
6521
  let mode = "width";
6324
6522
  let side = "both";
6523
+ let group = [];
6524
+ let shift = false;
6325
6525
  let t;
6326
6526
  let subpath;
6327
6527
  if (hit) {
6328
6528
  profile = sanitizeWidthProfile(node.strokeWidthProfile);
6329
6529
  ref = { nodeId: node.id, index: hit.ref.index, subpath: hit.ref.subpath };
6330
- t = _nullishCoalesce(_optionalChain([profile, 'access', _177 => _177[ref.index], 'optionalAccess', _178 => _178.t]), () => ( 0));
6530
+ t = _nullishCoalesce(_optionalChain([profile, 'access', _196 => _196[ref.index], 'optionalAccess', _197 => _197.t]), () => ( 0));
6331
6531
  subpath = hit.ref.subpath;
6332
- if (hit.ref.part === "center") mode = "move";
6532
+ if (hit.ref.part === "center") mode = input.alt && profile.length < VECTOR_WIDTH_PROFILE_MAX_POINTS ? "copy" : "move";
6333
6533
  else if (input.alt) side = hit.ref.part;
6534
+ const same = this.widthPoints.length > 0 && this.widthPoints[0].nodeId === node.id;
6535
+ const selected = same && this.widthPoints.some((r) => r.index === ref.index);
6536
+ if (input.shift && mode !== "copy") {
6537
+ if (selected) {
6538
+ this.widthPoints = this.widthPoints.filter((r) => r.index !== ref.index);
6539
+ this.notify();
6540
+ return;
6541
+ }
6542
+ this.widthPoints = same ? [...this.widthPoints, ref] : [ref];
6543
+ shift = true;
6544
+ } else if (mode === "copy" || !selected) this.widthPoints = [ref];
6545
+ if (mode !== "copy") group = this.widthPoints.filter((r) => r.index !== ref.index).map((r) => r.index);
6334
6546
  } else {
6335
6547
  const near2 = nearestStrokePosition(g.line, [local.x, local.y]);
6336
6548
  const inserted = insertWidthPoint(strokeWidthProfileOf(node), near2.t);
@@ -6339,17 +6551,21 @@ var VectorEditorController = (_class3 = class {
6339
6551
  t = near2.t;
6340
6552
  ref = { nodeId: node.id, index: inserted.index, subpath };
6341
6553
  if (input.alt) side = near2.side;
6554
+ this.widthPoints = [ref];
6342
6555
  }
6343
6556
  const at2 = strokePositionPoint(g.line, subpath, t);
6344
6557
  if (!at2) return;
6345
6558
  const base = { ...this.doc, nodes: mapTree(this.doc.nodes, (n) => n.id === node.id ? { ...n, strokeWidthProfile: profile } : n) };
6346
6559
  this.doc = base;
6347
- this.widthPoint = { nodeId: node.id, index: ref.index, subpath };
6348
6560
  this.drag = {
6349
6561
  kind: "width",
6350
6562
  ref,
6351
6563
  mode,
6352
6564
  side,
6565
+ source: ref.index,
6566
+ group,
6567
+ shift,
6568
+ renumber: null,
6353
6569
  origin: { x: at2.point[0], y: at2.point[1] },
6354
6570
  normal: { x: at2.tangent[1], y: -at2.tangent[0] },
6355
6571
  unit: g.unit,
@@ -6364,6 +6580,31 @@ var VectorEditorController = (_class3 = class {
6364
6580
  this.interaction = { type: "width", ref, mode, width: (w.left + w.right) * g.unit };
6365
6581
  this.notify(!hit);
6366
6582
  }
6583
+ /**
6584
+ * A width-tool slide to `t` on the dragged point's subpath lands exactly on another width
6585
+ * point (not `skip`) within the pick distance on screen: dropping a point onto another makes
6586
+ * a discontinuous point (two points at one `t`, the width steps there).
6587
+ */
6588
+ snapWidthT(drag, node, line2, t, skip) {
6589
+ const m = this.worldOf(node.id, drag.base);
6590
+ const at2 = strokePositionPoint(line2, drag.ref.subpath, t);
6591
+ if (!at2) return t;
6592
+ const here = this.documentToScreen(applyMatrix(m, { x: at2.point[0], y: at2.point[1] }));
6593
+ let best = t;
6594
+ let bestD = this.hitRadius;
6595
+ drag.profile.forEach((w, i) => {
6596
+ if (i === skip || drag.group.includes(i)) return;
6597
+ const q = strokePositionPoint(line2, drag.ref.subpath, w.t);
6598
+ if (!q) return;
6599
+ const s = this.documentToScreen(applyMatrix(m, { x: q.point[0], y: q.point[1] }));
6600
+ const d = Math.hypot(s.x - here.x, s.y - here.y);
6601
+ if (d <= bestD) {
6602
+ best = w.t;
6603
+ bestD = d;
6604
+ }
6605
+ });
6606
+ return best;
6607
+ }
6367
6608
  /** The nearest outline point of any visible, unlocked path within the pick distance of document point `p`. */
6368
6609
  nearestOutline(p) {
6369
6610
  let best = null;
@@ -6552,7 +6793,7 @@ var VectorEditorController = (_class3 = class {
6552
6793
  }
6553
6794
  downPen(screen, p, input) {
6554
6795
  const before = this.snapshot();
6555
- const sp = this.pen ? _optionalChain([this, 'access', _179 => _179.getNode, 'call', _180 => _180(this.pen.nodeId), 'optionalAccess', _181 => _181.subpaths, 'access', _182 => _182[this.pen.subpath]]) : void 0;
6796
+ const sp = this.pen ? _optionalChain([this, 'access', _198 => _198.getNode, 'call', _199 => _199(this.pen.nodeId), 'optionalAccess', _200 => _200.subpaths, 'access', _201 => _201[this.pen.subpath]]) : void 0;
6556
6797
  if (this.pen && sp) {
6557
6798
  const pen = this.pen;
6558
6799
  const first = { nodeId: pen.nodeId, subpath: pen.subpath, index: 0 };
@@ -6814,7 +7055,7 @@ var VectorEditorController = (_class3 = class {
6814
7055
  }
6815
7056
  case "shape": {
6816
7057
  const node = this.getNode(drag.nodeId);
6817
- if (_optionalChain([node, 'optionalAccess', _183 => _183.type]) !== "path") return;
7058
+ if (_optionalChain([node, 'optionalAccess', _202 => _202.type]) !== "path") return;
6818
7059
  const leave = { at: drag.start, constrain: input.shift ? drag.tool === "line" ? "angle" : "square" : null, curves: false };
6819
7060
  let end = this.snapDrawn(p, leave, input, () => drag.targets ??= this.drawingTargets(null));
6820
7061
  if (!this.snappedToObject && drag.tool !== "line" && !input.shift && !input.mod && this.snap.objects && drag.targets) end = this.matchShapeSize(drag.targets, drag.start, end, input.alt);
@@ -6848,19 +7089,35 @@ var VectorEditorController = (_class3 = class {
6848
7089
  drag.moved = true;
6849
7090
  const node = findVectorNode(drag.base.nodes, drag.ref.nodeId);
6850
7091
  const local = this.toLocal(node.id, p, drag.base);
7092
+ const src = drag.profile[drag.source];
6851
7093
  let profile;
6852
- if (drag.mode === "move") {
7094
+ let index = drag.source;
7095
+ if (drag.mode !== "width") {
6853
7096
  const g = vectorStrokeWidthGeometry(node);
6854
7097
  const near2 = g ? nearestStrokePosition(g.line, [local.x, local.y], drag.ref.subpath) : null;
6855
- if (!near2) return;
6856
- profile = setWidthPoint(drag.profile, drag.ref.index, { t: near2.t });
7098
+ if (!g || !near2) return;
7099
+ const t = drag.group.length ? near2.t : this.snapWidthT(drag, node, g.line, near2.t, drag.source);
7100
+ if (drag.mode === "copy") {
7101
+ ({ points: profile, index } = insertWidthPoint(drag.profile, t, { left: src.left, right: src.right }, { step: true }));
7102
+ } else if (drag.group.length) {
7103
+ const shifted = shiftWidthPoints(drag.profile, [drag.source, ...drag.group], t - src.t);
7104
+ profile = shifted.points;
7105
+ drag.renumber = shifted.renumber;
7106
+ index = _nullishCoalesce(shifted.renumber.get(drag.source), () => ( drag.source));
7107
+ } else profile = setWidthPoint(drag.profile, drag.source, { t });
6857
7108
  } else {
6858
7109
  const off = ((local.x - drag.origin.x) * drag.normal.x + (local.y - drag.origin.y) * drag.normal.y) / drag.unit;
6859
7110
  const patch = drag.side === "both" ? { left: Math.abs(off), right: Math.abs(off) } : drag.side === "left" ? { left: Math.max(0, off) } : { right: Math.max(0, -off) };
6860
- profile = setWidthPoint(drag.profile, drag.ref.index, patch);
7111
+ profile = setWidthPoint(drag.profile, drag.source, patch);
7112
+ if (drag.group.length) {
7113
+ const dl = profile[drag.source].left - src.left;
7114
+ const dr = profile[drag.source].right - src.right;
7115
+ for (const i of drag.group) profile = setWidthPoint(profile, i, { left: Math.max(0, drag.profile[i].left + dl), right: Math.max(0, drag.profile[i].right + dr) });
7116
+ }
6861
7117
  }
6862
7118
  this.doc = { ...drag.base, nodes: mapTree(drag.base.nodes, (n) => n.id === node.id ? { ...n, strokeWidthProfile: profile } : n) };
6863
- const w = profile[drag.ref.index];
7119
+ drag.ref = { ...drag.ref, index };
7120
+ const w = profile[index];
6864
7121
  this.interaction = { type: "width", ref: drag.ref, mode: drag.mode, width: (w.left + w.right) * drag.unit };
6865
7122
  this.notify(true);
6866
7123
  return;
@@ -6872,14 +7129,14 @@ var VectorEditorController = (_class3 = class {
6872
7129
  for (const q of drag.lazy.push([p.x, p.y])) {
6873
7130
  if (Math.hypot(q[0] - last[0], q[1] - last[1]) * this.viewport.zoom < 1.5) continue;
6874
7131
  drag.points.push(q);
6875
- _optionalChain([drag, 'access', _184 => _184.pressures, 'optionalAccess', _185 => _185.push, 'call', _186 => _186(drag.pressure)]);
7132
+ _optionalChain([drag, 'access', _203 => _203.pressures, 'optionalAccess', _204 => _204.push, 'call', _205 => _205(drag.pressure)]);
6876
7133
  moved = true;
6877
7134
  }
6878
7135
  const string = drag.lazy.string();
6879
7136
  const node = this.getNode(drag.nodeId);
6880
- if (_optionalChain([node, 'optionalAccess', _187 => _187.type]) !== "path") return;
7137
+ if (_optionalChain([node, 'optionalAccess', _206 => _206.type]) !== "path") return;
6881
7138
  if (!moved) {
6882
- if (string && _optionalChain([this, 'access', _188 => _188.interaction, 'optionalAccess', _189 => _189.type]) === "draw") {
7139
+ if (string && _optionalChain([this, 'access', _207 => _207.interaction, 'optionalAccess', _208 => _208.type]) === "draw") {
6883
7140
  this.interaction = { ...this.interaction, string: [{ x: string[0][0], y: string[0][1] }, { x: string[1][0], y: string[1][1] }] };
6884
7141
  this.notify();
6885
7142
  }
@@ -6915,8 +7172,8 @@ var VectorEditorController = (_class3 = class {
6915
7172
  }
6916
7173
  this.setGuides(res);
6917
7174
  return {
6918
- dx: _optionalChain([res, 'optionalAccess', _190 => _190.x]) ? dx + res.dx : axes.x ? this.gridValue(origin.x + dx) - origin.x : dx,
6919
- dy: _optionalChain([res, 'optionalAccess', _191 => _191.y]) ? dy + res.dy : axes.y ? this.gridValue(origin.y + dy) - origin.y : dy
7175
+ dx: _optionalChain([res, 'optionalAccess', _209 => _209.x]) ? dx + res.dx : axes.x ? this.gridValue(origin.x + dx) - origin.x : dx,
7176
+ dy: _optionalChain([res, 'optionalAccess', _210 => _210.y]) ? dy + res.dy : axes.y ? this.gridValue(origin.y + dy) - origin.y : dy
6920
7177
  };
6921
7178
  }
6922
7179
  /** The anchor a point drag snaps by, where it started: the grabbed one, else the moving one nearest the press. */
@@ -6995,7 +7252,7 @@ var VectorEditorController = (_class3 = class {
6995
7252
  if (drag.moved) return this.commit("Corner Radius", drag.before);
6996
7253
  if (drag.alt) {
6997
7254
  const kind = nextVectorCornerKind(drag.widget.kind);
6998
- this.doc = this.updateAnchors(drag.base, drag.refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _192 => _192.corner, 'optionalAccess', _193 => _193.radius]), () => ( 0)), kind));
7255
+ this.doc = this.updateAnchors(drag.base, drag.refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _211 => _211.corner, 'optionalAccess', _212 => _212.radius]), () => ( 0)), kind));
6999
7256
  return this.commit("Corner Kind", drag.before);
7000
7257
  }
7001
7258
  const ref = drag.widget.ref;
@@ -7006,12 +7263,23 @@ var VectorEditorController = (_class3 = class {
7006
7263
  return;
7007
7264
  }
7008
7265
  case "width": {
7009
- if (drag.moved) return this.commit(drag.mode === "move" ? "Move Width Point" : "Width Point", drag.before);
7266
+ if (drag.moved) {
7267
+ const several = drag.group.length > 0;
7268
+ if (drag.mode === "copy") this.widthPoints = [drag.ref];
7269
+ else if (drag.mode === "move" && several) {
7270
+ this.widthPoints = [drag.source, ...drag.group].map((i) => ({ ...drag.ref, index: _nullishCoalesce(_optionalChain([drag, 'access', _213 => _213.renumber, 'optionalAccess', _214 => _214.get, 'call', _215 => _215(i)]), () => ( i)) }));
7271
+ } else if (!several) this.widthPoints = [drag.ref];
7272
+ const label = drag.mode === "copy" ? "Copy Width Point" : drag.mode === "move" ? several ? "Move Width Points" : "Move Width Point" : several ? "Width Points" : "Width Point";
7273
+ return this.commit(label, drag.before);
7274
+ }
7010
7275
  if (drag.created) {
7011
7276
  this.doc = drag.before.doc;
7012
- this.widthPoint = null;
7277
+ this.widthPoints = [];
7013
7278
  this.notify(true);
7014
- } else this.notify();
7279
+ } else {
7280
+ if (!drag.shift && drag.group.length) this.widthPoints = [drag.ref];
7281
+ this.notify();
7282
+ }
7015
7283
  return;
7016
7284
  }
7017
7285
  case "builder":
@@ -7041,14 +7309,14 @@ var VectorEditorController = (_class3 = class {
7041
7309
  }
7042
7310
  case "pencil": {
7043
7311
  const node = this.getNode(drag.nodeId);
7044
- if (_optionalChain([node, 'optionalAccess', _194 => _194.type]) !== "path" || drag.points.length < 2) {
7312
+ if (_optionalChain([node, 'optionalAccess', _216 => _216.type]) !== "path" || drag.points.length < 2) {
7045
7313
  this.doc = drag.before.doc;
7046
7314
  this.notify(true);
7047
7315
  return;
7048
7316
  }
7049
7317
  for (const q of drag.lazy.finish()) {
7050
7318
  drag.points.push(q);
7051
- _optionalChain([drag, 'access', _195 => _195.pressures, 'optionalAccess', _196 => _196.push, 'call', _197 => _197(drag.pressure)]);
7319
+ _optionalChain([drag, 'access', _217 => _217.pressures, 'optionalAccess', _218 => _218.push, 'call', _219 => _219(drag.pressure)]);
7052
7320
  }
7053
7321
  const shape = this.editing.recognizeShapes ? recognizeInkShape(drag.points, { minSize: 6 / this.viewport.zoom }) : null;
7054
7322
  if (shape) {
@@ -7056,7 +7324,7 @@ var VectorEditorController = (_class3 = class {
7056
7324
  this.selection = [drag.nodeId];
7057
7325
  return this.commit(`Draw ${inkShapeLabel(shape)}`, drag.before);
7058
7326
  }
7059
- const fitted = _chunk7EDLVBOOcjs.fitCubics.call(void 0, drag.points, 1.5 / this.viewport.zoom, { cornerAngleDeg: 60 });
7327
+ const fitted = _chunkVNYRI265cjs.fitCubics.call(void 0, drag.points, 1.5 / this.viewport.zoom, { cornerAngleDeg: 60 });
7060
7328
  const anchors = [];
7061
7329
  for (const c of fitted) {
7062
7330
  if (!anchors.length) anchors.push({ x: c.p0[0], y: c.p0[1] });
@@ -7100,7 +7368,10 @@ var VectorEditorController = (_class3 = class {
7100
7368
  }
7101
7369
  this.restore(drag.before);
7102
7370
  }
7103
- /** Double click: select tool enters path editing, direct tool toggles an anchor or adds one. */
7371
+ /**
7372
+ * Double click: select tool enters path editing, direct tool toggles an anchor or adds one,
7373
+ * width tool opens the width-point editor (`widthPointEdit`).
7374
+ */
7104
7375
  doubleClick(screen) {
7105
7376
  const p = this.screenToDocument(screen);
7106
7377
  if (this.tool === "select") {
@@ -7120,7 +7391,7 @@ var VectorEditorController = (_class3 = class {
7120
7391
  this.convertAnchors(a.in || a.out ? "corner" : "smooth", [anchor]);
7121
7392
  return;
7122
7393
  }
7123
- const id = _nullishCoalesce(_optionalChain([this, 'access', _198 => _198.hover, 'optionalAccess', _199 => _199.segment, 'optionalAccess', _200 => _200.nodeId]), () => ( this.editingId));
7394
+ const id = _nullishCoalesce(_optionalChain([this, 'access', _220 => _220.hover, 'optionalAccess', _221 => _221.segment, 'optionalAccess', _222 => _222.nodeId]), () => ( this.editingId));
7124
7395
  if (id) {
7125
7396
  const node = this.getNode(id);
7126
7397
  const seg = node ? nearestSegment(node, this.toLocal(id, p)) : null;
@@ -7128,6 +7399,16 @@ var VectorEditorController = (_class3 = class {
7128
7399
  }
7129
7400
  return;
7130
7401
  }
7402
+ if (this.tool === "width") {
7403
+ const hit = this.hitWidthHandle(screen);
7404
+ if (hit) {
7405
+ this.openWidthPointEdit(hit.ref);
7406
+ return;
7407
+ }
7408
+ const spot = this.strokeSpotAt(p);
7409
+ if (spot) this.openWidthPointEdit({ nodeId: spot.nodeId, subpath: spot.subpath, t: spot.t });
7410
+ return;
7411
+ }
7131
7412
  if (this.tool === "pen") this.finishPath();
7132
7413
  }
7133
7414
  // -------------------------------------------------------------------------
@@ -7188,8 +7469,9 @@ var VectorEditorController = (_class3 = class {
7188
7469
  switch (key) {
7189
7470
  case "Escape":
7190
7471
  if (this.drag) this.cancelDrag();
7191
- else if (this.tool === "width" && this.widthPoint) {
7192
- this.widthPoint = null;
7472
+ else if (this.widthEdit) this.closeWidthPointEdit();
7473
+ else if (this.tool === "width" && this.widthPoints.length) {
7474
+ this.widthPoints = [];
7193
7475
  this.notify();
7194
7476
  } else if (this.pen) this.finishPath();
7195
7477
  else if (this.tool === "direct" && this.anchors.length) {
@@ -7203,7 +7485,7 @@ var VectorEditorController = (_class3 = class {
7203
7485
  this.finishPath();
7204
7486
  return true;
7205
7487
  }
7206
- if (this.tool === "select" && this.selection.length === 1 && _optionalChain([this, 'access', _201 => _201.getNode, 'call', _202 => _202(this.selection[0]), 'optionalAccess', _203 => _203.type]) === "path") {
7488
+ if (this.tool === "select" && this.selection.length === 1 && _optionalChain([this, 'access', _223 => _223.getNode, 'call', _224 => _224(this.selection[0]), 'optionalAccess', _225 => _225.type]) === "path") {
7207
7489
  this.tool = "direct";
7208
7490
  this.editingId = this.selection[0];
7209
7491
  this.notify();
@@ -7212,7 +7494,7 @@ var VectorEditorController = (_class3 = class {
7212
7494
  return false;
7213
7495
  case "Delete":
7214
7496
  case "Backspace":
7215
- if (this.tool === "width" && this.widthPoint) return this.removeWidthPoint(this.widthPoint).ok;
7497
+ if (this.tool === "width" && this.widthPoints.length) return this.removeWidthPoints().ok;
7216
7498
  if (this.pen) {
7217
7499
  const node = this.getNode(this.pen.nodeId);
7218
7500
  const sp = node.subpaths[this.pen.subpath];
@@ -7323,6 +7605,7 @@ function pencilPressureProfile(points, pressures) {
7323
7605
  // src/vector/render.ts
7324
7606
  var fmt = (n) => Math.round(n * 100) / 100;
7325
7607
  var fmtMatrix = (m) => `matrix(${m.map((n) => Math.round(n * 1e6) / 1e6).join(" ")})`;
7608
+ var VECTOR_BUILDER_HATCH_GAP = 6;
7326
7609
  function renderNode(node, defs, idPrefix) {
7327
7610
  if (node.visible === false) return null;
7328
7611
  if (node.type === "group") {
@@ -7345,7 +7628,7 @@ function renderNode(node, defs, idPrefix) {
7345
7628
  strokeWidth: _nullishCoalesce(node.strokeWidth, () => ( 1)),
7346
7629
  ...node.strokeLinecap ? { strokeLinecap: node.strokeLinecap } : {},
7347
7630
  ...node.strokeLinejoin ? { strokeLinejoin: node.strokeLinejoin } : {},
7348
- ..._optionalChain([node, 'access', _204 => _204.strokeDasharray, 'optionalAccess', _205 => _205.length]) ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
7631
+ ..._optionalChain([node, 'access', _226 => _226.strokeDasharray, 'optionalAccess', _227 => _227.length]) ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
7349
7632
  fillRule: _nullishCoalesce(node.fillRule, () => ( "nonzero")),
7350
7633
  opacity: _nullishCoalesce(node.opacity, () => ( 1))
7351
7634
  };
@@ -7369,7 +7652,7 @@ function screenOutline(state, node) {
7369
7652
  const v2 = state.viewport;
7370
7653
  const view = [v2.zoom, 0, 0, v2.zoom, v2.x, v2.y];
7371
7654
  const parent = _nullishCoalesce(vectorParentMatrix(state.document.nodes, node.id), () => ( IDENTITY_MATRIX));
7372
- return collectWithMatrix(node, parent).map(({ path, parent: m }) => _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, _chunk7EDLVBOOcjs.transformBezierPath.call(void 0, vectorPathWorldBezier(path, m), view), { precision: 2 })).join(" ");
7655
+ return collectWithMatrix(node, parent).map(({ path, parent: m }) => _chunkVNYRI265cjs.bezierPathToSvg.call(void 0, _chunkVNYRI265cjs.transformBezierPath.call(void 0, vectorPathWorldBezier(path, m), view), { precision: 2 })).join(" ");
7373
7656
  }
7374
7657
  function collectWithMatrix(node, parent) {
7375
7658
  if (node.type === "path") return [{ path: node, parent }];
@@ -7394,9 +7677,9 @@ function getVectorOverlayRenderData(state) {
7394
7677
  };
7395
7678
  for (const id of state.selection) outline(id, true);
7396
7679
  if (state.editingId) outline(state.editingId, true);
7397
- if (_optionalChain([state, 'access', _206 => _206.hover, 'optionalAccess', _207 => _207.nodeId]) && !state.interaction) outline(state.hover.nodeId, false);
7680
+ if (_optionalChain([state, 'access', _228 => _228.hover, 'optionalAccess', _229 => _229.nodeId]) && !state.interaction) outline(state.hover.nodeId, false);
7398
7681
  let selectionBox = null;
7399
- if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access', _208 => _208.interaction, 'optionalAccess', _209 => _209.type]) !== "marquee") {
7682
+ if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access', _230 => _230.interaction, 'optionalAccess', _231 => _231.type]) !== "marquee") {
7400
7683
  const g = getVectorSelectionBoxGeometry(state.selectionFrame, state.viewport);
7401
7684
  selectionBox = { ...g.box, angle: g.angle, transform: g.transform, corners: g.corners, handles: g.handles };
7402
7685
  }
@@ -7406,9 +7689,9 @@ function getVectorOverlayRenderData(state) {
7406
7689
  const a = getAnchorWorld(doc, ref);
7407
7690
  const s = toScreen(state, a);
7408
7691
  const closes = !!state.pen && state.pen.nodeId === ref.nodeId && state.pen.subpath === ref.subpath && ref.index === 0;
7409
- return { ref, x: s.x, y: s.y, type: _nullishCoalesce(a.type, () => ( inferAnchorType(a))), selected: selected(ref), hovered: sameAnchor(_optionalChain([state, 'access', _210 => _210.hover, 'optionalAccess', _211 => _211.anchor]), ref), closes };
7692
+ return { ref, x: s.x, y: s.y, type: _nullishCoalesce(a.type, () => ( inferAnchorType(a))), selected: selected(ref), hovered: sameAnchor(_optionalChain([state, 'access', _232 => _232.hover, 'optionalAccess', _233 => _233.anchor]), ref), closes };
7410
7693
  });
7411
- if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access', _212 => _212.hover, 'optionalAccess', _213 => _213.anchor])) {
7694
+ if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access', _234 => _234.hover, 'optionalAccess', _235 => _235.anchor])) {
7412
7695
  const a = getAnchorWorld(doc, state.hover.anchor);
7413
7696
  if (a) {
7414
7697
  const s = toScreen(state, a);
@@ -7417,15 +7700,15 @@ function getVectorOverlayRenderData(state) {
7417
7700
  }
7418
7701
  const handles = getVisibleHandles(state).flatMap((ref) => {
7419
7702
  const a = getAnchorWorld(doc, ref);
7420
- const h = _optionalChain([a, 'optionalAccess', _214 => _214[ref.handle]]);
7703
+ const h = _optionalChain([a, 'optionalAccess', _236 => _236[ref.handle]]);
7421
7704
  if (!a || !h) return [];
7422
7705
  const s = toScreen(state, h);
7423
7706
  const sa = toScreen(state, a);
7424
- const hovered = !!_optionalChain([state, 'access', _215 => _215.hover, 'optionalAccess', _216 => _216.handle]) && sameAnchor(state.hover.handle, ref) && state.hover.handle.handle === ref.handle;
7707
+ const hovered = !!_optionalChain([state, 'access', _237 => _237.hover, 'optionalAccess', _238 => _238.handle]) && sameAnchor(state.hover.handle, ref) && state.hover.handle.handle === ref.handle;
7425
7708
  return [{ ref, x: s.x, y: s.y, anchorX: sa.x, anchorY: sa.y, hovered }];
7426
7709
  });
7427
7710
  let marquee = null;
7428
- if (_optionalChain([state, 'access', _217 => _217.interaction, 'optionalAccess', _218 => _218.type]) === "marquee") {
7711
+ if (_optionalChain([state, 'access', _239 => _239.interaction, 'optionalAccess', _240 => _240.type]) === "marquee") {
7429
7712
  const b = state.interaction.bounds;
7430
7713
  const tl = toScreen(state, b);
7431
7714
  marquee = { x: tl.x, y: tl.y, width: b.width * z, height: b.height * z };
@@ -7433,7 +7716,7 @@ function getVectorOverlayRenderData(state) {
7433
7716
  let penPreview = null;
7434
7717
  if (state.tool === "pen" && state.pen && state.pointer && !state.interaction) {
7435
7718
  const node = findVectorNode(doc.nodes, state.pen.nodeId);
7436
- const sp = _optionalChain([node, 'optionalAccess', _219 => _219.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
7719
+ const sp = _optionalChain([node, 'optionalAccess', _241 => _241.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
7437
7720
  const last = sp ? getAnchorWorld(doc, { nodeId: state.pen.nodeId, subpath: state.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
7438
7721
  if (last) {
7439
7722
  const a = toScreen(state, last);
@@ -7446,9 +7729,9 @@ function getVectorOverlayRenderData(state) {
7446
7729
  const artboard = { x: origin.x, y: origin.y, width: doc.width * z, height: doc.height * z };
7447
7730
  let status = null;
7448
7731
  const it = state.interaction;
7449
- if (_optionalChain([it, 'optionalAccess', _220 => _220.type]) === "rotate") status = `${Math.round(it.angle)}\xB0`;
7450
- else if (_optionalChain([it, 'optionalAccess', _221 => _221.type]) === "move" || _optionalChain([it, 'optionalAccess', _222 => _222.type]) === "anchors") status = `\u0394 ${fmt(it.dx)}, ${fmt(it.dy)}`;
7451
- else if (_optionalChain([it, 'optionalAccess', _223 => _223.type]) === "scale" || _optionalChain([it, 'optionalAccess', _224 => _224.type]) === "draw") {
7732
+ if (_optionalChain([it, 'optionalAccess', _242 => _242.type]) === "rotate") status = `${Math.round(it.angle)}\xB0`;
7733
+ else if (_optionalChain([it, 'optionalAccess', _243 => _243.type]) === "move" || _optionalChain([it, 'optionalAccess', _244 => _244.type]) === "anchors") status = `\u0394 ${fmt(it.dx)}, ${fmt(it.dy)}`;
7734
+ else if (_optionalChain([it, 'optionalAccess', _245 => _245.type]) === "scale" || _optionalChain([it, 'optionalAccess', _246 => _246.type]) === "draw") {
7452
7735
  const b = it.type === "draw" ? findVectorNode(doc.nodes, it.nodeId) ? vectorNodeBounds(findVectorNode(doc.nodes, it.nodeId)) : null : state.selectionFrame;
7453
7736
  if (b) status = `W ${fmt(b.width)} \xD7 H ${fmt(b.height)}`;
7454
7737
  }
@@ -7465,24 +7748,24 @@ function getVectorOverlayRenderData(state) {
7465
7748
  }
7466
7749
  }
7467
7750
  let toolStroke = null;
7468
- if (_optionalChain([it, 'optionalAccess', _225 => _225.type]) === "stroke") {
7751
+ if (_optionalChain([it, 'optionalAccess', _247 => _247.type]) === "stroke") {
7469
7752
  const pts = it.points.map((q) => toScreen(state, q));
7470
7753
  const d = pts.map((q, i) => `${i ? "L" : "M"}${fmt(q.x)} ${fmt(q.y)}`).join("") + (pts.length === 1 ? `L${fmt(pts[0].x)} ${fmt(pts[0].y)}` : "");
7471
7754
  toolStroke = { tool: it.tool, d, width: fmt(it.width * z) };
7472
7755
  }
7473
7756
  let pencilString = null;
7474
- if (_optionalChain([it, 'optionalAccess', _226 => _226.type]) === "draw" && it.string) {
7757
+ if (_optionalChain([it, 'optionalAccess', _248 => _248.type]) === "draw" && it.string) {
7475
7758
  const a = toScreen(state, it.string[0]);
7476
7759
  const b = toScreen(state, it.string[1]);
7477
7760
  pencilString = { x1: fmt(a.x), y1: fmt(a.y), x2: fmt(b.x), y2: fmt(b.y) };
7478
7761
  }
7479
- const dragged = _optionalChain([it, 'optionalAccess', _227 => _227.type]) === "corner" ? it.ref : null;
7762
+ const dragged = _optionalChain([it, 'optionalAccess', _249 => _249.type]) === "corner" ? it.ref : null;
7480
7763
  const cornerWidgets = getVectorCornerWidgets(state).map((w) => {
7481
7764
  const s = toScreen(state, w);
7482
- return { ref: w.ref, x: fmt(s.x), y: fmt(s.y), radius: w.radius, kind: w.kind, hovered: sameAnchor(_optionalChain([state, 'access', _228 => _228.hover, 'optionalAccess', _229 => _229.corner]), w.ref), active: !!dragged };
7765
+ return { ref: w.ref, x: fmt(s.x), y: fmt(s.y), radius: w.radius, kind: w.kind, hovered: sameAnchor(_optionalChain([state, 'access', _250 => _250.hover, 'optionalAccess', _251 => _251.corner]), w.ref), active: !!dragged };
7483
7766
  });
7484
- if (_optionalChain([it, 'optionalAccess', _230 => _230.type]) === "corner") status = `R ${fmt(it.radius)}`;
7485
- const cutPoint = state.tool === "scissors" && _optionalChain([state, 'access', _231 => _231.hover, 'optionalAccess', _232 => _232.segment]) && !it ? (({ x, y }) => ({ x: fmt(x), y: fmt(y) }))(toScreen(state, state.hover.segment.point)) : null;
7767
+ if (_optionalChain([it, 'optionalAccess', _252 => _252.type]) === "corner") status = `R ${fmt(it.radius)}`;
7768
+ const cutPoint = state.tool === "scissors" && _optionalChain([state, 'access', _253 => _253.hover, 'optionalAccess', _254 => _254.segment]) && !it ? (({ x, y }) => ({ x: fmt(x), y: fmt(y) }))(toScreen(state, state.hover.segment.point)) : null;
7486
7769
  const sc = (p) => {
7487
7770
  const q = toScreen(state, p);
7488
7771
  return { x: fmt(q.x), y: fmt(q.y) };
@@ -7490,21 +7773,29 @@ function getVectorOverlayRenderData(state) {
7490
7773
  const sameWidth = (a, b) => !!a && a.nodeId === b.nodeId && a.index === b.index;
7491
7774
  const widthPoints = getVectorWidthHandles(state).map((h) => {
7492
7775
  const c = sc(h);
7493
- const hw = _optionalChain([state, 'access', _233 => _233.hover, 'optionalAccess', _234 => _234.widthPoint]);
7776
+ const hw = _optionalChain([state, 'access', _255 => _255.hover, 'optionalAccess', _256 => _256.widthPoint]);
7494
7777
  const hovered = hw && sameWidth(hw, h.ref) && hw.subpath === h.ref.subpath ? hw.part : null;
7495
- return { ref: h.ref, x: c.x, y: c.y, left: sc(h.left), right: sc(h.right), selected: h.selected, hovered, active: _optionalChain([it, 'optionalAccess', _235 => _235.type]) === "width" && sameWidth(it.ref, h.ref) };
7778
+ return { ref: h.ref, x: c.x, y: c.y, left: sc(h.left), right: sc(h.right), selected: h.selected, hovered, active: _optionalChain([it, 'optionalAccess', _257 => _257.type]) === "width" && sameWidth(it.ref, h.ref) };
7496
7779
  });
7497
- const spot = state.tool === "width" && !it ? _optionalChain([state, 'access', _236 => _236.hover, 'optionalAccess', _237 => _237.strokeSpot]) : null;
7780
+ const spot = state.tool === "width" && !it ? _optionalChain([state, 'access', _258 => _258.hover, 'optionalAccess', _259 => _259.strokeSpot]) : null;
7498
7781
  const widthSpot = spot ? { ...sc(spot.point), left: sc(spot.left), right: sc(spot.right) } : null;
7499
- if (_optionalChain([it, 'optionalAccess', _238 => _238.type]) === "width") status = it.mode === "move" ? "Move width point" : `W ${fmt(it.width)}`;
7782
+ if (_optionalChain([it, 'optionalAccess', _260 => _260.type]) === "width") status = it.mode === "move" ? "Move width point" : it.mode === "copy" ? "Copy width point" : `W ${fmt(it.width)}`;
7500
7783
  let builder = null;
7501
7784
  if (state.builder) {
7502
7785
  const v2 = state.viewport;
7503
7786
  const view = [v2.zoom, 0, 0, v2.zoom, v2.x, v2.y];
7504
- const d = (path) => _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, _chunk7EDLVBOOcjs.transformBezierPath.call(void 0, path, view), { precision: 2 });
7787
+ const svg = (path) => _chunkVNYRI265cjs.bezierPathToSvg.call(void 0, path, { precision: 2 });
7788
+ const screen = state.builder.faces.map((f) => _chunkVNYRI265cjs.transformBezierPath.call(void 0, f, view));
7789
+ const hatch = state.builder.mode !== "fill";
7505
7790
  const trail = state.builder.trail && state.builder.trail.length > 1 ? state.builder.trail.map((q, i) => `${i ? "L" : "M"}${sc(q).x} ${sc(q).y}`).join("") : null;
7506
- builder = { mode: state.builder.mode, faces: state.builder.faces.map(d), lines: state.builder.lines.map(d), trail };
7507
- if (state.builder.mode !== "fill" && _optionalChain([it, 'optionalAccess', _239 => _239.type]) === "builder") {
7791
+ builder = {
7792
+ mode: state.builder.mode,
7793
+ faces: screen.map(svg),
7794
+ hatches: screen.map((f) => hatch ? svg(_chunkVNYRI265cjs.hatchPath.call(void 0, f, { gap: VECTOR_BUILDER_HATCH_GAP })) : ""),
7795
+ lines: state.builder.lines.map((l) => svg(_chunkVNYRI265cjs.transformBezierPath.call(void 0, l, view))),
7796
+ trail
7797
+ };
7798
+ if (state.builder.mode !== "fill" && _optionalChain([it, 'optionalAccess', _261 => _261.type]) === "builder") {
7508
7799
  const n = state.builder.faces.length;
7509
7800
  status = state.builder.mode === "erase" ? `Delete ${n} ${n === 1 ? "face" : "faces"}` : `Merge ${n} ${n === 1 ? "face" : "faces"}`;
7510
7801
  }
@@ -7530,7 +7821,9 @@ var VECTOR_OVERLAY_THEME = {
7530
7821
  builderFill: "rgba(14, 165, 233, 0.22)",
7531
7822
  /** Shape Builder faces and line pieces an Alt gesture deletes. */
7532
7823
  builderErase: "#ef4444",
7533
- builderEraseFill: "rgba(239, 68, 68, 0.22)"
7824
+ builderEraseFill: "rgba(239, 68, 68, 0.22)",
7825
+ /** Opacity of the hatch lines over Shape Builder faces (drawn in the face's outline colour). */
7826
+ builderHatchOpacity: 0.6
7534
7827
  };
7535
7828
  function vectorDiamondPath(x, y, r = VECTOR_OVERLAY_THEME.widthPointSize) {
7536
7829
  return `M${fmt(x)} ${fmt(y - r)}L${fmt(x + r)} ${fmt(y)}L${fmt(x)} ${fmt(y + r)}L${fmt(x - r)} ${fmt(y)}Z`;
@@ -7554,7 +7847,7 @@ function isVectorTextTarget(target) {
7554
7847
  if (!el || typeof el !== "object") return false;
7555
7848
  const tag = (_nullishCoalesce(el.tagName, () => ( ""))).toUpperCase();
7556
7849
  if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
7557
- return !!el.isContentEditable || _optionalChain([el, 'access', _240 => _240.getAttribute, 'optionalCall', _241 => _241("contenteditable")]) === "true";
7850
+ return !!el.isContentEditable || _optionalChain([el, 'access', _262 => _262.getAttribute, 'optionalCall', _263 => _263("contenteditable")]) === "true";
7558
7851
  }
7559
7852
  function getVectorSelectionStyle(state) {
7560
7853
  const paths = [];
@@ -7562,12 +7855,12 @@ function getVectorSelectionStyle(state) {
7562
7855
  const node = findVectorNode(state.document.nodes, id);
7563
7856
  if (node) paths.push(...collect(node));
7564
7857
  }
7565
- const anchorMode = state.tool === "direct" && !!_optionalChain([state, 'access', _242 => _242.selectedAnchors, 'optionalAccess', _243 => _243.length]);
7858
+ const anchorMode = state.tool === "direct" && !!_optionalChain([state, 'access', _264 => _264.selectedAnchors, 'optionalAccess', _265 => _265.length]);
7566
7859
  if (anchorMode) {
7567
7860
  for (const r of state.selectedAnchors) {
7568
7861
  if (paths.some((p) => p.id === r.nodeId)) continue;
7569
7862
  const node = findVectorNode(state.document.nodes, r.nodeId);
7570
- if (_optionalChain([node, 'optionalAccess', _244 => _244.type]) === "path") paths.push(node);
7863
+ if (_optionalChain([node, 'optionalAccess', _266 => _266.type]) === "path") paths.push(node);
7571
7864
  }
7572
7865
  }
7573
7866
  if (!paths.length) return null;
@@ -7578,7 +7871,7 @@ function getVectorSelectionStyle(state) {
7578
7871
  for (const c of vectorCorners(p.subpaths)) {
7579
7872
  if (anchorMode && !state.selectedAnchors.some((r) => r.nodeId === p.id && r.subpath === c.subpath && r.index === c.index)) continue;
7580
7873
  const corner = p.subpaths[c.subpath].anchors[c.index].corner;
7581
- corners2.push({ radius: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _245 => _245.radius]), () => ( 0)), kind: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _246 => _246.kind]), () => ( "round")) });
7874
+ corners2.push({ radius: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _267 => _267.radius]), () => ( 0)), kind: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _268 => _268.kind]), () => ( "round")) });
7582
7875
  }
7583
7876
  }
7584
7877
  const c0 = _nullishCoalesce(corners2[0], () => ( { radius: 0, kind: "round" }));
@@ -7726,17 +8019,17 @@ function createVectorCommands(controller, options = {}) {
7726
8019
  const base = { group, ...options.scope ? { scope: options.scope } : {} };
7727
8020
  const hasSelection = () => controller.getState().selection.length ? true : "Nothing is selected.";
7728
8021
  const ids = (params) => {
7729
- const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _247 => _247.ids]), () => ( controller.getState().selection));
8022
+ const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _269 => _269.ids]), () => ( controller.getState().selection));
7730
8023
  for (const id of list2) if (!controller.getNode(id)) fail(`There is no node "${id}".`);
7731
8024
  if (!list2.length) fail("Nothing is selected; pass ids.");
7732
8025
  return list2;
7733
8026
  };
7734
- const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess', _248 => _248.ids, 'optionalAccess', _249 => _249.length]) ? true : hasSelection();
8027
+ const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess', _270 => _270.ids, 'optionalAccess', _271 => _271.length]) ? true : hasSelection();
7735
8028
  const anchorsOf = (refs) => {
7736
8029
  if (!refs) return [...controller.getState().selectedAnchors];
7737
8030
  for (const r of refs) {
7738
8031
  const node = controller.getNode(r.nodeId);
7739
- if (_optionalChain([node, 'optionalAccess', _250 => _250.type]) !== "path" || !_optionalChain([node, 'access', _251 => _251.subpaths, 'access', _252 => _252[r.subpath], 'optionalAccess', _253 => _253.anchors, 'access', _254 => _254[r.index]])) fail(`There is no anchor ${r.index} on subpath ${r.subpath} of "${r.nodeId}".`);
8032
+ if (_optionalChain([node, 'optionalAccess', _272 => _272.type]) !== "path" || !_optionalChain([node, 'access', _273 => _273.subpaths, 'access', _274 => _274[r.subpath], 'optionalAccess', _275 => _275.anchors, 'access', _276 => _276[r.index]])) fail(`There is no anchor ${r.index} on subpath ${r.subpath} of "${r.nodeId}".`);
7740
8033
  }
7741
8034
  return refs;
7742
8035
  };
@@ -7827,7 +8120,7 @@ function createVectorCommands(controller, options = {}) {
7827
8120
  run: (_ctx, params) => {
7828
8121
  const id = controller.addPath(params.d, { ...pickStyle(params), ...params.name ? { name: params.name } : {} });
7829
8122
  const node = controller.getNode(id);
7830
- if (_optionalChain([node, 'optionalAccess', _255 => _255.type]) === "path" && !node.subpaths.length) {
8123
+ if (_optionalChain([node, 'optionalAccess', _277 => _277.type]) === "path" && !node.subpaths.length) {
7831
8124
  controller.undo();
7832
8125
  fail("The path data has no points.");
7833
8126
  }
@@ -7859,10 +8152,10 @@ function createVectorCommands(controller, options = {}) {
7859
8152
  params: { type: "object", properties: { radius: { type: "number", minimum: 0 }, kind: { type: "string", enum: VECTOR_CORNER_KINDS }, ids: IDS, anchors: ANCHORS } },
7860
8153
  undoable: true,
7861
8154
  run: (_ctx, params) => {
7862
- const refs = _optionalChain([params, 'optionalAccess', _256 => _256.anchors]) ? anchorsOf(params.anchors) : void 0;
7863
- const target = { refs, ids: _optionalChain([params, 'optionalAccess', _257 => _257.ids]) ? ids(params) : void 0 };
7864
- if (_optionalChain([params, 'optionalAccess', _258 => _258.radius]) !== void 0) check(controller.setCornerRadius(params.radius, { ...target, kind: params.kind }));
7865
- else if (_optionalChain([params, 'optionalAccess', _259 => _259.kind])) check(controller.setCornerKind(params.kind, target));
8155
+ const refs = _optionalChain([params, 'optionalAccess', _278 => _278.anchors]) ? anchorsOf(params.anchors) : void 0;
8156
+ const target = { refs, ids: _optionalChain([params, 'optionalAccess', _279 => _279.ids]) ? ids(params) : void 0 };
8157
+ if (_optionalChain([params, 'optionalAccess', _280 => _280.radius]) !== void 0) check(controller.setCornerRadius(params.radius, { ...target, kind: params.kind }));
8158
+ else if (_optionalChain([params, 'optionalAccess', _281 => _281.kind])) check(controller.setCornerKind(params.kind, target));
7866
8159
  else fail("Pass `radius` and / or `kind`.");
7867
8160
  return { ok: true };
7868
8161
  }
@@ -7897,12 +8190,37 @@ function createVectorCommands(controller, options = {}) {
7897
8190
  undoable: true,
7898
8191
  run: (_ctx, params) => {
7899
8192
  const target = { ids: ids(params) };
7900
- if (_optionalChain([params, 'optionalAccess', _260 => _260.preset])) check(controller.setWidthProfile(params.preset, target));
7901
- else if (_optionalChain([params, 'optionalAccess', _261 => _261.points]) !== void 0) check(controller.setWidthProfile(_nullishCoalesce(params.points, () => ( null)), target));
7902
- else if (_optionalChain([params, 'optionalAccess', _262 => _262.taper]) || _optionalChain([params, 'optionalAccess', _263 => _263.wave])) check(controller.setWidthProfile({ taper: params.taper, wave: params.wave }, target));
8193
+ if (_optionalChain([params, 'optionalAccess', _282 => _282.preset])) check(controller.setWidthProfile(params.preset, target));
8194
+ else if (_optionalChain([params, 'optionalAccess', _283 => _283.points]) !== void 0) check(controller.setWidthProfile(_nullishCoalesce(params.points, () => ( null)), target));
8195
+ else if (_optionalChain([params, 'optionalAccess', _284 => _284.taper]) || _optionalChain([params, 'optionalAccess', _285 => _285.wave])) check(controller.setWidthProfile({ taper: params.taper, wave: params.wave }, target));
7903
8196
  else fail("Pass `preset`, `points`, `taper` or `wave`.");
7904
8197
  const node = controller.getNode(target.ids[0]);
7905
- return { ok: true, points: _optionalChain([node, 'optionalAccess', _264 => _264.type]) === "path" ? _nullishCoalesce(node.strokeWidthProfile, () => ( [])) : [] };
8198
+ return { ok: true, points: _optionalChain([node, 'optionalAccess', _286 => _286.type]) === "path" ? _nullishCoalesce(node.strokeWidthProfile, () => ( [])) : [] };
8199
+ }
8200
+ },
8201
+ {
8202
+ ...base,
8203
+ id: `${prefix}setWidthPoint`,
8204
+ label: "Width Point\u2026",
8205
+ keywords: ["width point", "variable width", "stroke width at", "taper", "profile"],
8206
+ description: "Sets one width point of a path's stroke profile numerically: `index` names an existing point (see `vector.setWidthProfile`'s result), leave it out to add one at `t`. `left` / `right` are side widths in document units (how far the stroke reaches on each side), `t` the position along each subpath (0\u20131); a point moved onto another's `t` makes a step. Returns the profile.",
8207
+ params: {
8208
+ type: "object",
8209
+ properties: {
8210
+ id: { type: "string", description: "The path." },
8211
+ index: { type: "integer", minimum: 0 },
8212
+ t: { type: "number", minimum: 0, maximum: 1 },
8213
+ left: { type: "number", minimum: 0 },
8214
+ right: { type: "number", minimum: 0 }
8215
+ },
8216
+ required: ["id"]
8217
+ },
8218
+ undoable: true,
8219
+ run: (_ctx, params) => {
8220
+ if (!_optionalChain([params, 'optionalAccess', _287 => _287.id])) fail("Pass the path `id`.");
8221
+ check(controller.setWidthPoint({ nodeId: params.id, index: _nullishCoalesce(params.index, () => ( null)) }, { t: params.t, left: params.left, right: params.right }));
8222
+ const node = controller.getNode(params.id);
8223
+ return { ok: true, points: _optionalChain([node, 'optionalAccess', _288 => _288.type]) === "path" ? _nullishCoalesce(node.strokeWidthProfile, () => ( [])) : [] };
7906
8224
  }
7907
8225
  },
7908
8226
  {
@@ -7915,7 +8233,7 @@ function createVectorCommands(controller, options = {}) {
7915
8233
  enabled: enabledFor,
7916
8234
  undoable: true,
7917
8235
  run: (_ctx, params) => {
7918
- const created = controller.outlineStroke({ ids: _optionalChain([params, 'optionalAccess', _265 => _265.ids]) ? ids(params) : void 0 });
8236
+ const created = controller.outlineStroke({ ids: _optionalChain([params, 'optionalAccess', _289 => _289.ids]) ? ids(params) : void 0 });
7919
8237
  if (!created.length) fail("No selected path has a stroke.");
7920
8238
  return { ids: created };
7921
8239
  }
@@ -8006,7 +8324,7 @@ function createVectorCommands(controller, options = {}) {
8006
8324
  id: `${prefix}shapeBuilder`,
8007
8325
  label: "Shape Builder",
8008
8326
  keywords: ["merge faces", "build shape", "combine regions", "delete region"],
8009
- description: "Shape Builder gesture over the faces of the given paths: `points` is the pointer path in document units (one point is a click; [[x, y], \u2026]; use the points from shapeBuilderFaces). Merges the faces it touches into one shape, or with `erase: true` deletes them and the pieces of unfilled open paths within `tolerance`. Shapes covering those faces lose that area.",
8327
+ description: "Shape Builder gesture over the faces of the given paths: `points` is the pointer path in document units (one point is a click; [[x, y], \u2026]; use the points from shapeBuilderFaces). Merges the faces it touches into one shape, or with `erase: true` deletes them, the pieces of unfilled open paths and the edges of other outlines (cut where paths meet) within `tolerance`; a deleted edge opens its path there, and edges bounding a deleted face go with it. Shapes covering those faces lose that area. Returns the counts of faces, line pieces and edges.",
8010
8328
  params: {
8011
8329
  type: "object",
8012
8330
  properties: { points: POINTS, erase: { type: "boolean" }, tolerance: { type: "number", minimum: 0 }, ids: IDS },
@@ -8016,7 +8334,7 @@ function createVectorCommands(controller, options = {}) {
8016
8334
  run: (_ctx, params) => {
8017
8335
  const result = controller.shapeBuilder(params.points.map(([x, y]) => ({ x, y })), { ids: ids(params), erase: params.erase, tolerance: params.tolerance });
8018
8336
  check(result);
8019
- return { merged: result.merged, faces: result.faces, lines: result.lines };
8337
+ return { merged: result.merged, faces: result.faces, lines: result.lines, edges: result.edges };
8020
8338
  }
8021
8339
  },
8022
8340
  {
@@ -8086,7 +8404,7 @@ function createVectorCommands(controller, options = {}) {
8086
8404
  enabled: enabledFor,
8087
8405
  undoable: true,
8088
8406
  run: (_ctx, params) => {
8089
- const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess', _266 => _266.name]) });
8407
+ const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess', _290 => _290.name]) });
8090
8408
  if (!id) fail("Only nodes that share a parent can be grouped.");
8091
8409
  return { id };
8092
8410
  }
@@ -8114,7 +8432,7 @@ function createVectorCommands(controller, options = {}) {
8114
8432
  params: { type: "object", properties: { anchors: ANCHORS, keepShape: { type: "boolean" } } },
8115
8433
  undoable: true,
8116
8434
  run: (_ctx, params) => {
8117
- check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _267 => _267.anchors])), { keepShape: _optionalChain([params, 'optionalAccess', _268 => _268.keepShape]) }));
8435
+ check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _291 => _291.anchors])), { keepShape: _optionalChain([params, 'optionalAccess', _292 => _292.keepShape]) }));
8118
8436
  return { ok: true };
8119
8437
  }
8120
8438
  },
@@ -8169,7 +8487,7 @@ function createVectorCommands(controller, options = {}) {
8169
8487
  params: { type: "object", properties: { ids: IDS, tolerance: { type: "number", minimum: 0 } } },
8170
8488
  undoable: true,
8171
8489
  run: (_ctx, params) => {
8172
- check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess', _269 => _269.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _270 => _270.tolerance]) }));
8490
+ check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess', _293 => _293.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _294 => _294.tolerance]) }));
8173
8491
  return { selection: controller.getState().selection };
8174
8492
  }
8175
8493
  },
@@ -8183,7 +8501,7 @@ function createVectorCommands(controller, options = {}) {
8183
8501
  params: { type: "object", properties: { axis: { type: "string", enum: ["horizontal", "vertical", "both"] }, anchors: ANCHORS } },
8184
8502
  undoable: true,
8185
8503
  run: (_ctx, params) => {
8186
- check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _271 => _271.axis]), () => ( "both")), anchorsOf(_optionalChain([params, 'optionalAccess', _272 => _272.anchors]))));
8504
+ check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _295 => _295.axis]), () => ( "both")), anchorsOf(_optionalChain([params, 'optionalAccess', _296 => _296.anchors]))));
8187
8505
  return { ok: true };
8188
8506
  }
8189
8507
  },
@@ -8197,7 +8515,7 @@ function createVectorCommands(controller, options = {}) {
8197
8515
  enabled: enabledFor,
8198
8516
  undoable: true,
8199
8517
  run: (_ctx, params) => {
8200
- check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess', _273 => _273.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _274 => _274.tolerance]) }));
8518
+ check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess', _297 => _297.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _298 => _298.tolerance]) }));
8201
8519
  return { ok: true };
8202
8520
  }
8203
8521
  },
@@ -8210,7 +8528,7 @@ function createVectorCommands(controller, options = {}) {
8210
8528
  params: { type: "object", properties: { ids: IDS, amount: { type: "number", minimum: 0, maximum: 1 }, anchors: ANCHORS } },
8211
8529
  undoable: true,
8212
8530
  run: (_ctx, params) => {
8213
- check(controller.smoothPaths({ ids: _optionalChain([params, 'optionalAccess', _275 => _275.ids]) ? ids(params) : void 0, amount: _optionalChain([params, 'optionalAccess', _276 => _276.amount]), refs: _optionalChain([params, 'optionalAccess', _277 => _277.anchors]) }));
8531
+ check(controller.smoothPaths({ ids: _optionalChain([params, 'optionalAccess', _299 => _299.ids]) ? ids(params) : void 0, amount: _optionalChain([params, 'optionalAccess', _300 => _300.amount]), refs: _optionalChain([params, 'optionalAccess', _301 => _301.anchors]) }));
8214
8532
  return { ok: true };
8215
8533
  }
8216
8534
  },
@@ -8223,14 +8541,14 @@ function createVectorCommands(controller, options = {}) {
8223
8541
  params: { type: "object", properties: { nodeId: { type: "string" }, x: { type: "number" }, y: { type: "number" }, anchors: ANCHORS } },
8224
8542
  undoable: true,
8225
8543
  run: (_ctx, params) => {
8226
- if (_optionalChain([params, 'optionalAccess', _278 => _278.nodeId]) !== void 0) {
8544
+ if (_optionalChain([params, 'optionalAccess', _302 => _302.nodeId]) !== void 0) {
8227
8545
  if (!controller.getNode(params.nodeId)) fail(`There is no node "${params.nodeId}".`);
8228
8546
  if (params.x === void 0 || params.y === void 0) fail("Pass x and y with nodeId.");
8229
8547
  const created = controller.cutPathAt(params.nodeId, { x: params.x, y: params.y });
8230
8548
  if (!created) fail("Nothing to cut there (the end of an open path, or a locked path).");
8231
8549
  return { ids: created };
8232
8550
  }
8233
- check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _279 => _279.anchors]))));
8551
+ check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _303 => _303.anchors]))));
8234
8552
  return { ids: controller.getState().selection };
8235
8553
  }
8236
8554
  },
@@ -8281,7 +8599,7 @@ function createVectorCommands(controller, options = {}) {
8281
8599
  },
8282
8600
  run: (_ctx, params) => {
8283
8601
  const patch = {};
8284
- for (const key of ["keepShapeOnDelete", "curvatureMoves", "pencilSmoothing", "recognizeShapes", "eraserSize", "pencilPressure"]) if (_optionalChain([params, 'optionalAccess', _280 => _280[key]]) !== void 0) patch[key] = params[key];
8602
+ for (const key of ["keepShapeOnDelete", "curvatureMoves", "pencilSmoothing", "recognizeShapes", "eraserSize", "pencilPressure"]) if (_optionalChain([params, 'optionalAccess', _304 => _304[key]]) !== void 0) patch[key] = params[key];
8285
8603
  controller.setEditing(patch);
8286
8604
  return { editing: controller.getState().editing };
8287
8605
  }
@@ -8306,7 +8624,7 @@ function createVectorCommands(controller, options = {}) {
8306
8624
  keywords: ["download", "save", "markup"],
8307
8625
  description: "Returns the document as SVG markup.",
8308
8626
  params: { type: "object", properties: { precision: { type: "integer", minimum: 0, maximum: 8 } } },
8309
- run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess', _281 => _281.precision]) }) })
8627
+ run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess', _305 => _305.precision]) }) })
8310
8628
  },
8311
8629
  {
8312
8630
  ...base,
@@ -8518,5 +8836,7 @@ function createVectorCommands(controller, options = {}) {
8518
8836
 
8519
8837
 
8520
8838
 
8521
- exports.DEFAULT_VECTOR_PATH_EDIT = DEFAULT_VECTOR_PATH_EDIT; exports.DEFAULT_VECTOR_SNAP = DEFAULT_VECTOR_SNAP; exports.IDENTITY_MATRIX = IDENTITY_MATRIX; exports.KAPPA = KAPPA2; exports.LazyMouse = LazyMouse; exports.VECTOR_ARROWHEADS = VECTOR_ARROWHEADS; exports.VECTOR_ARROWHEAD_LABELS = VECTOR_ARROWHEAD_LABELS; exports.VECTOR_CLIPBOARD_TYPE = VECTOR_CLIPBOARD_TYPE; exports.VECTOR_CONSTRUCTION_ANGLES = VECTOR_CONSTRUCTION_ANGLES; exports.VECTOR_CORNER_KINDS = VECTOR_CORNER_KINDS; exports.VECTOR_CORNER_KIND_LABELS = VECTOR_CORNER_KIND_LABELS; exports.VECTOR_CORNER_WIDGET_INSET = VECTOR_CORNER_WIDGET_INSET; exports.VECTOR_EDIT_TOGGLES = VECTOR_EDIT_TOGGLES; exports.VECTOR_FILL_GAP = VECTOR_FILL_GAP; exports.VECTOR_INK_MAX_CORNERS = VECTOR_INK_MAX_CORNERS; exports.VECTOR_OVERLAY_THEME = VECTOR_OVERLAY_THEME; exports.VECTOR_PATHFINDER_ACTIONS = VECTOR_PATHFINDER_ACTIONS; exports.VECTOR_PATHFINDER_LABELS = VECTOR_PATHFINDER_LABELS; exports.VECTOR_PATHFINDER_OPERATIONS = VECTOR_PATHFINDER_OPERATIONS; exports.VECTOR_PATH_ACTIONS = VECTOR_PATH_ACTIONS; exports.VECTOR_PENCIL_STRING_PX = VECTOR_PENCIL_STRING_PX; exports.VECTOR_PRESSURE_NOMINAL = VECTOR_PRESSURE_NOMINAL; exports.VECTOR_ROTATE_HANDLE_OFFSET = VECTOR_ROTATE_HANDLE_OFFSET; exports.VECTOR_STROKE_ALIGN_LABELS = VECTOR_STROKE_ALIGN_LABELS; exports.VECTOR_STYLE_KEYS = VECTOR_STYLE_KEYS; exports.VECTOR_TOOLS = VECTOR_TOOLS; exports.VECTOR_TOOL_GLYPHS = VECTOR_TOOL_GLYPHS; exports.VECTOR_TOOL_SHORTCUTS = VECTOR_TOOL_SHORTCUTS; exports.VECTOR_TRANSFORM_HANDLES = VECTOR_TRANSFORM_HANDLES; exports.VECTOR_WIDTH_PRESETS = VECTOR_WIDTH_PRESETS; exports.VECTOR_WIDTH_PRESET_IDS = VECTOR_WIDTH_PRESET_IDS; exports.VECTOR_WIDTH_PROFILE_MAX_POINTS = VECTOR_WIDTH_PROFILE_MAX_POINTS; exports.VECTOR_WIDTH_PROFILE_MAX_SCALE = VECTOR_WIDTH_PROFILE_MAX_SCALE; exports.VECTOR_WIDTH_SAME_T = VECTOR_WIDTH_SAME_T; exports.VectorEditorController = VectorEditorController; exports.VectorSnapTargets = VectorSnapTargets; exports.addBuilderHit = addBuilderHit; exports.applyMatrix = applyMatrix; exports.applyMatrixLinear = applyMatrixLinear; exports.applyVectorCornersAttribute = applyVectorCornersAttribute; exports.arrowheadInset = arrowheadInset; exports.arrowheadPath = arrowheadPath; exports.arrowheadWeight = arrowheadWeight; exports.bendSegment = bendSegment; exports.bezierToSubpaths = bezierToSubpaths; exports.boundsFromPoints = boundsFromPoints; exports.boundsIntersect = boundsIntersect; exports.cloneAnchor = cloneAnchor; exports.cloneSubpath = cloneSubpath; exports.cloneVectorDocument = cloneVectorDocument; exports.cloneVectorNode = cloneVectorNode; exports.collectPathNodes = collectPathNodes; exports.constrainToLeave = constrainToLeave; exports.convertAnchor = convertAnchor; exports.cornerBisectorPoint = cornerBisectorPoint; exports.cornerRadiusChange = cornerRadiusChange; exports.cornerSetback = cornerSetback; exports.createVectorCommands = createVectorCommands; exports.curvatureMoveAnchor = curvatureMoveAnchor; exports.cutSubpathAt = cutSubpathAt; exports.dashIntervals = dashIntervals; exports.decomposeRigid = decomposeRigid; exports.effectiveStrokeAlign = effectiveStrokeAlign; exports.ellipseSubpath = ellipseSubpath; exports.eraseFromFill = eraseFromFill; exports.eraseFromOutline = eraseFromOutline; exports.eraserRegion = eraserRegion; exports.expandVectorCorners = expandVectorCorners; exports.expandVectorCornersOf = expandVectorCornersOf; exports.findVectorNode = findVectorNode; exports.fitCornerRadius = fitCornerRadius; exports.generateWidthProfile = generateWidthProfile; exports.getAnchor = getAnchor; exports.getAnchorWorld = getAnchorWorld; exports.getEditablePathIds = getEditablePathIds; exports.getTransformHandlePositions = getTransformHandlePositions; exports.getVectorCornerWidgets = getVectorCornerWidgets; exports.getVectorOverlayRenderData = getVectorOverlayRenderData; exports.getVectorSceneRenderData = getVectorSceneRenderData; exports.getVectorSelectionBoxGeometry = getVectorSelectionBoxGeometry; exports.getVectorSelectionFrame = getVectorSelectionFrame; exports.getVectorSelectionStyle = getVectorSelectionStyle; exports.getVectorWidthHandles = getVectorWidthHandles; exports.getVisibleAnchors = getVisibleAnchors; exports.getVisibleHandles = getVisibleHandles; exports.hasPaint = hasPaint; exports.hasStrokeExtras = hasStrokeExtras; exports.hasVectorCorners = hasVectorCorners; exports.hitTestVectorNode = hitTestVectorNode; exports.hitTestVectorNodes = hitTestVectorNodes; exports.hitTestVectorStroke = hitTestVectorStroke; exports.inferAnchorType = inferAnchorType; exports.insertAnchor = insertAnchor; exports.insertWidthPoint = insertWidthPoint; exports.invertMatrix = invertMatrix; exports.isClosedBezierShape = isClosedBezierShape; exports.isIdentityMatrix = isIdentityMatrix; exports.isUniformWidthProfile = isUniformWidthProfile; exports.isVectorTextTarget = isVectorTextTarget; exports.joinSubpaths = joinSubpaths; exports.knifePath = knifePath; exports.lineSubpath = lineSubpath; exports.locateVectorNode = locateVectorNode; exports.matrixRotation = matrixRotation; exports.mirrorWidthProfile = mirrorWidthProfile; exports.moveAnchorHandle = moveAnchorHandle; exports.multiplyMatrices = multiplyMatrices; exports.nearestSegment = nearestSegment; exports.nearestStrokePosition = nearestStrokePosition; exports.nextVectorCornerKind = nextVectorCornerKind; exports.nodeMatrix = nodeMatrix; exports.parseSvgTransform = parseSvgTransform; exports.parseVectorSvg = parseVectorSvg; exports.planShapeBuilder = planShapeBuilder; exports.polygonSubpath = polygonSubpath; exports.polylineBounds = polylineBounds; exports.pressureWidthProfile = pressureWidthProfile; exports.recognizeInkShape = recognizeInkShape; exports.rectSubpath = rectSubpath; exports.removeAnchorKeepingShape = removeAnchorKeepingShape; exports.removeAnchors = removeAnchors; exports.removeAnchorsKeepingShape = removeAnchorsKeepingShape; exports.removeRedundantPoints = removeRedundantPoints; exports.removeWidthPoints = removeWidthPoints; exports.reverseSubpath = reverseSubpath; exports.reverseWidthProfile = reverseWidthProfile; exports.rotateMatrix = rotateMatrix; exports.roundSubpaths = roundSubpaths; exports.samplePolyline = samplePolyline; exports.sanitizeWidthProfile = sanitizeWidthProfile; exports.scaleFor = scaleFor; exports.scaleMatrix = scaleMatrix; exports.setAnchorCorner = setAnchorCorner; exports.setSubpathCorners = setSubpathCorners; exports.setWidthPoint = setWidthPoint; exports.simplifyPolyline = simplifyPolyline; exports.smoothSubpath = smoothSubpath; exports.splitIntoFaces = splitIntoFaces; exports.splitSubpathAt = splitSubpathAt; exports.starSubpath = starSubpath; exports.strokePositionPoint = strokePositionPoint; exports.strokeWidthProfileOf = strokeWidthProfileOf; exports.subpathEnclosesArea = subpathEnclosesArea; exports.subpathPointAt = subpathPointAt; exports.subpathSegment = subpathSegment; exports.subpathSegmentCount = subpathSegmentCount; exports.subpathsToBezier = subpathsToBezier; exports.taperWaveFactor = taperWaveFactor; exports.toVectorKeyInput = toVectorKeyInput; exports.toVectorModifiers = toVectorModifiers; exports.toVectorPointerInput = toVectorPointerInput; exports.transformVectorNode = transformVectorNode; exports.translateAnchor = translateAnchor; exports.translateMatrix = translateMatrix; exports.unionBounds = unionBounds; exports.variableStrokeOutline = variableStrokeOutline; exports.variableStrokeRings = variableStrokeRings; exports.vectorCornerAt = vectorCornerAt; exports.vectorCorners = vectorCorners; exports.vectorCornersAttribute = vectorCornersAttribute; exports.vectorDiamondPath = vectorDiamondPath; exports.vectorDocumentToSvg = vectorDocumentToSvg; exports.vectorFillMap = vectorFillMap; exports.vectorInkShapeName = vectorInkShapeName; exports.vectorNodeBounds = vectorNodeBounds; exports.vectorNodeRotation = vectorNodeRotation; exports.vectorNodeToBezier = vectorNodeToBezier; exports.vectorPaintToHex = vectorPaintToHex; exports.vectorPaintToSvg = vectorPaintToSvg; exports.vectorParentMatrix = vectorParentMatrix; exports.vectorPathData = vectorPathData; exports.vectorPathEntries = vectorPathEntries; exports.vectorPathWorldBezier = vectorPathWorldBezier; exports.vectorPathfinderShape = vectorPathfinderShape; exports.vectorResizeCursor = vectorResizeCursor; exports.vectorResultStyle = vectorResultStyle; exports.vectorShapeBuilderHit = vectorShapeBuilderHit; exports.vectorShapeBuilderMap = vectorShapeBuilderMap; exports.vectorStrokeLineArea = vectorStrokeLineArea; exports.vectorStrokeOutline = vectorStrokeOutline; exports.vectorStrokeOutlinePath = vectorStrokeOutlinePath; exports.vectorStrokeParts = vectorStrokeParts; exports.vectorStrokeReach = vectorStrokeReach; exports.vectorStrokeSvgParts = vectorStrokeSvgParts; exports.vectorStrokeWidthGeometry = vectorStrokeWidthGeometry; exports.vectorToolLabel = vectorToolLabel; exports.vectorWorldMatrix = vectorWorldMatrix; exports.walkVectorNodes = walkVectorNodes; exports.widthProfileAt = widthProfileAt; exports.widthProfileFromAttribute = widthProfileFromAttribute; exports.widthProfileMaxScale = widthProfileMaxScale; exports.widthProfilePreset = widthProfilePreset; exports.widthProfilePresetId = widthProfilePresetId; exports.widthProfileToAttribute = widthProfileToAttribute;
8839
+
8840
+
8841
+ exports.DEFAULT_VECTOR_PATH_EDIT = DEFAULT_VECTOR_PATH_EDIT; exports.DEFAULT_VECTOR_SNAP = DEFAULT_VECTOR_SNAP; exports.IDENTITY_MATRIX = IDENTITY_MATRIX; exports.KAPPA = KAPPA2; exports.LazyMouse = LazyMouse; exports.VECTOR_ARROWHEADS = VECTOR_ARROWHEADS; exports.VECTOR_ARROWHEAD_LABELS = VECTOR_ARROWHEAD_LABELS; exports.VECTOR_BUILDER_HATCH_GAP = VECTOR_BUILDER_HATCH_GAP; exports.VECTOR_CLIPBOARD_TYPE = VECTOR_CLIPBOARD_TYPE; exports.VECTOR_CONSTRUCTION_ANGLES = VECTOR_CONSTRUCTION_ANGLES; exports.VECTOR_CORNER_KINDS = VECTOR_CORNER_KINDS; exports.VECTOR_CORNER_KIND_LABELS = VECTOR_CORNER_KIND_LABELS; exports.VECTOR_CORNER_WIDGET_INSET = VECTOR_CORNER_WIDGET_INSET; exports.VECTOR_EDIT_TOGGLES = VECTOR_EDIT_TOGGLES; exports.VECTOR_FILL_GAP = VECTOR_FILL_GAP; exports.VECTOR_INK_MAX_CORNERS = VECTOR_INK_MAX_CORNERS; exports.VECTOR_OVERLAY_THEME = VECTOR_OVERLAY_THEME; exports.VECTOR_PATHFINDER_ACTIONS = VECTOR_PATHFINDER_ACTIONS; exports.VECTOR_PATHFINDER_LABELS = VECTOR_PATHFINDER_LABELS; exports.VECTOR_PATHFINDER_OPERATIONS = VECTOR_PATHFINDER_OPERATIONS; exports.VECTOR_PATH_ACTIONS = VECTOR_PATH_ACTIONS; exports.VECTOR_PENCIL_STRING_PX = VECTOR_PENCIL_STRING_PX; exports.VECTOR_PRESSURE_NOMINAL = VECTOR_PRESSURE_NOMINAL; exports.VECTOR_ROTATE_HANDLE_OFFSET = VECTOR_ROTATE_HANDLE_OFFSET; exports.VECTOR_STROKE_ALIGN_LABELS = VECTOR_STROKE_ALIGN_LABELS; exports.VECTOR_STYLE_KEYS = VECTOR_STYLE_KEYS; exports.VECTOR_TOOLS = VECTOR_TOOLS; exports.VECTOR_TOOL_GLYPHS = VECTOR_TOOL_GLYPHS; exports.VECTOR_TOOL_SHORTCUTS = VECTOR_TOOL_SHORTCUTS; exports.VECTOR_TRANSFORM_HANDLES = VECTOR_TRANSFORM_HANDLES; exports.VECTOR_WIDTH_PRESETS = VECTOR_WIDTH_PRESETS; exports.VECTOR_WIDTH_PRESET_IDS = VECTOR_WIDTH_PRESET_IDS; exports.VECTOR_WIDTH_PROFILE_MAX_POINTS = VECTOR_WIDTH_PROFILE_MAX_POINTS; exports.VECTOR_WIDTH_PROFILE_MAX_SCALE = VECTOR_WIDTH_PROFILE_MAX_SCALE; exports.VECTOR_WIDTH_SAME_T = VECTOR_WIDTH_SAME_T; exports.VectorEditorController = VectorEditorController; exports.VectorSnapTargets = VectorSnapTargets; exports.addBuilderHit = addBuilderHit; exports.applyMatrix = applyMatrix; exports.applyMatrixLinear = applyMatrixLinear; exports.applyVectorCornersAttribute = applyVectorCornersAttribute; exports.arrowheadInset = arrowheadInset; exports.arrowheadPath = arrowheadPath; exports.arrowheadWeight = arrowheadWeight; exports.bendSegment = bendSegment; exports.bezierToSubpaths = bezierToSubpaths; exports.boundsFromPoints = boundsFromPoints; exports.boundsIntersect = boundsIntersect; exports.cloneAnchor = cloneAnchor; exports.cloneSubpath = cloneSubpath; exports.cloneVectorDocument = cloneVectorDocument; exports.cloneVectorNode = cloneVectorNode; exports.collectPathNodes = collectPathNodes; exports.constrainToLeave = constrainToLeave; exports.convertAnchor = convertAnchor; exports.cornerBisectorPoint = cornerBisectorPoint; exports.cornerRadiusChange = cornerRadiusChange; exports.cornerSetback = cornerSetback; exports.createVectorCommands = createVectorCommands; exports.curvatureMoveAnchor = curvatureMoveAnchor; exports.cutSubpathAt = cutSubpathAt; exports.dashIntervals = dashIntervals; exports.decomposeRigid = decomposeRigid; exports.effectiveStrokeAlign = effectiveStrokeAlign; exports.ellipseSubpath = ellipseSubpath; exports.eraseFromFill = eraseFromFill; exports.eraseFromOutline = eraseFromOutline; exports.eraserRegion = eraserRegion; exports.expandVectorCorners = expandVectorCorners; exports.expandVectorCornersOf = expandVectorCornersOf; exports.findVectorNode = findVectorNode; exports.fitCornerRadius = fitCornerRadius; exports.generateWidthProfile = generateWidthProfile; exports.getAnchor = getAnchor; exports.getAnchorWorld = getAnchorWorld; exports.getEditablePathIds = getEditablePathIds; exports.getTransformHandlePositions = getTransformHandlePositions; exports.getVectorCornerWidgets = getVectorCornerWidgets; exports.getVectorOverlayRenderData = getVectorOverlayRenderData; exports.getVectorSceneRenderData = getVectorSceneRenderData; exports.getVectorSelectionBoxGeometry = getVectorSelectionBoxGeometry; exports.getVectorSelectionFrame = getVectorSelectionFrame; exports.getVectorSelectionStyle = getVectorSelectionStyle; exports.getVectorWidthHandles = getVectorWidthHandles; exports.getVisibleAnchors = getVisibleAnchors; exports.getVisibleHandles = getVisibleHandles; exports.hasPaint = hasPaint; exports.hasStrokeExtras = hasStrokeExtras; exports.hasVectorCorners = hasVectorCorners; exports.hitTestVectorNode = hitTestVectorNode; exports.hitTestVectorNodes = hitTestVectorNodes; exports.hitTestVectorStroke = hitTestVectorStroke; exports.inferAnchorType = inferAnchorType; exports.insertAnchor = insertAnchor; exports.insertWidthPoint = insertWidthPoint; exports.invertMatrix = invertMatrix; exports.isClosedBezierShape = isClosedBezierShape; exports.isIdentityMatrix = isIdentityMatrix; exports.isUniformWidthProfile = isUniformWidthProfile; exports.isVectorTextTarget = isVectorTextTarget; exports.joinSubpaths = joinSubpaths; exports.knifePath = knifePath; exports.lineSubpath = lineSubpath; exports.locateVectorNode = locateVectorNode; exports.matrixRotation = matrixRotation; exports.mirrorWidthProfile = mirrorWidthProfile; exports.moveAnchorHandle = moveAnchorHandle; exports.multiplyMatrices = multiplyMatrices; exports.nearestSegment = nearestSegment; exports.nearestStrokePosition = nearestStrokePosition; exports.nextVectorCornerKind = nextVectorCornerKind; exports.nodeMatrix = nodeMatrix; exports.parseSvgTransform = parseSvgTransform; exports.parseVectorSvg = parseVectorSvg; exports.planShapeBuilder = planShapeBuilder; exports.polygonSubpath = polygonSubpath; exports.polylineBounds = polylineBounds; exports.pressureWidthProfile = pressureWidthProfile; exports.recognizeInkShape = recognizeInkShape; exports.rectSubpath = rectSubpath; exports.removeAnchorKeepingShape = removeAnchorKeepingShape; exports.removeAnchors = removeAnchors; exports.removeAnchorsKeepingShape = removeAnchorsKeepingShape; exports.removeRedundantPoints = removeRedundantPoints; exports.removeWidthPoints = removeWidthPoints; exports.reverseSubpath = reverseSubpath; exports.reverseWidthProfile = reverseWidthProfile; exports.rotateMatrix = rotateMatrix; exports.roundSubpaths = roundSubpaths; exports.samplePolyline = samplePolyline; exports.sanitizeWidthProfile = sanitizeWidthProfile; exports.scaleFor = scaleFor; exports.scaleMatrix = scaleMatrix; exports.setAnchorCorner = setAnchorCorner; exports.setSubpathCorners = setSubpathCorners; exports.setWidthPoint = setWidthPoint; exports.simplifyPolyline = simplifyPolyline; exports.smoothSubpath = smoothSubpath; exports.splitIntoFaces = splitIntoFaces; exports.splitSubpathAt = splitSubpathAt; exports.starSubpath = starSubpath; exports.strokePositionPoint = strokePositionPoint; exports.strokeWidthProfileOf = strokeWidthProfileOf; exports.subpathEnclosesArea = subpathEnclosesArea; exports.subpathPointAt = subpathPointAt; exports.subpathSegment = subpathSegment; exports.subpathSegmentCount = subpathSegmentCount; exports.subpathsToBezier = subpathsToBezier; exports.taperWaveFactor = taperWaveFactor; exports.toVectorKeyInput = toVectorKeyInput; exports.toVectorModifiers = toVectorModifiers; exports.toVectorPointerInput = toVectorPointerInput; exports.transformVectorNode = transformVectorNode; exports.translateAnchor = translateAnchor; exports.translateMatrix = translateMatrix; exports.unionBounds = unionBounds; exports.variableStrokeOutline = variableStrokeOutline; exports.variableStrokeRings = variableStrokeRings; exports.vectorBuilderErasedEdges = vectorBuilderErasedEdges; exports.vectorCornerAt = vectorCornerAt; exports.vectorCorners = vectorCorners; exports.vectorCornersAttribute = vectorCornersAttribute; exports.vectorDiamondPath = vectorDiamondPath; exports.vectorDocumentToSvg = vectorDocumentToSvg; exports.vectorFillMap = vectorFillMap; exports.vectorInkShapeName = vectorInkShapeName; exports.vectorNodeBounds = vectorNodeBounds; exports.vectorNodeRotation = vectorNodeRotation; exports.vectorNodeToBezier = vectorNodeToBezier; exports.vectorPaintToHex = vectorPaintToHex; exports.vectorPaintToSvg = vectorPaintToSvg; exports.vectorParentMatrix = vectorParentMatrix; exports.vectorPathData = vectorPathData; exports.vectorPathEntries = vectorPathEntries; exports.vectorPathWorldBezier = vectorPathWorldBezier; exports.vectorPathfinderShape = vectorPathfinderShape; exports.vectorResizeCursor = vectorResizeCursor; exports.vectorResultStyle = vectorResultStyle; exports.vectorShapeBuilderHit = vectorShapeBuilderHit; exports.vectorShapeBuilderMap = vectorShapeBuilderMap; exports.vectorStrokeLineArea = vectorStrokeLineArea; exports.vectorStrokeOutline = vectorStrokeOutline; exports.vectorStrokeOutlinePath = vectorStrokeOutlinePath; exports.vectorStrokeParts = vectorStrokeParts; exports.vectorStrokeReach = vectorStrokeReach; exports.vectorStrokeSvgParts = vectorStrokeSvgParts; exports.vectorStrokeWidthGeometry = vectorStrokeWidthGeometry; exports.vectorToolLabel = vectorToolLabel; exports.vectorWorldMatrix = vectorWorldMatrix; exports.walkVectorNodes = walkVectorNodes; exports.widthProfileAt = widthProfileAt; exports.widthProfileFromAttribute = widthProfileFromAttribute; exports.widthProfileMaxScale = widthProfileMaxScale; exports.widthProfilePreset = widthProfilePreset; exports.widthProfilePresetId = widthProfilePresetId; exports.widthProfileToAttribute = widthProfileToAttribute;
8522
8842
  //# sourceMappingURL=index.cjs.map