@pixerate/editor 0.35.0 → 0.36.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.
@@ -22,6 +22,10 @@ var _chunkQKLRAA4Tcjs = require('../chunk-QKLRAA4T.cjs');
22
22
 
23
23
 
24
24
 
25
+
26
+
27
+
28
+
25
29
 
26
30
 
27
31
 
@@ -30,17 +34,430 @@ var _chunkQKLRAA4Tcjs = require('../chunk-QKLRAA4T.cjs');
30
34
  var _chunkBGNPT5PEcjs = require('../chunk-BGNPT5PE.cjs');
31
35
 
32
36
  // src/vector/types.ts
37
+ var VECTOR_CORNER_KINDS = ["round", "inverted", "chamfer"];
38
+ var VECTOR_ARROWHEADS = ["triangle", "arrow", "open", "circle", "square", "bar"];
33
39
  var VECTOR_TOOLS = ["select", "direct", "pen", "pencil", "rect", "ellipse", "polygon", "star", "line", "scissors", "knife", "eraser"];
34
40
  var VECTOR_TRANSFORM_HANDLES = ["nw", "n", "ne", "e", "se", "s", "sw", "w", "rotate"];
35
41
 
36
- // src/vector/geometry.ts
42
+ // src/vector/corners.ts
37
43
  var EPS = 1e-9;
44
+ var MIN_SINE = 1e-6;
45
+ var sub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
46
+ var add = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
47
+ var mul = (a, k) => ({ x: a.x * k, y: a.y * k });
48
+ var dot = (a, b) => a.x * b.x + a.y * b.y;
49
+ var cross = (a, b) => a.x * b.y - a.y * b.x;
50
+ var len = (a) => Math.hypot(a.x, a.y);
51
+ var pt = (p) => [p.x, p.y];
52
+ var vec = (p) => ({ x: p[0], y: p[1] });
53
+ var onAnchor = (a, h) => !h || Math.abs(h.x - a.x) < EPS && Math.abs(h.y - a.y) < EPS;
54
+ var Side = class {
55
+
56
+
57
+
58
+ constructor(a, h1, h2, b) {
59
+ this.straight = onAnchor(a, h1) && onAnchor(b, h2);
60
+ this.curve = this.straight ? [pt(a), pt(b)] : [pt(a), pt(_nullishCoalesce(h1, () => ( a))), pt(_nullishCoalesce(h2, () => ( b))), pt(b)];
61
+ this.length = this.straight ? Math.hypot(b.x - a.x, b.y - a.y) : _chunkBGNPT5PEcjs.segmentLength.call(void 0, this.curve);
62
+ }
63
+ /** Unit direction the side leaves the corner in (towards its first control point off the corner). */
64
+ direction() {
65
+ const p0 = vec(this.curve[0]);
66
+ for (const q of this.curve.slice(1)) {
67
+ const d = sub(vec(q), p0);
68
+ const l = len(d);
69
+ if (l > EPS) return mul(d, 1 / l);
70
+ }
71
+ return null;
72
+ }
73
+ /** The point `s` along the side from the corner: its parameter, position and the direction the side runs on in there. */
74
+ at(s, u) {
75
+ if (this.straight) {
76
+ const t2 = this.length > EPS ? Math.min(1, Math.max(0, s / this.length)) : 0;
77
+ return { t: t2, p: add(vec(this.curve[0]), mul(u, s)), dir: u };
78
+ }
79
+ const t = _chunkBGNPT5PEcjs.segmentParamAtLength.call(void 0, this.curve, s, this.length);
80
+ const d = vec(_chunkBGNPT5PEcjs.segmentDerivative.call(void 0, this.curve, t));
81
+ const l = len(d);
82
+ return { t, p: vec(_chunkBGNPT5PEcjs.segmentPoint.call(void 0, this.curve, t)), dir: l > EPS ? mul(d, 1 / l) : u };
83
+ }
84
+ };
85
+ function cornerOf(sp, s, i) {
86
+ const n = sp.anchors.length;
87
+ if (n < 2 || !sp.closed && (i === 0 || i === n - 1)) return null;
88
+ if (n === 2 && sp.closed) return null;
89
+ const a = sp.anchors[i];
90
+ if (a.type === "smooth" || a.type === "symmetric") return null;
91
+ const prev = sp.anchors[(i - 1 + n) % n];
92
+ const next = sp.anchors[(i + 1) % n];
93
+ const sideU = new Side(a, a.in, prev.out, prev);
94
+ const sideV = new Side(a, a.out, next.in, next);
95
+ const u = sideU.direction();
96
+ const v2 = sideV.direction();
97
+ if (!u || !v2) return null;
98
+ const lu = sideU.length;
99
+ const lv = sideV.length;
100
+ if (!(lu > EPS && lv > EPS) || !Number.isFinite(lu) || !Number.isFinite(lv)) return null;
101
+ const cos = Math.max(-1, Math.min(1, dot(u, v2)));
102
+ const sin = Math.abs(cross(u, v2));
103
+ if (sin <= MIN_SINE) return null;
104
+ return { subpath: s, index: i, at: { x: a.x, y: a.y }, u, v: v2, lu, lv, cos, sin, angle: Math.atan2(sin, cos) * 180 / Math.PI, maxRadius: Math.min(lu, lv) / 2 * sin / (1 + cos), sideU, sideV };
105
+ }
106
+ var strip = ({ sideU: _u, sideV: _v, ...info }) => info;
107
+ function vectorCornerAt(sp, index, subpath = 0) {
108
+ const c = cornerOf(sp, subpath, index);
109
+ return c ? strip(c) : null;
110
+ }
111
+ function vectorCorners(subpaths) {
112
+ const out = [];
113
+ subpaths.forEach((sp, s) => {
114
+ for (let i = 0; i < sp.anchors.length; i++) {
115
+ const c = cornerOf(sp, s, i);
116
+ if (c) out.push(strip(c));
117
+ }
118
+ });
119
+ return out;
120
+ }
121
+ var cornerSetback = (c, r) => r * (1 + c.cos) / c.sin;
122
+ var fitCornerRadius = (c, radius) => Number.isFinite(radius) ? Math.min(Math.max(0, radius), c.maxRadius) : 0;
123
+ var cornerBisectorPoint = (c, k) => add(c.at, mul(add(c.u, c.v), k));
124
+ var cornerRadiusChange = (c, d) => dot(d, add(c.u, c.v)) * c.sin / (2 + 2 * c.cos);
125
+ function arcHandle(cos) {
126
+ const c = Math.max(-1, Math.min(1, cos));
127
+ const sinHalf = Math.sqrt(Math.max(0, (1 - c) / 2));
128
+ const cosHalf = Math.sqrt(Math.max(0, (1 + c) / 2));
129
+ return 4 / 3 * sinHalf / (1 + cosHalf);
130
+ }
131
+ function squareTo(a, b) {
132
+ const w = sub(b, mul(a, dot(a, b)));
133
+ const l = len(w);
134
+ return l > EPS ? mul(w, 1 / l) : { x: 0, y: 0 };
135
+ }
136
+ function cutCorner(c, radius, kind) {
137
+ const s = Math.min(cornerSetback(c, radius), Math.min(c.lu, c.lv) / 2);
138
+ if (!(s > EPS)) return null;
139
+ const a = c.sideU.at(s, c.u);
140
+ const b = c.sideV.at(s, c.v);
141
+ if (kind === "chamfer") return { tu: a.t, tv: b.t, start: a.p, out: null, in: null, end: b.p };
142
+ if (kind === "inverted") {
143
+ const h = s * arcHandle(dot(a.dir, b.dir));
144
+ return { tu: a.t, tv: b.t, start: a.p, out: add(a.p, mul(squareTo(a.dir, b.dir), h)), in: add(b.p, mul(squareTo(b.dir, a.dir), h)), end: b.p };
145
+ }
146
+ const d1 = mul(a.dir, -1);
147
+ const d2 = mul(b.dir, -1);
148
+ const turn = Math.acos(Math.max(-1, Math.min(1, -dot(d1, d2))));
149
+ const chord = len(sub(b.p, a.p));
150
+ const k = turn < 1e-9 ? chord / 3 : 4 / 3 * Math.tan(turn / 4) * chord / (2 * Math.sin(turn / 2));
151
+ if (!Number.isFinite(k)) return null;
152
+ 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 };
153
+ }
154
+ var radiusOf = (a) => {
155
+ const r = _optionalChain([a, 'access', _2 => _2.corner, 'optionalAccess', _3 => _3.radius]);
156
+ return typeof r === "number" && r > 0 && Number.isFinite(r) ? r : 0;
157
+ };
158
+ function hasVectorCorners(subpaths) {
159
+ return subpaths.some((sp) => sp.anchors.some((a) => radiusOf(a) > 0));
160
+ }
161
+ function expandVectorCornersOf(sp, subpath = 0) {
162
+ const n = sp.anchors.length;
163
+ const cuts = sp.anchors.map((a, i) => {
164
+ const r = radiusOf(a);
165
+ if (!r) return null;
166
+ const c = cornerOf(sp, subpath, i);
167
+ if (!c) return null;
168
+ const fitted = fitCornerRadius(c, r);
169
+ return fitted > EPS ? cutCorner(c, fitted, _nullishCoalesce(_optionalChain([a, 'access', _4 => _4.corner, 'optionalAccess', _5 => _5.kind]), () => ( "round"))) : null;
170
+ });
171
+ if (!cuts.some(Boolean)) return { subpath: { closed: sp.closed, anchors: sp.anchors.map(plain) }, sources: sp.anchors.map((_, i) => i) };
172
+ const trimmed = sp.anchors.map((a, j) => {
173
+ const k = (j + 1) % n;
174
+ if (k === 0 && !sp.closed) return void 0;
175
+ const b = sp.anchors[k];
176
+ const from = cuts[j];
177
+ const to = cuts[k];
178
+ if (!from && !to) return void 0;
179
+ if (onAnchor(a, a.out) && onAnchor(b, b.in)) return { out: null, in: null };
180
+ const s0 = from ? from.tv : 0;
181
+ const s1 = to ? 1 - to.tu : 1;
182
+ const seg = [pt(a), pt(_nullishCoalesce(a.out, () => ( a))), pt(_nullishCoalesce(b.in, () => ( b))), pt(b)];
183
+ const part = _chunkBGNPT5PEcjs.subSegment.call(void 0, seg, s0, Math.max(s0, s1));
184
+ return { out: vec(part[1]), in: vec(part[2]) };
185
+ });
186
+ const anchors = [];
187
+ const sources = [];
188
+ let closing = null;
189
+ const handleIn = (i, a) => {
190
+ const t = trimmed[(i - 1 + n) % n];
191
+ return t ? t.in : _nullishCoalesce(a.in, () => ( null));
192
+ };
193
+ const handleOut = (i, a) => {
194
+ const t = trimmed[i];
195
+ return t ? t.out : _nullishCoalesce(a.out, () => ( null));
196
+ };
197
+ sp.anchors.forEach((a, i) => {
198
+ const c = cuts[i];
199
+ if (!c) {
200
+ anchors.push(withHandles({ x: a.x, y: a.y, ...a.type ? { type: a.type } : {} }, handleIn(i, a), handleOut(i, a)));
201
+ sources.push(i);
202
+ return;
203
+ }
204
+ const start = withHandles({ x: c.start.x, y: c.start.y }, handleIn(i, a), c.out);
205
+ const end = withHandles({ x: c.end.x, y: c.end.y }, c.in, handleOut(i, a));
206
+ if (i === 0 && sp.closed) closing = start;
207
+ else {
208
+ anchors.push(start);
209
+ sources.push(i);
210
+ }
211
+ anchors.push(end);
212
+ sources.push(i);
213
+ });
214
+ if (closing) {
215
+ anchors.push(closing);
216
+ sources.push(0);
217
+ }
218
+ return { subpath: { closed: sp.closed, anchors }, sources };
219
+ }
220
+ function withHandles(a, hin, hout) {
221
+ if (hin && !onAnchor(a, hin)) a.in = { x: hin.x, y: hin.y };
222
+ if (hout && !onAnchor(a, hout)) a.out = { x: hout.x, y: hout.y };
223
+ return a;
224
+ }
225
+ function plain(a) {
226
+ if (!("corner" in a)) return a;
227
+ const { corner: _c, ...rest } = a;
228
+ return rest;
229
+ }
230
+ function expandVectorCorners(subpaths) {
231
+ if (!hasVectorCorners(subpaths)) return subpaths;
232
+ return subpaths.map((sp, s) => sp.anchors.some((a) => radiusOf(a) > 0) ? expandVectorCornersOf(sp, s).subpath : sp);
233
+ }
234
+ function setAnchorCorner(a, radius, kind) {
235
+ const { corner, ...rest } = a;
236
+ const r = Number.isFinite(radius) ? Math.max(0, radius) : 0;
237
+ const k = _nullishCoalesce(kind, () => ( _optionalChain([corner, 'optionalAccess', _6 => _6.kind])));
238
+ if (r <= 0 && !kind) return rest;
239
+ return { ...rest, corner: { radius: r, ...k && k !== "round" ? { kind: k } : {} } };
240
+ }
241
+ function nextVectorCornerKind(kind) {
242
+ return kind === "inverted" ? "chamfer" : kind === "chamfer" ? "round" : "inverted";
243
+ }
244
+ function setSubpathCorners(sp, radius, kind) {
245
+ return { closed: sp.closed, anchors: sp.anchors.map((a) => setAnchorCorner(a, radius, kind)) };
246
+ }
247
+
248
+ // src/vector/stroke.ts
249
+ var KAPPA = 0.5522847498307936;
250
+ var NOTCH = 0.7;
251
+ function isClosedBezierShape(path) {
252
+ const contours = _chunkBGNPT5PEcjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
253
+ return contours.length > 0 && contours.every((c) => c.closed);
254
+ }
255
+ function effectiveStrokeAlign(style, closedShape) {
256
+ const a = _nullishCoalesce(style.strokeAlign, () => ( "center"));
257
+ return closedShape ? a : "center";
258
+ }
259
+ function hasStrokeExtras(style) {
260
+ return (_nullishCoalesce(style.strokeAlign, () => ( "center"))) !== "center" || !!style.startArrow || !!style.endArrow;
261
+ }
262
+ var capExtent = (style, width) => style.strokeLinecap === "round" || style.strokeLinecap === "square" ? width / 2 : 0;
263
+ function arrowheadWeight(style, end) {
264
+ const scale = end === "start" ? style.startArrowScale : style.endArrowScale;
265
+ const s = typeof scale === "number" && scale > 0 && Number.isFinite(scale) ? scale : 1;
266
+ return Math.max(0.25, (_nullishCoalesce(style.strokeWidth, () => ( 1))) * s);
267
+ }
268
+ function arrowheadInset(kind, hw, cap) {
269
+ const l = 4 * hw;
270
+ const solid = (back) => Math.max(back / 2, cap);
271
+ switch (kind) {
272
+ case "triangle":
273
+ case "circle":
274
+ case "square":
275
+ return solid(l);
276
+ case "arrow":
277
+ return solid(NOTCH * l);
278
+ case "bar":
279
+ return solid(0.75 * hw);
280
+ case "open":
281
+ return Math.max(hw * Math.sqrt(l * l + l * l / 4) / (l / 2), cap);
282
+ }
283
+ }
284
+ var v = (x, y) => ({ x, y });
285
+ var plus = (a, b, k = 1) => ({ x: a.x + b.x * k, y: a.y + b.y * k });
286
+ var unit = (a) => {
287
+ const l = Math.hypot(a.x, a.y);
288
+ return l > 0 ? { x: a.x / l, y: a.y / l } : { x: 1, y: 0 };
289
+ };
290
+ function polygon(pts) {
291
+ return [{ type: "M", x: pts[0].x, y: pts[0].y }, ...pts.slice(1).map((p) => ({ type: "L", x: p.x, y: p.y })), { type: "Z" }];
292
+ }
293
+ function circle(c, r) {
294
+ const k = r * KAPPA;
295
+ const { x, y } = c;
296
+ return [
297
+ { type: "M", x: x + r, y },
298
+ { type: "C", x1: x + r, y1: y + k, x2: x + k, y2: y + r, x, y: y + r },
299
+ { type: "C", x1: x - k, y1: y + r, x2: x - r, y2: y + k, x: x - r, y },
300
+ { type: "C", x1: x - r, y1: y - k, x2: x - k, y2: y - r, x, y: y - r },
301
+ { type: "C", x1: x + k, y1: y - r, x2: x + r, y2: y - k, x: x + r, y },
302
+ { type: "Z" }
303
+ ];
304
+ }
305
+ function arrowheadPath(kind, tip, dir, hw) {
306
+ const n = v(-dir.y, dir.x);
307
+ const l = 4 * hw;
308
+ const half = l / 2;
309
+ const base = plus(tip, dir, -l);
310
+ switch (kind) {
311
+ case "triangle":
312
+ return polygon([tip, plus(base, n, half), plus(base, n, -half)]);
313
+ case "arrow":
314
+ return polygon([tip, plus(base, n, half), plus(tip, dir, -NOTCH * l), plus(base, n, -half)]);
315
+ case "open": {
316
+ const t = hw;
317
+ const a = plus(base, n, half);
318
+ const b = plus(base, n, -half);
319
+ const ua = unit(plus(a, tip, -1));
320
+ const ub = unit(plus(b, tip, -1));
321
+ const na = v(-ua.y, ua.x);
322
+ const nb = v(ub.y, -ub.x);
323
+ const inner = plus(tip, dir, -(t * Math.hypot(a.x - tip.x, a.y - tip.y)) / half);
324
+ return polygon([tip, a, plus(a, na, t), inner, plus(b, nb, t), b]);
325
+ }
326
+ case "circle":
327
+ return circle(plus(tip, dir, -half), half);
328
+ case "square":
329
+ return polygon([plus(tip, n, half), plus(base, n, half), plus(base, n, -half), plus(tip, n, -half)]);
330
+ case "bar": {
331
+ const t = 0.75 * hw;
332
+ return polygon([plus(tip, n, half), plus(plus(tip, n, half), dir, -t), plus(plus(tip, n, -half), dir, -t), plus(tip, n, -half)]);
333
+ }
334
+ }
335
+ }
336
+ var toVec = (p) => ({ x: p[0], y: p[1] });
337
+ var segLen = (s) => _chunkBGNPT5PEcjs.segmentLength.call(void 0, s);
338
+ function endOf(segs, atStart) {
339
+ const long = (s2) => segLen(s2) > 1e-9;
340
+ if (atStart) {
341
+ const s2 = _nullishCoalesce(segs.find(long), () => ( segs[0]));
342
+ const t2 = _chunkBGNPT5PEcjs.segmentTangent.call(void 0, s2, 0);
343
+ return { p: toVec(segs[0][0]), out: v(-t2[0], -t2[1]) };
344
+ }
345
+ const s = _nullishCoalesce([...segs].reverse().find(long), () => ( segs[segs.length - 1]));
346
+ const t = _chunkBGNPT5PEcjs.segmentTangent.call(void 0, s, 1);
347
+ const last = segs[segs.length - 1];
348
+ return { p: toVec(last[last.length - 1]), out: v(t[0], t[1]) };
349
+ }
350
+ function pointAlong(segs, fromStart, dist3) {
351
+ const order = fromStart ? segs : [...segs].reverse();
352
+ let acc = 0;
353
+ for (const s of order) {
354
+ const l = segLen(s);
355
+ if (acc + l >= dist3) {
356
+ const want = fromStart ? dist3 - acc : l - (dist3 - acc);
357
+ const pts = _chunkBGNPT5PEcjs.trimSegments.call(void 0, [s], 0, want);
358
+ const p = pts.length ? pts[0][pts[0].length - 1] : s[0];
359
+ return toVec(p);
360
+ }
361
+ acc += l;
362
+ }
363
+ return null;
364
+ }
365
+ function vectorStrokeParts(path, style) {
366
+ const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
367
+ const contours = _chunkBGNPT5PEcjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
368
+ const closedShape = contours.length > 0 && contours.every((c) => c.closed);
369
+ const align = effectiveStrokeAlign(style, closedShape);
370
+ const drawWidth = align === "center" ? width : width * 2;
371
+ const heads = [];
372
+ if (!contours.length || !style.startArrow && !style.endArrow) return { align, line: path, drawWidth, heads };
373
+ const first = contours[0];
374
+ const last = contours[contours.length - 1];
375
+ const cap = capExtent(style, width);
376
+ const tipMode = style.arrowAlign === "tip";
377
+ const trims = contours.map(() => [0, 0]);
378
+ const place = (end, contour, ci) => {
379
+ const kind = end === "start" ? style.startArrow : style.endArrow;
380
+ if (!kind || contour.closed) return;
381
+ const hw = arrowheadWeight(style, end);
382
+ const inset = arrowheadInset(kind, hw, cap);
383
+ const atStart = end === "start";
384
+ const { p, out } = endOf(contour.segments, atStart);
385
+ let tip;
386
+ let dir;
387
+ if (tipMode) {
388
+ const q = pointAlong(contour.segments, atStart, inset);
389
+ const d = q ? v(p.x - q.x, p.y - q.y) : null;
390
+ dir = d && Math.hypot(d.x, d.y) > 1e-9 ? unit(d) : out;
391
+ tip = p;
392
+ trims[ci][atStart ? 0 : 1] = inset;
393
+ } else {
394
+ tip = plus(p, out, inset);
395
+ dir = out;
396
+ }
397
+ heads.push({ end, kind, tip, dir, inset, path: arrowheadPath(kind, tip, dir, hw) });
398
+ };
399
+ place("start", first, 0);
400
+ place("end", last, contours.length - 1);
401
+ if (!tipMode || !heads.length) return { align, line: path, drawWidth, heads };
402
+ const trimmed = contours.map((c, i) => {
403
+ const [s0, s1] = trims[i];
404
+ if (!s0 && !s1) return c;
405
+ const lengths = c.segments.map(segLen);
406
+ const total = lengths.reduce((a, b) => a + b, 0);
407
+ return { closed: false, segments: total - s1 > s0 ? _chunkBGNPT5PEcjs.trimSegments.call(void 0, c.segments, s0, total - s1, lengths) : [] };
408
+ });
409
+ return { align, line: _chunkBGNPT5PEcjs.contoursToPath.call(void 0, trimmed), drawWidth, heads };
410
+ }
411
+ function vectorStrokeOutlinePath(path, style) {
412
+ const parts = vectorStrokeParts(path, style);
413
+ const stroke = _chunkBGNPT5PEcjs.outlineStroke.call(void 0, parts.line, {
414
+ width: parts.drawWidth,
415
+ join: _nullishCoalesce(style.strokeLinejoin, () => ( "miter")),
416
+ cap: _nullishCoalesce(style.strokeLinecap, () => ( "butt")),
417
+ ..._optionalChain([style, 'access', _7 => _7.strokeDasharray, 'optionalAccess', _8 => _8.length]) ? { dash: style.strokeDasharray } : {}
418
+ });
419
+ const fill = { path, fillRule: _nullishCoalesce(style.fillRule, () => ( "nonzero")) };
420
+ let area = stroke;
421
+ if (parts.align === "inside") area = _chunkBGNPT5PEcjs.pathBoolean.call(void 0, "intersection", [stroke, fill]).path;
422
+ else if (parts.align === "outside") area = _chunkBGNPT5PEcjs.pathBoolean.call(void 0, "difference", [stroke, fill]).path;
423
+ if (!parts.heads.length) return area;
424
+ return _chunkBGNPT5PEcjs.pathBoolean.call(void 0, "union", [area, ...parts.heads.map((h) => h.path)]).path;
425
+ }
426
+ function hitTestVectorStroke(path, p, style, tolerance = 0) {
427
+ const parts = vectorStrokeParts(path, style);
428
+ const point = [p.x, p.y];
429
+ const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
430
+ for (const h of parts.heads) {
431
+ if (_chunkBGNPT5PEcjs.pointInBezierPath.call(void 0, h.path, point) || tolerance > 0 && _chunkBGNPT5PEcjs.distanceToBezierPath.call(void 0, h.path, point) <= tolerance) return true;
432
+ }
433
+ if (!parts.line.length) return false;
434
+ const d = _chunkBGNPT5PEcjs.distanceToBezierPath.call(void 0, parts.line, point);
435
+ if (parts.align === "center") return d <= width / 2 + tolerance;
436
+ if (d > width + tolerance) return false;
437
+ if (d <= tolerance) return true;
438
+ const inside = _chunkBGNPT5PEcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(style.fillRule, () => ( "nonzero")));
439
+ return parts.align === "inside" ? inside : !inside;
440
+ }
441
+ function vectorStrokeReach(style) {
442
+ const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
443
+ const align = _nullishCoalesce(style.strokeAlign, () => ( "center"));
444
+ let reach = align === "center" ? width / 2 : align === "outside" ? width : 0;
445
+ if (style.strokeLinejoin !== "round" && style.strokeLinejoin !== "bevel") reach *= 4;
446
+ for (const end of ["start", "end"]) {
447
+ if (end === "start" ? style.startArrow : style.endArrow) reach = Math.max(reach, 8 * arrowheadWeight(style, end) + width);
448
+ }
449
+ return reach;
450
+ }
451
+
452
+ // src/vector/geometry.ts
453
+ var EPS2 = 1e-9;
38
454
  var cloneAnchor = (a) => ({
39
455
  x: a.x,
40
456
  y: a.y,
41
457
  ...a.in ? { in: { x: a.in.x, y: a.in.y } } : {},
42
458
  ...a.out ? { out: { x: a.out.x, y: a.out.y } } : {},
43
- ...a.type && a.type !== "corner" ? { type: a.type } : {}
459
+ ...a.type && a.type !== "corner" ? { type: a.type } : {},
460
+ ...a.corner ? { corner: { ...a.corner } } : {}
44
461
  });
45
462
  var cloneSubpath = (s) => ({ anchors: s.anchors.map(cloneAnchor), closed: s.closed });
46
463
  function cloneVectorNode(node) {
@@ -58,7 +475,7 @@ function hasPaint(paint) {
58
475
  return true;
59
476
  }
60
477
  var handleOf = (a, side) => _nullishCoalesce(a[side], () => ( { x: a.x, y: a.y }));
61
- var isOnAnchor = (a, h) => !h || Math.abs(h.x - a.x) < EPS && Math.abs(h.y - a.y) < EPS;
478
+ var isOnAnchor = (a, h) => !h || Math.abs(h.x - a.x) < EPS2 && Math.abs(h.y - a.y) < EPS2;
62
479
  function subpathSegmentCount(sp) {
63
480
  const n = sp.anchors.length;
64
481
  if (n < 2) return 0;
@@ -72,9 +489,9 @@ function subpathSegment(sp, i) {
72
489
  const c2 = handleOf(b, "in");
73
490
  return [[a.x, a.y], [c1.x, c1.y], [c2.x, c2.y], [b.x, b.y]];
74
491
  }
75
- function subpathsToBezier(subpaths) {
492
+ function subpathsToBezier(subpaths, options = {}) {
76
493
  const out = [];
77
- for (const sp of subpaths) {
494
+ for (const sp of options.corners === false ? subpaths : expandVectorCorners(subpaths)) {
78
495
  if (sp.anchors.length === 0) continue;
79
496
  const first = sp.anchors[0];
80
497
  out.push({ type: "M", x: first.x, y: first.y });
@@ -104,9 +521,9 @@ function inferAnchorType(a) {
104
521
  const oy = a.out.y - a.y;
105
522
  const li = Math.hypot(ix, iy);
106
523
  const lo = Math.hypot(ox, oy);
107
- const cross = (ix * oy - iy * ox) / (li * lo);
108
- const dot = (ix * ox + iy * oy) / (li * lo);
109
- if (Math.abs(cross) > 1e-3 || dot > 0) return "corner";
524
+ const cross2 = (ix * oy - iy * ox) / (li * lo);
525
+ const dot2 = (ix * ox + iy * oy) / (li * lo);
526
+ if (Math.abs(cross2) > 1e-3 || dot2 > 0) return "corner";
110
527
  return Math.abs(li - lo) <= 1e-3 * Math.max(li, lo) ? "symmetric" : "smooth";
111
528
  }
112
529
  function bezierToSubpaths(input) {
@@ -249,8 +666,12 @@ function hitTestVectorNode(node, p, tolerance = 0, parent = IDENTITY_MATRIX) {
249
666
  if (path.length === 0) return false;
250
667
  const point = [p.x, p.y];
251
668
  if (hasPaint(node.fill) && _chunkBGNPT5PEcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(node.fillRule, () => ( "nonzero")))) return true;
252
- const reach = (hasPaint(node.stroke) ? (_nullishCoalesce(node.strokeWidth, () => ( 1))) / 2 : 0) + tolerance;
253
- return _chunkBGNPT5PEcjs.distanceToBezierPath.call(void 0, path, point) <= reach;
669
+ if (hasPaint(node.stroke)) return hitTestVectorStroke(path, p, node, tolerance);
670
+ return _chunkBGNPT5PEcjs.distanceToBezierPath.call(void 0, path, point) <= tolerance;
671
+ }
672
+ function vectorStrokeOutline(node, parent = IDENTITY_MATRIX) {
673
+ if (!hasPaint(node.stroke) || !((_nullishCoalesce(node.strokeWidth, () => ( 1))) > 0)) return [];
674
+ return vectorStrokeOutlinePath(vectorPathWorldBezier(node, parent), node);
254
675
  }
255
676
  function hitTestVectorNodes(nodes, p, tolerance = 0) {
256
677
  for (let i = nodes.length - 1; i >= 0; i--) {
@@ -268,8 +689,8 @@ function nearestSegment(node, p) {
268
689
  const seg = subpathSegment(sp, i);
269
690
  const hit = _chunkBGNPT5PEcjs.segmentNearest.call(void 0, seg, [p.x, p.y]);
270
691
  if (!best || hit.distance < best.distance) {
271
- const pt = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, hit.t);
272
- best = { subpath: s, segment: i, t: hit.t, point: { x: pt[0], y: pt[1] }, distance: hit.distance };
692
+ const pt2 = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, hit.t);
693
+ best = { subpath: s, segment: i, t: hit.t, point: { x: pt2[0], y: pt2[1] }, distance: hit.distance };
273
694
  }
274
695
  }
275
696
  });
@@ -303,8 +724,8 @@ function invertMatrix(m) {
303
724
  if (!det || !Number.isFinite(det)) return IDENTITY_MATRIX;
304
725
  return [d / det, -b / det, -c / det, a / det, (c * f - d * e) / det, (b * e - a * f) / det];
305
726
  }
306
- function applyMatrixLinear(m, v) {
307
- return { x: m[0] * v.x + m[2] * v.y, y: m[1] * v.x + m[3] * v.y };
727
+ function applyMatrixLinear(m, v2) {
728
+ return { x: m[0] * v2.x + m[2] * v2.y, y: m[1] * v2.x + m[3] * v2.y };
308
729
  }
309
730
  function matrixRotation(m) {
310
731
  const [a, b, c, d] = m;
@@ -338,6 +759,7 @@ function bakeIntoGeometry(node, m) {
338
759
  if (a.in) next.in = applyMatrix(m, a.in);
339
760
  if (a.out) next.out = applyMatrix(m, a.out);
340
761
  if (a.type) next.type = a.type;
762
+ if (a.corner) next.corner = { ...a.corner };
341
763
  return next;
342
764
  })
343
765
  }))
@@ -389,12 +811,12 @@ function moveAnchorHandle(a, side, pos, breakHandles = false) {
389
811
  if (breakHandles) delete next.type;
390
812
  const dx = pos.x - a.x;
391
813
  const dy = pos.y - a.y;
392
- const len = Math.hypot(dx, dy);
814
+ const len2 = Math.hypot(dx, dy);
393
815
  if (type === "symmetric") next[other] = { x: a.x - dx, y: a.y - dy };
394
- else if (type === "smooth" && len > EPS) {
816
+ else if (type === "smooth" && len2 > EPS2) {
395
817
  const o = a[other];
396
- const olen = o ? Math.hypot(o.x - a.x, o.y - a.y) : len;
397
- next[other] = { x: a.x - dx / len * olen, y: a.y - dy / len * olen };
818
+ const olen = o ? Math.hypot(o.x - a.x, o.y - a.y) : len2;
819
+ next[other] = { x: a.x - dx / len2 * olen, y: a.y - dy / len2 * olen };
398
820
  }
399
821
  if (isOnAnchor(next, next.in)) delete next.in;
400
822
  if (isOnAnchor(next, next.out)) delete next.out;
@@ -421,15 +843,15 @@ function convertAnchor(sp, index, type) {
421
843
  else if (prev && next) dir = { x: next.x - prev.x, y: next.y - prev.y };
422
844
  else if (next) dir = { x: next.x - a.x, y: next.y - a.y };
423
845
  else if (prev) dir = { x: a.x - prev.x, y: a.y - prev.y };
424
- if (!dir || Math.hypot(dir.x, dir.y) < EPS) return { anchors, closed: sp.closed };
846
+ if (!dir || Math.hypot(dir.x, dir.y) < EPS2) return { anchors, closed: sp.closed };
425
847
  const dl = Math.hypot(dir.x, dir.y);
426
848
  const ux = dir.x / dl;
427
849
  const uy = dir.y / dl;
428
850
  lenIn = a.in && !isOnAnchor(a, a.in) ? Math.hypot(a.in.x - a.x, a.in.y - a.y) : prev ? Math.hypot(prev.x - a.x, prev.y - a.y) / 3 : 0;
429
851
  lenOut = a.out && !isOnAnchor(a, a.out) ? Math.hypot(a.out.x - a.x, a.out.y - a.y) : next ? Math.hypot(next.x - a.x, next.y - a.y) / 3 : 0;
430
852
  if (type === "symmetric") lenIn = lenOut = (lenIn + lenOut) / 2 || Math.max(lenIn, lenOut);
431
- if (lenIn > EPS) a.in = { x: a.x - ux * lenIn, y: a.y - uy * lenIn };
432
- if (lenOut > EPS) a.out = { x: a.x + ux * lenOut, y: a.y + uy * lenOut };
853
+ if (lenIn > EPS2) a.in = { x: a.x - ux * lenIn, y: a.y - uy * lenIn };
854
+ if (lenOut > EPS2) a.out = { x: a.x + ux * lenOut, y: a.y + uy * lenOut };
433
855
  a.type = type;
434
856
  return { anchors, closed: sp.closed };
435
857
  }
@@ -465,37 +887,24 @@ function reverseSubpath(sp) {
465
887
  if (a.out) next.in = { ...a.out };
466
888
  if (a.in) next.out = { ...a.in };
467
889
  if (a.type) next.type = a.type;
890
+ if (a.corner) next.corner = { ...a.corner };
468
891
  return next;
469
892
  });
470
893
  return { anchors, closed: sp.closed };
471
894
  }
472
- var KAPPA = 0.5522847498307936;
473
- function rectSubpath(b, radius = 0) {
895
+ var KAPPA2 = 0.5522847498307936;
896
+ function rectSubpath(b, radius = 0, kind) {
474
897
  const { x, y, width: w, height: h } = b;
475
- const r = Math.max(0, Math.min(radius, w / 2, h / 2));
476
- if (r <= EPS) {
477
- return { anchors: [{ x, y }, { x: x + w, y }, { x: x + w, y: y + h }, { x, y: y + h }], closed: true };
478
- }
479
- const k = r * KAPPA;
480
- const anchors = [
481
- { x: x + r, y, in: { x: x + r - k, y } },
482
- { x: x + w - r, y, out: { x: x + w - r + k, y } },
483
- { x: x + w, y: y + r, in: { x: x + w, y: y + r - k } },
484
- { x: x + w, y: y + h - r, out: { x: x + w, y: y + h - r + k } },
485
- { x: x + w - r, y: y + h, in: { x: x + w - r + k, y: y + h } },
486
- { x: x + r, y: y + h, out: { x: x + r - k, y: y + h } },
487
- { x, y: y + h - r, in: { x, y: y + h - r + k } },
488
- { x, y: y + r, out: { x, y: y + r - k } }
489
- ];
490
- return { anchors, closed: true };
898
+ const sp = { anchors: [{ x, y }, { x: x + w, y }, { x: x + w, y: y + h }, { x, y: y + h }], closed: true };
899
+ return radius > 0 ? setSubpathCorners(sp, radius, kind) : sp;
491
900
  }
492
901
  function ellipseSubpath(b) {
493
902
  const rx = b.width / 2;
494
903
  const ry = b.height / 2;
495
904
  const cx = b.x + rx;
496
905
  const cy = b.y + ry;
497
- const kx = rx * KAPPA;
498
- const ky = ry * KAPPA;
906
+ const kx = rx * KAPPA2;
907
+ const ky = ry * KAPPA2;
499
908
  return {
500
909
  closed: true,
501
910
  anchors: [
@@ -543,15 +952,52 @@ function roundSubpaths(subpaths, precision = 3) {
543
952
  }
544
953
 
545
954
  // src/vector/svg.ts
955
+ function vectorCornersAttribute(subpaths, precision = 3) {
956
+ const out = [];
957
+ subpaths.forEach(
958
+ (sp, s) => sp.anchors.forEach((a, i) => {
959
+ if (!a.corner || !(a.corner.radius > 0)) return;
960
+ out.push(`${s}.${i}:${_chunkBGNPT5PEcjs.formatNumber.call(void 0, a.corner.radius, precision)}${a.corner.kind && a.corner.kind !== "round" ? `:${a.corner.kind}` : ""}`);
961
+ })
962
+ );
963
+ return out.join(" ");
964
+ }
965
+ function applyVectorCornersAttribute(subpaths, value) {
966
+ for (const item of value.trim().split(/\s+/)) {
967
+ const m = /^(\d+)\.(\d+):([\d.eE+-]+)(?::([a-z]+))?$/.exec(item);
968
+ if (!m) continue;
969
+ const a = _optionalChain([subpaths, 'access', _9 => _9[+m[1]], 'optionalAccess', _10 => _10.anchors, 'access', _11 => _11[+m[2]]]);
970
+ const radius = parseFloat(m[3]);
971
+ if (!a || !(radius > 0)) continue;
972
+ const kind = VECTOR_CORNER_KINDS.includes(m[4]) ? m[4] : void 0;
973
+ a.corner = { radius, ...kind && kind !== "round" ? { kind } : {} };
974
+ }
975
+ return subpaths;
976
+ }
546
977
  var escapeAttr = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
547
978
  function vectorPaintToSvg(paint, gradientId) {
548
979
  if (!hasPaint(paint)) return { value: "none", gradient: null };
549
980
  if (typeof paint === "string") return { value: paint, gradient: null };
550
981
  return { value: `url(#${gradientId})`, gradient: _chunkBGNPT5PEcjs.gradientToSvgDef.call(void 0, paint, gradientId) };
551
982
  }
983
+ function vectorStrokeSvgParts(node, clipId, precision = 3) {
984
+ if (!hasPaint(node.stroke) || !hasStrokeExtras(node)) return void 0;
985
+ const path = subpathsToBezier(node.subpaths);
986
+ const parts = vectorStrokeParts(path, node);
987
+ if (parts.align === "center" && !parts.heads.length) return void 0;
988
+ let clip = null;
989
+ if (parts.align !== "center") {
990
+ const b = _chunkBGNPT5PEcjs.bezierPathBounds.call(void 0, path);
991
+ const pad = vectorStrokeReach(node) + 1;
992
+ 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 };
993
+ clip = { kind: parts.align === "inside" ? "clip" : "mask", id: clipId, bounds };
994
+ }
995
+ return { align: parts.align, d: _chunkBGNPT5PEcjs.bezierPathToSvg.call(void 0, parts.line, { precision }), width: parts.drawWidth, clip, heads: parts.heads.map((h) => _chunkBGNPT5PEcjs.bezierPathToSvg.call(void 0, h.path, { precision })) };
996
+ }
997
+ var round2 = (n) => Math.round(n * 100) / 100;
552
998
  function gradientMarkup(def) {
553
999
  const tag = def.type === "radial" ? "radialGradient" : "linearGradient";
554
- const attrs = Object.entries(def.attrs).map(([k, v]) => ` ${k}="${escapeAttr(String(v))}"`).join("");
1000
+ const attrs = Object.entries(def.attrs).map(([k, v2]) => ` ${k}="${escapeAttr(String(v2))}"`).join("");
555
1001
  const stops = def.stops.map((s) => `<stop offset="${s.offset}" stop-color="${escapeAttr(s.stopColor)}"${s.stopOpacity !== void 0 ? ` stop-opacity="${s.stopOpacity}"` : ""}/>`).join("");
556
1002
  return `<${tag} id="${escapeAttr(def.id)}"${attrs}>${stops}</${tag}>`;
557
1003
  }
@@ -582,15 +1028,53 @@ function vectorDocumentToSvg(doc, options = {}) {
582
1028
  const stroke = vectorPaintToSvg(node.stroke, `${prefix}stroke-${node.id}`);
583
1029
  if (fill.gradient) defs.push(gradientMarkup(fill.gradient));
584
1030
  if (stroke.gradient) defs.push(gradientMarkup(stroke.gradient));
585
- let attrs = `${common(node)} d="${vectorPathData(node, precision)}" fill="${escapeAttr(fill.value)}"`;
586
- if (node.fillRule === "evenodd") attrs += ` fill-rule="evenodd"`;
587
- if (stroke.value !== "none") {
588
- attrs += ` stroke="${escapeAttr(stroke.value)}" stroke-width="${num(_nullishCoalesce(node.strokeWidth, () => ( 1)))}"`;
589
- if (node.strokeLinecap && node.strokeLinecap !== "butt") attrs += ` stroke-linecap="${node.strokeLinecap}"`;
590
- if (node.strokeLinejoin && node.strokeLinejoin !== "miter") attrs += ` stroke-linejoin="${node.strokeLinejoin}"`;
591
- if (_optionalChain([node, 'access', _2 => _2.strokeDasharray, 'optionalAccess', _3 => _3.length])) attrs += ` stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
592
- }
593
- lines.push(`${indent(depth)}<path${attrs}/>`);
1031
+ const d = vectorPathData(node, precision);
1032
+ const corners2 = vectorCornersAttribute(node.subpaths, precision);
1033
+ const live = corners2 ? ` data-vector-d="${_chunkBGNPT5PEcjs.bezierPathToSvg.call(void 0, subpathsToBezier(node.subpaths, { corners: false }), { precision })}" data-vector-corners="${corners2}"` : "";
1034
+ const fillAttrs = ` fill="${escapeAttr(fill.value)}"${node.fillRule === "evenodd" ? ` fill-rule="evenodd"` : ""}`;
1035
+ const lineAttrs = (width) => {
1036
+ let out2 = ` stroke="${escapeAttr(stroke.value)}" stroke-width="${num(width)}"`;
1037
+ if (node.strokeLinecap && node.strokeLinecap !== "butt") out2 += ` stroke-linecap="${node.strokeLinecap}"`;
1038
+ if (node.strokeLinejoin && node.strokeLinejoin !== "miter") out2 += ` stroke-linejoin="${node.strokeLinejoin}"`;
1039
+ if (_optionalChain([node, 'access', _12 => _12.strokeDasharray, 'optionalAccess', _13 => _13.length])) out2 += ` stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
1040
+ return out2;
1041
+ };
1042
+ const parts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${prefix}clip-${node.id}`, precision) : void 0;
1043
+ if (!parts) {
1044
+ lines.push(`${indent(depth)}<path${common(node)} d="${d}"${fillAttrs}${stroke.value !== "none" ? lineAttrs(_nullishCoalesce(node.strokeWidth, () => ( 1))) : ""}${live}/>`);
1045
+ return;
1046
+ }
1047
+ let data = ` data-vector-path="" data-vector-stroke-width="${num(_nullishCoalesce(node.strokeWidth, () => ( 1)))}"`;
1048
+ if (parts.align !== "center") data += ` data-vector-stroke-align="${parts.align}"`;
1049
+ if (node.startArrow) data += ` data-vector-start-arrow="${node.startArrow}"`;
1050
+ if (node.endArrow) data += ` data-vector-end-arrow="${node.endArrow}"`;
1051
+ if (node.arrowAlign && node.arrowAlign !== "extend") data += ` data-vector-arrow-align="${node.arrowAlign}"`;
1052
+ if (node.startArrowScale !== void 0 && node.startArrowScale !== 1) data += ` data-vector-start-arrow-scale="${num(node.startArrowScale)}"`;
1053
+ if (node.endArrowScale !== void 0 && node.endArrowScale !== 1) data += ` data-vector-end-arrow-scale="${num(node.endArrowScale)}"`;
1054
+ lines.push(`${indent(depth)}<g${common(node)}${data}>`);
1055
+ lines.push(`${indent(depth + 1)}<path data-vector-part="fill" d="${d}"${fillAttrs}${live}/>`);
1056
+ if (options.strokes === "outline") {
1057
+ const area = _chunkBGNPT5PEcjs.bezierPathToSvg.call(void 0, vectorStrokeOutlinePath(subpathsToBezier(node.subpaths), node), { precision });
1058
+ lines.push(`${indent(depth + 1)}<path data-vector-part="stroke-outline" d="${area}" fill="${escapeAttr(stroke.value)}"/>`);
1059
+ } else {
1060
+ let clip = "";
1061
+ if (parts.clip) {
1062
+ const rule = node.fillRule === "evenodd" ? "evenodd" : "nonzero";
1063
+ const id = escapeAttr(parts.clip.id);
1064
+ if (parts.clip.kind === "clip") {
1065
+ defs.push(`<clipPath id="${id}"><path d="${d}" clip-rule="${rule}"/></clipPath>`);
1066
+ clip = ` clip-path="url(#${id})"`;
1067
+ } else {
1068
+ const b = parts.clip.bounds;
1069
+ const box = `x="${num(b.x)}" y="${num(b.y)}" width="${num(b.width)}" height="${num(b.height)}"`;
1070
+ defs.push(`<mask id="${id}" maskUnits="userSpaceOnUse" ${box}><rect ${box} fill="#fff"/><path d="${d}" fill="#000" fill-rule="${rule}"/></mask>`);
1071
+ clip = ` mask="url(#${id})"`;
1072
+ }
1073
+ }
1074
+ lines.push(`${indent(depth + 1)}<path data-vector-part="stroke" d="${parts.d}" fill="none"${lineAttrs(parts.width)}${clip}/>`);
1075
+ for (const h2 of parts.heads) lines.push(`${indent(depth + 1)}<path data-vector-part="head" d="${h2}" fill="${escapeAttr(stroke.value)}"/>`);
1076
+ }
1077
+ lines.push(`${indent(depth)}</g>`);
594
1078
  };
595
1079
  for (const node of doc.nodes) emit(node, 1);
596
1080
  const w = num(doc.width);
@@ -733,7 +1217,7 @@ function resolveGradient(id, raw, seen = /* @__PURE__ */ new Set()) {
733
1217
  seen.add(id);
734
1218
  let stops = readStops(el);
735
1219
  const href = _nullishCoalesce(el.attrs.href, () => ( el.attrs["xlink:href"]));
736
- if (!stops.length && _optionalChain([href, 'optionalAccess', _4 => _4.startsWith, 'call', _5 => _5("#")])) stops = _nullishCoalesce(_optionalChain([resolveGradient, 'call', _6 => _6(href.slice(1), raw, seen), 'optionalAccess', _7 => _7.stops]), () => ( []));
1220
+ if (!stops.length && _optionalChain([href, 'optionalAccess', _14 => _14.startsWith, 'call', _15 => _15("#")])) stops = _nullishCoalesce(_optionalChain([resolveGradient, 'call', _16 => _16(href.slice(1), raw, seen), 'optionalAccess', _17 => _17.stops]), () => ( []));
737
1221
  if (!stops.length) return null;
738
1222
  if (el.name === "radialGradient") {
739
1223
  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 };
@@ -765,11 +1249,11 @@ function parseVectorSvg(svg, options = {}) {
765
1249
  const rawGradients = /* @__PURE__ */ new Map();
766
1250
  collectGradients(root, rawGradients);
767
1251
  const paint = (value) => {
768
- const v = value.trim();
769
- const url = /^url\(\s*['"]?#([^'")\s]+)['"]?\s*\)/.exec(v);
1252
+ const v2 = value.trim();
1253
+ const url = /^url\(\s*['"]?#([^'")\s]+)['"]?\s*\)/.exec(v2);
770
1254
  if (url) return _nullishCoalesce(resolveGradient(url[1], rawGradients), () => ( null));
771
- if (v === "none" || v === "") return null;
772
- return v;
1255
+ if (v2 === "none" || v2 === "") return null;
1256
+ return v2;
773
1257
  };
774
1258
  const viewBox = numberList(root.attrs.viewBox);
775
1259
  const width = viewBox.length === 4 ? viewBox[2] : length(root.attrs.width, 300);
@@ -779,8 +1263,13 @@ function parseVectorSvg(svg, options = {}) {
779
1263
  const shapeOf = (el) => {
780
1264
  const a = el.attrs;
781
1265
  switch (el.name) {
782
- case "path":
1266
+ case "path": {
1267
+ if (a["data-vector-d"] && a["data-vector-corners"]) {
1268
+ const sharp = bezierToSubpaths(a["data-vector-d"]);
1269
+ if (sharp.length) return applyVectorCornersAttribute(sharp, a["data-vector-corners"]);
1270
+ }
783
1271
  return a.d ? bezierToSubpaths(a.d) : null;
1272
+ }
784
1273
  case "rect": {
785
1274
  const w = length(a.width);
786
1275
  const h = length(a.height);
@@ -814,6 +1303,56 @@ function parseVectorSvg(svg, options = {}) {
814
1303
  }
815
1304
  return null;
816
1305
  };
1306
+ const strokeStyle = (s) => ({
1307
+ ...s.strokeWidth ? { strokeWidth: length(s.strokeWidth, 1) } : {},
1308
+ ...s.strokeLinecap ? { strokeLinecap: s.strokeLinecap } : {},
1309
+ ...s.strokeLinejoin ? { strokeLinejoin: s.strokeLinejoin } : {},
1310
+ ...s.strokeDasharray && s.strokeDasharray !== "none" ? { strokeDasharray: numberList(s.strokeDasharray) } : {},
1311
+ ...s.fillRule === "evenodd" ? { fillRule: "evenodd" } : {}
1312
+ });
1313
+ const placed = (node, m) => {
1314
+ if (m === IDENTITY_MATRIX) return node;
1315
+ const next = transformVectorNode(node, m);
1316
+ const strokeScale = Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2]));
1317
+ if (hasPaint(next.stroke) && strokeScale > 0 && Math.abs(strokeScale - 1) > 1e-9) next.strokeWidth = (_nullishCoalesce(next.strokeWidth, () => ( 1))) * strokeScale;
1318
+ return next;
1319
+ };
1320
+ const mergedPath = (g, style, m, meta) => {
1321
+ const part = (name) => g.children.find((c) => c.name === "path" && c.attrs["data-vector-part"] === name);
1322
+ const fillEl = part("fill");
1323
+ const subpaths = fillEl ? shapeOf(fillEl) : null;
1324
+ if (!fillEl || !_optionalChain([subpaths, 'optionalAccess', _18 => _18.length])) return null;
1325
+ const a = g.attrs;
1326
+ const fs = readStyle(fillEl, style);
1327
+ const strokeEl = part("stroke");
1328
+ const outlineEl = part("stroke-outline");
1329
+ const ss = strokeEl ? readStyle(strokeEl, style) : null;
1330
+ const width2 = length(a["data-vector-stroke-width"], ss ? length(ss.strokeWidth, 1) : 1);
1331
+ const arrow = (v2) => VECTOR_ARROWHEADS.includes(v2) ? v2 : void 0;
1332
+ const scale = (v2) => {
1333
+ const n = v2 === void 0 ? NaN : parseFloat(v2);
1334
+ return n > 0 ? n : void 0;
1335
+ };
1336
+ const align = a["data-vector-stroke-align"];
1337
+ const node = {
1338
+ id: idFor(g),
1339
+ type: "path",
1340
+ ...meta,
1341
+ subpaths,
1342
+ fill: paint(fs.fill),
1343
+ stroke: ss ? paint(ss.stroke) : outlineEl ? paint(readStyle(outlineEl, style).fill) : null,
1344
+ ...ss ? strokeStyle(ss) : {},
1345
+ ...fs.fillRule === "evenodd" ? { fillRule: "evenodd" } : {},
1346
+ strokeWidth: width2,
1347
+ ...align === "inside" || align === "outside" ? { strokeAlign: align } : {},
1348
+ ...arrow(a["data-vector-start-arrow"]) ? { startArrow: arrow(a["data-vector-start-arrow"]) } : {},
1349
+ ...arrow(a["data-vector-end-arrow"]) ? { endArrow: arrow(a["data-vector-end-arrow"]) } : {},
1350
+ ...a["data-vector-arrow-align"] === "tip" ? { arrowAlign: "tip" } : {},
1351
+ ...scale(a["data-vector-start-arrow-scale"]) ? { startArrowScale: scale(a["data-vector-start-arrow-scale"]) } : {},
1352
+ ...scale(a["data-vector-end-arrow-scale"]) ? { endArrowScale: scale(a["data-vector-end-arrow-scale"]) } : {}
1353
+ };
1354
+ return placed(node, m);
1355
+ };
817
1356
  const visit = (el, style, matrix) => {
818
1357
  const out = [];
819
1358
  for (const child of el.children) {
@@ -827,6 +1366,11 @@ function parseVectorSvg(svg, options = {}) {
827
1366
  ...opacity !== void 0 && Number.isFinite(opacity) && opacity < 1 ? { opacity } : {},
828
1367
  ...hidden ? { visible: false } : {}
829
1368
  };
1369
+ if (child.name === "g" && child.attrs["data-vector-path"] !== void 0) {
1370
+ const node2 = mergedPath(child, s, m, meta);
1371
+ if (node2) out.push(node2);
1372
+ continue;
1373
+ }
830
1374
  if (child.name === "g" || child.name === "svg" || child.name === "a" || child.name === "switch") {
831
1375
  const children = visit(child, s, m);
832
1376
  if (!children.length) continue;
@@ -836,24 +1380,18 @@ function parseVectorSvg(svg, options = {}) {
836
1380
  }
837
1381
  const subpaths = shapeOf(child);
838
1382
  if (!subpaths || !subpaths.length) continue;
839
- let node = {
840
- id: idFor(child),
841
- type: "path",
842
- ...meta,
843
- subpaths,
844
- fill: child.name === "line" ? null : paint(s.fill),
845
- stroke: paint(s.stroke),
846
- ...s.strokeWidth ? { strokeWidth: length(s.strokeWidth, 1) } : {},
847
- ...s.strokeLinecap ? { strokeLinecap: s.strokeLinecap } : {},
848
- ...s.strokeLinejoin ? { strokeLinejoin: s.strokeLinejoin } : {},
849
- ...s.strokeDasharray && s.strokeDasharray !== "none" ? { strokeDasharray: numberList(s.strokeDasharray) } : {},
850
- ...s.fillRule === "evenodd" ? { fillRule: "evenodd" } : {}
851
- };
852
- if (m !== IDENTITY_MATRIX) {
853
- node = transformVectorNode(node, m);
854
- const strokeScale = Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2]));
855
- if (hasPaint(node.stroke) && strokeScale > 0 && Math.abs(strokeScale - 1) > 1e-9) node.strokeWidth = (_nullishCoalesce(node.strokeWidth, () => ( 1))) * strokeScale;
856
- }
1383
+ const node = placed(
1384
+ {
1385
+ id: idFor(child),
1386
+ type: "path",
1387
+ ...meta,
1388
+ subpaths,
1389
+ fill: child.name === "line" ? null : paint(s.fill),
1390
+ stroke: paint(s.stroke),
1391
+ ...strokeStyle(s)
1392
+ },
1393
+ m
1394
+ );
857
1395
  out.push(node);
858
1396
  }
859
1397
  return out;
@@ -865,7 +1403,7 @@ function parseVectorSvg(svg, options = {}) {
865
1403
  // src/vector/snap.ts
866
1404
  var VECTOR_CONSTRUCTION_ANGLES = [0, 45, 90, 135];
867
1405
  function constrainToLeave(leave, p) {
868
- if (!_optionalChain([leave, 'optionalAccess', _8 => _8.constrain])) return p;
1406
+ if (!_optionalChain([leave, 'optionalAccess', _19 => _19.constrain])) return p;
869
1407
  const dx = p.x - leave.at.x;
870
1408
  const dy = p.y - leave.at.y;
871
1409
  switch (leave.constrain) {
@@ -876,9 +1414,9 @@ function constrainToLeave(leave, p) {
876
1414
  return { x: leave.at.x + Math.sign(dx || 1) * s, y: leave.at.y + Math.sign(dy || 1) * s };
877
1415
  }
878
1416
  default: {
879
- const len = Math.hypot(dx, dy);
1417
+ const len2 = Math.hypot(dx, dy);
880
1418
  const a = Math.round(Math.atan2(dy, dx) / (Math.PI / 4)) * (Math.PI / 4);
881
- return { x: leave.at.x + Math.cos(a) * len, y: leave.at.y + Math.sin(a) * len };
1419
+ return { x: leave.at.x + Math.cos(a) * len2, y: leave.at.y + Math.sin(a) * len2 };
882
1420
  }
883
1421
  }
884
1422
  }
@@ -887,24 +1425,24 @@ var CROSSING_PAIRS = 32;
887
1425
  var CURVE_SAMPLES = 48;
888
1426
  var BISECT = 50;
889
1427
  var EQUAL = 1e-6;
890
- function bound(sorted, key, v, strict) {
1428
+ function bound(sorted, key, v2, strict) {
891
1429
  let lo = 0;
892
1430
  let hi = sorted.length;
893
1431
  while (lo < hi) {
894
1432
  const mid = lo + hi >>> 1;
895
1433
  const k = key(sorted[mid]);
896
- if (strict ? k <= v : k < v) lo = mid + 1;
1434
+ if (strict ? k <= v2 : k < v2) lo = mid + 1;
897
1435
  else hi = mid;
898
1436
  }
899
1437
  return lo;
900
1438
  }
901
- function within(sorted, key, v, tol) {
902
- return sorted.slice(bound(sorted, key, v - tol, false), bound(sorted, key, v + tol, true));
1439
+ function within(sorted, key, v2, tol) {
1440
+ return sorted.slice(bound(sorted, key, v2 - tol, false), bound(sorted, key, v2 + tol, true));
903
1441
  }
904
- var linesNear = (lines, v, tol) => within(lines, (l) => l.v, v, tol);
1442
+ var linesNear = (lines, v2, tol) => within(lines, (l) => l.v, v2, tol);
905
1443
  var distance = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
906
1444
  var toPoint = (p) => [p.x, p.y];
907
- var toVec = (p) => ({ x: p[0], y: p[1] });
1445
+ var toVec2 = (p) => ({ x: p[0], y: p[1] });
908
1446
  var LABELS = {
909
1447
  anchor: "anchor",
910
1448
  centre: "centre",
@@ -976,7 +1514,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
976
1514
  moving.set(r.nodeId, set);
977
1515
  }
978
1516
  const t = _VectorSnapTargets.collect(doc, new Set(moving.keys()), "segments", false);
979
- t.pushPaths(doc, new Set(moving.keys()), (id, s, i) => !!_optionalChain([moving, 'access', _9 => _9.get, 'call', _10 => _10(id), 'optionalAccess', _11 => _11.has, 'call', _12 => _12(`${s}:${i}`)]), true);
1517
+ t.pushPaths(doc, new Set(moving.keys()), (id, s, i) => !!_optionalChain([moving, 'access', _20 => _20.get, 'call', _21 => _21(id), 'optionalAccess', _22 => _22.has, 'call', _23 => _23(`${s}:${i}`)]), true);
980
1518
  return t.index();
981
1519
  }
982
1520
  /**
@@ -1022,7 +1560,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1022
1560
  const count = subpathSegmentCount(sp);
1023
1561
  for (let i = 0; i < count && budget > 0; i++) {
1024
1562
  if (moving(i) || moving((i + 1) % n)) continue;
1025
- const seg = subpathSegment(sp, i).map((q) => toPoint(applyMatrix(m, toVec(q))));
1563
+ const seg = subpathSegment(sp, i).map((q) => toPoint(applyMatrix(m, toVec2(q))));
1026
1564
  let minX = Infinity;
1027
1565
  let minY = Infinity;
1028
1566
  let maxX = -Infinity;
@@ -1094,7 +1632,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1094
1632
  }
1095
1633
  const b = vectorNodeBounds(n, parent);
1096
1634
  const f = nodeFrame(n, parent, b);
1097
- if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess', _13 => _13.centre]), () => ( null)));
1635
+ if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess', _24 => _24.centre]), () => ( null)));
1098
1636
  }
1099
1637
  };
1100
1638
  visit(doc.nodes, IDENTITY_MATRIX);
@@ -1139,7 +1677,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1139
1677
  lines: lines2,
1140
1678
  labels: [this.label(q, kind)]
1141
1679
  });
1142
- if (_optionalChain([leave, 'optionalAccess', _14 => _14.constrain])) {
1680
+ if (_optionalChain([leave, 'optionalAccess', _25 => _25.constrain])) {
1143
1681
  const q = constrainToLeave(leave, p);
1144
1682
  const hit = this.slide(leave.at, { x: q.x - leave.at.x, y: q.y - leave.at.y }, q, tol, true);
1145
1683
  return hit ? { point: hit.point, x: true, y: true, ...this.linedUp(hit.point, hit.from) } : none(q);
@@ -1155,7 +1693,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1155
1693
  }
1156
1694
  const on = this.onSegment(near2);
1157
1695
  if (on) return at(on, "path");
1158
- if (_optionalChain([leave, 'optionalAccess', _15 => _15.angles, 'optionalAccess', _16 => _16.length])) {
1696
+ if (_optionalChain([leave, 'optionalAccess', _26 => _26.angles, 'optionalAccess', _27 => _27.length])) {
1159
1697
  const c = construction(leave, p, tol);
1160
1698
  if (c) {
1161
1699
  const hit = this.slide(leave.at, c.dir, c.point, tol, false);
@@ -1166,9 +1704,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1166
1704
  return { point: c.point, kind: "construction", x: true, y: true, lines: [line("construction", leave.at, c.point)], labels: [] };
1167
1705
  }
1168
1706
  }
1169
- const nearest = (lines2, v) => {
1707
+ const nearest = (lines2, v2) => {
1170
1708
  let best = null;
1171
- for (const l of linesNear(lines2, v, tol)) if (!best || Math.abs(l.v - v) < Math.abs(best.v - v)) best = l;
1709
+ for (const l of linesNear(lines2, v2, tol)) if (!best || Math.abs(l.v - v2) < Math.abs(best.v - v2)) best = l;
1172
1710
  return best;
1173
1711
  };
1174
1712
  const bx = nearest(this.xs, p.x);
@@ -1228,16 +1766,16 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1228
1766
  /** Target segments whose control box reaches `p` and which pass within `tol` of it, nearest first. */
1229
1767
  segmentsNear(p, tol) {
1230
1768
  const out = [];
1231
- const pt = toPoint(p);
1769
+ const pt2 = toPoint(p);
1232
1770
  for (const s of this.segments) {
1233
1771
  if (p.x < s.minX - tol || p.x > s.maxX + tol || p.y < s.minY - tol || p.y > s.maxY + tol) continue;
1234
- const hit = _chunkBGNPT5PEcjs.segmentNearest.call(void 0, s.seg, pt);
1772
+ const hit = _chunkBGNPT5PEcjs.segmentNearest.call(void 0, s.seg, pt2);
1235
1773
  if (hit.distance <= tol) out.push({ seg: s.seg, t: hit.t, distance: hit.distance });
1236
1774
  }
1237
1775
  return out.sort((a, b) => a.distance - b.distance);
1238
1776
  }
1239
1777
  onSegment(near2) {
1240
- return near2.length ? toVec(_chunkBGNPT5PEcjs.segmentPoint.call(void 0, near2[0].seg, near2[0].t)) : null;
1778
+ return near2.length ? toVec2(_chunkBGNPT5PEcjs.segmentPoint.call(void 0, near2[0].seg, near2[0].t)) : null;
1241
1779
  }
1242
1780
  /** Where two of the target segments near `p` cross, the crossing nearest `p` within `tol`. */
1243
1781
  crossing(near2, p, tol) {
@@ -1247,7 +1785,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1247
1785
  for (let i = 0; i < list.length; i++) {
1248
1786
  for (let j = i + 1; j < list.length; j++) {
1249
1787
  for (const hit of _chunkBGNPT5PEcjs.segmentIntersections.call(void 0, list[i].seg, list[j].seg)) {
1250
- const q = toVec(hit.point);
1788
+ const q = toVec2(hit.point);
1251
1789
  const d = distance(q, p);
1252
1790
  if (d <= tol && d < bestD) {
1253
1791
  best = q;
@@ -1285,15 +1823,15 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1285
1823
  * with nothing spaces the box evenly in its row or column. Returns the correction and guides.
1286
1824
  */
1287
1825
  snapRect(r, tol, options = {}) {
1288
- const axes = { x: _optionalChain([options, 'access', _17 => _17.axes, 'optionalAccess', _18 => _18.x]) !== false, y: _optionalChain([options, 'access', _19 => _19.axes, 'optionalAccess', _20 => _20.y]) !== false };
1826
+ const axes = { x: _optionalChain([options, 'access', _28 => _28.axes, 'optionalAccess', _29 => _29.x]) !== false, y: _optionalChain([options, 'access', _30 => _30.axes, 'optionalAccess', _31 => _31.y]) !== false };
1289
1827
  const nearest = (lines, values) => {
1290
1828
  let best = null;
1291
- for (const v of values) for (const l of linesNear(lines, v, tol)) if (!best || Math.abs(l.v - v) < Math.abs(best.d)) best = { d: l.v - v, from: l.from };
1829
+ 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 };
1292
1830
  return best;
1293
1831
  };
1294
1832
  const bx = axes.x ? nearest(this.xs, [r.x, r.x + r.width / 2, r.x + r.width]) : null;
1295
1833
  const by = axes.y ? nearest(this.ys, [r.y, r.y + r.height / 2, r.y + r.height]) : null;
1296
- const out = { dx: _nullishCoalesce(_optionalChain([bx, 'optionalAccess', _21 => _21.d]), () => ( 0)), dy: _nullishCoalesce(_optionalChain([by, 'optionalAccess', _22 => _22.d]), () => ( 0)), x: !!bx, y: !!by, lines: [], labels: [] };
1834
+ const out = { dx: _nullishCoalesce(_optionalChain([bx, 'optionalAccess', _32 => _32.d]), () => ( 0)), dy: _nullishCoalesce(_optionalChain([by, 'optionalAccess', _33 => _33.d]), () => ( 0)), x: !!bx, y: !!by, lines: [], labels: [] };
1297
1835
  const done = { ...r, x: r.x + out.dx, y: r.y + out.dy };
1298
1836
  if (bx) {
1299
1837
  const x = bx.from.x;
@@ -1331,7 +1869,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1331
1869
  evenSpacing(r, horizontal, tol) {
1332
1870
  const span = (b, x) => x ? [b.x, b.x + b.width] : [b.y, b.y + b.height];
1333
1871
  const [r0, r1] = span(r, horizontal);
1334
- const len = r1 - r0;
1872
+ const len2 = r1 - r0;
1335
1873
  const [c0, c1] = span(r, !horizontal);
1336
1874
  const row = this.boxes.map((b) => ({ b, a0: span(b, horizontal)[0], a1: span(b, horizontal)[1] })).filter((e) => span(e.b, !horizontal)[0] < c1 && span(e.b, !horizontal)[1] > c0 && (e.a1 <= r0 + tol || e.a0 >= r1 - tol)).sort((a, b) => a.a0 - b.a0);
1337
1875
  if (!row.length) return null;
@@ -1351,17 +1889,17 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1351
1889
  if (e.a0 >= r1 - tol && (!after || e.a0 < after.a0)) after = e;
1352
1890
  }
1353
1891
  const starts = [];
1354
- if (before && after && after.a0 - before.a1 > len) starts.push({ start: (before.a1 + after.a0 - len) / 2, repeats: null });
1892
+ if (before && after && after.a0 - before.a1 > len2) starts.push({ start: (before.a1 + after.a0 - len2) / 2, repeats: null });
1355
1893
  for (const { g } of pairs) {
1356
1894
  if (before) starts.push({ start: before.a1 + g, repeats: g });
1357
- if (after) starts.push({ start: after.a0 - g - len, repeats: g });
1895
+ if (after) starts.push({ start: after.a0 - g - len2, repeats: g });
1358
1896
  }
1359
1897
  let pick = null;
1360
1898
  for (const s of starts) if (Math.abs(s.start - r0) <= tol && (!pick || Math.abs(s.start - r0) < Math.abs(pick.start - r0))) pick = s;
1361
1899
  if (!pick) return null;
1362
1900
  const { start, repeats } = pick;
1363
1901
  const at = horizontal ? { ...r, x: start } : { ...r, y: start };
1364
- const end = start + len;
1902
+ const end = start + len2;
1365
1903
  const gaps = [];
1366
1904
  if (before && (repeats === null || Math.abs(start - before.a1 - repeats) <= EQUAL)) gaps.push([before.a1, start, mid(before.b, at)]);
1367
1905
  if (after && (repeats === null || Math.abs(after.a0 - end - repeats) <= EQUAL)) gaps.push([end, after.a0, mid(after.b, at)]);
@@ -1372,9 +1910,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1372
1910
  }
1373
1911
  /** A gap mark: a line across the gap with a tick at each end, and its length as a label. */
1374
1912
  gapMarks([u0, u1, c], horizontal, tick, out) {
1375
- const pt = (u, v) => horizontal ? { x: u, y: v } : { x: v, y: u };
1376
- out.lines.push(line("spacing", pt(u0, c), pt(u1, c)), line("spacing", pt(u0, c - tick), pt(u0, c + tick)), line("spacing", pt(u1, c - tick), pt(u1, c + tick)));
1377
- out.labels.push(this.label(pt((u0 + u1) / 2, c), "spacing", fmtLength(u1 - u0)));
1913
+ const pt2 = (u, v2) => horizontal ? { x: u, y: v2 } : { x: v2, y: u };
1914
+ out.lines.push(line("spacing", pt2(u0, c), pt2(u1, c)), line("spacing", pt2(u0, c - tick), pt2(u0, c + tick)), line("spacing", pt2(u1, c - tick), pt2(u1, c + tick)));
1915
+ out.labels.push(this.label(pt2((u0 + u1) / 2, c), "spacing", fmtLength(u1 - u0)));
1378
1916
  }
1379
1917
  /** The other objects' side length nearest `length`, within `tol`, or null. */
1380
1918
  matchSize(length2, tol) {
@@ -1401,9 +1939,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1401
1939
  const hp = handlePos(handle, frame);
1402
1940
  const d0 = { x: hp.x - origin.x, y: hp.y - origin.y };
1403
1941
  const valid = (n, s) => Number.isFinite(n) && n * s > 0;
1404
- const nearest = (v, lines) => {
1942
+ const nearest = (v2, lines) => {
1405
1943
  let best = null;
1406
- for (const l of linesNear(lines, v, tol)) if (!best || Math.abs(l.v - v) < Math.abs(best.v - v)) best = l;
1944
+ for (const l of linesNear(lines, v2, tol)) if (!best || Math.abs(l.v - v2) < Math.abs(best.v - v2)) best = l;
1407
1945
  return best;
1408
1946
  };
1409
1947
  let nx = sx;
@@ -1416,9 +1954,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1416
1954
  const on = this.nearestPoint(p, tol);
1417
1955
  const tries = [];
1418
1956
  if (on) tries.push({ tx: on.p.x, ty: on.p.y, s: { hx: null, hy: null, on } });
1419
- tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _23 => _23.v]), () => ( null)), ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _24 => _24.v]), () => ( null)), s: { hx, hy, on: null } });
1420
- tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _25 => _25.v]), () => ( null)), ty: null, s: { hx, hy: null, on: null } });
1421
- tries.push({ tx: null, ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _26 => _26.v]), () => ( null)), s: { hx: null, hy, on: null } });
1957
+ tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _34 => _34.v]), () => ( null)), ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _35 => _35.v]), () => ( null)), s: { hx, hy, on: null } });
1958
+ tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _36 => _36.v]), () => ( null)), ty: null, s: { hx, hy: null, on: null } });
1959
+ tries.push({ tx: null, ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _37 => _37.v]), () => ( null)), s: { hx: null, hy, on: null } });
1422
1960
  for (const { tx, ty, s } of tries) {
1423
1961
  if (tx === null && ty === null) continue;
1424
1962
  const q = applyMatrix(toLocal, { x: _nullishCoalesce(tx, () => ( p.x)), y: _nullishCoalesce(ty, () => ( p.y)) });
@@ -1454,9 +1992,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1454
1992
  };
1455
1993
  for (const h of [handle, ...ends]) {
1456
1994
  const hv = handlePos(h, frame);
1457
- const v = { x: hv.x - origin.x, y: hv.y - origin.y };
1458
- const base = applyMatrix(toDoc, { x: origin.x + c.x * v.x, y: origin.y + c.y * v.y });
1459
- const dir = applyMatrix(toDoc, { x: e.x * v.x, y: e.y * v.y });
1995
+ const v2 = { x: hv.x - origin.x, y: hv.y - origin.y };
1996
+ const base = applyMatrix(toDoc, { x: origin.x + c.x * v2.x, y: origin.y + c.y * v2.y });
1997
+ const dir = applyMatrix(toDoc, { x: e.x * v2.x, y: e.y * v2.y });
1460
1998
  const p = { x: base.x + dir.x * s0, y: base.y + dir.y * s0 };
1461
1999
  const l2 = dir.x * dir.x + dir.y * dir.y;
1462
2000
  const on = l2 > 1e-18 ? this.nearestPoint(p, tol) : null;
@@ -1489,16 +2027,16 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1489
2027
  const sized = [];
1490
2028
  if (!proportional) {
1491
2029
  if (movesX && !snap.hx && !(frame.angle && snap.hy) && frame.width > 1e-9) {
1492
- const len = this.matchSize(Math.abs(out.sx) * frame.width, tol);
1493
- if (len !== null && len > 0) {
1494
- out.sx = Math.sign(out.sx || 1) * (len / frame.width);
2030
+ const len2 = this.matchSize(Math.abs(out.sx) * frame.width, tol);
2031
+ if (len2 !== null && len2 > 0) {
2032
+ out.sx = Math.sign(out.sx || 1) * (len2 / frame.width);
1495
2033
  sized.push("x");
1496
2034
  }
1497
2035
  }
1498
2036
  if (movesY && !snap.hy && !(frame.angle && snap.hx) && frame.height > 1e-9) {
1499
- const len = this.matchSize(Math.abs(out.sy) * frame.height, tol);
1500
- if (len !== null && len > 0) {
1501
- out.sy = Math.sign(out.sy || 1) * (len / frame.height);
2037
+ const len2 = this.matchSize(Math.abs(out.sy) * frame.height, tol);
2038
+ if (len2 !== null && len2 > 0) {
2039
+ out.sy = Math.sign(out.sy || 1) * (len2 / frame.height);
1502
2040
  sized.push("y");
1503
2041
  }
1504
2042
  }
@@ -1549,14 +2087,14 @@ function line(kind, a, b) {
1549
2087
  return { kind, x1: a.x, y1: a.y, x2: b.x, y2: b.y };
1550
2088
  }
1551
2089
  function construction(leave, p, tol) {
1552
- const v = { x: p.x - leave.at.x, y: p.y - leave.at.y };
1553
- if (Math.hypot(v.x, v.y) <= tol) return null;
2090
+ const v2 = { x: p.x - leave.at.x, y: p.y - leave.at.y };
2091
+ if (Math.hypot(v2.x, v2.y) <= tol) return null;
1554
2092
  let best = null;
1555
2093
  for (const a of _nullishCoalesce(leave.angles, () => ( []))) {
1556
2094
  const r = a * Math.PI / 180;
1557
2095
  const exact = (n) => Math.abs(n) < 1e-12 ? 0 : n;
1558
2096
  const dir = { x: exact(Math.cos(r)), y: exact(-Math.sin(r)) };
1559
- const k = v.x * dir.x + v.y * dir.y;
2097
+ const k = v2.x * dir.x + v2.y * dir.y;
1560
2098
  const point = { x: leave.at.x + dir.x * k, y: leave.at.y + dir.y * k };
1561
2099
  const d = distance(point, p);
1562
2100
  if (d <= tol && (!best || d < best.d)) best = { dir, point, d };
@@ -1581,7 +2119,7 @@ function solveOnSegment(seg, base, cond) {
1581
2119
  vals.push(cond(r, d));
1582
2120
  scale = Math.max(scale, Math.hypot(r[0], r[1]) * Math.hypot(d[0], d[1]));
1583
2121
  }
1584
- if (!(scale > 0) || vals.every((v) => Math.abs(v) <= scale * 1e-12)) return [];
2122
+ if (!(scale > 0) || vals.every((v2) => Math.abs(v2) <= scale * 1e-12)) return [];
1585
2123
  const out = [];
1586
2124
  for (let i = 0; i < CURVE_SAMPLES; i++) {
1587
2125
  const ga = vals[i];
@@ -1614,8 +2152,8 @@ function solveOnSegment(seg, base, cond) {
1614
2152
  }
1615
2153
 
1616
2154
  // src/vector/pathEdit.ts
1617
- var EPS2 = 1e-9;
1618
- var unit = (x, y) => {
2155
+ var EPS3 = 1e-9;
2156
+ var unit2 = (x, y) => {
1619
2157
  const l = Math.hypot(x, y);
1620
2158
  return l > 1e-12 && Number.isFinite(l) ? [x / l, y / l] : null;
1621
2159
  };
@@ -1628,14 +2166,14 @@ function cubicOf(sp, i) {
1628
2166
  var isLine = (sp, i) => subpathSegment(sp, i).length === 2;
1629
2167
  function startTangent(c) {
1630
2168
  for (let k = 1; k < 4; k++) {
1631
- const u = unit(c[k][0] - c[0][0], c[k][1] - c[0][1]);
2169
+ const u = unit2(c[k][0] - c[0][0], c[k][1] - c[0][1]);
1632
2170
  if (u) return u;
1633
2171
  }
1634
2172
  return [1, 0];
1635
2173
  }
1636
2174
  function endTangent(c) {
1637
2175
  for (let k = 2; k >= 0; k--) {
1638
- const u = unit(c[3][0] - c[k][0], c[3][1] - c[k][1]);
2176
+ const u = unit2(c[3][0] - c[k][0], c[3][1] - c[k][1]);
1639
2177
  if (u) return u;
1640
2178
  }
1641
2179
  return [1, 0];
@@ -1643,11 +2181,11 @@ function endTangent(c) {
1643
2181
  function sample(c, n, out, withStart) {
1644
2182
  for (let i = withStart ? 0 : 1; i <= n; i++) out.push(_chunkBGNPT5PEcjs.segmentPoint.call(void 0, c, i / n));
1645
2183
  }
1646
- var onAnchor = (a, h) => !h || Math.abs(h.x - a.x) < EPS2 && Math.abs(h.y - a.y) < EPS2;
2184
+ var onAnchor2 = (a, h) => !h || Math.abs(h.x - a.x) < EPS3 && Math.abs(h.y - a.y) < EPS3;
1647
2185
  function tidy(a, keepType = true) {
1648
2186
  const next = cloneAnchor(a);
1649
- if (onAnchor(next, next.in)) delete next.in;
1650
- if (onAnchor(next, next.out)) delete next.out;
2187
+ if (onAnchor2(next, next.in)) delete next.in;
2188
+ if (onAnchor2(next, next.out)) delete next.out;
1651
2189
  const shown = inferAnchorType(next);
1652
2190
  const type = keepType && a.type ? a.type === "symmetric" && shown !== "symmetric" ? shown === "corner" ? "corner" : "smooth" : a.type === "smooth" && shown === "corner" ? "corner" : a.type : shown;
1653
2191
  if (type === "corner") delete next.type;
@@ -1755,8 +2293,8 @@ function curvatureMoveAnchor(sp, index, q) {
1755
2293
  const a = sp.anchors[index];
1756
2294
  if (!a) return { anchors, closed: sp.closed };
1757
2295
  const own = [
1758
- a.in && !onAnchor(a, a.in) ? { x: a.in.x - a.x, y: a.in.y - a.y } : null,
1759
- a.out && !onAnchor(a, a.out) ? { x: a.out.x - a.x, y: a.out.y - a.y } : null
2296
+ a.in && !onAnchor2(a, a.in) ? { x: a.in.x - a.x, y: a.in.y - a.y } : null,
2297
+ a.out && !onAnchor2(a, a.out) ? { x: a.out.x - a.x, y: a.out.y - a.y } : null
1760
2298
  ];
1761
2299
  const ends = [null, null];
1762
2300
  neighbours(sp, index).forEach((j, side) => {
@@ -1775,15 +2313,16 @@ function curvatureMoveAnchor(sp, index, q) {
1775
2313
  if (own[0]) moved.in = { x: q.x + own[0].x, y: q.y + own[0].y };
1776
2314
  if (own[1]) moved.out = { x: q.x + own[1].x, y: q.y + own[1].y };
1777
2315
  if (a.type) moved.type = a.type;
2316
+ if (a.corner) moved.corner = { ...a.corner };
1778
2317
  const [prev, next] = ends;
1779
2318
  if (a.type && a.type !== "corner" && prev && next) {
1780
2319
  const dx = next.x - prev.x;
1781
2320
  const dy = next.y - prev.y;
1782
2321
  const l = Math.hypot(dx, dy);
1783
2322
  if (l > 1e-9) {
1784
- const len = (h, to) => h ? Math.hypot(h.x, h.y) : Math.hypot(q.x - to.x, q.y - to.y) / 3;
1785
- let li = len(own[0], prev);
1786
- let lo = len(own[1], next);
2323
+ const len2 = (h, to) => h ? Math.hypot(h.x, h.y) : Math.hypot(q.x - to.x, q.y - to.y) / 3;
2324
+ let li = len2(own[0], prev);
2325
+ let lo = len2(own[1], next);
1787
2326
  if (a.type === "symmetric") li = lo = (li + lo) / 2;
1788
2327
  moved.in = { x: q.x - dx * li / l, y: q.y - dy * li / l };
1789
2328
  moved.out = { x: q.x + dx * lo / l, y: q.y + dy * lo / l };
@@ -1824,7 +2363,7 @@ function splitSubpathAt(sp, segment, t, endTolerance = 1e-6) {
1824
2363
  const { subpath, index } = insertAnchor(sp, segment, t);
1825
2364
  return cutSubpathAt(subpath, [index]);
1826
2365
  }
1827
- var endOf = (sp, last) => sp.anchors[last ? sp.anchors.length - 1 : 0];
2366
+ var endOf2 = (sp, last) => sp.anchors[last ? sp.anchors.length - 1 : 0];
1828
2367
  var dist = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
1829
2368
  function reverse(sp) {
1830
2369
  return {
@@ -1834,6 +2373,7 @@ function reverse(sp) {
1834
2373
  if (a.out) next.in = { ...a.out };
1835
2374
  if (a.in) next.out = { ...a.in };
1836
2375
  if (a.type) next.type = a.type;
2376
+ if (a.corner) next.corner = { ...a.corner };
1837
2377
  return next;
1838
2378
  })
1839
2379
  };
@@ -1867,7 +2407,7 @@ function joinSubpaths(subpaths, tolerance) {
1867
2407
  for (let j = i + 1; j < open.length; j++) {
1868
2408
  for (const ei of [false, true]) {
1869
2409
  for (const ej of [false, true]) {
1870
- const d = dist(endOf(open[i], ei), endOf(open[j], ej));
2410
+ const d = dist(endOf2(open[i], ei), endOf2(open[j], ej));
1871
2411
  if (d < best.d) best = { d, i, j, ei, ej };
1872
2412
  }
1873
2413
  }
@@ -1905,7 +2445,7 @@ function smoothSubpath(sp, amount = 1, only) {
1905
2445
  const prev = orig[(i + n - 1) % n];
1906
2446
  const next = orig[(i + 1) % n];
1907
2447
  const p = orig[i];
1908
- const dir = unit(next.x - prev.x, next.y - prev.y);
2448
+ const dir = unit2(next.x - prev.x, next.y - prev.y);
1909
2449
  if (!dir) continue;
1910
2450
  const li = dist(p, prev) / 3;
1911
2451
  const lo = dist(p, next) / 3;
@@ -1962,12 +2502,12 @@ function mergePair(a, b, tol) {
1962
2502
  if (a.line && b.line) {
1963
2503
  const cx = b3[0] - a0[0];
1964
2504
  const cy = b3[1] - a0[1];
1965
- const len = Math.hypot(cx, cy);
1966
- if (len < 1e-12) return null;
2505
+ const len2 = Math.hypot(cx, cy);
2506
+ if (len2 < 1e-12) return null;
1967
2507
  const mx = a3[0] - a0[0];
1968
2508
  const my = a3[1] - a0[1];
1969
- const along = (mx * cx + my * cy) / len;
1970
- return Math.abs((mx * cy - my * cx) / len) <= tol && along >= 0 && along <= len ? { c: [a0, b3], line: true } : null;
2509
+ const along = (mx * cx + my * cy) / len2;
2510
+ return Math.abs((mx * cy - my * cx) / len2) <= tol && along >= 0 && along <= len2 ? { c: [a0, b3], line: true } : null;
1971
2511
  }
1972
2512
  const ca = a.line ? [a0, a0, a3, a3] : a.c;
1973
2513
  const cb = b.line ? [a3, a3, b3, b3] : b.c;
@@ -2091,7 +2631,7 @@ function lineHits(p, q, seg) {
2091
2631
  const dy = q[1] - p[1];
2092
2632
  const len2 = dx * dx + dy * dy;
2093
2633
  if (len2 < 1e-24) return [];
2094
- const h = (pt) => (pt[0] - p[0]) * -dy + (pt[1] - p[1]) * dx;
2634
+ const h = (pt2) => (pt2[0] - p[0]) * -dy + (pt2[1] - p[1]) * dx;
2095
2635
  let coeffs;
2096
2636
  if (seg.length === 2) coeffs = [0, 0, h(seg[1]) - h(seg[0]), h(seg[0])];
2097
2637
  else {
@@ -2099,7 +2639,7 @@ function lineHits(p, q, seg) {
2099
2639
  coeffs = [-h0 + 3 * h1 - 3 * h2 + h3, 3 * h0 - 6 * h1 + 3 * h2, -3 * h0 + 3 * h1, h0];
2100
2640
  }
2101
2641
  if (coeffs.every((c) => Math.abs(c) < 1e-12 * Math.max(1, len2))) return [];
2102
- const scale = Math.sqrt(len2) * Math.max(1, ...seg.map((pt) => Math.abs(pt[0]) + Math.abs(pt[1])));
2642
+ const scale = Math.sqrt(len2) * Math.max(1, ...seg.map((pt2) => Math.abs(pt2[0]) + Math.abs(pt2[1])));
2103
2643
  const out = [];
2104
2644
  for (const t of rootsInUnit(...coeffs, scale)) {
2105
2645
  const point = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, t);
@@ -2326,12 +2866,12 @@ function simplifyPolyline(points, tolerance) {
2326
2866
  const [ax, ay] = points[a];
2327
2867
  const dx = points[b][0] - ax;
2328
2868
  const dy = points[b][1] - ay;
2329
- const len = Math.hypot(dx, dy);
2869
+ const len2 = Math.hypot(dx, dy);
2330
2870
  let far = -1;
2331
2871
  let farD = 0;
2332
2872
  for (let i = a + 1; i < b; i++) {
2333
2873
  const [px, py] = points[i];
2334
- const d = len < 1e-12 ? Math.hypot(px - ax, py - ay) : Math.abs((px - ax) * dy - (py - ay) * dx) / len;
2874
+ const d = len2 < 1e-12 ? Math.hypot(px - ax, py - ay) : Math.abs((px - ax) * dy - (py - ay) * dx) / len2;
2335
2875
  if (d > farD) {
2336
2876
  farD = d;
2337
2877
  far = i;
@@ -2443,11 +2983,11 @@ function snapSquare(x, y, w, h) {
2443
2983
  }
2444
2984
  function turning(a, b, c) {
2445
2985
  const u = [b[0] - a[0], b[1] - a[1]];
2446
- const v = [c[0] - b[0], c[1] - b[1]];
2986
+ const v2 = [c[0] - b[0], c[1] - b[1]];
2447
2987
  const lu = Math.hypot(u[0], u[1]);
2448
- const lv = Math.hypot(v[0], v[1]);
2988
+ const lv = Math.hypot(v2[0], v2[1]);
2449
2989
  if (lu <= 0 || lv <= 0) return 0;
2450
- return Math.acos(Math.min(1, Math.max(-1, (u[0] * v[0] + u[1] * v[1]) / (lu * lv)))) * 180 / Math.PI;
2990
+ return Math.acos(Math.min(1, Math.max(-1, (u[0] * v2[0] + u[1] * v2[1]) / (lu * lv)))) * 180 / Math.PI;
2451
2991
  }
2452
2992
  function rdp(p, eps) {
2453
2993
  const n = p.length;
@@ -2575,6 +3115,21 @@ function recognizeInkShape(points, options = {}) {
2575
3115
  var DEFAULT_VECTOR_SNAP = { grid: 0, objects: true, guides: true, threshold: 5, angles: VECTOR_CONSTRUCTION_ANGLES };
2576
3116
  var DEFAULT_VECTOR_PATH_EDIT = { keepShapeOnDelete: true, curvatureMoves: false, pencilSmoothing: 0, recognizeShapes: false, eraserSize: 10 };
2577
3117
  var VECTOR_PENCIL_STRING_PX = 100;
3118
+ var VECTOR_STYLE_KEYS = [
3119
+ "fill",
3120
+ "stroke",
3121
+ "strokeWidth",
3122
+ "strokeLinecap",
3123
+ "strokeLinejoin",
3124
+ "strokeDasharray",
3125
+ "fillRule",
3126
+ "strokeAlign",
3127
+ "startArrow",
3128
+ "endArrow",
3129
+ "arrowAlign",
3130
+ "startArrowScale",
3131
+ "endArrowScale"
3132
+ ];
2578
3133
  var VECTOR_CLIPBOARD_TYPE = "pixerate/vector";
2579
3134
  var DEFAULT_DEFAULTS = {
2580
3135
  fill: "#d4d4d8",
@@ -2602,7 +3157,7 @@ function getEditablePathIds(state) {
2602
3157
  }
2603
3158
  }
2604
3159
  }
2605
- return [...ids].filter((id) => _optionalChain([findVectorNode, 'call', _27 => _27(state.document.nodes, id), 'optionalAccess', _28 => _28.type]) === "path");
3160
+ return [...ids].filter((id) => _optionalChain([findVectorNode, 'call', _38 => _38(state.document.nodes, id), 'optionalAccess', _39 => _39.type]) === "path");
2606
3161
  }
2607
3162
  function getVisibleAnchors(state) {
2608
3163
  const out = [];
@@ -2617,7 +3172,7 @@ function getVisibleHandles(state) {
2617
3172
  const seen = /* @__PURE__ */ new Set();
2618
3173
  const push = (node, s, i, handle) => {
2619
3174
  const sp = node.subpaths[s];
2620
- const a = _optionalChain([sp, 'optionalAccess', _29 => _29.anchors, 'access', _30 => _30[i]]);
3175
+ const a = _optionalChain([sp, 'optionalAccess', _40 => _40.anchors, 'access', _41 => _41[i]]);
2621
3176
  if (!a || !a[handle]) return;
2622
3177
  const key = `${node.id}:${s}:${i}:${handle}`;
2623
3178
  if (seen.has(key)) return;
@@ -2626,8 +3181,8 @@ function getVisibleHandles(state) {
2626
3181
  };
2627
3182
  if (state.tool === "pen" && state.pen) {
2628
3183
  const node = findVectorNode(state.document.nodes, state.pen.nodeId);
2629
- const sp = _optionalChain([node, 'optionalAccess', _31 => _31.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
2630
- if (_optionalChain([node, 'optionalAccess', _32 => _32.type]) === "path" && sp) {
3184
+ const sp = _optionalChain([node, 'optionalAccess', _42 => _42.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
3185
+ if (_optionalChain([node, 'optionalAccess', _43 => _43.type]) === "path" && sp) {
2631
3186
  const last = sp.anchors.length - 1;
2632
3187
  push(node, state.pen.subpath, last, "in");
2633
3188
  push(node, state.pen.subpath, last, "out");
@@ -2651,9 +3206,32 @@ function getVisibleHandles(state) {
2651
3206
  }
2652
3207
  return out;
2653
3208
  }
3209
+ var VECTOR_CORNER_WIDGET_INSET = 10;
3210
+ function getVectorCornerWidgets(state) {
3211
+ if (state.tool !== "direct" || state.pen) return [];
3212
+ const zoom = state.viewport.zoom;
3213
+ const out = [];
3214
+ for (const id of getEditablePathIds(state)) {
3215
+ const node = findVectorNode(state.document.nodes, id);
3216
+ const picked = state.selectedAnchors.filter((r) => r.nodeId === id);
3217
+ const m = _nullishCoalesce(vectorWorldMatrix(state.document.nodes, id), () => ( IDENTITY_MATRIX));
3218
+ for (const c of vectorCorners(node.subpaths)) {
3219
+ if (picked.length && !picked.some((r) => r.subpath === c.subpath && r.index === c.index)) continue;
3220
+ const side = Math.min(c.lu, c.lv);
3221
+ if (side * zoom < 3 * VECTOR_CORNER_WIDGET_INSET) continue;
3222
+ const a = node.subpaths[c.subpath].anchors[c.index];
3223
+ const radius = _nullishCoalesce(_optionalChain([a, 'access', _44 => _44.corner, 'optionalAccess', _45 => _45.radius]), () => ( 0));
3224
+ const drawn = fitCornerRadius(c, radius);
3225
+ const k = Math.min(Math.max(drawn / c.sin, VECTOR_CORNER_WIDGET_INSET / (zoom * Math.sqrt(1 + c.cos))), side / 2);
3226
+ const p = applyMatrix(m, cornerBisectorPoint(c, k));
3227
+ 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', _46 => _46.corner, 'optionalAccess', _47 => _47.kind]), () => ( "round")), corner: c });
3228
+ }
3229
+ }
3230
+ return out;
3231
+ }
2654
3232
  function getAnchor(doc, ref) {
2655
3233
  const node = findVectorNode(doc.nodes, ref.nodeId);
2656
- return _optionalChain([node, 'optionalAccess', _33 => _33.type]) === "path" ? _optionalChain([node, 'access', _34 => _34.subpaths, 'access', _35 => _35[ref.subpath], 'optionalAccess', _36 => _36.anchors, 'access', _37 => _37[ref.index]]) : void 0;
3234
+ return _optionalChain([node, 'optionalAccess', _48 => _48.type]) === "path" ? _optionalChain([node, 'access', _49 => _49.subpaths, 'access', _50 => _50[ref.subpath], 'optionalAccess', _51 => _51.anchors, 'access', _52 => _52[ref.index]]) : void 0;
2657
3235
  }
2658
3236
  function getAnchorWorld(doc, ref) {
2659
3237
  const a = getAnchor(doc, ref);
@@ -2909,7 +3487,7 @@ var VectorEditorController = (_class3 = class {
2909
3487
  notify(docChanged = false) {
2910
3488
  this.revision++;
2911
3489
  this.state = this.computeState();
2912
- if (docChanged) _optionalChain([this, 'access', _38 => _38.onChange, 'optionalCall', _39 => _39(this.doc)]);
3490
+ if (docChanged) _optionalChain([this, 'access', _53 => _53.onChange, 'optionalCall', _54 => _54(this.doc)]);
2913
3491
  const state = this.state;
2914
3492
  this.listeners.forEach((listener) => {
2915
3493
  try {
@@ -2939,20 +3517,20 @@ var VectorEditorController = (_class3 = class {
2939
3517
  return applyMatrix(invertMatrix(this.worldOf(id, doc)), p);
2940
3518
  }
2941
3519
  cursorFor() {
2942
- if (_optionalChain([this, 'access', _40 => _40.drag, 'optionalAccess', _41 => _41.kind]) === "pan" || this.spaceHeld) return this.drag ? "grabbing" : "grab";
2943
- if (_optionalChain([this, 'access', _42 => _42.drag, 'optionalAccess', _43 => _43.kind]) === "rotate") return "grabbing";
2944
- if (_optionalChain([this, 'access', _44 => _44.drag, 'optionalAccess', _45 => _45.kind]) === "move") return "move";
3520
+ if (_optionalChain([this, 'access', _55 => _55.drag, 'optionalAccess', _56 => _56.kind]) === "pan" || this.spaceHeld) return this.drag ? "grabbing" : "grab";
3521
+ if (_optionalChain([this, 'access', _57 => _57.drag, 'optionalAccess', _58 => _58.kind]) === "rotate") return "grabbing";
3522
+ if (_optionalChain([this, 'access', _59 => _59.drag, 'optionalAccess', _60 => _60.kind]) === "move") return "move";
2945
3523
  const h = this.hover;
2946
3524
  switch (this.tool) {
2947
3525
  case "select":
2948
- if (_optionalChain([h, 'optionalAccess', _46 => _46.transformHandle])) return vectorResizeCursor(h.transformHandle, _nullishCoalesce(_optionalChain([this, 'access', _47 => _47.frameOf, 'call', _48 => _48(this.selectedRoots()), 'optionalAccess', _49 => _49.angle]), () => ( 0)));
2949
- return _optionalChain([h, 'optionalAccess', _50 => _50.nodeId]) ? "move" : "default";
3526
+ if (_optionalChain([h, 'optionalAccess', _61 => _61.transformHandle])) return vectorResizeCursor(h.transformHandle, _nullishCoalesce(_optionalChain([this, 'access', _62 => _62.frameOf, 'call', _63 => _63(this.selectedRoots()), 'optionalAccess', _64 => _64.angle]), () => ( 0)));
3527
+ return _optionalChain([h, 'optionalAccess', _65 => _65.nodeId]) ? "move" : "default";
2950
3528
  case "direct":
2951
- if (_optionalChain([h, 'optionalAccess', _51 => _51.anchor]) || _optionalChain([h, 'optionalAccess', _52 => _52.handle])) return "pointer";
2952
- if (_optionalChain([h, 'optionalAccess', _53 => _53.segment])) return "copy";
2953
- return _optionalChain([h, 'optionalAccess', _54 => _54.nodeId]) ? "move" : "default";
3529
+ if (_optionalChain([h, 'optionalAccess', _66 => _66.anchor]) || _optionalChain([h, 'optionalAccess', _67 => _67.handle]) || _optionalChain([h, 'optionalAccess', _68 => _68.corner])) return "pointer";
3530
+ if (_optionalChain([h, 'optionalAccess', _69 => _69.segment])) return "copy";
3531
+ return _optionalChain([h, 'optionalAccess', _70 => _70.nodeId]) ? "move" : "default";
2954
3532
  case "pen":
2955
- return _optionalChain([h, 'optionalAccess', _55 => _55.anchor]) ? "pointer" : "crosshair";
3533
+ return _optionalChain([h, 'optionalAccess', _71 => _71.anchor]) ? "pointer" : "crosshair";
2956
3534
  default:
2957
3535
  return "crosshair";
2958
3536
  }
@@ -2972,7 +3550,7 @@ var VectorEditorController = (_class3 = class {
2972
3550
  this.selection = s.selection.filter((id) => this.getNode(id));
2973
3551
  this.anchors = s.anchors.filter((r) => getAnchor(this.doc, r));
2974
3552
  this.editingId = s.editingId && this.getNode(s.editingId) ? s.editingId : null;
2975
- this.pen = s.pen && _optionalChain([this, 'access', _56 => _56.getNode, 'call', _57 => _57(s.pen.nodeId), 'optionalAccess', _58 => _58.subpaths, 'optionalAccess', _59 => _59[s.pen.subpath]]) ? s.pen : null;
3553
+ this.pen = s.pen && _optionalChain([this, 'access', _72 => _72.getNode, 'call', _73 => _73(s.pen.nodeId), 'optionalAccess', _74 => _74.subpaths, 'optionalAccess', _75 => _75[s.pen.subpath]]) ? s.pen : null;
2976
3554
  } finally {
2977
3555
  this.applying = false;
2978
3556
  }
@@ -3033,12 +3611,12 @@ var VectorEditorController = (_class3 = class {
3033
3611
  // Viewport
3034
3612
  // -------------------------------------------------------------------------
3035
3613
  screenToDocument(p) {
3036
- const v = this.viewport;
3037
- return { x: (p.x - v.x) / v.zoom, y: (p.y - v.y) / v.zoom };
3614
+ const v2 = this.viewport;
3615
+ return { x: (p.x - v2.x) / v2.zoom, y: (p.y - v2.y) / v2.zoom };
3038
3616
  }
3039
3617
  documentToScreen(p) {
3040
- const v = this.viewport;
3041
- return { x: p.x * v.zoom + v.x, y: p.y * v.zoom + v.y };
3618
+ const v2 = this.viewport;
3619
+ return { x: p.x * v2.zoom + v2.x, y: p.y * v2.zoom + v2.y };
3042
3620
  }
3043
3621
  setViewport(viewport) {
3044
3622
  const next = { ...this.viewport, ...viewport };
@@ -3105,9 +3683,9 @@ var VectorEditorController = (_class3 = class {
3105
3683
  toggleSnapping(on = !this.snap.objects) {
3106
3684
  this.setSnap({ objects: on });
3107
3685
  }
3108
- gridValue(v) {
3686
+ gridValue(v2) {
3109
3687
  const g = this.snap.grid;
3110
- return g > 0 ? Math.round(v / g) * g : v;
3688
+ return g > 0 ? Math.round(v2 / g) * g : v2;
3111
3689
  }
3112
3690
  snapPoint(p) {
3113
3691
  return { x: this.gridValue(p.x), y: this.gridValue(p.y) };
@@ -3136,7 +3714,7 @@ var VectorEditorController = (_class3 = class {
3136
3714
  if (res.kind) {
3137
3715
  this.snappedToObject = true;
3138
3716
  this.setGuides(res);
3139
- return _optionalChain([leave, 'optionalAccess', _60 => _60.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) };
3717
+ return _optionalChain([leave, 'optionalAccess', _76 => _76.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) };
3140
3718
  }
3141
3719
  }
3142
3720
  this.setGuides(null);
@@ -3154,14 +3732,14 @@ var VectorEditorController = (_class3 = class {
3154
3732
  /** Where the pointer at `p` would place a point with the pen or a shape tool (and the guides showing why). */
3155
3733
  hoverSnap(p, input) {
3156
3734
  if (this.tool === "pen") {
3157
- const sp = this.pen ? _optionalChain([this, 'access', _61 => _61.getNode, 'call', _62 => _62(this.pen.nodeId), 'optionalAccess', _63 => _63.subpaths, 'access', _64 => _64[this.pen.subpath]]) : void 0;
3158
- if (this.pen && sp && _optionalChain([this, 'access', _65 => _65.hover, 'optionalAccess', _66 => _66.anchor])) {
3735
+ const sp = this.pen ? _optionalChain([this, 'access', _77 => _77.getNode, 'call', _78 => _78(this.pen.nodeId), 'optionalAccess', _79 => _79.subpaths, 'access', _80 => _80[this.pen.subpath]]) : void 0;
3736
+ if (this.pen && sp && _optionalChain([this, 'access', _81 => _81.hover, 'optionalAccess', _82 => _82.anchor])) {
3159
3737
  this.setGuides(null);
3160
3738
  return p;
3161
3739
  }
3162
3740
  const last = this.pen && sp ? getAnchorWorld(this.doc, { nodeId: this.pen.nodeId, subpath: this.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
3163
3741
  const leave = last ? { at: { x: last.x, y: last.y }, constrain: input.shift ? "angle" : null } : null;
3164
- return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access', _67 => _67.pen, 'optionalAccess', _68 => _68.nodeId]), () => ( null))));
3742
+ return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access', _83 => _83.pen, 'optionalAccess', _84 => _84.nodeId]), () => ( null))));
3165
3743
  }
3166
3744
  if (SHAPE_TOOLS.has(this.tool)) return this.snapDrawn(p, null, input, () => this.drawingTargets(null));
3167
3745
  this.setGuides(null);
@@ -3263,7 +3841,7 @@ var VectorEditorController = (_class3 = class {
3263
3841
  let subpath;
3264
3842
  switch (input.kind) {
3265
3843
  case "rect":
3266
- subpath = rectSubpath(b, _nullishCoalesce(input.cornerRadius, () => ( d.cornerRadius)));
3844
+ subpath = rectSubpath(b);
3267
3845
  break;
3268
3846
  case "ellipse":
3269
3847
  subpath = ellipseSubpath(b);
@@ -3278,10 +3856,13 @@ var VectorEditorController = (_class3 = class {
3278
3856
  subpath = lineSubpath({ x: b.x, y: b.y }, { x: b.x + b.width, y: b.y + b.height });
3279
3857
  break;
3280
3858
  }
3859
+ const radius = _nullishCoalesce(input.cornerRadius, () => ( d.cornerRadius));
3860
+ const kind = _nullishCoalesce(input.cornerKind, () => ( d.cornerKind));
3861
+ if ((input.kind === "rect" || input.kind === "polygon" || input.kind === "star") && radius > 0) subpath = setSubpathCorners(subpath, radius, kind);
3281
3862
  const names = { rect: "Rectangle", ellipse: "Ellipse", polygon: "Polygon", star: "Star", line: "Line" };
3282
3863
  const style = this.styleFor(input.kind === "line");
3283
3864
  const own = {};
3284
- for (const key of ["fill", "stroke", "strokeWidth", "strokeLinecap", "strokeLinejoin", "strokeDasharray", "fillRule"]) {
3865
+ for (const key of VECTOR_STYLE_KEYS) {
3285
3866
  if (input[key] !== void 0) own[key] = input[key];
3286
3867
  }
3287
3868
  return { id: this.newId(), type: "path", name: _nullishCoalesce(input.name, () => ( names[input.kind])), ...style, ...own, subpaths: [subpath] };
@@ -3304,6 +3885,8 @@ var VectorEditorController = (_class3 = class {
3304
3885
  if (d.strokeLinejoin) out.strokeLinejoin = d.strokeLinejoin;
3305
3886
  if (d.strokeDasharray) out.strokeDasharray = d.strokeDasharray;
3306
3887
  if (d.fillRule) out.fillRule = d.fillRule;
3888
+ if (d.strokeAlign && d.strokeAlign !== "center") out.strokeAlign = d.strokeAlign;
3889
+ for (const key of ["startArrow", "endArrow", "arrowAlign", "startArrowScale", "endArrowScale"]) if (d[key]) out[key] = d[key];
3307
3890
  return out;
3308
3891
  }
3309
3892
  /** Merges `patch` into a node (paths take style and geometry; groups name / visibility / opacity). */
@@ -3328,6 +3911,95 @@ var VectorEditorController = (_class3 = class {
3328
3911
  });
3329
3912
  return { ok: true };
3330
3913
  }
3914
+ /**
3915
+ * Sets the live corner radius (and optionally the kind) of anchors: `refs`, else the selected
3916
+ * anchors (direct tool), else every anchor of the `ids` / selected paths. Radius 0 makes the
3917
+ * corners sharp again. Corners shrink to fit when drawn, so any radius is accepted.
3918
+ */
3919
+ setCornerRadius(radius, options = {}) {
3920
+ if (!Number.isFinite(radius) || radius < 0) return { ok: false, message: "The radius must be a number of at least 0." };
3921
+ const refs = this.cornerTargets(options);
3922
+ if (!refs.length) return { ok: false, message: "Nothing is selected." };
3923
+ this.change(radius > 0 ? "Corner Radius" : "Sharpen Corners", () => {
3924
+ this.doc = this.updateAnchors(this.doc, refs, (a) => setAnchorCorner(a, radius, options.kind));
3925
+ });
3926
+ return { ok: true };
3927
+ }
3928
+ /** Sets the live corner kind of the same anchors as `setCornerRadius`, keeping their radii. */
3929
+ setCornerKind(kind, options = {}) {
3930
+ const refs = this.cornerTargets(options);
3931
+ if (!refs.length) return { ok: false, message: "Nothing is selected." };
3932
+ this.change("Corner Kind", () => {
3933
+ this.doc = this.updateAnchors(this.doc, refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _85 => _85.corner, 'optionalAccess', _86 => _86.radius]), () => ( 0)), kind));
3934
+ });
3935
+ return { ok: true };
3936
+ }
3937
+ /** Anchors a corner change applies to (see `setCornerRadius`). */
3938
+ cornerTargets(options) {
3939
+ if (options.refs) return options.refs.filter((r) => getAnchor(this.doc, r));
3940
+ if (!options.ids && this.tool === "direct" && this.anchors.length) return this.anchors.slice();
3941
+ const out = [];
3942
+ for (const id of _nullishCoalesce(options.ids, () => ( this.selection))) {
3943
+ const node = this.getNode(id);
3944
+ if (!node) continue;
3945
+ for (const p of collectPathNodes(node)) {
3946
+ if (p.locked) continue;
3947
+ p.subpaths.forEach((sp, s) => sp.anchors.forEach((_, i) => out.push({ nodeId: p.id, subpath: s, index: i })));
3948
+ }
3949
+ }
3950
+ return out;
3951
+ }
3952
+ /**
3953
+ * Outline Stroke: replaces each selected (or given) path's stroke with a filled path of the
3954
+ * area it paints (alignment, joins, caps, dashes and arrowheads included), placed above a copy
3955
+ * of the path that keeps the fill (left out when there is none). Returns the new paths' ids.
3956
+ */
3957
+ outlineStroke(options = {}) {
3958
+ const roots = _nullishCoalesce(options.ids, () => ( this.selectedRoots()));
3959
+ const paths = [];
3960
+ for (const id of roots) {
3961
+ const node = this.getNode(id);
3962
+ if (node) {
3963
+ for (const p of collectPathNodes(node)) if (!p.locked && hasPaint(p.stroke) && (_nullishCoalesce(p.strokeWidth, () => ( 1))) > 0) paths.push(p);
3964
+ }
3965
+ }
3966
+ if (!paths.length) return [];
3967
+ const created = [];
3968
+ const replace = /* @__PURE__ */ new Map();
3969
+ for (const p of paths) {
3970
+ const outline = bezierToSubpaths(vectorStrokeOutlinePath(subpathsToBezier(p.subpaths), p));
3971
+ if (!outline.length) continue;
3972
+ const {
3973
+ stroke,
3974
+ strokeWidth: _w,
3975
+ strokeLinecap: _c,
3976
+ strokeLinejoin: _j,
3977
+ strokeDasharray: _d,
3978
+ strokeAlign: _a,
3979
+ startArrow: _s,
3980
+ endArrow: _e,
3981
+ arrowAlign: _al,
3982
+ startArrowScale: _ss,
3983
+ endArrowScale: _es,
3984
+ ...rest
3985
+ } = cloneVectorNode(p);
3986
+ const id = this.newId();
3987
+ const strokeNode = { ...rest, id, name: `${_nullishCoalesce(p.name, () => ( "Path"))} Stroke`, subpaths: outline, fill: _nullishCoalesce(stroke, () => ( null)), stroke: null, fillRule: "nonzero" };
3988
+ const list = hasPaint(p.fill) ? [{ ...rest, stroke: null }, strokeNode] : [strokeNode];
3989
+ created.push(id);
3990
+ replace.set(p.id, list);
3991
+ }
3992
+ if (!created.length) return [];
3993
+ this.change("Outline Stroke", () => {
3994
+ let nodes = this.doc.nodes;
3995
+ for (const [id, list] of replace) nodes = spliceTree(nodes, id, list);
3996
+ this.setDoc(nodes);
3997
+ this.selection = created;
3998
+ this.anchors = [];
3999
+ this.editingId = null;
4000
+ });
4001
+ return created;
4002
+ }
3331
4003
  rename(id, name) {
3332
4004
  return this.updateNode(id, { name }, { label: "Rename" });
3333
4005
  }
@@ -3396,10 +4068,10 @@ var VectorEditorController = (_class3 = class {
3396
4068
  return this.selection.filter((id) => {
3397
4069
  let covered = false;
3398
4070
  const loc = locateVectorNode(this.doc.nodes, id);
3399
- let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _69 => _69.parent]), () => ( null));
4071
+ let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _87 => _87.parent]), () => ( null));
3400
4072
  while (parent) {
3401
4073
  if (this.selection.includes(parent.id)) covered = true;
3402
- parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _70 => _70(this.doc.nodes, parent.id), 'optionalAccess', _71 => _71.parent]), () => ( null));
4074
+ parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _88 => _88(this.doc.nodes, parent.id), 'optionalAccess', _89 => _89.parent]), () => ( null));
3403
4075
  }
3404
4076
  return !covered;
3405
4077
  });
@@ -3582,8 +4254,8 @@ var VectorEditorController = (_class3 = class {
3582
4254
  if (!ids.length) return null;
3583
4255
  const locs = ids.map((id2) => locateVectorNode(this.doc.nodes, id2));
3584
4256
  if (locs.some((l) => !l)) return null;
3585
- const parent = _nullishCoalesce(_optionalChain([locs, 'access', _72 => _72[0], 'access', _73 => _73.parent, 'optionalAccess', _74 => _74.id]), () => ( null));
3586
- if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access', _75 => _75.parent, 'optionalAccess', _76 => _76.id]), () => ( null))) !== parent)) return null;
4257
+ const parent = _nullishCoalesce(_optionalChain([locs, 'access', _90 => _90[0], 'access', _91 => _91.parent, 'optionalAccess', _92 => _92.id]), () => ( null));
4258
+ if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access', _93 => _93.parent, 'optionalAccess', _94 => _94.id]), () => ( null))) !== parent)) return null;
3587
4259
  const id = this.newId();
3588
4260
  this.change("Group", () => {
3589
4261
  const set = new Set(ids);
@@ -3606,7 +4278,7 @@ var VectorEditorController = (_class3 = class {
3606
4278
  }
3607
4279
  /** Replaces selected groups with their children. */
3608
4280
  ungroup(options = {}) {
3609
- const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _77 => _77.type]) === "group");
4281
+ const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _95 => _95.type]) === "group");
3610
4282
  if (!groups.length) return { ok: false, message: "Select a group to ungroup." };
3611
4283
  const set = new Set(groups.map((g) => g.id));
3612
4284
  this.change("Ungroup", () => {
@@ -3720,7 +4392,7 @@ var VectorEditorController = (_class3 = class {
3720
4392
  }
3721
4393
  /** Splits selected compound paths into one path per subpath. */
3722
4394
  breakApart(options = {}) {
3723
- const paths = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _78 => _78.type]) === "path" && n.subpaths.length > 1);
4395
+ const paths = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _96 => _96.type]) === "path" && n.subpaths.length > 1);
3724
4396
  if (!paths.length) return { ok: false, message: "Select a compound path to break apart." };
3725
4397
  const created = [];
3726
4398
  const set = new Set(paths.map((p) => p.id));
@@ -3782,7 +4454,7 @@ var VectorEditorController = (_class3 = class {
3782
4454
  /** `doc` with anchor `ref` moved to document point `to` by a Curvature-tool move. */
3783
4455
  curvatureMoved(doc, ref, to) {
3784
4456
  const node = findVectorNode(doc.nodes, ref.nodeId);
3785
- if (_optionalChain([node, 'optionalAccess', _79 => _79.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
4457
+ if (_optionalChain([node, 'optionalAccess', _97 => _97.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
3786
4458
  const q = this.toLocal(ref.nodeId, to, doc);
3787
4459
  const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? curvatureMoveAnchor(sp, ref.index, q) : sp);
3788
4460
  const nodes = mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n);
@@ -3791,7 +4463,7 @@ var VectorEditorController = (_class3 = class {
3791
4463
  /** `doc` with segment `ref` bent through document point `p` at parameter `t`. */
3792
4464
  bent(doc, ref, t, p) {
3793
4465
  const node = findVectorNode(doc.nodes, ref.nodeId);
3794
- if (_optionalChain([node, 'optionalAccess', _80 => _80.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
4466
+ if (_optionalChain([node, 'optionalAccess', _98 => _98.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
3795
4467
  const q = this.toLocal(ref.nodeId, p, doc);
3796
4468
  const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? bendSegment(sp, ref.segment, t, q) : sp);
3797
4469
  return { ...doc, nodes: mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n) };
@@ -3802,7 +4474,7 @@ var VectorEditorController = (_class3 = class {
3802
4474
  */
3803
4475
  bendSegment(ref, to, t = 0.5) {
3804
4476
  const node = this.getNode(ref.nodeId);
3805
- const sp = _optionalChain([node, 'optionalAccess', _81 => _81.type]) === "path" ? node.subpaths[ref.subpath] : void 0;
4477
+ const sp = _optionalChain([node, 'optionalAccess', _99 => _99.type]) === "path" ? node.subpaths[ref.subpath] : void 0;
3806
4478
  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." };
3807
4479
  if (node.locked) return { ok: false, message: "The path is locked." };
3808
4480
  this.change("Bend Segment", () => {
@@ -3837,7 +4509,7 @@ var VectorEditorController = (_class3 = class {
3837
4509
  /** Adds an anchor on the nearest segment of `nodeId` to document point `point` (shape unchanged). */
3838
4510
  insertAnchorAt(nodeId, point) {
3839
4511
  const node = this.getNode(nodeId);
3840
- if (_optionalChain([node, 'optionalAccess', _82 => _82.type]) !== "path") return null;
4512
+ if (_optionalChain([node, 'optionalAccess', _100 => _100.type]) !== "path") return null;
3841
4513
  const hit = nearestSegment(node, this.toLocal(nodeId, point));
3842
4514
  if (!hit) return null;
3843
4515
  const { subpath, index } = insertAnchor(node.subpaths[hit.subpath], hit.segment, hit.t);
@@ -3881,7 +4553,7 @@ var VectorEditorController = (_class3 = class {
3881
4553
  setClosed(closed, nodeId) {
3882
4554
  const targets = nodeId ? (() => {
3883
4555
  const n = this.getNode(nodeId);
3884
- return _optionalChain([n, 'optionalAccess', _83 => _83.type]) === "path" ? n.subpaths.map((_, s) => ({ nodeId, subpath: s })) : [];
4556
+ return _optionalChain([n, 'optionalAccess', _101 => _101.type]) === "path" ? n.subpaths.map((_, s) => ({ nodeId, subpath: s })) : [];
3885
4557
  })() : this.anchors.map((r) => ({ nodeId: r.nodeId, subpath: r.subpath }));
3886
4558
  if (!targets.length) return { ok: false, message: "Select a path or points." };
3887
4559
  this.change(closed ? "Close Path" : "Open Path", () => {
@@ -3930,7 +4602,7 @@ var VectorEditorController = (_class3 = class {
3930
4602
  const map = (p) => applyMatrix(m, p);
3931
4603
  return from.subpaths.map((sp) => ({
3932
4604
  closed: sp.closed,
3933
- anchors: sp.anchors.map((a) => ({ ...map(a), ...a.in ? { in: map(a.in) } : {}, ...a.out ? { out: map(a.out) } : {}, ...a.type ? { type: a.type } : {} }))
4605
+ anchors: sp.anchors.map((a) => ({ ...map(a), ...a.in ? { in: map(a.in) } : {}, ...a.out ? { out: map(a.out) } : {}, ...a.type ? { type: a.type } : {}, ...a.corner ? { corner: { ...a.corner } } : {} }))
3934
4606
  }));
3935
4607
  }
3936
4608
  /**
@@ -4035,7 +4707,7 @@ var VectorEditorController = (_class3 = class {
4035
4707
  */
4036
4708
  cutPathAt(nodeId, point) {
4037
4709
  const node = this.getNode(nodeId);
4038
- if (_optionalChain([node, 'optionalAccess', _84 => _84.type]) !== "path" || node.locked) return null;
4710
+ if (_optionalChain([node, 'optionalAccess', _102 => _102.type]) !== "path" || node.locked) return null;
4039
4711
  const local = this.toLocal(nodeId, point);
4040
4712
  const hit = nearestSegment(node, local);
4041
4713
  if (!hit) return null;
@@ -4085,7 +4757,7 @@ var VectorEditorController = (_class3 = class {
4085
4757
  if (n.visible === false || n.locked || parent && blocked.has(parent.id)) blocked.add(n.id);
4086
4758
  });
4087
4759
  const visible = (p) => !blocked.has(p.id);
4088
- if (_optionalChain([ids, 'optionalAccess', _85 => _85.length]) || this.selection.length) return this.pathTargets(_nullishCoalesce(ids, () => ( this.selection))).filter(visible);
4760
+ if (_optionalChain([ids, 'optionalAccess', _103 => _103.length]) || this.selection.length) return this.pathTargets(_nullishCoalesce(ids, () => ( this.selection))).filter(visible);
4089
4761
  const box = polylineBounds(points.map(toPoint2), pad);
4090
4762
  if (!box) return [];
4091
4763
  const out = [];
@@ -4171,7 +4843,7 @@ var VectorEditorController = (_class3 = class {
4171
4843
  finishPath() {
4172
4844
  if (!this.pen) return;
4173
4845
  const node = this.getNode(this.pen.nodeId);
4174
- const sp = _optionalChain([node, 'optionalAccess', _86 => _86.subpaths, 'access', _87 => _87[this.pen.subpath]]);
4846
+ const sp = _optionalChain([node, 'optionalAccess', _104 => _104.subpaths, 'access', _105 => _105[this.pen.subpath]]);
4175
4847
  const pen = this.pen;
4176
4848
  this.pen = null;
4177
4849
  this.pointer = null;
@@ -4215,7 +4887,7 @@ var VectorEditorController = (_class3 = class {
4215
4887
  if (trimmed.startsWith("{")) {
4216
4888
  try {
4217
4889
  const data = JSON.parse(trimmed);
4218
- if (_optionalChain([data, 'optionalAccess', _88 => _88.type]) === VECTOR_CLIPBOARD_TYPE && Array.isArray(data.nodes)) nodes = data.nodes;
4890
+ if (_optionalChain([data, 'optionalAccess', _106 => _106.type]) === VECTOR_CLIPBOARD_TYPE && Array.isArray(data.nodes)) nodes = data.nodes;
4219
4891
  } catch (e2) {
4220
4892
  nodes = null;
4221
4893
  }
@@ -4227,7 +4899,7 @@ var VectorEditorController = (_class3 = class {
4227
4899
  }
4228
4900
  }
4229
4901
  } else nodes = this.clipboard;
4230
- if (!_optionalChain([nodes, 'optionalAccess', _89 => _89.length])) return [];
4902
+ if (!_optionalChain([nodes, 'optionalAccess', _107 => _107.length])) return [];
4231
4903
  const offset = _nullishCoalesce(options.offset, () => ( (text && !text.trim().startsWith("{") ? { x: 0, y: 0 } : { x: 10, y: 10 })));
4232
4904
  const copies = nodes.map((n) => transformVectorNode(this.freshIds(n), translateMatrix(offset.x, offset.y)));
4233
4905
  this.change("Paste", () => {
@@ -4278,13 +4950,29 @@ var VectorEditorController = (_class3 = class {
4278
4950
  hitHandle(screen) {
4279
4951
  for (const ref of getVisibleHandles(this.view()).reverse()) {
4280
4952
  const a = getAnchorWorld(this.doc, ref);
4281
- const h = _optionalChain([a, 'optionalAccess', _90 => _90[ref.handle]]);
4953
+ const h = _optionalChain([a, 'optionalAccess', _108 => _108[ref.handle]]);
4282
4954
  if (!h) continue;
4283
4955
  const s = this.documentToScreen(h);
4284
4956
  if (Math.hypot(s.x - screen.x, s.y - screen.y) <= this.hitRadius) return ref;
4285
4957
  }
4286
4958
  return null;
4287
4959
  }
4960
+ cornerView() {
4961
+ return { ...this.view(), viewport: this.viewport };
4962
+ }
4963
+ hitCornerWidget(screen) {
4964
+ let best = null;
4965
+ let bestD = Infinity;
4966
+ for (const w of getVectorCornerWidgets(this.cornerView())) {
4967
+ const s = this.documentToScreen(w);
4968
+ const d = Math.hypot(s.x - screen.x, s.y - screen.y);
4969
+ if (d <= this.hitRadius && d < bestD) {
4970
+ best = w;
4971
+ bestD = d;
4972
+ }
4973
+ }
4974
+ return best;
4975
+ }
4288
4976
  hitAnchor(screen, refs) {
4289
4977
  let best = null;
4290
4978
  let bestD = Infinity;
@@ -4343,6 +5031,8 @@ var VectorEditorController = (_class3 = class {
4343
5031
  if (handle) return { nodeId: handle.nodeId, handle };
4344
5032
  const anchor = this.hitAnchor(screen, getVisibleAnchors(this.view()));
4345
5033
  if (anchor) return { nodeId: anchor.nodeId, anchor };
5034
+ const widget = this.hitCornerWidget(screen);
5035
+ if (widget) return { nodeId: widget.ref.nodeId, corner: widget.ref };
4346
5036
  for (const id of getEditablePathIds(this.view())) {
4347
5037
  const node = this.getNode(id);
4348
5038
  const seg = nearestSegment(node, this.toLocal(id, p));
@@ -4500,6 +5190,13 @@ var VectorEditorController = (_class3 = class {
4500
5190
  this.notify();
4501
5191
  return;
4502
5192
  }
5193
+ const widget = this.hitCornerWidget(screen);
5194
+ if (widget) {
5195
+ const refs = getVectorCornerWidgets(this.cornerView()).map((w) => w.ref);
5196
+ this.drag = { kind: "corner", widget, refs, start: p, before: this.snapshot(), base: this.doc, moved: false, alt: !!input.alt, shift: !!input.shift };
5197
+ this.notify();
5198
+ return;
5199
+ }
4503
5200
  for (const id of getEditablePathIds(this.view())) {
4504
5201
  const node = this.getNode(id);
4505
5202
  const seg = nearestSegment(node, this.toLocal(id, p));
@@ -4543,7 +5240,7 @@ var VectorEditorController = (_class3 = class {
4543
5240
  }
4544
5241
  downPen(screen, p, input) {
4545
5242
  const before = this.snapshot();
4546
- const sp = this.pen ? _optionalChain([this, 'access', _91 => _91.getNode, 'call', _92 => _92(this.pen.nodeId), 'optionalAccess', _93 => _93.subpaths, 'access', _94 => _94[this.pen.subpath]]) : void 0;
5243
+ const sp = this.pen ? _optionalChain([this, 'access', _109 => _109.getNode, 'call', _110 => _110(this.pen.nodeId), 'optionalAccess', _111 => _111.subpaths, 'access', _112 => _112[this.pen.subpath]]) : void 0;
4547
5244
  if (this.pen && sp) {
4548
5245
  const pen = this.pen;
4549
5246
  const first = { nodeId: pen.nodeId, subpath: pen.subpath, index: 0 };
@@ -4557,10 +5254,10 @@ var VectorEditorController = (_class3 = class {
4557
5254
  }
4558
5255
  const last = getAnchorWorld(this.doc, { nodeId: pen.nodeId, subpath: pen.subpath, index: sp.anchors.length - 1 });
4559
5256
  const leave = { at: { x: last.x, y: last.y }, constrain: input.shift ? "angle" : null };
4560
- let pt2 = this.snapDrawn(p, leave, input, () => this.drawingTargets(pen.nodeId));
4561
- pt2 = this.toLocal(pen.nodeId, pt2);
5257
+ let pt3 = this.snapDrawn(p, leave, input, () => this.drawingTargets(pen.nodeId));
5258
+ pt3 = this.toLocal(pen.nodeId, pt3);
4562
5259
  const node2 = this.getNode(pen.nodeId);
4563
- const anchors = [...sp.anchors, { x: pt2.x, y: pt2.y }];
5260
+ const anchors = [...sp.anchors, { x: pt3.x, y: pt3.y }];
4564
5261
  this.replaceNode(pen.nodeId, { ...node2, subpaths: node2.subpaths.map((s, i) => i === pen.subpath ? { ...s, anchors } : s) });
4565
5262
  const ref2 = { nodeId: pen.nodeId, subpath: pen.subpath, index: anchors.length - 1 };
4566
5263
  this.anchors = [ref2];
@@ -4588,8 +5285,8 @@ var VectorEditorController = (_class3 = class {
4588
5285
  this.notify(this.doc !== before.doc);
4589
5286
  return;
4590
5287
  }
4591
- const pt = this.snapDrawn(p, null, input, () => this.drawingTargets(null));
4592
- const node = { id: this.newId(), type: "path", name: "Path", ...this.styleFor(true), subpaths: [{ anchors: [{ x: pt.x, y: pt.y }], closed: false }] };
5288
+ const pt2 = this.snapDrawn(p, null, input, () => this.drawingTargets(null));
5289
+ const node = { id: this.newId(), type: "path", name: "Path", ...this.styleFor(true), subpaths: [{ anchors: [{ x: pt2.x, y: pt2.y }], closed: false }] };
4593
5290
  this.setDoc([...this.doc.nodes, node]);
4594
5291
  this.pen = { nodeId: node.id, subpath: 0 };
4595
5292
  this.selection = [node.id];
@@ -4737,13 +5434,25 @@ var VectorEditorController = (_class3 = class {
4737
5434
  case "handle": {
4738
5435
  const anchor = getAnchorWorld(drag.base, drag.ref);
4739
5436
  const leave = anchor ? { at: { x: anchor.x, y: anchor.y }, constrain: input.shift ? "angle" : null } : null;
4740
- const pt = this.snapDrawn(p, leave, input, () => drag.targets ??= VectorSnapTargets.forHandle(drag.base, drag.ref.nodeId));
4741
- const local = this.toLocal(drag.ref.nodeId, pt, drag.base);
5437
+ const pt2 = this.snapDrawn(p, leave, input, () => drag.targets ??= VectorSnapTargets.forHandle(drag.base, drag.ref.nodeId));
5438
+ const local = this.toLocal(drag.ref.nodeId, pt2, drag.base);
4742
5439
  this.doc = this.updateAnchors(drag.base, [drag.ref], (a) => moveAnchorHandle(a, drag.ref.handle, local, input.alt));
4743
5440
  this.interaction = { type: "handle", ref: drag.ref };
4744
5441
  this.notify(true);
4745
5442
  return;
4746
5443
  }
5444
+ case "corner": {
5445
+ if (!drag.moved && Math.hypot(p.x - drag.start.x, p.y - drag.start.y) * this.viewport.zoom < 3) return;
5446
+ drag.moved = true;
5447
+ const c = drag.widget.corner;
5448
+ const inv = invertMatrix(this.worldOf(drag.widget.ref.nodeId, drag.base));
5449
+ const d = applyMatrixLinear(inv, { x: p.x - drag.start.x, y: p.y - drag.start.y });
5450
+ const radius = Math.max(0, Math.min(c.maxRadius, drag.widget.drawnRadius + cornerRadiusChange(c, d)));
5451
+ this.doc = this.updateAnchors(drag.base, drag.refs, (a) => setAnchorCorner(a, radius));
5452
+ this.interaction = { type: "corner", ref: drag.widget.ref, radius };
5453
+ this.notify(true);
5454
+ return;
5455
+ }
4747
5456
  case "bend": {
4748
5457
  if (!drag.moved && Math.hypot(p.x - drag.start.x, p.y - drag.start.y) * this.viewport.zoom < 2) return;
4749
5458
  drag.moved = true;
@@ -4769,10 +5478,10 @@ var VectorEditorController = (_class3 = class {
4769
5478
  return;
4770
5479
  }
4771
5480
  this.guides = null;
4772
- let pt = p;
4773
- if (input.shift) pt = constrain45(anchor, pt);
5481
+ let pt2 = p;
5482
+ if (input.shift) pt2 = constrain45(anchor, pt2);
4774
5483
  const side = drag.closing ? "in" : "out";
4775
- const pos = this.toLocal(drag.ref.nodeId, drag.closing ? { x: 2 * anchor.x - pt.x, y: 2 * anchor.y - pt.y } : pt, drag.base);
5484
+ const pos = this.toLocal(drag.ref.nodeId, drag.closing ? { x: 2 * anchor.x - pt2.x, y: 2 * anchor.y - pt2.y } : pt2, drag.base);
4776
5485
  this.doc = this.updateAnchors(drag.base, [drag.ref], (a) => {
4777
5486
  if (drag.outOnly || drag.altBreaks && input.alt) return moveAnchorHandle(a, side, pos, true);
4778
5487
  return moveAnchorHandle({ ...a, type: "symmetric" }, side, pos);
@@ -4782,7 +5491,7 @@ var VectorEditorController = (_class3 = class {
4782
5491
  }
4783
5492
  case "shape": {
4784
5493
  const node = this.getNode(drag.nodeId);
4785
- if (_optionalChain([node, 'optionalAccess', _95 => _95.type]) !== "path") return;
5494
+ if (_optionalChain([node, 'optionalAccess', _113 => _113.type]) !== "path") return;
4786
5495
  const leave = { at: drag.start, constrain: input.shift ? drag.tool === "line" ? "angle" : "square" : null, curves: false };
4787
5496
  let end = this.snapDrawn(p, leave, input, () => drag.targets ??= this.drawingTargets(null));
4788
5497
  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);
@@ -4821,9 +5530,9 @@ var VectorEditorController = (_class3 = class {
4821
5530
  }
4822
5531
  const string = drag.lazy.string();
4823
5532
  const node = this.getNode(drag.nodeId);
4824
- if (_optionalChain([node, 'optionalAccess', _96 => _96.type]) !== "path") return;
5533
+ if (_optionalChain([node, 'optionalAccess', _114 => _114.type]) !== "path") return;
4825
5534
  if (!moved) {
4826
- if (string && _optionalChain([this, 'access', _97 => _97.interaction, 'optionalAccess', _98 => _98.type]) === "draw") {
5535
+ if (string && _optionalChain([this, 'access', _115 => _115.interaction, 'optionalAccess', _116 => _116.type]) === "draw") {
4827
5536
  this.interaction = { ...this.interaction, string: [{ x: string[0][0], y: string[0][1] }, { x: string[1][0], y: string[1][1] }] };
4828
5537
  this.notify();
4829
5538
  }
@@ -4859,8 +5568,8 @@ var VectorEditorController = (_class3 = class {
4859
5568
  }
4860
5569
  this.setGuides(res);
4861
5570
  return {
4862
- dx: _optionalChain([res, 'optionalAccess', _99 => _99.x]) ? dx + res.dx : axes.x ? this.gridValue(origin.x + dx) - origin.x : dx,
4863
- dy: _optionalChain([res, 'optionalAccess', _100 => _100.y]) ? dy + res.dy : axes.y ? this.gridValue(origin.y + dy) - origin.y : dy
5571
+ dx: _optionalChain([res, 'optionalAccess', _117 => _117.x]) ? dx + res.dx : axes.x ? this.gridValue(origin.x + dx) - origin.x : dx,
5572
+ dy: _optionalChain([res, 'optionalAccess', _118 => _118.y]) ? dy + res.dy : axes.y ? this.gridValue(origin.y + dy) - origin.y : dy
4864
5573
  };
4865
5574
  }
4866
5575
  /** The anchor a point drag snaps by, where it started: the grabbed one, else the moving one nearest the press. */
@@ -4885,12 +5594,12 @@ var VectorEditorController = (_class3 = class {
4885
5594
  const guides = { lines: [], labels: [] };
4886
5595
  for (const axis of ["x", "y"]) {
4887
5596
  const d = end[axis] - start[axis];
4888
- const len = Math.abs(d) > 1e-9 ? targets.matchSize(Math.abs(d) * k, tol) : null;
4889
- if (len === null || len <= 0) continue;
4890
- out[axis] = start[axis] + Math.sign(d) * len / k;
5597
+ const len2 = Math.abs(d) > 1e-9 ? targets.matchSize(Math.abs(d) * k, tol) : null;
5598
+ if (len2 === null || len2 <= 0) continue;
5599
+ out[axis] = start[axis] + Math.sign(d) * len2 / k;
4891
5600
  const from = { ...out, [axis]: fromCentre ? 2 * start[axis] - out[axis] : start[axis] };
4892
5601
  guides.lines.push({ kind: "size", x1: from.x, y1: from.y, x2: out.x, y2: out.y });
4893
- guides.labels.push({ kind: "size", x: (from.x + out.x) / 2, y: (from.y + out.y) / 2, text: `${axis === "x" ? "W" : "H"} ${Math.round(len * 100) / 100}` });
5602
+ guides.labels.push({ kind: "size", x: (from.x + out.x) / 2, y: (from.y + out.y) / 2, text: `${axis === "x" ? "W" : "H"} ${Math.round(len2 * 100) / 100}` });
4894
5603
  }
4895
5604
  this.setGuides(guides);
4896
5605
  return out;
@@ -4935,6 +5644,20 @@ var VectorEditorController = (_class3 = class {
4935
5644
  return;
4936
5645
  }
4937
5646
  return this.commit("Bend Segment", drag.before);
5647
+ case "corner": {
5648
+ if (drag.moved) return this.commit("Corner Radius", drag.before);
5649
+ if (drag.alt) {
5650
+ const kind = nextVectorCornerKind(drag.widget.kind);
5651
+ this.doc = this.updateAnchors(drag.base, drag.refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _119 => _119.corner, 'optionalAccess', _120 => _120.radius]), () => ( 0)), kind));
5652
+ return this.commit("Corner Kind", drag.before);
5653
+ }
5654
+ const ref = drag.widget.ref;
5655
+ const has = this.anchors.some((a) => sameRef(a, ref));
5656
+ this.editingId = ref.nodeId;
5657
+ this.anchors = drag.shift ? has ? this.anchors.filter((a) => !sameRef(a, ref)) : [...this.anchors, ref] : [ref];
5658
+ this.notify();
5659
+ return;
5660
+ }
4938
5661
  case "stroke": {
4939
5662
  this.notify();
4940
5663
  if (drag.tool === "knife") this.knife(drag.points);
@@ -4960,7 +5683,7 @@ var VectorEditorController = (_class3 = class {
4960
5683
  }
4961
5684
  case "pencil": {
4962
5685
  const node = this.getNode(drag.nodeId);
4963
- if (_optionalChain([node, 'optionalAccess', _101 => _101.type]) !== "path" || drag.points.length < 2) {
5686
+ if (_optionalChain([node, 'optionalAccess', _121 => _121.type]) !== "path" || drag.points.length < 2) {
4964
5687
  this.doc = drag.before.doc;
4965
5688
  this.notify(true);
4966
5689
  return;
@@ -5034,7 +5757,7 @@ var VectorEditorController = (_class3 = class {
5034
5757
  this.convertAnchors(a.in || a.out ? "corner" : "smooth", [anchor]);
5035
5758
  return;
5036
5759
  }
5037
- const id = _nullishCoalesce(_optionalChain([this, 'access', _102 => _102.hover, 'optionalAccess', _103 => _103.segment, 'optionalAccess', _104 => _104.nodeId]), () => ( this.editingId));
5760
+ const id = _nullishCoalesce(_optionalChain([this, 'access', _122 => _122.hover, 'optionalAccess', _123 => _123.segment, 'optionalAccess', _124 => _124.nodeId]), () => ( this.editingId));
5038
5761
  if (id) {
5039
5762
  const node = this.getNode(id);
5040
5763
  const seg = node ? nearestSegment(node, this.toLocal(id, p)) : null;
@@ -5114,7 +5837,7 @@ var VectorEditorController = (_class3 = class {
5114
5837
  this.finishPath();
5115
5838
  return true;
5116
5839
  }
5117
- if (this.tool === "select" && this.selection.length === 1 && _optionalChain([this, 'access', _105 => _105.getNode, 'call', _106 => _106(this.selection[0]), 'optionalAccess', _107 => _107.type]) === "path") {
5840
+ if (this.tool === "select" && this.selection.length === 1 && _optionalChain([this, 'access', _125 => _125.getNode, 'call', _126 => _126(this.selection[0]), 'optionalAccess', _127 => _127.type]) === "path") {
5118
5841
  this.tool = "direct";
5119
5842
  this.editingId = this.selection[0];
5120
5843
  this.notify();
@@ -5179,9 +5902,9 @@ function inkShapeSubpath(shape) {
5179
5902
  function constrain45(origin, p) {
5180
5903
  const dx = p.x - origin.x;
5181
5904
  const dy = p.y - origin.y;
5182
- const len = Math.hypot(dx, dy);
5905
+ const len2 = Math.hypot(dx, dy);
5183
5906
  const a = Math.round(Math.atan2(dy, dx) / (Math.PI / 4)) * (Math.PI / 4);
5184
- return { x: origin.x + Math.cos(a) * len, y: origin.y + Math.sin(a) * len };
5907
+ return { x: origin.x + Math.cos(a) * len2, y: origin.y + Math.sin(a) * len2 };
5185
5908
  }
5186
5909
  function scaleFor(handle, b, p, mods = {}) {
5187
5910
  const west = handle.includes("w");
@@ -5218,17 +5941,19 @@ function renderNode(node, defs, idPrefix) {
5218
5941
  const stroke = vectorPaintToSvg(node.stroke, `${idPrefix}stroke-${node.id}`);
5219
5942
  if (fill.gradient) defs.push(fill.gradient);
5220
5943
  if (stroke.gradient) defs.push(stroke.gradient);
5944
+ const strokeParts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${idPrefix}clip-${node.id}`) : void 0;
5221
5945
  return {
5222
5946
  kind: "path",
5223
5947
  id: node.id,
5224
5948
  ...node.transform ? { transform: fmtMatrix(node.transform) } : {},
5949
+ ...strokeParts ? { strokeParts } : {},
5225
5950
  d: vectorPathData(node),
5226
5951
  fill: fill.value,
5227
5952
  stroke: stroke.value,
5228
5953
  strokeWidth: _nullishCoalesce(node.strokeWidth, () => ( 1)),
5229
5954
  ...node.strokeLinecap ? { strokeLinecap: node.strokeLinecap } : {},
5230
5955
  ...node.strokeLinejoin ? { strokeLinejoin: node.strokeLinejoin } : {},
5231
- ..._optionalChain([node, 'access', _108 => _108.strokeDasharray, 'optionalAccess', _109 => _109.length]) ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
5956
+ ..._optionalChain([node, 'access', _128 => _128.strokeDasharray, 'optionalAccess', _129 => _129.length]) ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
5232
5957
  fillRule: _nullishCoalesce(node.fillRule, () => ( "nonzero")),
5233
5958
  opacity: _nullishCoalesce(node.opacity, () => ( 1))
5234
5959
  };
@@ -5237,9 +5962,9 @@ function getVectorSceneRenderData(state, options = {}) {
5237
5962
  const defs = [];
5238
5963
  const prefix = _nullishCoalesce(options.idPrefix, () => ( "ve-"));
5239
5964
  const nodes = state.document.nodes.map((n) => renderNode(n, defs, prefix)).filter((n) => !!n);
5240
- const v = state.viewport;
5965
+ const v2 = state.viewport;
5241
5966
  return {
5242
- transform: `translate(${fmt(v.x)} ${fmt(v.y)}) scale(${v.zoom})`,
5967
+ transform: `translate(${fmt(v2.x)} ${fmt(v2.y)}) scale(${v2.zoom})`,
5243
5968
  width: state.document.width,
5244
5969
  height: state.document.height,
5245
5970
  ...state.document.background ? { background: state.document.background } : {},
@@ -5249,8 +5974,8 @@ function getVectorSceneRenderData(state, options = {}) {
5249
5974
  }
5250
5975
  var toScreen = (state, p) => ({ x: p.x * state.viewport.zoom + state.viewport.x, y: p.y * state.viewport.zoom + state.viewport.y });
5251
5976
  function screenOutline(state, node) {
5252
- const v = state.viewport;
5253
- const view = [v.zoom, 0, 0, v.zoom, v.x, v.y];
5977
+ const v2 = state.viewport;
5978
+ const view = [v2.zoom, 0, 0, v2.zoom, v2.x, v2.y];
5254
5979
  const parent = _nullishCoalesce(vectorParentMatrix(state.document.nodes, node.id), () => ( IDENTITY_MATRIX));
5255
5980
  return collectWithMatrix(node, parent).map(({ path, parent: m }) => _chunkBGNPT5PEcjs.bezierPathToSvg.call(void 0, _chunkBGNPT5PEcjs.transformBezierPath.call(void 0, vectorPathWorldBezier(path, m), view), { precision: 2 })).join(" ");
5256
5981
  }
@@ -5277,9 +6002,9 @@ function getVectorOverlayRenderData(state) {
5277
6002
  };
5278
6003
  for (const id of state.selection) outline(id, true);
5279
6004
  if (state.editingId) outline(state.editingId, true);
5280
- if (_optionalChain([state, 'access', _110 => _110.hover, 'optionalAccess', _111 => _111.nodeId]) && !state.interaction) outline(state.hover.nodeId, false);
6005
+ if (_optionalChain([state, 'access', _130 => _130.hover, 'optionalAccess', _131 => _131.nodeId]) && !state.interaction) outline(state.hover.nodeId, false);
5281
6006
  let selectionBox = null;
5282
- if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access', _112 => _112.interaction, 'optionalAccess', _113 => _113.type]) !== "marquee") {
6007
+ if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access', _132 => _132.interaction, 'optionalAccess', _133 => _133.type]) !== "marquee") {
5283
6008
  const g = getVectorSelectionBoxGeometry(state.selectionFrame, state.viewport);
5284
6009
  selectionBox = { ...g.box, angle: g.angle, transform: g.transform, corners: g.corners, handles: g.handles };
5285
6010
  }
@@ -5289,9 +6014,9 @@ function getVectorOverlayRenderData(state) {
5289
6014
  const a = getAnchorWorld(doc, ref);
5290
6015
  const s = toScreen(state, a);
5291
6016
  const closes = !!state.pen && state.pen.nodeId === ref.nodeId && state.pen.subpath === ref.subpath && ref.index === 0;
5292
- return { ref, x: s.x, y: s.y, type: _nullishCoalesce(a.type, () => ( inferAnchorType(a))), selected: selected(ref), hovered: sameAnchor(_optionalChain([state, 'access', _114 => _114.hover, 'optionalAccess', _115 => _115.anchor]), ref), closes };
6017
+ return { ref, x: s.x, y: s.y, type: _nullishCoalesce(a.type, () => ( inferAnchorType(a))), selected: selected(ref), hovered: sameAnchor(_optionalChain([state, 'access', _134 => _134.hover, 'optionalAccess', _135 => _135.anchor]), ref), closes };
5293
6018
  });
5294
- if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access', _116 => _116.hover, 'optionalAccess', _117 => _117.anchor])) {
6019
+ if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access', _136 => _136.hover, 'optionalAccess', _137 => _137.anchor])) {
5295
6020
  const a = getAnchorWorld(doc, state.hover.anchor);
5296
6021
  if (a) {
5297
6022
  const s = toScreen(state, a);
@@ -5300,15 +6025,15 @@ function getVectorOverlayRenderData(state) {
5300
6025
  }
5301
6026
  const handles = getVisibleHandles(state).flatMap((ref) => {
5302
6027
  const a = getAnchorWorld(doc, ref);
5303
- const h = _optionalChain([a, 'optionalAccess', _118 => _118[ref.handle]]);
6028
+ const h = _optionalChain([a, 'optionalAccess', _138 => _138[ref.handle]]);
5304
6029
  if (!a || !h) return [];
5305
6030
  const s = toScreen(state, h);
5306
6031
  const sa = toScreen(state, a);
5307
- const hovered = !!_optionalChain([state, 'access', _119 => _119.hover, 'optionalAccess', _120 => _120.handle]) && sameAnchor(state.hover.handle, ref) && state.hover.handle.handle === ref.handle;
6032
+ const hovered = !!_optionalChain([state, 'access', _139 => _139.hover, 'optionalAccess', _140 => _140.handle]) && sameAnchor(state.hover.handle, ref) && state.hover.handle.handle === ref.handle;
5308
6033
  return [{ ref, x: s.x, y: s.y, anchorX: sa.x, anchorY: sa.y, hovered }];
5309
6034
  });
5310
6035
  let marquee = null;
5311
- if (_optionalChain([state, 'access', _121 => _121.interaction, 'optionalAccess', _122 => _122.type]) === "marquee") {
6036
+ if (_optionalChain([state, 'access', _141 => _141.interaction, 'optionalAccess', _142 => _142.type]) === "marquee") {
5312
6037
  const b = state.interaction.bounds;
5313
6038
  const tl = toScreen(state, b);
5314
6039
  marquee = { x: tl.x, y: tl.y, width: b.width * z, height: b.height * z };
@@ -5316,7 +6041,7 @@ function getVectorOverlayRenderData(state) {
5316
6041
  let penPreview = null;
5317
6042
  if (state.tool === "pen" && state.pen && state.pointer && !state.interaction) {
5318
6043
  const node = findVectorNode(doc.nodes, state.pen.nodeId);
5319
- const sp = _optionalChain([node, 'optionalAccess', _123 => _123.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
6044
+ const sp = _optionalChain([node, 'optionalAccess', _143 => _143.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
5320
6045
  const last = sp ? getAnchorWorld(doc, { nodeId: state.pen.nodeId, subpath: state.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
5321
6046
  if (last) {
5322
6047
  const a = toScreen(state, last);
@@ -5329,9 +6054,9 @@ function getVectorOverlayRenderData(state) {
5329
6054
  const artboard = { x: origin.x, y: origin.y, width: doc.width * z, height: doc.height * z };
5330
6055
  let status = null;
5331
6056
  const it = state.interaction;
5332
- if (_optionalChain([it, 'optionalAccess', _124 => _124.type]) === "rotate") status = `${Math.round(it.angle)}\xB0`;
5333
- else if (_optionalChain([it, 'optionalAccess', _125 => _125.type]) === "move" || _optionalChain([it, 'optionalAccess', _126 => _126.type]) === "anchors") status = `\u0394 ${fmt(it.dx)}, ${fmt(it.dy)}`;
5334
- else if (_optionalChain([it, 'optionalAccess', _127 => _127.type]) === "scale" || _optionalChain([it, 'optionalAccess', _128 => _128.type]) === "draw") {
6057
+ if (_optionalChain([it, 'optionalAccess', _144 => _144.type]) === "rotate") status = `${Math.round(it.angle)}\xB0`;
6058
+ else if (_optionalChain([it, 'optionalAccess', _145 => _145.type]) === "move" || _optionalChain([it, 'optionalAccess', _146 => _146.type]) === "anchors") status = `\u0394 ${fmt(it.dx)}, ${fmt(it.dy)}`;
6059
+ else if (_optionalChain([it, 'optionalAccess', _147 => _147.type]) === "scale" || _optionalChain([it, 'optionalAccess', _148 => _148.type]) === "draw") {
5335
6060
  const b = it.type === "draw" ? findVectorNode(doc.nodes, it.nodeId) ? vectorNodeBounds(findVectorNode(doc.nodes, it.nodeId)) : null : state.selectionFrame;
5336
6061
  if (b) status = `W ${fmt(b.width)} \xD7 H ${fmt(b.height)}`;
5337
6062
  }
@@ -5348,19 +6073,25 @@ function getVectorOverlayRenderData(state) {
5348
6073
  }
5349
6074
  }
5350
6075
  let toolStroke = null;
5351
- if (_optionalChain([it, 'optionalAccess', _129 => _129.type]) === "stroke") {
6076
+ if (_optionalChain([it, 'optionalAccess', _149 => _149.type]) === "stroke") {
5352
6077
  const pts = it.points.map((q) => toScreen(state, q));
5353
6078
  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)}` : "");
5354
6079
  toolStroke = { tool: it.tool, d, width: fmt(it.width * z) };
5355
6080
  }
5356
6081
  let pencilString = null;
5357
- if (_optionalChain([it, 'optionalAccess', _130 => _130.type]) === "draw" && it.string) {
6082
+ if (_optionalChain([it, 'optionalAccess', _150 => _150.type]) === "draw" && it.string) {
5358
6083
  const a = toScreen(state, it.string[0]);
5359
6084
  const b = toScreen(state, it.string[1]);
5360
6085
  pencilString = { x1: fmt(a.x), y1: fmt(a.y), x2: fmt(b.x), y2: fmt(b.y) };
5361
6086
  }
5362
- const cutPoint = state.tool === "scissors" && _optionalChain([state, 'access', _131 => _131.hover, 'optionalAccess', _132 => _132.segment]) && !it ? (({ x, y }) => ({ x: fmt(x), y: fmt(y) }))(toScreen(state, state.hover.segment.point)) : null;
5363
- return { outlines, selectionBox, anchors, handles, marquee, penPreview, artboard, guides, status, toolStroke, pencilString, cutPoint };
6087
+ const dragged = _optionalChain([it, 'optionalAccess', _151 => _151.type]) === "corner" ? it.ref : null;
6088
+ const cornerWidgets = getVectorCornerWidgets(state).map((w) => {
6089
+ const s = toScreen(state, w);
6090
+ return { ref: w.ref, x: fmt(s.x), y: fmt(s.y), radius: w.radius, kind: w.kind, hovered: sameAnchor(_optionalChain([state, 'access', _152 => _152.hover, 'optionalAccess', _153 => _153.corner]), w.ref), active: !!dragged };
6091
+ });
6092
+ if (_optionalChain([it, 'optionalAccess', _154 => _154.type]) === "corner") status = `R ${fmt(it.radius)}`;
6093
+ const cutPoint = state.tool === "scissors" && _optionalChain([state, 'access', _155 => _155.hover, 'optionalAccess', _156 => _156.segment]) && !it ? (({ x, y }) => ({ x: fmt(x), y: fmt(y) }))(toScreen(state, state.hover.segment.point)) : null;
6094
+ return { outlines, selectionBox, anchors, handles, marquee, penPreview, artboard, guides, status, toolStroke, pencilString, cutPoint, cornerWidgets };
5364
6095
  }
5365
6096
  var VECTOR_OVERLAY_THEME = {
5366
6097
  accent: "#0ea5e9",
@@ -5372,7 +6103,9 @@ var VECTOR_OVERLAY_THEME = {
5372
6103
  eraserFill: "rgba(239, 68, 68, 0.25)",
5373
6104
  transformHandleSize: 8,
5374
6105
  anchorSize: 7,
5375
- handleRadius: 3.5
6106
+ handleRadius: 3.5,
6107
+ /** Radius of a live corner widget (a ring with a dot). */
6108
+ cornerWidgetRadius: 4
5376
6109
  };
5377
6110
  function toVectorModifiers(event) {
5378
6111
  return { shift: !!event.shiftKey, alt: !!event.altKey, mod: !!(event.metaKey || event.ctrlKey) };
@@ -5388,7 +6121,7 @@ function isVectorTextTarget(target) {
5388
6121
  if (!el || typeof el !== "object") return false;
5389
6122
  const tag = (_nullishCoalesce(el.tagName, () => ( ""))).toUpperCase();
5390
6123
  if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
5391
- return !!el.isContentEditable || _optionalChain([el, 'access', _133 => _133.getAttribute, 'optionalCall', _134 => _134("contenteditable")]) === "true";
6124
+ return !!el.isContentEditable || _optionalChain([el, 'access', _157 => _157.getAttribute, 'optionalCall', _158 => _158("contenteditable")]) === "true";
5392
6125
  }
5393
6126
  function getVectorSelectionStyle(state) {
5394
6127
  const paths = [];
@@ -5396,18 +6129,68 @@ function getVectorSelectionStyle(state) {
5396
6129
  const node = findVectorNode(state.document.nodes, id);
5397
6130
  if (node) paths.push(...collect(node));
5398
6131
  }
6132
+ const anchorMode = state.tool === "direct" && !!_optionalChain([state, 'access', _159 => _159.selectedAnchors, 'optionalAccess', _160 => _160.length]);
6133
+ if (anchorMode) {
6134
+ for (const r of state.selectedAnchors) {
6135
+ if (paths.some((p) => p.id === r.nodeId)) continue;
6136
+ const node = findVectorNode(state.document.nodes, r.nodeId);
6137
+ if (_optionalChain([node, 'optionalAccess', _161 => _161.type]) === "path") paths.push(node);
6138
+ }
6139
+ }
5399
6140
  if (!paths.length) return null;
5400
6141
  const first = paths[0];
5401
6142
  const same = (get) => paths.every((p) => JSON.stringify(_nullishCoalesce(get(p), () => ( null))) === JSON.stringify(_nullishCoalesce(get(first), () => ( null))));
6143
+ const corners2 = [];
6144
+ for (const p of paths) {
6145
+ for (const c of vectorCorners(p.subpaths)) {
6146
+ if (anchorMode && !state.selectedAnchors.some((r) => r.nodeId === p.id && r.subpath === c.subpath && r.index === c.index)) continue;
6147
+ const corner = p.subpaths[c.subpath].anchors[c.index].corner;
6148
+ corners2.push({ radius: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _162 => _162.radius]), () => ( 0)), kind: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _163 => _163.kind]), () => ( "round")) });
6149
+ }
6150
+ }
6151
+ const c0 = _nullishCoalesce(corners2[0], () => ( { radius: 0, kind: "round" }));
5402
6152
  return {
5403
6153
  fill: _nullishCoalesce(first.fill, () => ( null)),
5404
6154
  stroke: _nullishCoalesce(first.stroke, () => ( null)),
5405
6155
  strokeWidth: _nullishCoalesce(first.strokeWidth, () => ( 1)),
5406
6156
  opacity: _nullishCoalesce(first.opacity, () => ( 1)),
5407
- mixed: { fill: !same((p) => p.fill), stroke: !same((p) => p.stroke), strokeWidth: !same((p) => _nullishCoalesce(p.strokeWidth, () => ( 1))), opacity: !same((p) => _nullishCoalesce(p.opacity, () => ( 1))) },
6157
+ strokeAlign: _nullishCoalesce(first.strokeAlign, () => ( "center")),
6158
+ startArrow: _nullishCoalesce(first.startArrow, () => ( null)),
6159
+ endArrow: _nullishCoalesce(first.endArrow, () => ( null)),
6160
+ arrowAlign: _nullishCoalesce(first.arrowAlign, () => ( "extend")),
6161
+ startArrowScale: _nullishCoalesce(first.startArrowScale, () => ( 1)),
6162
+ endArrowScale: _nullishCoalesce(first.endArrowScale, () => ( 1)),
6163
+ cornerRadius: c0.radius,
6164
+ cornerKind: c0.kind,
6165
+ corners: corners2.length,
6166
+ hasOpenPath: paths.some((p) => p.subpaths.some((sp) => !sp.closed && sp.anchors.length > 1)),
6167
+ mixed: {
6168
+ fill: !same((p) => p.fill),
6169
+ stroke: !same((p) => p.stroke),
6170
+ strokeWidth: !same((p) => _nullishCoalesce(p.strokeWidth, () => ( 1))),
6171
+ opacity: !same((p) => _nullishCoalesce(p.opacity, () => ( 1))),
6172
+ strokeAlign: !same((p) => _nullishCoalesce(p.strokeAlign, () => ( "center"))),
6173
+ startArrow: !same((p) => _nullishCoalesce(p.startArrow, () => ( null))),
6174
+ endArrow: !same((p) => _nullishCoalesce(p.endArrow, () => ( null))),
6175
+ arrowAlign: !same((p) => _nullishCoalesce(p.arrowAlign, () => ( "extend"))),
6176
+ startArrowScale: !same((p) => _nullishCoalesce(p.startArrowScale, () => ( 1))),
6177
+ endArrowScale: !same((p) => _nullishCoalesce(p.endArrowScale, () => ( 1))),
6178
+ cornerRadius: corners2.some((c) => c.radius !== c0.radius),
6179
+ cornerKind: corners2.some((c) => c.kind !== c0.kind)
6180
+ },
5408
6181
  pathIds: paths.map((p) => p.id)
5409
6182
  };
5410
6183
  }
6184
+ var VECTOR_STROKE_ALIGN_LABELS = { center: "Center", inside: "Inside", outside: "Outside" };
6185
+ var VECTOR_CORNER_KIND_LABELS = { round: "Round", inverted: "Inverted", chamfer: "Chamfer" };
6186
+ var VECTOR_ARROWHEAD_LABELS = {
6187
+ triangle: "Triangle",
6188
+ arrow: "Arrow",
6189
+ open: "Open arrow",
6190
+ circle: "Circle",
6191
+ square: "Square",
6192
+ bar: "Bar"
6193
+ };
5411
6194
  function vectorPaintToHex(paint) {
5412
6195
  if (typeof paint !== "string") return null;
5413
6196
  const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(paint.trim());
@@ -5420,7 +6203,8 @@ var VECTOR_PATH_ACTIONS = [
5420
6203
  { id: "average", label: "\u22B9", title: "Average Points", shortcut: "Alt+J", enabled: (_s, p) => p > 1, run: (c) => c.averageAnchors("both") },
5421
6204
  { id: "cut", label: "\u2702\xB7", title: "Cut at Points", enabled: (_s, p) => p > 0, run: (c) => c.cutAtAnchors() },
5422
6205
  { id: "clean", label: "\u2301", title: "Remove Redundant Points", enabled: (s, p) => s + p > 0, run: (c) => c.removeRedundantPoints() },
5423
- { id: "smooth", label: "\u223F", title: "Smooth", enabled: (s, p) => s + p > 0, run: (c) => c.smoothPaths() }
6206
+ { id: "smooth", label: "\u223F", title: "Smooth", enabled: (s, p) => s + p > 0, run: (c) => c.smoothPaths() },
6207
+ { id: "outline", label: "\u22A1", title: "Outline Stroke", enabled: (s) => s > 0, run: (c) => c.outlineStroke() }
5424
6208
  ];
5425
6209
  var VECTOR_EDIT_TOGGLES = [
5426
6210
  { key: "keepShapeOnDelete", label: "Keep", title: "Keep shape when deleting points" },
@@ -5458,7 +6242,13 @@ var STYLE_PROPS = {
5458
6242
  strokeLinecap: { type: "string", enum: ["butt", "round", "square"] },
5459
6243
  strokeLinejoin: { type: "string", enum: ["miter", "round", "bevel"] },
5460
6244
  strokeDasharray: { type: "array", items: { type: "number" } },
5461
- fillRule: { type: "string", enum: ["nonzero", "evenodd"] }
6245
+ fillRule: { type: "string", enum: ["nonzero", "evenodd"] },
6246
+ strokeAlign: { type: "string", enum: ["center", "inside", "outside"], description: "Where the stroke sits on the outline (closed shapes only; open paths stroke centred)." },
6247
+ startArrow: { type: ["string", "null"], enum: [...VECTOR_ARROWHEADS, null], description: "Head at the start of an open path, or null for none." },
6248
+ endArrow: { type: ["string", "null"], enum: [...VECTOR_ARROWHEADS, null], description: "Head at the end of an open path, or null for none." },
6249
+ arrowAlign: { type: "string", enum: ["extend", "tip"], description: "`extend`: the tip sits past the end point; `tip`: on it, the stroke shortened under the head." },
6250
+ startArrowScale: { type: "number", exclusiveMinimum: 0, description: "Start head size as a multiple of the stroke width (default 1)." },
6251
+ endArrowScale: { type: "number", exclusiveMinimum: 0, description: "End head size as a multiple of the stroke width (default 1)." }
5462
6252
  };
5463
6253
  var pickStyle = (params) => {
5464
6254
  const out = {};
@@ -5474,17 +6264,17 @@ function createVectorCommands(controller, options = {}) {
5474
6264
  const base = { group, ...options.scope ? { scope: options.scope } : {} };
5475
6265
  const hasSelection = () => controller.getState().selection.length ? true : "Nothing is selected.";
5476
6266
  const ids = (params) => {
5477
- const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _135 => _135.ids]), () => ( controller.getState().selection));
6267
+ const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _164 => _164.ids]), () => ( controller.getState().selection));
5478
6268
  for (const id of list2) if (!controller.getNode(id)) fail(`There is no node "${id}".`);
5479
6269
  if (!list2.length) fail("Nothing is selected; pass ids.");
5480
6270
  return list2;
5481
6271
  };
5482
- const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess', _136 => _136.ids, 'optionalAccess', _137 => _137.length]) ? true : hasSelection();
6272
+ const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess', _165 => _165.ids, 'optionalAccess', _166 => _166.length]) ? true : hasSelection();
5483
6273
  const anchorsOf = (refs) => {
5484
6274
  if (!refs) return [...controller.getState().selectedAnchors];
5485
6275
  for (const r of refs) {
5486
6276
  const node = controller.getNode(r.nodeId);
5487
- if (_optionalChain([node, 'optionalAccess', _138 => _138.type]) !== "path" || !_optionalChain([node, 'access', _139 => _139.subpaths, 'access', _140 => _140[r.subpath], 'optionalAccess', _141 => _141.anchors, 'access', _142 => _142[r.index]])) fail(`There is no anchor ${r.index} on subpath ${r.subpath} of "${r.nodeId}".`);
6277
+ if (_optionalChain([node, 'optionalAccess', _167 => _167.type]) !== "path" || !_optionalChain([node, 'access', _168 => _168.subpaths, 'access', _169 => _169[r.subpath], 'optionalAccess', _170 => _170.anchors, 'access', _171 => _171[r.index]])) fail(`There is no anchor ${r.index} on subpath ${r.subpath} of "${r.nodeId}".`);
5488
6278
  }
5489
6279
  return refs;
5490
6280
  };
@@ -5551,7 +6341,8 @@ function createVectorCommands(controller, options = {}) {
5551
6341
  y: { type: "number" },
5552
6342
  width: { type: "number", description: "For a line: the x offset of its end." },
5553
6343
  height: { type: "number", description: "For a line: the y offset of its end." },
5554
- cornerRadius: { type: "number", minimum: 0 },
6344
+ cornerRadius: { type: "number", minimum: 0, description: "Live corner radius of a rect, polygon or star." },
6345
+ cornerKind: { type: "string", enum: VECTOR_CORNER_KINDS },
5555
6346
  sides: { type: "integer", minimum: 3 },
5556
6347
  points: { type: "integer", minimum: 3 },
5557
6348
  innerRatio: { type: "number", minimum: 0, maximum: 1 },
@@ -5574,7 +6365,7 @@ function createVectorCommands(controller, options = {}) {
5574
6365
  run: (_ctx, params) => {
5575
6366
  const id = controller.addPath(params.d, { ...pickStyle(params), ...params.name ? { name: params.name } : {} });
5576
6367
  const node = controller.getNode(id);
5577
- if (_optionalChain([node, 'optionalAccess', _143 => _143.type]) === "path" && !node.subpaths.length) {
6368
+ if (_optionalChain([node, 'optionalAccess', _172 => _172.type]) === "path" && !node.subpaths.length) {
5578
6369
  controller.undo();
5579
6370
  fail("The path data has no points.");
5580
6371
  }
@@ -5597,6 +6388,38 @@ function createVectorCommands(controller, options = {}) {
5597
6388
  return { ok: true };
5598
6389
  }
5599
6390
  },
6391
+ {
6392
+ ...base,
6393
+ id: `${prefix}setCorners`,
6394
+ label: "Corner Radius\u2026",
6395
+ keywords: ["round", "rounded", "corners", "radius", "fillet", "chamfer", "live corners"],
6396
+ description: "Sets the live corner radius (and optionally `kind`: round, inverted or chamfer) of `anchors`, else the selected points (direct tool), else every corner of `ids` / the selection. Radius 0 makes corners sharp; radii shrink to fit short sides. The anchors stay editable.",
6397
+ params: { type: "object", properties: { radius: { type: "number", minimum: 0 }, kind: { type: "string", enum: VECTOR_CORNER_KINDS }, ids: IDS, anchors: ANCHORS } },
6398
+ undoable: true,
6399
+ run: (_ctx, params) => {
6400
+ const refs = _optionalChain([params, 'optionalAccess', _173 => _173.anchors]) ? anchorsOf(params.anchors) : void 0;
6401
+ const target = { refs, ids: _optionalChain([params, 'optionalAccess', _174 => _174.ids]) ? ids(params) : void 0 };
6402
+ if (_optionalChain([params, 'optionalAccess', _175 => _175.radius]) !== void 0) check(controller.setCornerRadius(params.radius, { ...target, kind: params.kind }));
6403
+ else if (_optionalChain([params, 'optionalAccess', _176 => _176.kind])) check(controller.setCornerKind(params.kind, target));
6404
+ else fail("Pass `radius` and / or `kind`.");
6405
+ return { ok: true };
6406
+ }
6407
+ },
6408
+ {
6409
+ ...base,
6410
+ id: `${prefix}outlineStroke`,
6411
+ label: "Outline Stroke",
6412
+ keywords: ["expand", "stroke to path", "outline", "convert stroke"],
6413
+ description: "Turns the stroke of paths into filled shapes of the area it paints (alignment, caps, joins, dashes and arrowheads included). Returns the new ids.",
6414
+ params: { type: "object", properties: { ids: IDS } },
6415
+ enabled: enabledFor,
6416
+ undoable: true,
6417
+ run: (_ctx, params) => {
6418
+ const created = controller.outlineStroke({ ids: _optionalChain([params, 'optionalAccess', _177 => _177.ids]) ? ids(params) : void 0 });
6419
+ if (!created.length) fail("No selected path has a stroke.");
6420
+ return { ids: created };
6421
+ }
6422
+ },
5600
6423
  {
5601
6424
  ...base,
5602
6425
  id: `${prefix}transform`,
@@ -5707,7 +6530,7 @@ function createVectorCommands(controller, options = {}) {
5707
6530
  enabled: enabledFor,
5708
6531
  undoable: true,
5709
6532
  run: (_ctx, params) => {
5710
- const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess', _144 => _144.name]) });
6533
+ const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess', _178 => _178.name]) });
5711
6534
  if (!id) fail("Only nodes that share a parent can be grouped.");
5712
6535
  return { id };
5713
6536
  }
@@ -5735,7 +6558,7 @@ function createVectorCommands(controller, options = {}) {
5735
6558
  params: { type: "object", properties: { anchors: ANCHORS, keepShape: { type: "boolean" } } },
5736
6559
  undoable: true,
5737
6560
  run: (_ctx, params) => {
5738
- check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _145 => _145.anchors])), { keepShape: _optionalChain([params, 'optionalAccess', _146 => _146.keepShape]) }));
6561
+ check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _179 => _179.anchors])), { keepShape: _optionalChain([params, 'optionalAccess', _180 => _180.keepShape]) }));
5739
6562
  return { ok: true };
5740
6563
  }
5741
6564
  },
@@ -5790,7 +6613,7 @@ function createVectorCommands(controller, options = {}) {
5790
6613
  params: { type: "object", properties: { ids: IDS, tolerance: { type: "number", minimum: 0 } } },
5791
6614
  undoable: true,
5792
6615
  run: (_ctx, params) => {
5793
- check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess', _147 => _147.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _148 => _148.tolerance]) }));
6616
+ check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess', _181 => _181.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _182 => _182.tolerance]) }));
5794
6617
  return { selection: controller.getState().selection };
5795
6618
  }
5796
6619
  },
@@ -5804,7 +6627,7 @@ function createVectorCommands(controller, options = {}) {
5804
6627
  params: { type: "object", properties: { axis: { type: "string", enum: ["horizontal", "vertical", "both"] }, anchors: ANCHORS } },
5805
6628
  undoable: true,
5806
6629
  run: (_ctx, params) => {
5807
- check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _149 => _149.axis]), () => ( "both")), anchorsOf(_optionalChain([params, 'optionalAccess', _150 => _150.anchors]))));
6630
+ check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _183 => _183.axis]), () => ( "both")), anchorsOf(_optionalChain([params, 'optionalAccess', _184 => _184.anchors]))));
5808
6631
  return { ok: true };
5809
6632
  }
5810
6633
  },
@@ -5818,7 +6641,7 @@ function createVectorCommands(controller, options = {}) {
5818
6641
  enabled: enabledFor,
5819
6642
  undoable: true,
5820
6643
  run: (_ctx, params) => {
5821
- check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess', _151 => _151.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _152 => _152.tolerance]) }));
6644
+ check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess', _185 => _185.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _186 => _186.tolerance]) }));
5822
6645
  return { ok: true };
5823
6646
  }
5824
6647
  },
@@ -5831,7 +6654,7 @@ function createVectorCommands(controller, options = {}) {
5831
6654
  params: { type: "object", properties: { ids: IDS, amount: { type: "number", minimum: 0, maximum: 1 }, anchors: ANCHORS } },
5832
6655
  undoable: true,
5833
6656
  run: (_ctx, params) => {
5834
- check(controller.smoothPaths({ ids: _optionalChain([params, 'optionalAccess', _153 => _153.ids]) ? ids(params) : void 0, amount: _optionalChain([params, 'optionalAccess', _154 => _154.amount]), refs: _optionalChain([params, 'optionalAccess', _155 => _155.anchors]) }));
6657
+ check(controller.smoothPaths({ ids: _optionalChain([params, 'optionalAccess', _187 => _187.ids]) ? ids(params) : void 0, amount: _optionalChain([params, 'optionalAccess', _188 => _188.amount]), refs: _optionalChain([params, 'optionalAccess', _189 => _189.anchors]) }));
5835
6658
  return { ok: true };
5836
6659
  }
5837
6660
  },
@@ -5844,14 +6667,14 @@ function createVectorCommands(controller, options = {}) {
5844
6667
  params: { type: "object", properties: { nodeId: { type: "string" }, x: { type: "number" }, y: { type: "number" }, anchors: ANCHORS } },
5845
6668
  undoable: true,
5846
6669
  run: (_ctx, params) => {
5847
- if (_optionalChain([params, 'optionalAccess', _156 => _156.nodeId]) !== void 0) {
6670
+ if (_optionalChain([params, 'optionalAccess', _190 => _190.nodeId]) !== void 0) {
5848
6671
  if (!controller.getNode(params.nodeId)) fail(`There is no node "${params.nodeId}".`);
5849
6672
  if (params.x === void 0 || params.y === void 0) fail("Pass x and y with nodeId.");
5850
6673
  const created = controller.cutPathAt(params.nodeId, { x: params.x, y: params.y });
5851
6674
  if (!created) fail("Nothing to cut there (the end of an open path, or a locked path).");
5852
6675
  return { ids: created };
5853
6676
  }
5854
- check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _157 => _157.anchors]))));
6677
+ check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _191 => _191.anchors]))));
5855
6678
  return { ids: controller.getState().selection };
5856
6679
  }
5857
6680
  },
@@ -5901,7 +6724,7 @@ function createVectorCommands(controller, options = {}) {
5901
6724
  },
5902
6725
  run: (_ctx, params) => {
5903
6726
  const patch = {};
5904
- for (const key of ["keepShapeOnDelete", "curvatureMoves", "pencilSmoothing", "recognizeShapes", "eraserSize"]) if (_optionalChain([params, 'optionalAccess', _158 => _158[key]]) !== void 0) patch[key] = params[key];
6727
+ for (const key of ["keepShapeOnDelete", "curvatureMoves", "pencilSmoothing", "recognizeShapes", "eraserSize"]) if (_optionalChain([params, 'optionalAccess', _192 => _192[key]]) !== void 0) patch[key] = params[key];
5905
6728
  controller.setEditing(patch);
5906
6729
  return { editing: controller.getState().editing };
5907
6730
  }
@@ -5926,7 +6749,7 @@ function createVectorCommands(controller, options = {}) {
5926
6749
  keywords: ["download", "save", "markup"],
5927
6750
  description: "Returns the document as SVG markup.",
5928
6751
  params: { type: "object", properties: { precision: { type: "integer", minimum: 0, maximum: 8 } } },
5929
- run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess', _159 => _159.precision]) }) })
6752
+ run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess', _193 => _193.precision]) }) })
5930
6753
  },
5931
6754
  {
5932
6755
  ...base,
@@ -6058,5 +6881,39 @@ function createVectorCommands(controller, options = {}) {
6058
6881
 
6059
6882
 
6060
6883
 
6061
- exports.DEFAULT_VECTOR_PATH_EDIT = DEFAULT_VECTOR_PATH_EDIT; exports.DEFAULT_VECTOR_SNAP = DEFAULT_VECTOR_SNAP; exports.IDENTITY_MATRIX = IDENTITY_MATRIX; exports.KAPPA = KAPPA; exports.LazyMouse = LazyMouse; exports.VECTOR_CLIPBOARD_TYPE = VECTOR_CLIPBOARD_TYPE; exports.VECTOR_CONSTRUCTION_ANGLES = VECTOR_CONSTRUCTION_ANGLES; exports.VECTOR_EDIT_TOGGLES = VECTOR_EDIT_TOGGLES; exports.VECTOR_INK_MAX_CORNERS = VECTOR_INK_MAX_CORNERS; exports.VECTOR_OVERLAY_THEME = VECTOR_OVERLAY_THEME; exports.VECTOR_PATH_ACTIONS = VECTOR_PATH_ACTIONS; exports.VECTOR_PENCIL_STRING_PX = VECTOR_PENCIL_STRING_PX; exports.VECTOR_ROTATE_HANDLE_OFFSET = VECTOR_ROTATE_HANDLE_OFFSET; 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.VectorEditorController = VectorEditorController; exports.VectorSnapTargets = VectorSnapTargets; exports.applyMatrix = applyMatrix; exports.applyMatrixLinear = applyMatrixLinear; 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.createVectorCommands = createVectorCommands; exports.curvatureMoveAnchor = curvatureMoveAnchor; exports.cutSubpathAt = cutSubpathAt; exports.decomposeRigid = decomposeRigid; exports.ellipseSubpath = ellipseSubpath; exports.eraseFromFill = eraseFromFill; exports.eraseFromOutline = eraseFromOutline; exports.eraserRegion = eraserRegion; exports.findVectorNode = findVectorNode; exports.getAnchor = getAnchor; exports.getAnchorWorld = getAnchorWorld; exports.getEditablePathIds = getEditablePathIds; exports.getTransformHandlePositions = getTransformHandlePositions; exports.getVectorOverlayRenderData = getVectorOverlayRenderData; exports.getVectorSceneRenderData = getVectorSceneRenderData; exports.getVectorSelectionBoxGeometry = getVectorSelectionBoxGeometry; exports.getVectorSelectionFrame = getVectorSelectionFrame; exports.getVectorSelectionStyle = getVectorSelectionStyle; exports.getVisibleAnchors = getVisibleAnchors; exports.getVisibleHandles = getVisibleHandles; exports.hasPaint = hasPaint; exports.hitTestVectorNode = hitTestVectorNode; exports.hitTestVectorNodes = hitTestVectorNodes; exports.inferAnchorType = inferAnchorType; exports.insertAnchor = insertAnchor; exports.invertMatrix = invertMatrix; exports.isIdentityMatrix = isIdentityMatrix; exports.isVectorTextTarget = isVectorTextTarget; exports.joinSubpaths = joinSubpaths; exports.knifePath = knifePath; exports.lineSubpath = lineSubpath; exports.locateVectorNode = locateVectorNode; exports.matrixRotation = matrixRotation; exports.moveAnchorHandle = moveAnchorHandle; exports.multiplyMatrices = multiplyMatrices; exports.nearestSegment = nearestSegment; exports.nodeMatrix = nodeMatrix; exports.parseSvgTransform = parseSvgTransform; exports.parseVectorSvg = parseVectorSvg; exports.polygonSubpath = polygonSubpath; exports.polylineBounds = polylineBounds; exports.recognizeInkShape = recognizeInkShape; exports.rectSubpath = rectSubpath; exports.removeAnchorKeepingShape = removeAnchorKeepingShape; exports.removeAnchors = removeAnchors; exports.removeAnchorsKeepingShape = removeAnchorsKeepingShape; exports.removeRedundantPoints = removeRedundantPoints; exports.reverseSubpath = reverseSubpath; exports.rotateMatrix = rotateMatrix; exports.roundSubpaths = roundSubpaths; exports.scaleFor = scaleFor; exports.scaleMatrix = scaleMatrix; exports.simplifyPolyline = simplifyPolyline; exports.smoothSubpath = smoothSubpath; exports.splitIntoFaces = splitIntoFaces; exports.splitSubpathAt = splitSubpathAt; exports.starSubpath = starSubpath; exports.subpathPointAt = subpathPointAt; exports.subpathSegment = subpathSegment; exports.subpathSegmentCount = subpathSegmentCount; exports.subpathsToBezier = subpathsToBezier; exports.toVectorKeyInput = toVectorKeyInput; exports.toVectorModifiers = toVectorModifiers; exports.toVectorPointerInput = toVectorPointerInput; exports.transformVectorNode = transformVectorNode; exports.translateAnchor = translateAnchor; exports.translateMatrix = translateMatrix; exports.unionBounds = unionBounds; exports.vectorDocumentToSvg = vectorDocumentToSvg; 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.vectorPathWorldBezier = vectorPathWorldBezier; exports.vectorResizeCursor = vectorResizeCursor; exports.vectorToolLabel = vectorToolLabel; exports.vectorWorldMatrix = vectorWorldMatrix; exports.walkVectorNodes = walkVectorNodes;
6884
+
6885
+
6886
+
6887
+
6888
+
6889
+
6890
+
6891
+
6892
+
6893
+
6894
+
6895
+
6896
+
6897
+
6898
+
6899
+
6900
+
6901
+
6902
+
6903
+
6904
+
6905
+
6906
+
6907
+
6908
+
6909
+
6910
+
6911
+
6912
+
6913
+
6914
+
6915
+
6916
+
6917
+
6918
+ 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_INK_MAX_CORNERS = VECTOR_INK_MAX_CORNERS; exports.VECTOR_OVERLAY_THEME = VECTOR_OVERLAY_THEME; exports.VECTOR_PATH_ACTIONS = VECTOR_PATH_ACTIONS; exports.VECTOR_PENCIL_STRING_PX = VECTOR_PENCIL_STRING_PX; 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.VectorEditorController = VectorEditorController; exports.VectorSnapTargets = VectorSnapTargets; 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.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.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.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.invertMatrix = invertMatrix; exports.isClosedBezierShape = isClosedBezierShape; exports.isIdentityMatrix = isIdentityMatrix; exports.isVectorTextTarget = isVectorTextTarget; exports.joinSubpaths = joinSubpaths; exports.knifePath = knifePath; exports.lineSubpath = lineSubpath; exports.locateVectorNode = locateVectorNode; exports.matrixRotation = matrixRotation; exports.moveAnchorHandle = moveAnchorHandle; exports.multiplyMatrices = multiplyMatrices; exports.nearestSegment = nearestSegment; exports.nextVectorCornerKind = nextVectorCornerKind; exports.nodeMatrix = nodeMatrix; exports.parseSvgTransform = parseSvgTransform; exports.parseVectorSvg = parseVectorSvg; exports.polygonSubpath = polygonSubpath; exports.polylineBounds = polylineBounds; exports.recognizeInkShape = recognizeInkShape; exports.rectSubpath = rectSubpath; exports.removeAnchorKeepingShape = removeAnchorKeepingShape; exports.removeAnchors = removeAnchors; exports.removeAnchorsKeepingShape = removeAnchorsKeepingShape; exports.removeRedundantPoints = removeRedundantPoints; exports.reverseSubpath = reverseSubpath; exports.rotateMatrix = rotateMatrix; exports.roundSubpaths = roundSubpaths; exports.scaleFor = scaleFor; exports.scaleMatrix = scaleMatrix; exports.setAnchorCorner = setAnchorCorner; exports.setSubpathCorners = setSubpathCorners; exports.simplifyPolyline = simplifyPolyline; exports.smoothSubpath = smoothSubpath; exports.splitIntoFaces = splitIntoFaces; exports.splitSubpathAt = splitSubpathAt; exports.starSubpath = starSubpath; exports.subpathPointAt = subpathPointAt; exports.subpathSegment = subpathSegment; exports.subpathSegmentCount = subpathSegmentCount; exports.subpathsToBezier = subpathsToBezier; exports.toVectorKeyInput = toVectorKeyInput; exports.toVectorModifiers = toVectorModifiers; exports.toVectorPointerInput = toVectorPointerInput; exports.transformVectorNode = transformVectorNode; exports.translateAnchor = translateAnchor; exports.translateMatrix = translateMatrix; exports.unionBounds = unionBounds; exports.vectorCornerAt = vectorCornerAt; exports.vectorCorners = vectorCorners; exports.vectorCornersAttribute = vectorCornersAttribute; exports.vectorDocumentToSvg = vectorDocumentToSvg; 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.vectorPathWorldBezier = vectorPathWorldBezier; exports.vectorResizeCursor = vectorResizeCursor; exports.vectorStrokeOutline = vectorStrokeOutline; exports.vectorStrokeOutlinePath = vectorStrokeOutlinePath; exports.vectorStrokeParts = vectorStrokeParts; exports.vectorStrokeReach = vectorStrokeReach; exports.vectorStrokeSvgParts = vectorStrokeSvgParts; exports.vectorToolLabel = vectorToolLabel; exports.vectorWorldMatrix = vectorWorldMatrix; exports.walkVectorNodes = walkVectorNodes;
6062
6919
  //# sourceMappingURL=index.cjs.map