@pixerate/editor 0.42.0 → 0.43.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.
@@ -42,16 +42,24 @@ var _chunkQKLRAA4Tcjs = require('../chunk-QKLRAA4T.cjs');
42
42
  var _chunkVNYRI265cjs = require('../chunk-VNYRI265.cjs');
43
43
 
44
44
 
45
+
46
+
45
47
  var _chunkMR3Z4OQIcjs = require('../chunk-MR3Z4OQI.cjs');
46
48
 
47
49
 
48
50
  var _chunkXZ4L3R4Kcjs = require('../chunk-XZ4L3R4K.cjs');
49
51
 
50
52
 
53
+
54
+
51
55
  var _chunkA6PHCAGXcjs = require('../chunk-A6PHCAGX.cjs');
52
56
 
53
57
  // src/vector/types.ts
54
58
  var VECTOR_CORNER_KINDS = ["round", "inverted", "chamfer"];
59
+ var VECTOR_GRADIENT_TYPES = ["linear", "radial", "conic", "reflected", "diamond"];
60
+ var VECTOR_GRADIENT_SPREADS = ["pad", "reflect", "repeat"];
61
+ var VECTOR_GRADIENT_INTERPOLATIONS = ["srgb", "linear", "oklab"];
62
+ var VECTOR_STROKE_GRADIENT_MODES = ["within", "along", "across"];
55
63
  var VECTOR_ARROWHEADS = ["triangle", "arrow", "open", "circle", "square", "bar"];
56
64
  var VECTOR_BLEND_SPACINGS = ["steps", "distance", "smoothColor"];
57
65
  var VECTOR_TOOLS = [
@@ -70,20 +78,824 @@ var VECTOR_TOOLS = [
70
78
  "width",
71
79
  "shapeBuilder",
72
80
  "paintBucket",
73
- "blend"
81
+ "blend",
82
+ "gradient"
74
83
  ];
75
84
  var VECTOR_TRANSFORM_HANDLES = ["nw", "n", "ne", "e", "se", "s", "sw", "w", "rotate"];
76
85
  var VECTOR_PATHFINDER_OPERATIONS = ["divide", "trim", "merge", "crop", "outline", "minusBack"];
77
86
  var VECTOR_EFFECT_TYPES = ["trim", "roundCorners", "puckerBloat", "twist", "zigzag", "wiggle", "offset", "merge", "repeater"];
78
87
  var VECTOR_MERGE_MODES = ["merge", "add", "subtract", "intersect", "exclude"];
79
88
 
80
- // src/vector/corners.ts
81
- var EPS = 1e-9;
82
- var MIN_SINE = 1e-6;
89
+ // src/vector/gradient.ts
90
+ var clamp = (v2, lo, hi) => Math.min(hi, Math.max(lo, v2));
91
+ var IDENTITY = [1, 0, 0, 1, 0, 0];
92
+ var mul = (a, b) => [
93
+ b[0] * a[0] + b[2] * a[1],
94
+ b[1] * a[0] + b[3] * a[1],
95
+ b[0] * a[2] + b[2] * a[3],
96
+ b[1] * a[2] + b[3] * a[3],
97
+ b[0] * a[4] + b[2] * a[5] + b[4],
98
+ b[1] * a[4] + b[3] * a[5] + b[5]
99
+ ];
100
+ var apply = (m, p) => ({ x: m[0] * p.x + m[2] * p.y + m[4], y: m[1] * p.x + m[3] * p.y + m[5] });
101
+ var invert = (m) => {
102
+ const [a, b, c, d, e, f] = m;
103
+ const det = a * d - b * c;
104
+ if (!Number.isFinite(det) || Math.abs(det) < 1e-18) return null;
105
+ return [d / det, -b / det, -c / det, a / det, (c * f - d * e) / det, (b * e - a * f) / det];
106
+ };
107
+ var isIdentity = (m) => m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] === 0 && m[5] === 0;
83
108
  var sub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
84
109
  var add = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
85
- var mul = (a, k) => ({ x: a.x * k, y: a.y * k });
110
+ var scale = (a, k) => ({ x: a.x * k, y: a.y * k });
86
111
  var dot = (a, b) => a.x * b.x + a.y * b.y;
112
+ var perp = (v2) => ({ x: -v2.y, y: v2.x });
113
+ var finitePoint = (p) => !!p && typeof p === "object" && Number.isFinite(p.x) && Number.isFinite(p.y);
114
+ var fmt = (n, digits = 4) => {
115
+ const k = 10 ** digits;
116
+ const v2 = Math.round(n * k) / k;
117
+ return Object.is(v2, -0) ? "0" : String(v2);
118
+ };
119
+ var fmtMatrix = (m) => `matrix(${m.map((n) => fmt(n, 6)).join(" ")})`;
120
+ function isVectorGradient(paint) {
121
+ return !!paint && typeof paint === "object" && VECTOR_GRADIENT_TYPES.includes(paint.type) && Array.isArray(paint.stops);
122
+ }
123
+ function parseVectorColor(color) {
124
+ const hex = _chunkA6PHCAGXcjs.normalizeTokenColor.call(void 0, color);
125
+ if (hex === null) return null;
126
+ const c = _chunkA6PHCAGXcjs.hexToRgba.call(void 0, hex);
127
+ return [c.r, c.g, c.b, c.a];
128
+ }
129
+ var toLinear = (c) => c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
130
+ var toGamma = (c) => {
131
+ const v2 = clamp(c, 0, 1);
132
+ return v2 <= 31308e-7 ? 12.92 * v2 : 1.055 * v2 ** (1 / 2.4) - 0.055;
133
+ };
134
+ function intoSpace(space, [r, g, b]) {
135
+ if (space === "linear") return [toLinear(r), toLinear(g), toLinear(b)];
136
+ if (space === "oklab") {
137
+ const c = _chunkMR3Z4OQIcjs.rgbToOklab.call(void 0, r, g, b);
138
+ return [c.l, c.a, c.b];
139
+ }
140
+ return [r, g, b];
141
+ }
142
+ function outOfSpace(space, [x, y, z]) {
143
+ if (space === "linear") return [toGamma(x), toGamma(y), toGamma(z)];
144
+ if (space === "oklab") {
145
+ const c = _chunkMR3Z4OQIcjs.oklabToRgb.call(void 0, { l: x, a: y, b: z, alpha: 1 });
146
+ return [c.r, c.g, c.b];
147
+ }
148
+ return [clamp(x, 0, 1), clamp(y, 0, 1), clamp(z, 0, 1)];
149
+ }
150
+ var MID_MIN = 0.01;
151
+ var MID_MAX = 0.99;
152
+ var VECTOR_GRADIENT_MIN_STOPS = 2;
153
+ function gradientMidpointRemap(u, m) {
154
+ const mid = clamp(m, MID_MIN, MID_MAX);
155
+ if (Math.abs(mid - 0.5) < 1e-6) return u;
156
+ return u <= mid ? 0.5 * u / mid : 0.5 + 0.5 * (u - mid) / (1 - mid);
157
+ }
158
+ var hasMidpoint = (s) => s.midpoint !== void 0 && Number.isFinite(s.midpoint) && Math.abs(s.midpoint - 0.5) > 1e-3;
159
+ function sortedVectorGradientStops(g) {
160
+ return (_nullishCoalesce(g.stops, () => ( []))).filter((s) => s && Number.isFinite(s.offset) && typeof s.color === "string").map((s, i) => ({ s: { ...s, offset: clamp(s.offset, 0, 1) }, i })).sort((a, b) => a.s.offset - b.s.offset || a.i - b.i).map((e) => e.s);
161
+ }
162
+ function vectorGradientNeedsExpansion(g) {
163
+ return (_nullishCoalesce(g.interpolation, () => ( "srgb"))) !== "srgb" || (_nullishCoalesce(g.smoothness, () => ( 0))) > 0 || (_nullishCoalesce(g.stops, () => ( []))).some(hasMidpoint);
164
+ }
165
+ function vectorGradientRamp(g) {
166
+ const space = g.interpolation && VECTOR_GRADIENT_INTERPOLATIONS.includes(g.interpolation) ? g.interpolation : "srgb";
167
+ const smooth = clamp(Number.isFinite(g.smoothness) ? g.smoothness : 0, 0, 1);
168
+ const pts = [];
169
+ for (const s of sortedVectorGradientStops(g)) {
170
+ const c = parseVectorColor(s.color);
171
+ if (!c) return null;
172
+ pts.push({ t: s.offset, v: intoSpace(space, c), a: c[3] * clamp(_nullishCoalesce(s.opacity, () => ( 1)), 0, 1), mid: _nullishCoalesce(s.midpoint, () => ( 0.5)) });
173
+ }
174
+ return {
175
+ sample(t) {
176
+ const n = pts.length;
177
+ if (!n) return [0, 0, 0, 0];
178
+ const first = pts[0];
179
+ const last = pts[n - 1];
180
+ const at2 = (p) => [...outOfSpace(space, p.v), p.a];
181
+ if (!(t > first.t) || n < 2) return at2(first);
182
+ if (t >= last.t) return at2(last);
183
+ let i = 0;
184
+ while (i + 2 < n && !(t >= pts[i].t && t <= pts[i + 1].t)) i++;
185
+ const p0 = pts[i];
186
+ const p1 = pts[i + 1];
187
+ const h = Math.max(p1.t - p0.t, 1e-6);
188
+ const u = gradientMidpointRemap((t - p0.t) / h, p0.mid);
189
+ const alpha = p0.a + (p1.a - p0.a) * u;
190
+ if (smooth <= 0) return [...outOfSpace(space, [0, 1, 2].map((c) => p0.v[c] + (p1.v[c] - p0.v[c]) * u)), alpha];
191
+ const tangent2 = (k) => {
192
+ const [a, b] = k === 0 ? [0, 1] : k === n - 1 ? [n - 2, n - 1] : [k - 1, k + 1];
193
+ const d = Math.max(pts[b].t - pts[a].t, 1e-6);
194
+ const half = k === 0 || k === n - 1 ? 0.5 : 1;
195
+ return [0, 1, 2].map((c) => (pts[b].v[c] - pts[a].v[c]) / d * half);
196
+ };
197
+ const m0 = tangent2(i);
198
+ const m1 = tangent2(i + 1);
199
+ const u2 = u * u;
200
+ const u3 = u2 * u;
201
+ const h00 = 2 * u3 - 3 * u2 + 1;
202
+ const h10 = u3 - 2 * u2 + u;
203
+ const h01 = -2 * u3 + 3 * u2;
204
+ const h11 = u3 - u2;
205
+ const v2 = [0, 1, 2].map((c) => {
206
+ const linear = p0.v[c] + (p1.v[c] - p0.v[c]) * u;
207
+ const spline = h00 * p0.v[c] + h10 * h * m0[c] + h01 * p1.v[c] + h11 * h * m1[c];
208
+ const value = linear + (spline - linear) * smooth;
209
+ return clamp(value, Math.min(p0.v[c], p1.v[c]), Math.max(p0.v[c], p1.v[c]));
210
+ });
211
+ return [...outOfSpace(space, v2), alpha];
212
+ }
213
+ };
214
+ }
215
+ function sampleVectorGradient(g, t) {
216
+ const ramp = vectorGradientRamp(g);
217
+ if (ramp) {
218
+ const [r, gg, b, a] = ramp.sample(t);
219
+ return { color: _chunkA6PHCAGXcjs.rgbaToHex.call(void 0, r, gg, b), opacity: a };
220
+ }
221
+ const stops = sortedVectorGradientStops(g);
222
+ const s = _nullishCoalesce([...stops].reverse().find((x) => x.offset <= t), () => ( stops[0]));
223
+ return s ? { color: s.color, opacity: _nullishCoalesce(s.opacity, () => ( 1)) } : { color: "#000000", opacity: 0 };
224
+ }
225
+ var stopOf = (offset, c) => {
226
+ const color = _chunkA6PHCAGXcjs.rgbaToHex.call(void 0, c[0], c[1], c[2]);
227
+ const opacity = Math.round(c[3] * 1e4) / 1e4;
228
+ return opacity < 1 ? { offset, color, opacity } : { offset, color };
229
+ };
230
+ var maxDiff = (a, b) => Math.max(Math.abs(a[0] - b[0]), Math.abs(a[1] - b[1]), Math.abs(a[2] - b[2]), Math.abs(a[3] - b[3]));
231
+ var lerpRgba = (a, b, u) => [0, 1, 2, 3].map((c) => a[c] + (b[c] - a[c]) * u);
232
+ function expandVectorGradientStops(g, options = {}) {
233
+ const stops = sortedVectorGradientStops(g);
234
+ const plain3 = () => stops.map(({ offset, color, opacity }) => opacity !== void 0 && opacity !== 1 ? { offset, color, opacity } : { offset, color });
235
+ if (!vectorGradientNeedsExpansion(g)) return plain3();
236
+ const ramp = vectorGradientRamp(g);
237
+ if (!ramp || stops.length < 2) return plain3();
238
+ const tol = _nullishCoalesce(options.tolerance, () => ( 0.5 / 255));
239
+ const sRgbOnly = (_nullishCoalesce(g.interpolation, () => ( "srgb"))) === "srgb" && !((_nullishCoalesce(g.smoothness, () => ( 0))) > 0);
240
+ const own = (s) => {
241
+ const c = parseVectorColor(s.color);
242
+ return [c[0], c[1], c[2], c[3] * clamp(_nullishCoalesce(s.opacity, () => ( 1)), 0, 1)];
243
+ };
244
+ const out = [];
245
+ const push = (offset, c) => out.push(stopOf(offset, c));
246
+ for (let i = 0; i < stops.length; i++) {
247
+ const a = stops[i];
248
+ const b = stops[i + 1];
249
+ push(a.offset, own(a));
250
+ if (!b || b.offset - a.offset < 1e-9) continue;
251
+ const knots = [a.offset];
252
+ if (hasMidpoint(a)) knots.push(a.offset + (b.offset - a.offset) * clamp(a.midpoint, MID_MIN, MID_MAX));
253
+ knots.push(b.offset);
254
+ const inside = (t) => ramp.sample(clamp(t, a.offset + 1e-9, b.offset - 1e-9));
255
+ for (let k = 0; k + 1 < knots.length; k++) {
256
+ const t0 = knots[k];
257
+ const t1 = knots[k + 1];
258
+ if (k > 0) push(t0, inside(t0));
259
+ if (sRgbOnly) continue;
260
+ const refine = (x0, c0, x1, c1, depth) => {
261
+ const xm = (x0 + x1) / 2;
262
+ const cm = inside(xm);
263
+ const ok = depth >= 8 || [0.03, 0.1, 0.25, 0.5, 0.75, 0.9, 0.97].every((f) => maxDiff(inside(x0 + (x1 - x0) * f), lerpRgba(c0, c1, f)) <= tol);
264
+ if (ok) return;
265
+ refine(x0, c0, xm, cm, depth + 1);
266
+ push(xm, cm);
267
+ refine(xm, cm, x1, c1, depth + 1);
268
+ };
269
+ refine(t0, inside(t0), t1, inside(t1), 0);
270
+ }
271
+ }
272
+ return out;
273
+ }
274
+ function samplePlainStops(stops, t) {
275
+ const parsed = stops.map((s) => {
276
+ const c = _nullishCoalesce(parseVectorColor(s.color), () => ( [0, 0, 0, 1]));
277
+ return { t: s.offset, c: [c[0], c[1], c[2], c[3] * (_nullishCoalesce(s.opacity, () => ( 1)))] };
278
+ });
279
+ if (!parsed.length) return [0, 0, 0, 0];
280
+ if (t <= parsed[0].t) return parsed[0].c;
281
+ for (let i = 0; i + 1 < parsed.length; i++) {
282
+ const a = parsed[i];
283
+ const b = parsed[i + 1];
284
+ if (t <= b.t) return b.t - a.t < 1e-12 ? b.c : lerpRgba(a.c, b.c, (t - a.t) / (b.t - a.t));
285
+ }
286
+ return parsed[parsed.length - 1].c;
287
+ }
288
+ function reverseVectorGradient(g) {
289
+ const stops = sortedVectorGradientStops(g).reverse();
290
+ const next = stops.map((s, i) => {
291
+ const following = stops[i + 1];
292
+ const mid = following ? 1 - (_nullishCoalesce(following.midpoint, () => ( 0.5))) : 0.5;
293
+ const { midpoint: _m, ...rest } = s;
294
+ return { ...rest, offset: 1 - s.offset, ...Math.abs(mid - 0.5) > 1e-6 ? { midpoint: mid } : {} };
295
+ });
296
+ return { ...g, stops: next };
297
+ }
298
+ function placeStop(stops, stop) {
299
+ let i = stops.findIndex((o) => o.offset > stop.offset);
300
+ if (i < 0) i = stops.length;
301
+ stops.splice(i, 0, stop);
302
+ return i;
303
+ }
304
+ function insertVectorGradientStop(g, offset) {
305
+ const at2 = clamp(offset, 0, 1);
306
+ const { color, opacity } = sampleVectorGradient(g, at2);
307
+ const stops = sortedVectorGradientStops(g);
308
+ const index = placeStop(stops, opacity < 1 ? { offset: at2, color, opacity } : { offset: at2, color });
309
+ return { stops, index };
310
+ }
311
+ function moveVectorGradientStop(stops, index, offset) {
312
+ const next = stops.map((s2) => ({ ...s2 }));
313
+ if (index < 0 || index >= next.length) return { stops: next, index };
314
+ const [s] = next.splice(index, 1);
315
+ return { stops: next, index: placeStop(next, { ...s, offset: clamp(offset, 0, 1) }) };
316
+ }
317
+ function removeVectorGradientStop(stops, index) {
318
+ if (stops.length <= VECTOR_GRADIENT_MIN_STOPS || index < 0 || index >= stops.length) return null;
319
+ return stops.filter((_, i) => i !== index);
320
+ }
321
+ function vectorGradientMidpointPosition(stops, index) {
322
+ const a = stops[index];
323
+ const b = stops[index + 1];
324
+ if (!a || !b) return null;
325
+ return a.offset + (b.offset - a.offset) * (_nullishCoalesce(a.midpoint, () => ( 0.5)));
326
+ }
327
+ function vectorGradientMidpointFromPosition(stops, index, pos) {
328
+ const a = stops[index];
329
+ const b = stops[index + 1];
330
+ if (!a || !b) return null;
331
+ const span = Math.max(b.offset - a.offset, 1e-6);
332
+ return clamp((pos - a.offset) / span, 0.05, 0.95);
333
+ }
334
+ function spanVectorGradientStops(g, from, to, expanded = expandVectorGradientStops(g)) {
335
+ if (to < from) {
336
+ const s = spanVectorGradientStops(g, to, from, expanded);
337
+ return s.map((x) => ({ ...x, offset: 1 - x.offset })).reverse();
338
+ }
339
+ const d = to - from;
340
+ const at2 = (offset, t) => stopOf(offset, samplePlainStops(expanded, t));
341
+ const out = [at2(0, from)];
342
+ if (d > 1e-9) {
343
+ for (const s of expanded) if (s.offset > from && s.offset < to) out.push({ ...s, offset: (s.offset - from) / d });
344
+ }
345
+ out.push(at2(1, to));
346
+ return out;
347
+ }
348
+ var hasExplicitGradientGeometry = (g) => finitePoint(g.start) && finitePoint(g.end);
349
+ function shorthandPoints(g) {
350
+ if (g.type === "linear" || g.type === "reflected") {
351
+ const c = _chunkVNYRI265cjs.angleToSvgLinearCoords.call(void 0, Number.isFinite(g.angle) ? g.angle : 180);
352
+ const start2 = g.type === "linear" ? { x: c.x1, y: c.y1 } : { x: 0.5, y: 0.5 };
353
+ const end = { x: c.x2, y: c.y2 };
354
+ return { start: start2, end, width: add(start2, perp(sub(end, start2))) };
355
+ }
356
+ const start = { x: Number.isFinite(g.cx) ? g.cx : 0.5, y: Number.isFinite(g.cy) ? g.cy : 0.5 };
357
+ const r = Number.isFinite(g.r) ? g.r : 0.5;
358
+ if (g.type === "radial") return { start, end: { x: start.x + r, y: start.y }, width: { x: start.x, y: start.y + r } };
359
+ const a = (Number.isFinite(g.angle) ? g.angle : 90) * Math.PI / 180;
360
+ const dir = { x: Math.sin(a), y: -Math.cos(a) };
361
+ return { start, end: add(start, scale(dir, r)), width: add(start, scale(perp(dir), r)) };
362
+ }
363
+ function vectorGradientPoints(g) {
364
+ const explicit = hasExplicitGradientGeometry(g);
365
+ const base = explicit ? null : shorthandPoints(g);
366
+ const start = explicit ? { x: g.start.x, y: g.start.y } : base.start;
367
+ const end = explicit ? { x: g.end.x, y: g.end.y } : base.end;
368
+ const width = explicit ? finitePoint(g.width) ? { x: g.width.x, y: g.width.y } : add(start, perp(sub(end, start))) : base.width;
369
+ return {
370
+ start,
371
+ end,
372
+ width,
373
+ focal: g.type === "radial" && finitePoint(g.focal) ? { x: g.focal.x, y: g.focal.y } : null,
374
+ focalRadius: g.type === "radial" ? clamp(Number.isFinite(g.focalRadius) ? g.focalRadius : 0, 0, 0.99) : 0,
375
+ units: explicit && g.units === "userSpaceOnUse" ? "userSpaceOnUse" : "objectBoundingBox"
376
+ };
377
+ }
378
+ function vectorBoxMatrix(bounds) {
379
+ if (!bounds) return IDENTITY;
380
+ let { x, y, width: w, height: h } = bounds;
381
+ const fallback = Math.max(w, h, 1e-9) > 1e-9 ? Math.max(w, h) : 1;
382
+ if (!(w > 1e-9)) {
383
+ x -= fallback / 2 - w / 2;
384
+ w = fallback;
385
+ }
386
+ if (!(h > 1e-9)) {
387
+ y -= fallback / 2 - h / 2;
388
+ h = fallback;
389
+ }
390
+ return [w, 0, 0, h, x, y];
391
+ }
392
+ var framePointsMatrix = (p) => [p.end.x - p.start.x, p.end.y - p.start.y, p.width.x - p.start.x, p.width.y - p.start.y, p.start.x, p.start.y];
393
+ function vectorGradientUnitsMatrix(g, bounds) {
394
+ return vectorGradientPoints(g).units === "objectBoundingBox" ? vectorBoxMatrix(bounds) : IDENTITY;
395
+ }
396
+ function vectorGradientMatrix(g, bounds) {
397
+ return mul(framePointsMatrix(vectorGradientPoints(g)), vectorGradientUnitsMatrix(g, bounds));
398
+ }
399
+ var FOCAL_MAX = 0.99;
400
+ function unitFocal(g) {
401
+ const p = vectorGradientPoints(g);
402
+ if (!p.focal && !(p.focalRadius > 0)) return null;
403
+ const inv = invert(framePointsMatrix(p));
404
+ if (!inv) return null;
405
+ let f = p.focal ? apply(inv, p.focal) : { x: 0, y: 0 };
406
+ const reach = FOCAL_MAX * (1 - p.focalRadius);
407
+ const r = Math.hypot(f.x, f.y);
408
+ if (r > reach) f = scale(f, reach / r);
409
+ return { f, fr: p.focalRadius };
410
+ }
411
+ function vectorGradientFocalParam(q, f = { x: 0, y: 0 }, fr = 0) {
412
+ const d = sub(q, f);
413
+ const dr = 1 - fr;
414
+ const a = dot(f, f) - dr * dr;
415
+ const b = dot(d, f) - fr * dr;
416
+ const c = dot(d, d) - fr * fr;
417
+ if (Math.abs(a) < 1e-12) return Math.abs(b) < 1e-12 ? 0 : -c / (2 * b);
418
+ return (-b - Math.sqrt(Math.max(0, b * b - a * c))) / a;
419
+ }
420
+ function vectorGradientParam(type, q, focal) {
421
+ switch (type) {
422
+ case "linear":
423
+ return q.x;
424
+ case "reflected":
425
+ return Math.abs(q.x);
426
+ case "diamond":
427
+ return Math.abs(q.x) + Math.abs(q.y);
428
+ case "conic": {
429
+ const turn = Math.atan2(q.y, q.x) / (2 * Math.PI);
430
+ return turn - Math.floor(turn);
431
+ }
432
+ case "radial":
433
+ return focal ? vectorGradientFocalParam(q, focal.f, focal.fr) : Math.hypot(q.x, q.y);
434
+ }
435
+ }
436
+ function applyVectorGradientSpread(t, spread = "pad") {
437
+ if (!Number.isFinite(t)) return 0;
438
+ if (spread === "repeat") return t - Math.floor(t);
439
+ if (spread === "reflect") {
440
+ const m = (t % 2 + 2) % 2;
441
+ return m <= 1 ? m : 2 - m;
442
+ }
443
+ return clamp(t, 0, 1);
444
+ }
445
+ function vectorGradientTAt(g, bounds, p) {
446
+ const inv = invert(vectorGradientMatrix(g, bounds));
447
+ if (!inv) return 0;
448
+ const raw = vectorGradientParam(g.type, apply(inv, p), g.type === "radial" ? unitFocal(g) : null);
449
+ return g.type === "conic" ? raw : applyVectorGradientSpread(raw, _nullishCoalesce(g.spread, () => ( "pad")));
450
+ }
451
+ function sampleVectorGradientAt(g, bounds, p) {
452
+ const t = vectorGradientTAt(g, bounds, p);
453
+ const ramp = vectorGradientRamp(g);
454
+ return ramp ? ramp.sample(t) : samplePlainStops(sortedVectorGradientStops(g), t);
455
+ }
456
+ function vectorGradientAspect(g) {
457
+ const p = vectorGradientPoints(g);
458
+ const u = sub(p.end, p.start);
459
+ const l2 = dot(u, u);
460
+ return l2 > 1e-18 ? dot(sub(p.width, p.start), perp(u)) / l2 : 1;
461
+ }
462
+ function explicitVectorGradient(g) {
463
+ const p = vectorGradientPoints(g);
464
+ const { angle: _a, cx: _cx, cy: _cy, r: _r, ...rest } = g;
465
+ return {
466
+ ...rest,
467
+ start: p.start,
468
+ end: p.end,
469
+ width: p.width,
470
+ ...p.focal ? { focal: p.focal } : {},
471
+ units: p.units
472
+ };
473
+ }
474
+ function pinVectorGradient(g, bounds) {
475
+ const e = explicitVectorGradient(g);
476
+ if (e.units === "userSpaceOnUse") return e;
477
+ const m = vectorBoxMatrix(bounds);
478
+ return { ...e, start: apply(m, e.start), end: apply(m, e.end), width: apply(m, e.width), ...e.focal ? { focal: apply(m, e.focal) } : {}, units: "userSpaceOnUse" };
479
+ }
480
+ function transformVectorGradient(g, m) {
481
+ if (!hasExplicitGradientGeometry(g) || g.units !== "userSpaceOnUse" || isIdentity(m)) return g;
482
+ const p = vectorGradientPoints(g);
483
+ return { ...g, start: apply(m, p.start), end: apply(m, p.end), width: apply(m, p.width), ...p.focal ? { focal: apply(m, p.focal) } : {} };
484
+ }
485
+ function transformVectorPaint(paint, m) {
486
+ return isVectorGradient(paint) ? transformVectorGradient(paint, m) : paint;
487
+ }
488
+ var isCentredVectorGradient = (type) => type === "radial" || type === "conic" || type === "diamond";
489
+ function vectorGradientHandles(g, bounds, toWorld = IDENTITY) {
490
+ const p = vectorGradientPoints(g);
491
+ const m = mul(vectorGradientUnitsMatrix(g, bounds), toWorld);
492
+ const f = g.type === "radial" ? unitFocal(g) : null;
493
+ const frame2 = mul(framePointsMatrix(p), m);
494
+ return {
495
+ start: apply(m, p.start),
496
+ end: apply(m, p.end),
497
+ aspect: isCentredVectorGradient(g.type) ? apply(m, p.width) : null,
498
+ focal: g.type === "radial" ? apply(frame2, _nullishCoalesce(_optionalChain([f, 'optionalAccess', _2 => _2.f]), () => ( { x: 0, y: 0 }))) : null
499
+ };
500
+ }
501
+ function withMainAxis(p, start, end) {
502
+ const before = invert(framePointsMatrix(p));
503
+ const u = sub(p.end, p.start);
504
+ const l2 = dot(u, u);
505
+ const w = sub(p.width, p.start);
506
+ const rel = l2 > 1e-18 ? { a: dot(w, u) / l2, b: dot(w, perp(u)) / l2 } : { a: 0, b: 1 };
507
+ const u2 = sub(end, start);
508
+ const width = add(start, add(scale(u2, rel.a), scale(perp(u2), rel.b)));
509
+ const next = { start, end, width };
510
+ const focal = p.focal && before ? apply(framePointsMatrix(next), apply(before, p.focal)) : p.focal;
511
+ return { ...next, focal };
512
+ }
513
+ function moveVectorGradientHandle(g, bounds, handle, to, from = to) {
514
+ const pinned = pinVectorGradient(g, bounds);
515
+ const p = vectorGradientPoints(pinned);
516
+ const set = (q) => {
517
+ const next = { ...pinned, ...q };
518
+ if (next.focal === null || next.focal === void 0) delete next.focal;
519
+ return next;
520
+ };
521
+ switch (handle) {
522
+ case "start": {
523
+ const d = sub(to, from);
524
+ return set({ start: add(p.start, d), end: add(p.end, d), width: add(p.width, d), focal: p.focal ? add(p.focal, d) : null });
525
+ }
526
+ case "end":
527
+ case "radius": {
528
+ if (Math.hypot(to.x - p.start.x, to.y - p.start.y) < 1e-6) return pinned;
529
+ const n = withMainAxis(p, p.start, to);
530
+ return set({ end: n.end, width: n.width, focal: n.focal });
531
+ }
532
+ case "aspect": {
533
+ const u = sub(p.end, p.start);
534
+ const l2 = dot(u, u);
535
+ if (l2 < 1e-18) return pinned;
536
+ let aspect = dot(sub(to, p.start), perp(u)) / l2;
537
+ if (Math.abs(aspect) < 0.01) aspect = aspect < 0 ? -0.01 : 0.01;
538
+ const before = invert(framePointsMatrix(p));
539
+ const width = add(p.start, scale(perp(u), aspect));
540
+ const focal = p.focal && before ? apply(framePointsMatrix({ start: p.start, end: p.end, width }), apply(before, p.focal)) : null;
541
+ return set({ width, focal });
542
+ }
543
+ case "focal": {
544
+ const frame2 = framePointsMatrix(p);
545
+ const inv = invert(frame2);
546
+ if (!inv) return pinned;
547
+ let q = apply(inv, to);
548
+ const r = Math.hypot(q.x, q.y);
549
+ if (r < 1e-6) return set({ focal: null });
550
+ const reach = FOCAL_MAX * (1 - p.focalRadius);
551
+ if (r > reach) q = scale(q, reach / r);
552
+ return set({ focal: apply(frame2, q) });
553
+ }
554
+ }
555
+ }
556
+ function setVectorGradientVector(g, bounds, start, end) {
557
+ const pinned = pinVectorGradient(g, bounds);
558
+ const p = vectorGradientPoints(pinned);
559
+ const aspect = Math.abs(vectorGradientAspect(pinned)) > 1e-6 ? vectorGradientAspect(pinned) : 1;
560
+ const u = sub(end, start);
561
+ const width = add(start, scale(perp(u), aspect));
562
+ const before = invert(framePointsMatrix(p));
563
+ const focal = p.focal && before ? apply(framePointsMatrix({ start, end, width }), apply(before, p.focal)) : null;
564
+ const next = { ...pinned, start, end, width };
565
+ if (focal) next.focal = focal;
566
+ else delete next.focal;
567
+ return next;
568
+ }
569
+ var VECTOR_GRADIENT_MAX_PERIODS = 64;
570
+ function wholePeriod(stops) {
571
+ const out = stops.map((s) => ({ ...s }));
572
+ if (out.length && out[0].offset > 0) out.unshift({ ...out[0], offset: 0 });
573
+ const last = out[out.length - 1];
574
+ if (last && last.offset < 1) out.push({ ...last, offset: 1 });
575
+ return out;
576
+ }
577
+ function periodicVectorGradientStops(stops, lo, hi, mirrored) {
578
+ const period = wholePeriod(stops);
579
+ const back = reverseVectorGradient({ stops: period }).stops;
580
+ const n = Math.max(1, hi - lo);
581
+ const out = [];
582
+ for (let j = 0; j < n; j++) {
583
+ const one = mirrored(lo + j) ? back : period;
584
+ for (const s of one) out.push({ ...s, offset: (j + s.offset) / n });
585
+ }
586
+ return out;
587
+ }
588
+ function expandVectorGradientSpread(g, bounds, area) {
589
+ const m = vectorGradientMatrix(g, bounds);
590
+ const inv = invert(m);
591
+ const spread = _nullishCoalesce(g.spread, () => ( "pad"));
592
+ const reflected = g.type === "reflected";
593
+ if (!inv || !reflected && spread === "pad") return null;
594
+ const odd = (k) => (k % 2 + 2) % 2 === 1;
595
+ const stops = expandVectorGradientStops(g);
596
+ const along2 = (a, b, mirrored2, capped2 = false) => ({
597
+ start: apply(m, { x: a, y: 0 }),
598
+ end: apply(m, { x: b, y: 0 }),
599
+ from: a,
600
+ to: b,
601
+ matrix: m,
602
+ stops: periodicVectorGradientStops(stops, a, b, mirrored2),
603
+ capped: capped2
604
+ });
605
+ if (reflected && spread === "pad") return along2(-1, 1, (k) => k < 0);
606
+ const us = boxCorners(area).map((c) => apply(inv, c).x);
607
+ let lo = Math.floor(Math.min(...us));
608
+ let hi = Math.ceil(Math.max(...us));
609
+ if (reflected) {
610
+ lo = Math.min(lo, -1);
611
+ hi = Math.max(hi, 1);
612
+ }
613
+ hi = Math.max(hi, lo + 1);
614
+ if (!reflected && lo >= 0 && hi <= 1) return null;
615
+ const capped = hi - lo > VECTOR_GRADIENT_MAX_PERIODS;
616
+ if (capped) {
617
+ lo = Math.max(lo, -VECTOR_GRADIENT_MAX_PERIODS / 2);
618
+ hi = lo + VECTOR_GRADIENT_MAX_PERIODS;
619
+ }
620
+ const mirrored = reflected ? (k) => {
621
+ const j = k >= 0 ? k : -k - 1;
622
+ const own = spread === "reflect" && odd(j);
623
+ return k < 0 ? !own : own;
624
+ } : (k) => spread === "reflect" && odd(k);
625
+ return along2(lo, hi, mirrored, capped);
626
+ }
627
+ var boxCorners = (b) => [
628
+ { x: b.x, y: b.y },
629
+ { x: b.x + b.width, y: b.y },
630
+ { x: b.x + b.width, y: b.y + b.height },
631
+ { x: b.x, y: b.y + b.height }
632
+ ];
633
+ var toSvgStops = (stops) => stops.map((s) => ({ offset: `${fmt(s.offset * 100, 3)}%`, stopColor: s.color, ...s.opacity !== void 0 && s.opacity !== 1 ? { stopOpacity: Math.round(s.opacity * 1e4) / 1e4 } : {} }));
634
+ var polygonD = (pts, precision) => `M${pts.map((p) => `${fmt(p.x, precision)} ${fmt(p.y, precision)}`).join("L")}Z`;
635
+ var padBounds = (b, pad) => ({ x: b.x - pad, y: b.y - pad, width: b.width + 2 * pad, height: b.height + 2 * pad });
636
+ var vectorGradientArea = (bounds) => padBounds(_nullishCoalesce(bounds, () => ( { x: 0, y: 0, width: 1, height: 1 })), 1);
637
+ var transformAttr = (m) => isIdentity(m) ? {} : { gradientTransform: fmtMatrix(m) };
638
+ function minScale(m) {
639
+ const [a, b, c, d] = m;
640
+ const p = a * a + b * b;
641
+ const q = c * c + d * d;
642
+ const r = a * c + b * d;
643
+ return Math.sqrt(Math.max(0, (p + q - Math.sqrt((p - q) ** 2 + 4 * r * r)) / 2));
644
+ }
645
+ function patternAttrs(area) {
646
+ const [x, y, w, h] = [area.x, area.y, Math.max(area.width, 1e-3), Math.max(area.height, 1e-3)].map((n) => fmt(n, 3));
647
+ return { patternUnits: "userSpaceOnUse", x, y, width: w, height: h, viewBox: `${x} ${y} ${w} ${h}` };
648
+ }
649
+ function patternDef(id, area, gradients, tiles) {
650
+ return {
651
+ id,
652
+ type: "pattern",
653
+ attrs: patternAttrs(area),
654
+ gradients,
655
+ tiles
656
+ };
657
+ }
658
+ function vectorGradientSvgDefs(g, id, options = {}) {
659
+ const precision = _nullishCoalesce(options.precision, () => ( 3));
660
+ const bounds = _nullishCoalesce(options.bounds, () => ( null));
661
+ const area = _nullishCoalesce(options.area, () => ( vectorGradientArea(bounds)));
662
+ const spread = g.spread && VECTOR_GRADIENT_SPREADS.includes(g.spread) ? g.spread : "pad";
663
+ const stops = expandVectorGradientStops(g);
664
+ const p = vectorGradientPoints(g);
665
+ const unitsM = vectorGradientUnitsMatrix(g, bounds);
666
+ const frameM = mul(framePointsMatrix(p), unitsM);
667
+ const spreadAttr = (s) => s === "pad" ? {} : { spreadMethod: s };
668
+ const base = { gradientUnits: "userSpaceOnUse" };
669
+ switch (g.type) {
670
+ case "linear":
671
+ case "reflected": {
672
+ if (g.type === "reflected" && spread !== "reflect") {
673
+ const ex = expandVectorGradientSpread(g, bounds, area);
674
+ const [a, b] = ex ? [ex.from, ex.to] : [-1, 1];
675
+ const st = _nullishCoalesce(_optionalChain([ex, 'optionalAccess', _3 => _3.stops]), () => ( periodicVectorGradientStops(stops, -1, 1, (k) => k < 0)));
676
+ return [{ id, type: "linear", attrs: { ...base, x1: a, y1: 0, x2: b, y2: 0, gradientTransform: fmtMatrix(frameM) }, stops: toSvgStops(st) }];
677
+ }
678
+ const u = sub(p.end, p.start);
679
+ const w = sub(p.width, p.start);
680
+ const square = Math.abs(dot(u, w)) <= 1e-9 * Math.max(dot(u, u), dot(w, w), 1e-18);
681
+ const coords = square ? { x1: fmt(p.start.x, 6), y1: fmt(p.start.y, 6), x2: fmt(p.end.x, 6), y2: fmt(p.end.y, 6), ...transformAttr(unitsM) } : { x1: 0, y1: 0, x2: 1, y2: 0, gradientTransform: fmtMatrix(frameM) };
682
+ return [{ id, type: "linear", attrs: { ...base, ...coords, ...spreadAttr(spread) }, stops: toSvgStops(stops) }];
683
+ }
684
+ case "radial": {
685
+ const f = unitFocal(g);
686
+ const attrs = { ...base, cx: 0, cy: 0, r: 1, gradientTransform: fmtMatrix(frameM), ...spreadAttr(spread) };
687
+ if (f && (Math.abs(f.f.x) > 1e-9 || Math.abs(f.f.y) > 1e-9)) {
688
+ attrs.fx = fmt(f.f.x, 6);
689
+ attrs.fy = fmt(f.f.y, 6);
690
+ }
691
+ if (f && f.fr > 0) attrs.fr = fmt(f.fr, 6);
692
+ return [{ id, type: "radial", attrs, stops: toSvgStops(stops) }];
693
+ }
694
+ case "diamond": {
695
+ const inv = invert(frameM);
696
+ if (!inv) return [patternDef(id, area, [], [{ d: polygonD(boxCorners(area), precision), fill: _nullishCoalesce(_optionalChain([stops, 'access', _4 => _4[stops.length - 1], 'optionalAccess', _5 => _5.color]), () => ( "none")) }])];
697
+ const reach = 2 * Math.max(...boxCorners(area).map((c) => {
698
+ const q = apply(inv, c);
699
+ return Math.abs(q.x) + Math.abs(q.y);
700
+ })) + 1;
701
+ const delta = Math.min(0.05 * reach, 0.75 / Math.max(minScale(frameM), 1e-9));
702
+ const gradients = [];
703
+ const tiles = [];
704
+ [[1, 1], [-1, 1], [-1, -1], [1, -1]].forEach(([sx, sy], k) => {
705
+ const gid = `${id}-q${k}`;
706
+ gradients.push({ id: gid, type: "linear", attrs: { ...base, x1: 0, y1: 0, x2: sx / 2, y2: sy / 2, gradientTransform: fmtMatrix(frameM), ...spreadAttr(spread) }, stops: toSvgStops(stops) });
707
+ const quad = [{ x: -delta * sx, y: -delta * sy }, { x: reach * sx, y: -delta * sy }, { x: -delta * sx, y: reach * sy }].map((q) => apply(frameM, q));
708
+ tiles.push({ d: polygonD(quad, precision), fill: `url(#${gid})` });
709
+ });
710
+ return [patternDef(id, area, gradients, tiles)];
711
+ }
712
+ case "conic":
713
+ return [conicPattern(id, frameM, stops, area, _nullishCoalesce(options.wedges, () => ( 72)), precision)];
714
+ }
715
+ }
716
+ function conicPattern(id, frameM, stops, area, wedges, precision) {
717
+ const inv = invert(frameM);
718
+ const n = Math.max(8, Math.min(720, Math.round(wedges)));
719
+ const half = Math.PI / n;
720
+ if (!inv || !stops.length) return patternDef(id, area, [], [{ d: polygonD(boxCorners(area), precision), fill: _nullishCoalesce(_optionalChain([stops, 'access', _6 => _6[0], 'optionalAccess', _7 => _7.color]), () => ( "none")) }]);
721
+ const reach = (Math.max(...boxCorners(area).map((c) => Math.hypot(apply(inv, c).x, apply(inv, c).y))) + 1) / Math.cos(half);
722
+ const overlap = Math.min(half, 2e-3);
723
+ const gradients = [];
724
+ const tiles = [];
725
+ for (let k = 0; k < n; k++) {
726
+ const a0 = 2 * Math.PI * k / n;
727
+ const a1 = 2 * Math.PI * (k + 1) / n;
728
+ const mid = (a0 + a1) / 2;
729
+ const tau = { x: -Math.sin(mid), y: Math.cos(mid) };
730
+ const s = Math.sin(half);
731
+ const at2 = (angle) => (Math.sin(angle - mid) / s + 1) / 2;
732
+ const t0 = k / n;
733
+ const t1 = (k + 1) / n;
734
+ const own = [stopOf(0, samplePlainStops(stops, t0))];
735
+ for (const st of stops) if (st.offset > t0 && st.offset < t1) own.push({ ...st, offset: at2(st.offset * 2 * Math.PI) });
736
+ own.push(stopOf(1, samplePlainStops(stops, t1)));
737
+ const gid = `${id}-w${k}`;
738
+ gradients.push({
739
+ id: gid,
740
+ type: "linear",
741
+ attrs: { gradientUnits: "userSpaceOnUse", x1: fmt(-s * tau.x, 6), y1: fmt(-s * tau.y, 6), x2: fmt(s * tau.x, 6), y2: fmt(s * tau.y, 6), gradientTransform: fmtMatrix(frameM) },
742
+ stops: toSvgStops(own)
743
+ });
744
+ const from = a0 - (k === 0 ? 0 : overlap);
745
+ const pts = [{ x: 0, y: 0 }, { x: reach * Math.cos(from), y: reach * Math.sin(from) }, { x: reach * Math.cos(a1), y: reach * Math.sin(a1) }].map((q) => apply(frameM, q));
746
+ tiles.push({ d: polygonD(pts, precision), fill: `url(#${gid})` });
747
+ }
748
+ return patternDef(id, area, gradients, tiles);
749
+ }
750
+ var num = (v2, fallback = 0) => {
751
+ if (v2 === void 0) return fallback;
752
+ const n = typeof v2 === "number" ? v2 : parseFloat(v2);
753
+ return Number.isFinite(n) ? n : fallback;
754
+ };
755
+ var parseMatrixAttr = (v2) => {
756
+ const m = typeof v2 === "string" ? /^matrix\(([^)]*)\)$/.exec(v2.trim()) : null;
757
+ if (!m) return IDENTITY;
758
+ const n = m[1].split(/[\s,]+/).map(Number);
759
+ return n.length === 6 && n.every(Number.isFinite) ? n : IDENTITY;
760
+ };
761
+ var plainOf = (stops) => stops.map((s) => ({ offset: parseFloat(s.offset) / (s.offset.endsWith("%") ? 100 : 1), color: s.stopColor, ...s.stopOpacity !== void 0 ? { opacity: s.stopOpacity } : {} }));
762
+ function inTile(d, p) {
763
+ const n = _nullishCoalesce(_optionalChain([d, 'access', _8 => _8.match, 'call', _9 => _9(/-?[\d.]+(?:e-?\d+)?/gi), 'optionalAccess', _10 => _10.map, 'call', _11 => _11(Number)]), () => ( []));
764
+ let inside = false;
765
+ for (let i = 0, j = n.length - 2; i + 1 < n.length; j = i, i += 2) {
766
+ const [xi, yi, xj, yj] = [n[i], n[i + 1], n[j], n[j + 1]];
767
+ if (yi > p.y !== yj > p.y && p.x < (xj - xi) * (p.y - yi) / (yj - yi) + xi) inside = !inside;
768
+ }
769
+ return inside;
770
+ }
771
+ function sampleVectorPaintDefAt(defs, id, p) {
772
+ const all = /* @__PURE__ */ new Map();
773
+ for (const d of defs) {
774
+ all.set(d.id, d);
775
+ if (d.type === "pattern") for (const gd of d.gradients) all.set(gd.id, gd);
776
+ }
777
+ const paint = (ref, q) => {
778
+ const url = /^url\(#(.+)\)$/.exec(ref);
779
+ if (!url) return parseVectorColor(ref);
780
+ const def = all.get(url[1]);
781
+ if (!def) return null;
782
+ if (def.type === "pattern") {
783
+ for (let i = def.tiles.length - 1; i >= 0; i--) if (inTile(def.tiles[i].d, q)) return paint(def.tiles[i].fill, q);
784
+ return null;
785
+ }
786
+ const inv = _nullishCoalesce(invert(parseMatrixAttr(def.attrs.gradientTransform)), () => ( IDENTITY));
787
+ const local = apply(inv, q);
788
+ const spread = _nullishCoalesce(def.attrs.spreadMethod, () => ( "pad"));
789
+ let t;
790
+ if (def.type === "radial") {
791
+ const r = num(def.attrs.r, 0.5);
792
+ const c = { x: num(def.attrs.cx), y: num(def.attrs.cy) };
793
+ const f = { x: num(def.attrs.fx, c.x), y: num(def.attrs.fy, c.y) };
794
+ const u = { x: (local.x - c.x) / r, y: (local.y - c.y) / r };
795
+ t = vectorGradientFocalParam(u, { x: (f.x - c.x) / r, y: (f.y - c.y) / r }, num(def.attrs.fr) / r);
796
+ } else {
797
+ const a = { x: num(def.attrs.x1), y: num(def.attrs.y1) };
798
+ const b = { x: num(def.attrs.x2, 1), y: num(def.attrs.y2) };
799
+ const v2 = sub(b, a);
800
+ const l2 = dot(v2, v2);
801
+ t = l2 > 1e-18 ? dot(sub(local, a), v2) / l2 : 0;
802
+ }
803
+ return samplePlainStops(plainOf(def.stops), applyVectorGradientSpread(t, spread));
804
+ };
805
+ return paint(`url(#${id})`, p);
806
+ }
807
+ var readPoint = (v2) => finitePoint(v2) ? { x: v2.x, y: v2.y } : void 0;
808
+ function sanitizeVectorGradient(input) {
809
+ if (!input || typeof input !== "object") return null;
810
+ const r = input;
811
+ if (!VECTOR_GRADIENT_TYPES.includes(r.type) || !Array.isArray(r.stops)) return null;
812
+ const stops = [];
813
+ for (const s of r.stops) {
814
+ if (!s || typeof s !== "object") continue;
815
+ const x = s;
816
+ if (typeof x.color !== "string" || typeof x.offset !== "number" || !Number.isFinite(x.offset)) continue;
817
+ const stop = { offset: clamp(x.offset, 0, 1), color: x.color.slice(0, 100) };
818
+ if (typeof x.opacity === "number" && Number.isFinite(x.opacity) && x.opacity !== 1) stop.opacity = clamp(x.opacity, 0, 1);
819
+ if (typeof x.midpoint === "number" && Number.isFinite(x.midpoint) && Math.abs(x.midpoint - 0.5) > 1e-6) stop.midpoint = clamp(x.midpoint, 0.05, 0.95);
820
+ stops.push(stop);
821
+ }
822
+ if (!stops.length) return null;
823
+ const g = { type: r.type, stops };
824
+ for (const k of ["angle", "cx", "cy", "r"]) if (typeof r[k] === "number" && Number.isFinite(r[k])) g[k] = r[k];
825
+ const start = readPoint(r.start);
826
+ const end = readPoint(r.end);
827
+ if (start && end) {
828
+ g.start = start;
829
+ g.end = end;
830
+ const width = readPoint(r.width);
831
+ if (width) g.width = width;
832
+ if (r.units === "userSpaceOnUse" || r.units === "objectBoundingBox") g.units = r.units;
833
+ }
834
+ const focal = readPoint(r.focal);
835
+ if (focal && g.type === "radial") g.focal = focal;
836
+ if (typeof r.focalRadius === "number" && r.focalRadius > 0 && g.type === "radial") g.focalRadius = clamp(r.focalRadius, 0, 0.99);
837
+ if (VECTOR_GRADIENT_SPREADS.includes(r.spread) && r.spread !== "pad") g.spread = r.spread;
838
+ if (VECTOR_GRADIENT_INTERPOLATIONS.includes(r.interpolation) && r.interpolation !== "srgb") g.interpolation = r.interpolation;
839
+ if (typeof r.smoothness === "number" && r.smoothness > 0) g.smoothness = clamp(r.smoothness, 0, 1);
840
+ if (VECTOR_STROKE_GRADIENT_MODES.includes(r.strokeMode) && r.strokeMode !== "within") g.strokeMode = r.strokeMode;
841
+ return g;
842
+ }
843
+ function convertVectorGradient(g, type) {
844
+ if (g.type === type) return g;
845
+ const next = { ...g, type };
846
+ if (type !== "radial") {
847
+ delete next.focal;
848
+ delete next.focalRadius;
849
+ }
850
+ if (!hasExplicitGradientGeometry(g)) {
851
+ if (isCentredVectorGradient(type) !== isCentredVectorGradient(g.type)) {
852
+ delete next.angle;
853
+ delete next.cx;
854
+ delete next.cy;
855
+ delete next.r;
856
+ }
857
+ }
858
+ return next;
859
+ }
860
+ function defaultVectorGradient(type = "linear", from) {
861
+ if (isVectorGradient(from)) return convertVectorGradient(from, type);
862
+ const colour = typeof from === "string" && parseVectorColor(from) ? from : "#000000";
863
+ return { type, stops: [{ offset: 0, color: colour }, { offset: 1, color: "#ffffff" }] };
864
+ }
865
+ function applyVectorGradientPatch(g, patch) {
866
+ const { aspect, reverse: reverse2, type, ...fields } = patch;
867
+ let next = type && type !== g.type ? convertVectorGradient(g, type) : { ...g };
868
+ const record = next;
869
+ for (const [k, v2] of Object.entries(fields)) {
870
+ if (v2 === void 0) continue;
871
+ if (v2 === null) delete record[k];
872
+ else record[k] = v2;
873
+ }
874
+ if ((fields.start || fields.end) && !hasExplicitGradientGeometry(next)) {
875
+ const p = vectorGradientPoints({ ...g, start: void 0, end: void 0 });
876
+ next.start = _nullishCoalesce(next.start, () => ( p.start));
877
+ next.end = _nullishCoalesce(next.end, () => ( p.end));
878
+ }
879
+ if (aspect !== void 0 && Number.isFinite(aspect)) {
880
+ const e = explicitVectorGradient(next);
881
+ const u = sub(e.end, e.start);
882
+ const a = Math.abs(aspect) < 0.01 ? aspect < 0 ? -0.01 : 0.01 : aspect;
883
+ next = { ...e, width: add(e.start, scale(perp(u), a)) };
884
+ }
885
+ if (reverse2) next = reverseVectorGradient(next);
886
+ const clean2 = sanitizeVectorGradient(next);
887
+ if (!clean2) return null;
888
+ clean2.stops = sortedVectorGradientStops(clean2);
889
+ return clean2;
890
+ }
891
+
892
+ // src/vector/corners.ts
893
+ var EPS = 1e-9;
894
+ var MIN_SINE = 1e-6;
895
+ var sub2 = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
896
+ var add2 = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
897
+ var mul2 = (a, k) => ({ x: a.x * k, y: a.y * k });
898
+ var dot2 = (a, b) => a.x * b.x + a.y * b.y;
87
899
  var cross = (a, b) => a.x * b.y - a.y * b.x;
88
900
  var len = (a) => Math.hypot(a.x, a.y);
89
901
  var pt = (p) => [p.x, p.y];
@@ -102,9 +914,9 @@ var Side = class {
102
914
  direction() {
103
915
  const p0 = vec(this.curve[0]);
104
916
  for (const q of this.curve.slice(1)) {
105
- const d = sub(vec(q), p0);
917
+ const d = sub2(vec(q), p0);
106
918
  const l = len(d);
107
- if (l > EPS) return mul(d, 1 / l);
919
+ if (l > EPS) return mul2(d, 1 / l);
108
920
  }
109
921
  return null;
110
922
  }
@@ -112,12 +924,12 @@ var Side = class {
112
924
  at(s, u) {
113
925
  if (this.straight) {
114
926
  const t2 = this.length > EPS ? Math.min(1, Math.max(0, s / this.length)) : 0;
115
- return { t: t2, p: add(vec(this.curve[0]), mul(u, s)), dir: u };
927
+ return { t: t2, p: add2(vec(this.curve[0]), mul2(u, s)), dir: u };
116
928
  }
117
929
  const t = _chunkVNYRI265cjs.segmentParamAtLength.call(void 0, this.curve, s, this.length);
118
930
  const d = vec(_chunkVNYRI265cjs.segmentDerivative.call(void 0, this.curve, t));
119
931
  const l = len(d);
120
- return { t, p: vec(_chunkVNYRI265cjs.segmentPoint.call(void 0, this.curve, t)), dir: l > EPS ? mul(d, 1 / l) : u };
932
+ return { t, p: vec(_chunkVNYRI265cjs.segmentPoint.call(void 0, this.curve, t)), dir: l > EPS ? mul2(d, 1 / l) : u };
121
933
  }
122
934
  };
123
935
  function cornerOf(sp, s, i) {
@@ -136,7 +948,7 @@ function cornerOf(sp, s, i) {
136
948
  const lu = sideU.length;
137
949
  const lv = sideV.length;
138
950
  if (!(lu > EPS && lv > EPS) || !Number.isFinite(lu) || !Number.isFinite(lv)) return null;
139
- const cos = Math.max(-1, Math.min(1, dot(u, v2)));
951
+ const cos = Math.max(-1, Math.min(1, dot2(u, v2)));
140
952
  const sin = Math.abs(cross(u, v2));
141
953
  if (sin <= MIN_SINE) return null;
142
954
  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 };
@@ -158,8 +970,8 @@ function vectorCorners(subpaths) {
158
970
  }
159
971
  var cornerSetback = (c, r) => r * (1 + c.cos) / c.sin;
160
972
  var fitCornerRadius = (c, radius) => Number.isFinite(radius) ? Math.min(Math.max(0, radius), c.maxRadius) : 0;
161
- var cornerBisectorPoint = (c, k) => add(c.at, mul(add(c.u, c.v), k));
162
- var cornerRadiusChange = (c, d) => dot(d, add(c.u, c.v)) * c.sin / (2 + 2 * c.cos);
973
+ var cornerBisectorPoint = (c, k) => add2(c.at, mul2(add2(c.u, c.v), k));
974
+ var cornerRadiusChange = (c, d) => dot2(d, add2(c.u, c.v)) * c.sin / (2 + 2 * c.cos);
163
975
  function arcHandle(cos) {
164
976
  const c = Math.max(-1, Math.min(1, cos));
165
977
  const sinHalf = Math.sqrt(Math.max(0, (1 - c) / 2));
@@ -167,9 +979,9 @@ function arcHandle(cos) {
167
979
  return 4 / 3 * sinHalf / (1 + cosHalf);
168
980
  }
169
981
  function squareTo(a, b) {
170
- const w = sub(b, mul(a, dot(a, b)));
982
+ const w = sub2(b, mul2(a, dot2(a, b)));
171
983
  const l = len(w);
172
- return l > EPS ? mul(w, 1 / l) : { x: 0, y: 0 };
984
+ return l > EPS ? mul2(w, 1 / l) : { x: 0, y: 0 };
173
985
  }
174
986
  function cutCorner(c, radius, kind) {
175
987
  const s = Math.min(cornerSetback(c, radius), Math.min(c.lu, c.lv) / 2);
@@ -178,19 +990,19 @@ function cutCorner(c, radius, kind) {
178
990
  const b = c.sideV.at(s, c.v);
179
991
  if (kind === "chamfer") return { tu: a.t, tv: b.t, start: a.p, out: null, in: null, end: b.p };
180
992
  if (kind === "inverted") {
181
- const h = s * arcHandle(dot(a.dir, b.dir));
182
- 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 };
993
+ const h = s * arcHandle(dot2(a.dir, b.dir));
994
+ return { tu: a.t, tv: b.t, start: a.p, out: add2(a.p, mul2(squareTo(a.dir, b.dir), h)), in: add2(b.p, mul2(squareTo(b.dir, a.dir), h)), end: b.p };
183
995
  }
184
- const d1 = mul(a.dir, -1);
185
- const d2 = mul(b.dir, -1);
186
- const turn = Math.acos(Math.max(-1, Math.min(1, -dot(d1, d2))));
187
- const chord = len(sub(b.p, a.p));
996
+ const d1 = mul2(a.dir, -1);
997
+ const d2 = mul2(b.dir, -1);
998
+ const turn = Math.acos(Math.max(-1, Math.min(1, -dot2(d1, d2))));
999
+ const chord = len(sub2(b.p, a.p));
188
1000
  const k = turn < 1e-9 ? chord / 3 : 4 / 3 * Math.tan(turn / 4) * chord / (2 * Math.sin(turn / 2));
189
1001
  if (!Number.isFinite(k)) return null;
190
- 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 };
1002
+ return { tu: a.t, tv: b.t, start: a.p, out: add2(a.p, mul2(d1, k)), in: add2(b.p, mul2(d2, k)), end: b.p };
191
1003
  }
192
1004
  var radiusOf = (a) => {
193
- const r = _optionalChain([a, 'access', _2 => _2.corner, 'optionalAccess', _3 => _3.radius]);
1005
+ const r = _optionalChain([a, 'access', _12 => _12.corner, 'optionalAccess', _13 => _13.radius]);
194
1006
  return typeof r === "number" && r > 0 && Number.isFinite(r) ? r : 0;
195
1007
  };
196
1008
  function hasVectorCorners(subpaths) {
@@ -204,7 +1016,7 @@ function expandVectorCornersOf(sp, subpath = 0) {
204
1016
  const c = cornerOf(sp, subpath, i);
205
1017
  if (!c) return null;
206
1018
  const fitted = fitCornerRadius(c, r);
207
- return fitted > EPS ? cutCorner(c, fitted, _nullishCoalesce(_optionalChain([a, 'access', _4 => _4.corner, 'optionalAccess', _5 => _5.kind]), () => ( "round"))) : null;
1019
+ return fitted > EPS ? cutCorner(c, fitted, _nullishCoalesce(_optionalChain([a, 'access', _14 => _14.corner, 'optionalAccess', _15 => _15.kind]), () => ( "round"))) : null;
208
1020
  });
209
1021
  if (!cuts.some(Boolean)) return { subpath: { closed: sp.closed, anchors: sp.anchors.map(plain) }, sources: sp.anchors.map((_, i) => i) };
210
1022
  const trimmed = sp.anchors.map((a, j) => {
@@ -272,7 +1084,7 @@ function expandVectorCorners(subpaths) {
272
1084
  function setAnchorCorner(a, radius, kind) {
273
1085
  const { corner, ...rest } = a;
274
1086
  const r = Number.isFinite(radius) ? Math.max(0, radius) : 0;
275
- const k = _nullishCoalesce(kind, () => ( _optionalChain([corner, 'optionalAccess', _6 => _6.kind])));
1087
+ const k = _nullishCoalesce(kind, () => ( _optionalChain([corner, 'optionalAccess', _16 => _16.kind])));
276
1088
  if (r <= 0 && !kind) return rest;
277
1089
  return { ...rest, corner: { radius: r, ...k && k !== "round" ? { kind: k } : {} } };
278
1090
  }
@@ -321,7 +1133,7 @@ function sanitizeWidthProfile(points) {
321
1133
  return out.map((p, i) => [p, i]).sort((a, b) => a[0].t - b[0].t || a[1] - b[1]).map(([p]) => p);
322
1134
  }
323
1135
  function isUniformWidthProfile(points) {
324
- return !_optionalChain([points, 'optionalAccess', _7 => _7.length]) || points.every((p) => p.left === 1 && p.right === 1);
1136
+ return !_optionalChain([points, 'optionalAccess', _17 => _17.length]) || points.every((p) => p.left === 1 && p.right === 1);
325
1137
  }
326
1138
  function secant(pts, i, k) {
327
1139
  const a = pts[i];
@@ -354,11 +1166,11 @@ function interp(pts, t, k) {
354
1166
  return Math.max(0, w);
355
1167
  }
356
1168
  function widthProfileAt(points, t) {
357
- if (!_optionalChain([points, 'optionalAccess', _8 => _8.length])) return { left: 1, right: 1 };
1169
+ if (!_optionalChain([points, 'optionalAccess', _18 => _18.length])) return { left: 1, right: 1 };
358
1170
  return { left: interp(points, t, "left"), right: interp(points, t, "right") };
359
1171
  }
360
1172
  function widthProfileMaxScale(points) {
361
- if (!_optionalChain([points, 'optionalAccess', _9 => _9.length])) return 1;
1173
+ if (!_optionalChain([points, 'optionalAccess', _19 => _19.length])) return 1;
362
1174
  return points.reduce((m, p) => Math.max(m, p.left, p.right), 0);
363
1175
  }
364
1176
  function widthProfilePreset(id) {
@@ -368,7 +1180,7 @@ function widthProfilePreset(id) {
368
1180
  function widthProfilePresetId(points) {
369
1181
  if (isUniformWidthProfile(points)) return "uniform";
370
1182
  const same = (a, b) => a.length === b.length && a.every((p, i) => Math.abs(p.t - b[i].t) < 1e-9 && Math.abs(p.left - b[i].left) < 1e-9 && Math.abs(p.right - b[i].right) < 1e-9);
371
- return _nullishCoalesce(_optionalChain([VECTOR_WIDTH_PRESETS, 'access', _10 => _10.find, 'call', _11 => _11((p) => p.points.length && same(points, p.points)), 'optionalAccess', _12 => _12.id]), () => ( "custom"));
1183
+ return _nullishCoalesce(_optionalChain([VECTOR_WIDTH_PRESETS, 'access', _20 => _20.find, 'call', _21 => _21((p) => p.points.length && same(points, p.points)), 'optionalAccess', _22 => _22.id]), () => ( "custom"));
372
1184
  }
373
1185
  function reverseWidthProfile(points) {
374
1186
  return points.map((p) => ({ t: 1 - p.t, left: p.right, right: p.left })).reverse();
@@ -377,12 +1189,12 @@ function mirrorWidthProfile(points) {
377
1189
  return points.map((p) => ({ t: p.t, left: p.right, right: p.left }));
378
1190
  }
379
1191
  function editable(points) {
380
- return _optionalChain([points, 'optionalAccess', _13 => _13.length]) ? points.map((p) => ({ ...p })) : [sym(0, 1), sym(1, 1)];
1192
+ return _optionalChain([points, 'optionalAccess', _23 => _23.length]) ? points.map((p) => ({ ...p })) : [sym(0, 1), sym(1, 1)];
381
1193
  }
382
1194
  function insertWidthPoint(points, t, sides2 = {}, options = {}) {
383
1195
  const pts = editable(points);
384
1196
  const tt = Math.min(Math.max(Number.isFinite(t) ? t : 0, 0), 1);
385
- const here = widthProfileAt(_optionalChain([points, 'optionalAccess', _14 => _14.length]) ? points : null, tt);
1197
+ const here = widthProfileAt(_optionalChain([points, 'optionalAccess', _24 => _24.length]) ? points : null, tt);
386
1198
  const p = {
387
1199
  t: tt,
388
1200
  left: _nullishCoalesce(clean(sides2.left, VECTOR_WIDTH_PROFILE_MAX_SCALE), () => ( here.left)),
@@ -453,7 +1265,7 @@ function generateWidthProfile(options) {
453
1265
  const n = Math.max(8, Math.min(VECTOR_WIDTH_PROFILE_MAX_POINTS * 4, _nullishCoalesce(options.samples, () => ( Math.ceil(32 + cycles * 16)))));
454
1266
  const raw = [];
455
1267
  for (let i = 0; i <= n; i++) raw.push([i / n, taperWaveFactor(i / n, options.taper, options.wave)]);
456
- const ts = [_optionalChain([options, 'access', _15 => _15.taper, 'optionalAccess', _16 => _16.startLength]), _optionalChain([options, 'access', _17 => _17.taper, 'optionalAccess', _18 => _18.endLength]) !== void 0 ? 1 - options.taper.endLength : void 0];
1268
+ const ts = [_optionalChain([options, 'access', _25 => _25.taper, 'optionalAccess', _26 => _26.startLength]), _optionalChain([options, 'access', _27 => _27.taper, 'optionalAccess', _28 => _28.endLength]) !== void 0 ? 1 - options.taper.endLength : void 0];
457
1269
  for (const t of ts) if (typeof t === "number" && t > 0 && t < 1) raw.push([t, taperWaveFactor(t, options.taper, options.wave)]);
458
1270
  raw.sort((a, b) => a[0] - b[0]);
459
1271
  const kept = simplifySamples(raw, _nullishCoalesce(options.tolerance, () => ( 0.01)), VECTOR_WIDTH_PROFILE_MAX_POINTS);
@@ -499,7 +1311,7 @@ function pressureWidthProfile(samples, options = {}) {
499
1311
  // src/vector/varstroke.ts
500
1312
  var SAMPLES_PER_SUBPATH = 128;
501
1313
  var MAX_DASHES = 5e3;
502
- var sub2 = (a, b) => [a[0] - b[0], a[1] - b[1]];
1314
+ var sub3 = (a, b) => [a[0] - b[0], a[1] - b[1]];
503
1315
  var along = (p, v2, k) => [p[0] + v2[0] * k, p[1] + v2[1] * k];
504
1316
  var lerp = (a, b, u) => [a[0] + (b[0] - a[0]) * u, a[1] + (b[1] - a[1]) * u];
505
1317
  var dist = (a, b) => Math.hypot(a[0] - b[0], a[1] - b[1]);
@@ -590,11 +1402,11 @@ function arcPoints(c, a0, sweep, r0, r1, tol) {
590
1402
  return out;
591
1403
  }
592
1404
  function crossing(a0, a1, b0, b1) {
593
- const r = sub2(a1, a0);
594
- const q = sub2(b1, b0);
1405
+ const r = sub3(a1, a0);
1406
+ const q = sub3(b1, b0);
595
1407
  const den = r[0] * q[1] - r[1] * q[0];
596
1408
  if (Math.abs(den) < 1e-12) return null;
597
- const w = sub2(b0, a0);
1409
+ const w = sub3(b0, a0);
598
1410
  const t = (w[0] * q[1] - w[1] * q[0]) / den;
599
1411
  const u = (w[0] * r[1] - w[1] * r[0]) / den;
600
1412
  return t > 1e-9 && t <= 1 && u >= 0 && u < 1 - 1e-9 ? [a0[0] + r[0] * t, a0[1] + r[1] * t] : null;
@@ -620,7 +1432,7 @@ function variableStrokeRings(path, options) {
620
1432
  const w = widthProfileAt(options.profile, s / len2);
621
1433
  return [w.left * half, w.right * half];
622
1434
  };
623
- const intervals = _optionalChain([options, 'access', _19 => _19.dash, 'optionalAccess', _20 => _20.some, 'call', _21 => _21((v2) => v2 > 0)]) ? dashIntervals(options.dash, _nullishCoalesce(options.dashOffset, () => ( 0)), len2) : [[0, len2]];
1435
+ const intervals = _optionalChain([options, 'access', _29 => _29.dash, 'optionalAccess', _30 => _30.some, 'call', _31 => _31((v2) => v2 > 0)]) ? dashIntervals(options.dash, _nullishCoalesce(options.dashOffset, () => ( 0)), len2) : [[0, len2]];
624
1436
  const whole = intervals.length === 1 && intervals[0][0] <= 0 && intervals[0][1] >= len2;
625
1437
  for (const [a, b] of intervals) {
626
1438
  const [pa, ia] = at(poly, a);
@@ -638,7 +1450,7 @@ function variableStrokeRings(path, options) {
638
1450
  const closed = whole && poly.closed;
639
1451
  const m = run.length - 1;
640
1452
  const dirs = [];
641
- for (let i = 0; i < m; i++) dirs.push(unitOf(sub2(run[i + 1].p, run[i].p)));
1453
+ for (let i = 0; i < m; i++) dirs.push(unitOf(sub3(run[i + 1].p, run[i].p)));
642
1454
  const normals = dirs.map(leftNormal);
643
1455
  const w = run.map((q) => sides2(q.s));
644
1456
  const side = (sigma) => {
@@ -657,12 +1469,12 @@ function variableStrokeRings(path, options) {
657
1469
  const d0 = dirs[i];
658
1470
  const d1 = dirs[j];
659
1471
  const cross2 = d0[0] * d1[1] - d0[1] * d1[0];
660
- const dot2 = d0[0] * d1[0] + d0[1] * d1[1];
1472
+ const dot3 = d0[0] * d1[0] + d0[1] * d1[1];
661
1473
  const r0 = w[i + 1][k];
662
1474
  const r1 = w[j][k];
663
1475
  const e = E(i);
664
1476
  const st = S(j);
665
- if (Math.abs(cross2) < 1e-12 && dot2 > 0) {
1477
+ if (Math.abs(cross2) < 1e-12 && dot3 > 0) {
666
1478
  push(out, e);
667
1479
  if (!seam) push(out, st);
668
1480
  continue;
@@ -680,7 +1492,7 @@ function variableStrokeRings(path, options) {
680
1492
  continue;
681
1493
  }
682
1494
  push(out, e);
683
- const theta = uturn ? Math.PI : Math.atan2(cross2, dot2);
1495
+ const theta = uturn ? Math.PI : Math.atan2(cross2, dot3);
684
1496
  const r = (r0 + r1) / 2;
685
1497
  const kind = run[i + 1].corner || seam && poly.corner[0] ? join : "round";
686
1498
  if (r > 1e-9) {
@@ -700,10 +1512,10 @@ function variableStrokeRings(path, options) {
700
1512
  if (closed && out.length > 1 && dist(out[0], out[out.length - 1]) <= 1e-12) out.pop();
701
1513
  return out;
702
1514
  };
703
- const left = side(1);
1515
+ const left2 = side(1);
704
1516
  const right = side(-1);
705
1517
  if (closed) {
706
- rings.push(left, right.reverse());
1518
+ rings.push(left2, right.reverse());
707
1519
  continue;
708
1520
  }
709
1521
  const capPoints = (atStart) => {
@@ -725,7 +1537,7 @@ function variableStrokeRings(path, options) {
725
1537
  return atStart ? [along(R, o, ext), along(L, o, ext)] : [along(L, o, ext), along(R, o, ext)];
726
1538
  };
727
1539
  const ring = [];
728
- for (const q of [...left, ...capPoints(false), ...right.reverse(), ...capPoints(true)]) push(ring, q);
1540
+ for (const q of [...left2, ...capPoints(false), ...right.reverse(), ...capPoints(true)]) push(ring, q);
729
1541
  if (ring.length > 2) rings.push(ring);
730
1542
  }
731
1543
  }
@@ -788,7 +1600,7 @@ function evalSeg(seg, t) {
788
1600
  }
789
1601
  function strokePositionPoint(path, index, t) {
790
1602
  const c = _chunkVNYRI265cjs.pathToContours.call(void 0, path)[index];
791
- if (!_optionalChain([c, 'optionalAccess', _22 => _22.segments, 'access', _23 => _23.length])) return null;
1603
+ if (!_optionalChain([c, 'optionalAccess', _32 => _32.segments, 'access', _33 => _33.length])) return null;
792
1604
  const lengths = c.segments.map((s) => _chunkVNYRI265cjs.segmentLength.call(void 0, s));
793
1605
  const total = lengths.reduce((a, b) => a + b, 0);
794
1606
  let want = Math.min(Math.max(t, 0), 1) * total;
@@ -824,7 +1636,7 @@ function effectiveStrokeAlign(style, closedShape) {
824
1636
  return closedShape ? a : "center";
825
1637
  }
826
1638
  function strokeWidthProfileOf(style) {
827
- if (!_optionalChain([style, 'access', _24 => _24.strokeWidthProfile, 'optionalAccess', _25 => _25.length])) return null;
1639
+ if (!_optionalChain([style, 'access', _34 => _34.strokeWidthProfile, 'optionalAccess', _35 => _35.length])) return null;
828
1640
  const p = sanitizeWidthProfile(style.strokeWidthProfile);
829
1641
  return isUniformWidthProfile(p) ? null : p;
830
1642
  }
@@ -833,7 +1645,7 @@ function hasStrokeExtras(style) {
833
1645
  }
834
1646
  function vectorStrokeLineArea(parts, style, tolerance) {
835
1647
  const profile = strokeWidthProfileOf(style);
836
- const dash = _optionalChain([style, 'access', _26 => _26.strokeDasharray, 'optionalAccess', _27 => _27.length]) ? { dash: style.strokeDasharray } : {};
1648
+ const dash = _optionalChain([style, 'access', _36 => _36.strokeDasharray, 'optionalAccess', _37 => _37.length]) ? { dash: style.strokeDasharray } : {};
837
1649
  const join = _nullishCoalesce(style.strokeLinejoin, () => ( "miter"));
838
1650
  const cap = _nullishCoalesce(style.strokeLinecap, () => ( "butt"));
839
1651
  if (profile) return variableStrokeOutline(parts.line, { width: parts.drawWidth, profile, join, cap, ...dash, ...tolerance ? { tolerance } : {} });
@@ -841,8 +1653,8 @@ function vectorStrokeLineArea(parts, style, tolerance) {
841
1653
  }
842
1654
  var capExtent = (style, width) => style.strokeLinecap === "round" || style.strokeLinecap === "square" ? width / 2 : 0;
843
1655
  function arrowheadWeight(style, end) {
844
- const scale = end === "start" ? style.startArrowScale : style.endArrowScale;
845
- const s = typeof scale === "number" && scale > 0 && Number.isFinite(scale) ? scale : 1;
1656
+ const scale3 = end === "start" ? style.startArrowScale : style.endArrowScale;
1657
+ const s = typeof scale3 === "number" && scale3 > 0 && Number.isFinite(scale3) ? scale3 : 1;
846
1658
  const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
847
1659
  const profile = strokeWidthProfileOf(style);
848
1660
  const at2 = profile ? widthProfileAt(profile, end === "start" ? 0 : 1) : null;
@@ -1082,7 +1894,7 @@ function tangentAt(s, t) {
1082
1894
  const d = _chunkVNYRI265cjs.segmentDerivative.call(void 0, s, t);
1083
1895
  return _nullishCoalesce(unit2(d[0], d[1]), () => ( startTangent(s)));
1084
1896
  }
1085
- var perp = (v2) => [-v2[1], v2[0]];
1897
+ var perp2 = (v2) => [-v2[1], v2[0]];
1086
1898
  function effectPathLength(path) {
1087
1899
  let total = 0;
1088
1900
  for (const s of subsOf(path)) for (const seg of s.segs) total += _chunkVNYRI265cjs.segmentLength.call(void 0, seg);
@@ -1107,8 +1919,8 @@ var Measured = class {
1107
1919
  append(sa, sb, out) {
1108
1920
  for (const piece of _chunkVNYRI265cjs.trimSegments.call(void 0, this.segs, sa, sb, this.lens)) {
1109
1921
  if (degenerate(piece)) continue;
1110
- const last = _optionalChain([out, 'access', _28 => _28[out.length - 1], 'optionalAccess', _29 => _29.segs]);
1111
- const end = _optionalChain([last, 'optionalAccess', _30 => _30[last.length - 1], 'optionalAccess', _31 => _31.at, 'call', _32 => _32(-1)]);
1922
+ const last = _optionalChain([out, 'access', _38 => _38[out.length - 1], 'optionalAccess', _39 => _39.segs]);
1923
+ const end = _optionalChain([last, 'optionalAccess', _40 => _40[last.length - 1], 'optionalAccess', _41 => _41.at, 'call', _42 => _42(-1)]);
1112
1924
  if (!end || Math.hypot(end[0] - piece[0][0], end[1] - piece[0][1]) > 1e-6) out.push({ segs: [piece], closed: false });
1113
1925
  else last.push(piece);
1114
1926
  }
@@ -1252,9 +2064,9 @@ function twistPaths(paths, angle, center) {
1252
2064
  };
1253
2065
  return paths.map((p) => {
1254
2066
  const out = [];
1255
- for (const sub3 of subsOf(p)) {
2067
+ for (const sub5 of subsOf(p)) {
1256
2068
  const segs = [];
1257
- for (const seg of sub3.segs) {
2069
+ for (const seg of sub5.segs) {
1258
2070
  const cubic = seg.length === 2 ? [seg[0], seg[0], seg[1], seg[1]] : seg;
1259
2071
  const len2 = _chunkVNYRI265cjs.segmentLength.call(void 0, seg);
1260
2072
  const m = Math.min(64, Math.max(1, Math.ceil(Math.abs(a) * len2 / rmax / 0.15 + len2 / 50)));
@@ -1267,13 +2079,13 @@ function twistPaths(paths, angle, center) {
1267
2079
  segs.push([p0, [p0[0] + h1[0], p0[1] + h1[1]], [p3[0] + h2[0], p3[1] + h2[1]], p3]);
1268
2080
  }
1269
2081
  }
1270
- out.push({ segs, closed: sub3.closed });
2082
+ out.push({ segs, closed: sub5.closed });
1271
2083
  }
1272
2084
  return subsToPath(out);
1273
2085
  });
1274
2086
  }
1275
- function resample(sub3, m) {
1276
- const segs = sub3.segs;
2087
+ function resample(sub5, m) {
2088
+ const segs = sub5.segs;
1277
2089
  const n = segs.length;
1278
2090
  const lens = segs.map((s) => _chunkVNYRI265cjs.segmentLength.call(void 0, s));
1279
2091
  const out = [];
@@ -1282,7 +2094,7 @@ function resample(sub3, m) {
1282
2094
  for (let k = 0; k < m; k++) {
1283
2095
  if (k === 0) {
1284
2096
  const tout = startTangent(segs[j]);
1285
- const prev = sub3.closed || j > 0 ? (j + n - 1) % n : null;
2097
+ const prev = sub5.closed || j > 0 ? (j + n - 1) % n : null;
1286
2098
  let tan = tout;
1287
2099
  if (prev !== null) {
1288
2100
  const tin = endTangent(segs[prev]);
@@ -1295,7 +2107,7 @@ function resample(sub3, m) {
1295
2107
  }
1296
2108
  }
1297
2109
  }
1298
- if (!sub3.closed) {
2110
+ if (!sub5.closed) {
1299
2111
  const last = segs[n - 1];
1300
2112
  out.push({ p: last[last.length - 1], tan: endTangent(last), lenPrev: lens[n - 1] / m, lenNext: 0 });
1301
2113
  }
@@ -1323,14 +2135,14 @@ function zigzagPaths(paths, size, ridges, smooth = false) {
1323
2135
  const m = Math.min(1e3, Math.max(0, Math.round(Number.isFinite(ridges) ? ridges : 0))) + 1;
1324
2136
  return paths.map((p) => {
1325
2137
  const out = [];
1326
- for (const sub3 of subsOf(p)) {
1327
- const s = resample(sub3, m);
2138
+ for (const sub5 of subsOf(p)) {
2139
+ const s = resample(sub5, m);
1328
2140
  const disp = s.map((x, i) => {
1329
- const n = perp(x.tan);
2141
+ const n = perp2(x.tan);
1330
2142
  const d = i % 2 === 0 ? size : -size;
1331
2143
  return [n[0] * d, n[1] * d];
1332
2144
  });
1333
- const e = emit(s, disp, sub3.closed, smooth, 0.36);
2145
+ const e = emit(s, disp, sub5.closed, smooth, 0.36);
1334
2146
  if (e) out.push(e);
1335
2147
  }
1336
2148
  return subsToPath(out);
@@ -1369,8 +2181,8 @@ function wigglePaths(paths, w) {
1369
2181
  let idx = 0;
1370
2182
  return paths.map((p) => {
1371
2183
  const out = [];
1372
- for (const sub3 of subsOf(p)) {
1373
- const s = resample(sub3, m);
2184
+ for (const sub5 of subsOf(p)) {
2185
+ const s = resample(sub5, m);
1374
2186
  const disp = s.map(() => {
1375
2187
  const i = BigInt(idx);
1376
2188
  const ox = noise(seed, i * 2n, t + idx * 0.37);
@@ -1378,7 +2190,7 @@ function wigglePaths(paths, w) {
1378
2190
  idx++;
1379
2191
  return [(sx * corr + ox * (1 - corr)) * w.size, (sy * corr + oy * (1 - corr)) * w.size];
1380
2192
  });
1381
- const e = emit(s, disp, sub3.closed, w.smooth, 1 / 3);
2193
+ const e = emit(s, disp, sub5.closed, w.smooth, 1 / 3);
1382
2194
  if (e) out.push(e);
1383
2195
  }
1384
2196
  return subsToPath(out);
@@ -1402,9 +2214,9 @@ function vectorRepeaterCopies(r, pivot) {
1402
2214
  for (let i = 0; i < n; i++) {
1403
2215
  const k = i + offset;
1404
2216
  let m = translateMatrix(-pivot.x, -pivot.y);
1405
- m = multiplyMatrices(m, scaleMatrix(Math.pow((_nullishCoalesce(_optionalChain([r, 'access', _33 => _33.scale, 'optionalAccess', _34 => _34.x]), () => ( 100))) / 100, k), Math.pow((_nullishCoalesce(_optionalChain([r, 'access', _35 => _35.scale, 'optionalAccess', _36 => _36.y]), () => ( 100))) / 100, k)));
2217
+ m = multiplyMatrices(m, scaleMatrix(Math.pow((_nullishCoalesce(_optionalChain([r, 'access', _43 => _43.scale, 'optionalAccess', _44 => _44.x]), () => ( 100))) / 100, k), Math.pow((_nullishCoalesce(_optionalChain([r, 'access', _45 => _45.scale, 'optionalAccess', _46 => _46.y]), () => ( 100))) / 100, k)));
1406
2218
  m = multiplyMatrices(m, rotateMatrix((_nullishCoalesce(r.rotation, () => ( 0))) * k));
1407
- m = multiplyMatrices(m, translateMatrix(pivot.x + (_nullishCoalesce(_optionalChain([r, 'access', _37 => _37.position, 'optionalAccess', _38 => _38.x]), () => ( 0))) * k, pivot.y + (_nullishCoalesce(_optionalChain([r, 'access', _39 => _39.position, 'optionalAccess', _40 => _40.y]), () => ( 0))) * k));
2219
+ m = multiplyMatrices(m, translateMatrix(pivot.x + (_nullishCoalesce(_optionalChain([r, 'access', _47 => _47.position, 'optionalAccess', _48 => _48.x]), () => ( 0))) * k, pivot.y + (_nullishCoalesce(_optionalChain([r, 'access', _49 => _49.position, 'optionalAccess', _50 => _50.y]), () => ( 0))) * k));
1408
2220
  const t = n > 1 ? i / (n - 1) : 0;
1409
2221
  const partial = i + 1 === n && fract > 0 ? fract : 1;
1410
2222
  out.push({ index: i, matrix: m, opacity: Math.min(1, Math.max(0, (so + (eo - so) * t) * partial)) });
@@ -1544,7 +2356,7 @@ function normalizeVectorEffectParams(type, params, base) {
1544
2356
  if (!defaults) return null;
1545
2357
  const out = { ...defaults, ..._nullishCoalesce(base, () => ( {})) };
1546
2358
  for (const spec of VECTOR_EFFECT_PARAM_SPECS[type]) {
1547
- const v2 = _optionalChain([params, 'optionalAccess', _41 => _41[spec.key]]);
2359
+ const v2 = _optionalChain([params, 'optionalAccess', _51 => _51[spec.key]]);
1548
2360
  if (v2 === void 0) continue;
1549
2361
  if (spec.kind === "number" && typeof v2 === "number" && Number.isFinite(v2)) out[spec.key] = Math.min(_nullishCoalesce(spec.max, () => ( Infinity)), Math.max(_nullishCoalesce(spec.min, () => ( -Infinity)), v2));
1550
2362
  else if (spec.kind === "boolean" && typeof v2 === "boolean") out[spec.key] = v2;
@@ -1559,10 +2371,10 @@ function createVectorEffect(type, id, params) {
1559
2371
  }
1560
2372
  var isEnabled = (e) => e.enabled !== false && !!VECTOR_EFFECT_DEFAULTS[e.type];
1561
2373
  function activeVectorEffects(node) {
1562
- return _nullishCoalesce(_optionalChain([node, 'access', _42 => _42.effects, 'optionalAccess', _43 => _43.filter, 'call', _44 => _44(isEnabled)]), () => ( []));
2374
+ return _nullishCoalesce(_optionalChain([node, 'access', _52 => _52.effects, 'optionalAccess', _53 => _53.filter, 'call', _54 => _54(isEnabled)]), () => ( []));
1563
2375
  }
1564
2376
  function hasOwnVectorAppearance(node) {
1565
- return node.type === "group" && !!node.liveBoolean || !!_optionalChain([node, 'access', _45 => _45.effects, 'optionalAccess', _46 => _46.some, 'call', _47 => _47(isEnabled)]);
2377
+ return node.type === "group" && !!node.liveBoolean || !!_optionalChain([node, 'access', _55 => _55.effects, 'optionalAccess', _56 => _56.some, 'call', _57 => _57(isEnabled)]);
1566
2378
  }
1567
2379
  var liveCache = /* @__PURE__ */ new WeakMap();
1568
2380
  function hasVectorAppearance(node) {
@@ -1574,7 +2386,7 @@ function hasVectorAppearance(node) {
1574
2386
  return v2;
1575
2387
  }
1576
2388
  function isVectorLiveBoolean(node) {
1577
- return _optionalChain([node, 'optionalAccess', _48 => _48.type]) === "group" && !!node.liveBoolean;
2389
+ return _optionalChain([node, 'optionalAccess', _58 => _58.type]) === "group" && !!node.liveBoolean;
1578
2390
  }
1579
2391
  function vectorOperandOperation(group, child) {
1580
2392
  return _nullishCoalesce(_nullishCoalesce(child.booleanOperation, () => ( group.liveBoolean)), () => ( "union"));
@@ -1691,7 +2503,7 @@ var compareOrder = (a, b) => {
1691
2503
  for (let i = 0; i < n; i++) if (a[i] !== b[i]) return a[i] - b[i];
1692
2504
  return a.length - b.length;
1693
2505
  };
1694
- var add2 = (a, b) => ({ x: a.x + (_nullishCoalesce(_optionalChain([b, 'optionalAccess', _49 => _49.x]), () => ( 0))), y: a.y + (_nullishCoalesce(_optionalChain([b, 'optionalAccess', _50 => _50.y]), () => ( 0))) });
2506
+ var add3 = (a, b) => ({ x: a.x + (_nullishCoalesce(_optionalChain([b, 'optionalAccess', _59 => _59.x]), () => ( 0))), y: a.y + (_nullishCoalesce(_optionalChain([b, 'optionalAccess', _60 => _60.y]), () => ( 0))) });
1695
2507
  var mapPaths = (shapes, fn) => {
1696
2508
  const out = fn(shapes.map((s) => s.path));
1697
2509
  return shapes.map((s, i) => ({ ...s, path: _nullishCoalesce(out[i], () => ( [])) }));
@@ -1713,7 +2525,7 @@ function applyVectorEffects(shapes, effects) {
1713
2525
  list = mapPaths(list, (p) => puckerBloatPaths(p, e.params.amount));
1714
2526
  break;
1715
2527
  case "twist":
1716
- list = mapPaths(list, (p) => twistPaths(p, e.params.angle, add2(pathsCenter(p), e.params.center)));
2528
+ list = mapPaths(list, (p) => twistPaths(p, e.params.angle, add3(pathsCenter(p), e.params.center)));
1717
2529
  break;
1718
2530
  case "zigzag":
1719
2531
  list = mapPaths(list, (p) => zigzagPaths(p, e.params.size, e.params.ridges, e.params.smooth));
@@ -1736,7 +2548,7 @@ function applyVectorEffects(shapes, effects) {
1736
2548
  const copies = repeatPaths(
1737
2549
  list.map((s) => s.path),
1738
2550
  r,
1739
- add2(pathsCenter(list.map((s) => s.path)), r.anchor)
2551
+ add3(pathsCenter(list.map((s) => s.path)), r.anchor)
1740
2552
  );
1741
2553
  const n = copies.length;
1742
2554
  const next = [];
@@ -1982,8 +2794,8 @@ function inferAnchorType(a) {
1982
2794
  const li = Math.hypot(ix, iy);
1983
2795
  const lo = Math.hypot(ox, oy);
1984
2796
  const cross2 = (ix * oy - iy * ox) / (li * lo);
1985
- const dot2 = (ix * ox + iy * oy) / (li * lo);
1986
- if (Math.abs(cross2) > 1e-3 || dot2 > 0) return "corner";
2797
+ const dot3 = (ix * ox + iy * oy) / (li * lo);
2798
+ if (Math.abs(cross2) > 1e-3 || dot3 > 0) return "corner";
1987
2799
  return Math.abs(li - lo) <= 1e-3 * Math.max(li, lo) ? "symmetric" : "smooth";
1988
2800
  }
1989
2801
  function bezierToSubpaths(input) {
@@ -2119,7 +2931,7 @@ function boundsFromPoints(a, b) {
2119
2931
  }
2120
2932
  function hitTestVectorNode(node, p, tolerance = 0, parent = IDENTITY_MATRIX, options = {}) {
2121
2933
  if (node.visible === false) return false;
2122
- if (!(node.type === "group" && (!hasOwnVectorAppearance(node) || _optionalChain([options, 'access', _51 => _51.entered, 'optionalAccess', _52 => _52.has, 'call', _53 => _53(node.id)])))) node = expandVectorAppearance(node);
2934
+ if (!(node.type === "group" && (!hasOwnVectorAppearance(node) || _optionalChain([options, 'access', _61 => _61.entered, 'optionalAccess', _62 => _62.has, 'call', _63 => _63(node.id)])))) node = expandVectorAppearance(node);
2123
2935
  if (node.type === "group") {
2124
2936
  const m = multiplyMatrices(nodeMatrix(node), parent);
2125
2937
  return node.children.some((c) => hitTestVectorNode(c, p, tolerance, m, options));
@@ -2218,20 +3030,23 @@ function mapAnchor(a, m) {
2218
3030
  }
2219
3031
  function bakeIntoGeometry(node, m) {
2220
3032
  if (isIdentityMatrix(m)) return node;
2221
- if (_optionalChain([node, 'access', _54 => _54.effects, 'optionalAccess', _55 => _55.length])) node = { ...node, effects: transformVectorEffects(node.effects, m) };
3033
+ if (_optionalChain([node, 'access', _64 => _64.effects, 'optionalAccess', _65 => _65.length])) node = { ...node, effects: transformVectorEffects(node.effects, m) };
2222
3034
  if (node.type === "group") {
2223
3035
  const group = node;
2224
3036
  const children = group.children.map((c) => transformVectorNode(c, m));
2225
- const spine = _optionalChain([group, 'access', _56 => _56.blend, 'optionalAccess', _57 => _57.spine]);
3037
+ const spine = _optionalChain([group, 'access', _66 => _66.blend, 'optionalAccess', _67 => _67.spine]);
2226
3038
  if (!spine) return { ...group, children };
2227
3039
  const moved = { closed: spine.closed, anchors: spine.anchors.map((a) => mapAnchor(a, m)) };
2228
3040
  return { ...group, children, blend: { ...group.blend, spine: moved } };
2229
3041
  }
2230
3042
  const path = node;
2231
- const mirrored = m[0] * m[3] - m[1] * m[2] < 0 && _optionalChain([path, 'access', _58 => _58.strokeWidthProfile, 'optionalAccess', _59 => _59.length]);
3043
+ const mirrored = m[0] * m[3] - m[1] * m[2] < 0 && _optionalChain([path, 'access', _68 => _68.strokeWidthProfile, 'optionalAccess', _69 => _69.length]);
2232
3044
  return {
2233
3045
  ...path,
2234
3046
  ...mirrored ? { strokeWidthProfile: mirrorWidthProfile(path.strokeWidthProfile) } : {},
3047
+ // Gradients in the node's own space move with its geometry.
3048
+ ...isVectorGradient(path.fill) ? { fill: transformVectorPaint(path.fill, m) } : {},
3049
+ ...isVectorGradient(path.stroke) ? { stroke: transformVectorPaint(path.stroke, m) } : {},
2235
3050
  subpaths: path.subpaths.map((sp) => ({
2236
3051
  closed: sp.closed,
2237
3052
  anchors: sp.anchors.map((a) => mapAnchor(a, m))
@@ -2334,12 +3149,12 @@ function insertAnchor(sp, segment, t) {
2334
3149
  const ia = segment;
2335
3150
  const ib = (segment + 1) % n;
2336
3151
  const seg = subpathSegment(sp, segment);
2337
- const [left, right] = _chunkVNYRI265cjs.splitSegment.call(void 0, seg, t);
2338
- const mid = left[left.length - 1];
3152
+ const [left2, right] = _chunkVNYRI265cjs.splitSegment.call(void 0, seg, t);
3153
+ const mid = left2[left2.length - 1];
2339
3154
  const anchor = { x: mid[0], y: mid[1] };
2340
3155
  if (seg.length === 4) {
2341
- anchors[ia].out = { x: left[1][0], y: left[1][1] };
2342
- anchor.in = { x: left[2][0], y: left[2][1] };
3156
+ anchors[ia].out = { x: left2[1][0], y: left2[1][1] };
3157
+ anchor.in = { x: left2[2][0], y: left2[2][1] };
2343
3158
  anchor.out = { x: right[1][0], y: right[1][1] };
2344
3159
  anchor.type = "smooth";
2345
3160
  anchors[ib].in = { x: right[2][0], y: right[2][1] };
@@ -2424,6 +3239,166 @@ function roundSubpaths(subpaths, precision = 3) {
2424
3239
  }));
2425
3240
  }
2426
3241
 
3242
+ // src/vector/gradientStroke.ts
3243
+ var SHARP = 1.25;
3244
+ var TOLERANCE = 0.2;
3245
+ var OVERLAP = 0.5;
3246
+ var fmt2 = (n, digits = 3) => {
3247
+ const k = 10 ** digits;
3248
+ const v2 = Math.round(n * k) / k;
3249
+ return Object.is(v2, -0) ? "0" : String(v2);
3250
+ };
3251
+ var toSvgStops2 = (stops) => stops.map((s) => ({ offset: `${fmt2(s.offset * 100)}%`, stopColor: s.color, ...s.opacity !== void 0 && s.opacity !== 1 ? { stopOpacity: Math.round(s.opacity * 1e4) / 1e4 } : {} }));
3252
+ var polygonD2 = (pts, precision) => `M${pts.map((p) => `${fmt2(p.x, precision)} ${fmt2(p.y, precision)}`).join("L")}Z`;
3253
+ var add4 = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
3254
+ var sub4 = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
3255
+ var scale2 = (a, k) => ({ x: a.x * k, y: a.y * k });
3256
+ var left = (d) => ({ x: d.y, y: -d.x });
3257
+ var unit3 = (v2) => {
3258
+ const l = Math.hypot(v2.x, v2.y);
3259
+ return l > 1e-12 ? { x: v2.x / l, y: v2.y / l } : { x: 1, y: 0 };
3260
+ };
3261
+ function flattenedSubpaths(node) {
3262
+ const out = [];
3263
+ for (const contour of _chunkVNYRI265cjs.pathToContours.call(void 0, subpathsToBezier(node.subpaths))) {
3264
+ const segs = _chunkVNYRI265cjs.closeContour.call(void 0, contour);
3265
+ if (!segs.length) continue;
3266
+ const ring = _chunkVNYRI265cjs.flattenChain.call(void 0, segs, TOLERANCE);
3267
+ const pts = ring.map(([x, y]) => ({ x, y })).filter((p, i, a) => i === 0 || Math.hypot(p.x - a[i - 1].x, p.y - a[i - 1].y) > 1e-9);
3268
+ if (contour.closed && pts.length > 2 && Math.hypot(pts[0].x - pts[pts.length - 1].x, pts[0].y - pts[pts.length - 1].y) < 1e-9) pts.pop();
3269
+ if (pts.length >= 2) out.push({ pts, closed: contour.closed && pts.length > 2 });
3270
+ }
3271
+ return out;
3272
+ }
3273
+ var signedArea = (pts) => {
3274
+ let a = 0;
3275
+ for (let i = 0; i < pts.length; i++) {
3276
+ const p = pts[i];
3277
+ const q = pts[(i + 1) % pts.length];
3278
+ a += p.x * q.y - q.x * p.y;
3279
+ }
3280
+ return a / 2;
3281
+ };
3282
+ function vectorStrokeGradientDefs(g, id, node, options = {}) {
3283
+ const precision = _nullishCoalesce(options.precision, () => ( 3));
3284
+ const mode = g.strokeMode === "across" ? "across" : "along";
3285
+ const expanded = expandVectorGradientStops(g);
3286
+ const reach = vectorStrokeReach(node) + 1;
3287
+ const area = _nullishCoalesce(options.area, () => ( vectorGradientArea(options.bounds && { x: options.bounds.x - reach, y: options.bounds.y - reach, width: options.bounds.width + 2 * reach, height: options.bounds.height + 2 * reach })));
3288
+ const width = _nullishCoalesce(node.strokeWidth, () => ( 1));
3289
+ const align = _nullishCoalesce(node.strokeAlign, () => ( "center"));
3290
+ const profile = strokeWidthProfileOf(node);
3291
+ const gradients = [];
3292
+ const patches = [];
3293
+ const slices = [];
3294
+ const colourAt = (t) => {
3295
+ const s = spanVectorGradientStops(g, t, t, expanded)[0];
3296
+ return s;
3297
+ };
3298
+ let counter = 0;
3299
+ const solid = (pts, t) => {
3300
+ const s = colourAt(t);
3301
+ const gid = `${id}-p${counter++}`;
3302
+ gradients.push({ id: gid, type: "linear", attrs: { gradientUnits: "userSpaceOnUse", x1: 0, y1: 0, x2: 1, y2: 0 }, stops: toSvgStops2([{ ...s, offset: 0 }]) });
3303
+ patches.push({ d: polygonD2(pts, precision), fill: `url(#${gid})` });
3304
+ };
3305
+ const disc = (c, r) => Array.from({ length: 16 }, (_, k) => add4(c, scale2({ x: Math.cos(k * Math.PI / 8), y: Math.sin(k * Math.PI / 8) }, r)));
3306
+ for (const { pts, closed } of flattenedSubpaths(node)) {
3307
+ const n = pts.length;
3308
+ const segCount = closed ? n : n - 1;
3309
+ const dirs = [];
3310
+ const lengths = [0];
3311
+ for (let i = 0; i < segCount; i++) {
3312
+ const a = pts[i];
3313
+ const b = pts[(i + 1) % n];
3314
+ dirs.push(unit3(sub4(b, a)));
3315
+ lengths.push(lengths[i] + Math.hypot(b.x - a.x, b.y - a.y));
3316
+ }
3317
+ const total = lengths[segCount];
3318
+ if (!(total > 1e-9)) continue;
3319
+ const tAt = (i) => lengths[i] / total;
3320
+ const clockwise = closed && signedArea(pts) > 0;
3321
+ const visibleLeft = align === "center" || !closed ? null : align === "inside" ? !clockwise : clockwise;
3322
+ const ribs = [];
3323
+ for (let i = 0; i < n; i++) {
3324
+ const p = pts[i];
3325
+ const din = i > 0 || closed ? dirs[(i - 1 + segCount) % segCount] : null;
3326
+ const dout = i < segCount ? dirs[i] : null;
3327
+ const square = (d) => ({ l: add4(p, scale2(left(d), reach)), r: sub4(p, scale2(left(d), reach)) });
3328
+ if (!din || !dout) {
3329
+ const rib = square(_nullishCoalesce(din, () => ( dout)));
3330
+ ribs.push({ before: rib, after: rib });
3331
+ continue;
3332
+ }
3333
+ const nin = left(din);
3334
+ const nout = left(dout);
3335
+ const den = 1 + nin.x * nout.x + nin.y * nout.y;
3336
+ const stretch = den > 1e-12 ? Math.sqrt(2 / den) : Infinity;
3337
+ if (stretch <= SHARP) {
3338
+ const m = scale2(add4(nin, nout), reach / den);
3339
+ const rib = { l: add4(p, m), r: sub4(p, m) };
3340
+ ribs.push({ before: rib, after: rib });
3341
+ } else {
3342
+ ribs.push({ before: square(din), after: square(dout) });
3343
+ solid(disc(p, reach * 1.5), tAt(i));
3344
+ }
3345
+ }
3346
+ if (!closed) {
3347
+ const d0 = dirs[0];
3348
+ const d1 = dirs[segCount - 1];
3349
+ const a = pts[0];
3350
+ const b = pts[n - 1];
3351
+ const cap = (p, d) => [add4(p, scale2(left(d), reach)), add4(add4(p, scale2(d, reach)), scale2(left(d), reach)), sub4(add4(p, scale2(d, reach)), scale2(left(d), reach)), sub4(p, scale2(left(d), reach))];
3352
+ solid(cap(a, scale2(d0, -1)), 0);
3353
+ solid(cap(b, d1), 1);
3354
+ }
3355
+ for (let i = 0; i < segCount; i++) {
3356
+ const j = (i + 1) % n;
3357
+ const a = pts[i];
3358
+ const b = pts[j];
3359
+ const d = dirs[i];
3360
+ const from = ribs[i].after;
3361
+ const to = ribs[j].before;
3362
+ const ext = scale2(d, OVERLAP);
3363
+ const quad = [from.l, add4(to.l, ext), add4(to.r, ext), from.r];
3364
+ const gid = `${id}-s${counter++}`;
3365
+ const t0 = tAt(i);
3366
+ const t1 = closed && i === segCount - 1 ? 1 : tAt(i + 1);
3367
+ if (mode === "along") {
3368
+ gradients.push({
3369
+ id: gid,
3370
+ type: "linear",
3371
+ attrs: { gradientUnits: "userSpaceOnUse", x1: fmt2(a.x, 4), y1: fmt2(a.y, 4), x2: fmt2(b.x, 4), y2: fmt2(b.y, 4) },
3372
+ stops: toSvgStops2(spanVectorGradientStops(g, t0, t1, expanded))
3373
+ });
3374
+ } else {
3375
+ const mid = scale2(add4(a, b), 0.5);
3376
+ const side = widthProfileAt(profile, (t0 + t1) / 2);
3377
+ const nrm = left(d);
3378
+ const reachL = visibleLeft === null ? width / 2 * side.left : visibleLeft ? width * side.left : 0;
3379
+ const reachR = visibleLeft === null ? width / 2 * side.right : visibleLeft ? 0 : width * side.right;
3380
+ const p0 = add4(mid, scale2(nrm, reachL));
3381
+ const p1 = sub4(mid, scale2(nrm, reachR));
3382
+ gradients.push({
3383
+ id: gid,
3384
+ type: "linear",
3385
+ attrs: { gradientUnits: "userSpaceOnUse", x1: fmt2(p0.x, 4), y1: fmt2(p0.y, 4), x2: fmt2(p1.x, 4), y2: fmt2(p1.y, 4) },
3386
+ stops: toSvgStops2(expanded)
3387
+ });
3388
+ }
3389
+ slices.push({ d: polygonD2(quad, precision), fill: `url(#${gid})` });
3390
+ }
3391
+ }
3392
+ const def = {
3393
+ id,
3394
+ type: "pattern",
3395
+ attrs: patternAttrs(area),
3396
+ gradients,
3397
+ tiles: [...patches, ...slices]
3398
+ };
3399
+ return [def];
3400
+ }
3401
+
2427
3402
  // src/vector/svg.ts
2428
3403
  function vectorCornersAttribute(subpaths, precision = 3) {
2429
3404
  const out = [];
@@ -2439,7 +3414,7 @@ function applyVectorCornersAttribute(subpaths, value) {
2439
3414
  for (const item of value.trim().split(/\s+/)) {
2440
3415
  const m = /^(\d+)\.(\d+):([\d.eE+-]+)(?::([a-z]+))?$/.exec(item);
2441
3416
  if (!m) continue;
2442
- const a = _optionalChain([subpaths, 'access', _60 => _60[+m[1]], 'optionalAccess', _61 => _61.anchors, 'access', _62 => _62[+m[2]]]);
3417
+ const a = _optionalChain([subpaths, 'access', _70 => _70[+m[1]], 'optionalAccess', _71 => _71.anchors, 'access', _72 => _72[+m[2]]]);
2443
3418
  const radius = parseFloat(m[3]);
2444
3419
  if (!a || !(radius > 0)) continue;
2445
3420
  const kind = VECTOR_CORNER_KINDS.includes(m[4]) ? m[4] : void 0;
@@ -2448,10 +3423,30 @@ function applyVectorCornersAttribute(subpaths, value) {
2448
3423
  return subpaths;
2449
3424
  }
2450
3425
  var escapeAttr = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2451
- function vectorPaintToSvg(paint, gradientId) {
2452
- if (!hasPaint(paint)) return { value: "none", gradient: null };
2453
- if (typeof paint === "string") return { value: paint, gradient: null };
2454
- return { value: `url(#${gradientId})`, gradient: _chunkVNYRI265cjs.gradientToSvgDef.call(void 0, paint, gradientId) };
3426
+ function vectorPathLocalBounds(node) {
3427
+ const b = _chunkVNYRI265cjs.bezierPathBounds.call(void 0, subpathsToBezier(node.subpaths));
3428
+ return b ? { x: b.minX, y: b.minY, width: b.maxX - b.minX, height: b.maxY - b.minY } : null;
3429
+ }
3430
+ var paintCache = /* @__PURE__ */ new WeakMap();
3431
+ function vectorPaintToSvg(paint, gradientId, options = {}) {
3432
+ if (!hasPaint(paint)) return { value: "none", defs: [] };
3433
+ if (typeof paint === "string") return { value: paint, defs: [] };
3434
+ if (!isVectorGradient(paint)) return { value: "none", defs: [] };
3435
+ const role = _nullishCoalesce(options.role, () => ( "fill"));
3436
+ const precision = _nullishCoalesce(options.precision, () => ( 3));
3437
+ const key = `${gradientId}|${role}|${precision}`;
3438
+ const owner = _nullishCoalesce(options.node, () => ( paint));
3439
+ const cached = _optionalChain([paintCache, 'access', _73 => _73.get, 'call', _74 => _74(owner), 'optionalAccess', _75 => _75.get, 'call', _76 => _76(key)]);
3440
+ if (cached && (!options.node || (role === "fill" ? options.node.fill : options.node.stroke) === paint)) return { value: `url(#${gradientId})`, defs: cached };
3441
+ const node = options.node;
3442
+ const bounds = node ? vectorPathLocalBounds(node) : null;
3443
+ const reach = node && role === "stroke" ? vectorStrokeReach(node) + 1 : 1;
3444
+ const area = bounds ? { x: bounds.x - reach, y: bounds.y - reach, width: bounds.width + 2 * reach, height: bounds.height + 2 * reach } : null;
3445
+ const defs = node && role === "stroke" && (paint.strokeMode === "along" || paint.strokeMode === "across") ? vectorStrokeGradientDefs(paint, gradientId, node, { bounds, area, precision }) : vectorGradientSvgDefs(paint, gradientId, { bounds, area, precision });
3446
+ let byKey = paintCache.get(owner);
3447
+ if (!byKey) paintCache.set(owner, byKey = /* @__PURE__ */ new Map());
3448
+ byKey.set(key, defs);
3449
+ return { value: `url(#${gradientId})`, defs };
2455
3450
  }
2456
3451
  var partsCache = /* @__PURE__ */ new WeakMap();
2457
3452
  function vectorStrokeSvgParts(node, clipId, precision = 3) {
@@ -2490,18 +3485,26 @@ function widthProfileFromAttribute(value) {
2490
3485
  if (!value) return [];
2491
3486
  return sanitizeWidthProfile(
2492
3487
  value.split(";").map((part) => {
2493
- const [t, left, right] = part.split(",").map((n) => parseFloat(n));
2494
- return { t, left, right: Number.isFinite(right) ? right : left };
3488
+ const [t, left2, right] = part.split(",").map((n) => parseFloat(n));
3489
+ return { t, left: left2, right: Number.isFinite(right) ? right : left2 };
2495
3490
  })
2496
3491
  );
2497
3492
  }
2498
3493
  var round2 = (n) => Math.round(n * 100) / 100;
2499
- function gradientMarkup(def) {
3494
+ function gradientMarkup(def, extra = "") {
2500
3495
  const tag = def.type === "radial" ? "radialGradient" : "linearGradient";
2501
3496
  const attrs = Object.entries(def.attrs).map(([k, v2]) => ` ${k}="${escapeAttr(String(v2))}"`).join("");
2502
3497
  const stops = def.stops.map((s) => `<stop offset="${s.offset}" stop-color="${escapeAttr(s.stopColor)}"${s.stopOpacity !== void 0 ? ` stop-opacity="${s.stopOpacity}"` : ""}/>`).join("");
2503
- return `<${tag} id="${escapeAttr(def.id)}"${attrs}>${stops}</${tag}>`;
3498
+ return `<${tag} id="${escapeAttr(def.id)}"${attrs}${extra}>${stops}</${tag}>`;
2504
3499
  }
3500
+ function paintDefMarkup(def, gradient) {
3501
+ const extra = gradient ? ` data-vector-gradient="${escapeAttr(JSON.stringify(gradient))}"` : "";
3502
+ if (def.type !== "pattern") return gradientMarkup(def, extra);
3503
+ const attrs = Object.entries(def.attrs).map(([k, v2]) => ` ${k}="${escapeAttr(String(v2))}"`).join("");
3504
+ const inner = def.gradients.map((g) => gradientMarkup(g)).join("") + def.tiles.map((t) => `<path d="${t.d}" fill="${escapeAttr(t.fill)}"/>`).join("");
3505
+ return `<pattern id="${escapeAttr(def.id)}"${attrs}${extra}>${inner}</pattern>`;
3506
+ }
3507
+ var paintMarkup = (paint, defs) => defs.map((d, i) => paintDefMarkup(d, i === 0 && isVectorGradient(paint) ? paint : void 0));
2505
3508
  function blendFromAttribute(value, m) {
2506
3509
  if (!value) return null;
2507
3510
  let raw;
@@ -2535,12 +3538,12 @@ function vectorDocumentToSvg(doc, options = {}) {
2535
3538
  const pretty = options.pretty !== false;
2536
3539
  const defs = [];
2537
3540
  const lines = [];
2538
- const num = (n) => _chunkVNYRI265cjs.formatNumber.call(void 0, n, precision);
3541
+ const num2 = (n) => _chunkVNYRI265cjs.formatNumber.call(void 0, n, precision);
2539
3542
  const indent = (depth) => pretty ? " ".repeat(depth) : "";
2540
3543
  const common = (node) => {
2541
3544
  let out2 = ` id="${escapeAttr(node.id)}"`;
2542
3545
  if (node.transform) out2 += ` transform="matrix(${node.transform.map((n) => _chunkVNYRI265cjs.formatNumber.call(void 0, n, 6)).join(" ")})"`;
2543
- if (node.opacity !== void 0 && node.opacity < 1) out2 += ` opacity="${num(node.opacity)}"`;
3546
+ if (node.opacity !== void 0 && node.opacity < 1) out2 += ` opacity="${num2(node.opacity)}"`;
2544
3547
  if (node.visible === false) out2 += ` display="none"`;
2545
3548
  if (node.blendStep) out2 += ` data-vector-blend-step=""`;
2546
3549
  if (node.type === "group" && node.blend) out2 += ` data-vector-blend="${escapeAttr(JSON.stringify(node.blend))}"`;
@@ -2552,7 +3555,7 @@ function vectorDocumentToSvg(doc, options = {}) {
2552
3555
  if (hasOwnVectorAppearance(node)) {
2553
3556
  const live2 = {
2554
3557
  kind: node.type,
2555
- ..._optionalChain([node, 'access', _63 => _63.effects, 'optionalAccess', _64 => _64.length]) ? { effects: node.effects } : {},
3558
+ ..._optionalChain([node, 'access', _77 => _77.effects, 'optionalAccess', _78 => _78.length]) ? { effects: node.effects } : {},
2556
3559
  ...node.type === "group" && node.liveBoolean ? { liveBoolean: node.liveBoolean } : {}
2557
3560
  };
2558
3561
  lines.push(`${indent(depth)}<g${common(node)} data-vector-live="${escapeAttr(JSON.stringify(live2))}">`);
@@ -2574,19 +3577,18 @@ function vectorDocumentToSvg(doc, options = {}) {
2574
3577
  lines.push(`${indent(depth)}</g>`);
2575
3578
  return;
2576
3579
  }
2577
- const fill = vectorPaintToSvg(node.fill, `${prefix}fill-${node.id}`);
2578
- const stroke = vectorPaintToSvg(node.stroke, `${prefix}stroke-${node.id}`);
2579
- if (fill.gradient) defs.push(gradientMarkup(fill.gradient));
2580
- if (stroke.gradient) defs.push(gradientMarkup(stroke.gradient));
3580
+ const fill = vectorPaintToSvg(node.fill, `${prefix}fill-${node.id}`, { node, precision });
3581
+ const stroke = vectorPaintToSvg(node.stroke, `${prefix}stroke-${node.id}`, { node, role: "stroke", precision });
3582
+ defs.push(...paintMarkup(node.fill, fill.defs), ...paintMarkup(node.stroke, stroke.defs));
2581
3583
  const d = vectorPathData(node, precision);
2582
3584
  const corners2 = vectorCornersAttribute(node.subpaths, precision);
2583
3585
  const live = corners2 ? ` data-vector-d="${_chunkVNYRI265cjs.bezierPathToSvg.call(void 0, subpathsToBezier(node.subpaths, { corners: false }), { precision })}" data-vector-corners="${corners2}"` : "";
2584
3586
  const fillAttrs = ` fill="${escapeAttr(fill.value)}"${node.fillRule === "evenodd" ? ` fill-rule="evenodd"` : ""}`;
2585
3587
  const lineAttrs = (width) => {
2586
- let out2 = ` stroke="${escapeAttr(stroke.value)}" stroke-width="${num(width)}"`;
3588
+ let out2 = ` stroke="${escapeAttr(stroke.value)}" stroke-width="${num2(width)}"`;
2587
3589
  if (node.strokeLinecap && node.strokeLinecap !== "butt") out2 += ` stroke-linecap="${node.strokeLinecap}"`;
2588
3590
  if (node.strokeLinejoin && node.strokeLinejoin !== "miter") out2 += ` stroke-linejoin="${node.strokeLinejoin}"`;
2589
- if (_optionalChain([node, 'access', _65 => _65.strokeDasharray, 'optionalAccess', _66 => _66.length])) out2 += ` stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
3591
+ if (_optionalChain([node, 'access', _79 => _79.strokeDasharray, 'optionalAccess', _80 => _80.length])) out2 += ` stroke-dasharray="${node.strokeDasharray.map(num2).join(" ")}"`;
2590
3592
  return out2;
2591
3593
  };
2592
3594
  const parts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${prefix}clip-${node.id}`, precision) : void 0;
@@ -2594,19 +3596,19 @@ function vectorDocumentToSvg(doc, options = {}) {
2594
3596
  lines.push(`${indent(depth)}<path${common(node)} d="${d}"${fillAttrs}${stroke.value !== "none" ? lineAttrs(_nullishCoalesce(node.strokeWidth, () => ( 1))) : ""}${live}/>`);
2595
3597
  return;
2596
3598
  }
2597
- let data = ` data-vector-path="" data-vector-stroke-width="${num(_nullishCoalesce(node.strokeWidth, () => ( 1)))}"`;
3599
+ let data = ` data-vector-path="" data-vector-stroke-width="${num2(_nullishCoalesce(node.strokeWidth, () => ( 1)))}"`;
2598
3600
  if (parts.align !== "center") data += ` data-vector-stroke-align="${parts.align}"`;
2599
3601
  if (node.startArrow) data += ` data-vector-start-arrow="${node.startArrow}"`;
2600
3602
  if (node.endArrow) data += ` data-vector-end-arrow="${node.endArrow}"`;
2601
3603
  if (node.arrowAlign && node.arrowAlign !== "extend") data += ` data-vector-arrow-align="${node.arrowAlign}"`;
2602
- if (node.startArrowScale !== void 0 && node.startArrowScale !== 1) data += ` data-vector-start-arrow-scale="${num(node.startArrowScale)}"`;
2603
- if (node.endArrowScale !== void 0 && node.endArrowScale !== 1) data += ` data-vector-end-arrow-scale="${num(node.endArrowScale)}"`;
3604
+ if (node.startArrowScale !== void 0 && node.startArrowScale !== 1) data += ` data-vector-start-arrow-scale="${num2(node.startArrowScale)}"`;
3605
+ if (node.endArrowScale !== void 0 && node.endArrowScale !== 1) data += ` data-vector-end-arrow-scale="${num2(node.endArrowScale)}"`;
2604
3606
  const profile = strokeWidthProfileOf(node);
2605
3607
  if (profile) {
2606
3608
  data += ` data-vector-width-profile="${widthProfileToAttribute(profile)}"`;
2607
3609
  if (node.strokeLinecap && node.strokeLinecap !== "butt") data += ` data-vector-stroke-linecap="${node.strokeLinecap}"`;
2608
3610
  if (node.strokeLinejoin && node.strokeLinejoin !== "miter") data += ` data-vector-stroke-linejoin="${node.strokeLinejoin}"`;
2609
- if (_optionalChain([node, 'access', _67 => _67.strokeDasharray, 'optionalAccess', _68 => _68.length])) data += ` data-vector-stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
3611
+ if (_optionalChain([node, 'access', _81 => _81.strokeDasharray, 'optionalAccess', _82 => _82.length])) data += ` data-vector-stroke-dasharray="${node.strokeDasharray.map(num2).join(" ")}"`;
2610
3612
  }
2611
3613
  lines.push(`${indent(depth)}<g${common(node)}${data}>`);
2612
3614
  lines.push(`${indent(depth + 1)}<path data-vector-part="fill" d="${d}"${fillAttrs}${live}/>`);
@@ -2623,7 +3625,7 @@ function vectorDocumentToSvg(doc, options = {}) {
2623
3625
  clip = ` clip-path="url(#${id})"`;
2624
3626
  } else {
2625
3627
  const b = parts.clip.bounds;
2626
- const box = `x="${num(b.x)}" y="${num(b.y)}" width="${num(b.width)}" height="${num(b.height)}"`;
3628
+ const box = `x="${num2(b.x)}" y="${num2(b.y)}" width="${num2(b.width)}" height="${num2(b.height)}"`;
2627
3629
  defs.push(`<mask id="${id}" maskUnits="userSpaceOnUse" ${box}><rect ${box} fill="#fff"/><path d="${d}" fill="#000" fill-rule="${rule}"/></mask>`);
2628
3630
  clip = ` mask="url(#${id})"`;
2629
3631
  }
@@ -2635,8 +3637,8 @@ function vectorDocumentToSvg(doc, options = {}) {
2635
3637
  lines.push(`${indent(depth)}</g>`);
2636
3638
  };
2637
3639
  for (const node of doc.nodes) emit2(node, 1);
2638
- const w = num(doc.width);
2639
- const h = num(doc.height);
3640
+ const w = num2(doc.width);
3641
+ const h = num2(doc.height);
2640
3642
  const nl = pretty ? "\n" : "";
2641
3643
  const out = [`<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">`];
2642
3644
  if (defs.length) out.push(`${indent(1)}<defs>${defs.join("")}</defs>`);
@@ -2766,28 +3768,89 @@ var fraction = (s, fallback) => {
2766
3768
  if (!Number.isFinite(n)) return fallback;
2767
3769
  return s.trim().endsWith("%") ? n / 100 : n;
2768
3770
  };
3771
+ var PAINT_SERVERS = /* @__PURE__ */ new Set(["linearGradient", "radialGradient", "pattern"]);
2769
3772
  function collectGradients(el, raw) {
2770
- if ((el.name === "linearGradient" || el.name === "radialGradient") && el.attrs.id) raw.set(el.attrs.id, el);
3773
+ if (PAINT_SERVERS.has(el.name) && el.attrs.id) raw.set(el.attrs.id, el);
2771
3774
  for (const c of el.children) collectGradients(c, raw);
2772
3775
  }
2773
- function resolveGradient(id, raw, seen = /* @__PURE__ */ new Set()) {
2774
- const el = raw.get(id);
2775
- if (!el || seen.has(id)) return null;
2776
- seen.add(id);
2777
- let stops = readStops(el);
2778
- const href = _nullishCoalesce(el.attrs.href, () => ( el.attrs["xlink:href"]));
2779
- if (!stops.length && _optionalChain([href, 'optionalAccess', _69 => _69.startsWith, 'call', _70 => _70("#")])) stops = _nullishCoalesce(_optionalChain([resolveGradient, 'call', _71 => _71(href.slice(1), raw, seen), 'optionalAccess', _72 => _72.stops]), () => ( []));
2780
- if (!stops.length) return null;
2781
- if (el.name === "radialGradient") {
2782
- 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 };
3776
+ function gradientFromAttribute(value) {
3777
+ if (!value) return null;
3778
+ try {
3779
+ return sanitizeVectorGradient(JSON.parse(value));
3780
+ } catch (e5) {
3781
+ return null;
2783
3782
  }
2784
- const x1 = fraction(el.attrs.x1, 0);
2785
- const y1 = fraction(el.attrs.y1, 0);
2786
- const x2 = fraction(el.attrs.x2, 1);
2787
- const y2 = fraction(el.attrs.y2, 0);
2788
- const angle = Math.round((Math.atan2(x2 - x1, -(y2 - y1)) * 180 / Math.PI + 360) % 360);
2789
- return { type: "linear", angle, stops };
2790
3783
  }
3784
+ function resolveGradient(id, raw) {
3785
+ const first = raw.get(id);
3786
+ if (!first) return null;
3787
+ const own = gradientFromAttribute(first.attrs["data-vector-gradient"]);
3788
+ if (own) return own;
3789
+ if (first.name === "pattern") return null;
3790
+ const chain = [];
3791
+ for (let el = first; el && !chain.includes(el) && chain.length < 32; ) {
3792
+ chain.push(el);
3793
+ const href = _nullishCoalesce(el.attrs.href, () => ( el.attrs["xlink:href"]));
3794
+ el = _optionalChain([href, 'optionalAccess', _83 => _83.startsWith, 'call', _84 => _84("#")]) ? raw.get(href.slice(1)) : void 0;
3795
+ }
3796
+ const attr = (name) => _optionalChain([chain, 'access', _85 => _85.find, 'call', _86 => _86((el) => el.attrs[name] !== void 0), 'optionalAccess', _87 => _87.attrs, 'access', _88 => _88[name]]);
3797
+ const stops = _nullishCoalesce(chain.map(readStops).find((s) => s.length), () => ( []));
3798
+ if (!stops.length) return null;
3799
+ const user = attr("gradientUnits") === "userSpaceOnUse";
3800
+ const t = parseSvgTransform(attr("gradientTransform"));
3801
+ const plain3 = t === IDENTITY_MATRIX || t[0] === 1 && t[1] === 0 && t[2] === 0 && t[3] === 1 && t[4] === 0 && t[5] === 0;
3802
+ const at2 = (name, fallback) => user ? length(attr(name), fallback) : fraction(attr(name), fallback);
3803
+ const spreadAttr = attr("spreadMethod");
3804
+ const spread = spreadAttr === "reflect" || spreadAttr === "repeat" ? { spread: spreadAttr } : {};
3805
+ const map = (p) => applySvgMatrix(t, p);
3806
+ if (first.name === "radialGradient") {
3807
+ const cx = at2("cx", user ? 0 : 0.5);
3808
+ const cy = at2("cy", user ? 0 : 0.5);
3809
+ const r = at2("r", user ? 0 : 0.5);
3810
+ const fx = attr("fx") !== void 0 ? at2("fx", cx) : cx;
3811
+ const fy = attr("fy") !== void 0 ? at2("fy", cy) : cy;
3812
+ const fr = attr("fr") !== void 0 ? at2("fr", 0) : 0;
3813
+ const focal = Math.abs(fx - cx) > 1e-9 || Math.abs(fy - cy) > 1e-9;
3814
+ if (!user && plain3 && !focal && !(fr > 0)) return { type: "radial", cx, cy, r, stops, ...spread };
3815
+ const g = {
3816
+ type: "radial",
3817
+ stops,
3818
+ start: map({ x: cx, y: cy }),
3819
+ end: map({ x: cx + r, y: cy }),
3820
+ width: map({ x: cx, y: cy + r }),
3821
+ units: user ? "userSpaceOnUse" : "objectBoundingBox",
3822
+ ...spread
3823
+ };
3824
+ if (focal) g.focal = map({ x: fx, y: fy });
3825
+ if (fr > 0 && r > 0) g.focalRadius = Math.min(0.99, fr / r);
3826
+ return g;
3827
+ }
3828
+ const x1 = at2("x1", 0);
3829
+ const y1 = at2("y1", 0);
3830
+ const x2 = attr("x2") !== void 0 ? at2("x2", 1) : user ? 0 : 1;
3831
+ const y2 = at2("y2", 0);
3832
+ if (!user && plain3) {
3833
+ const angle = Math.round((Math.atan2(x2 - x1, -(y2 - y1)) * 180 / Math.PI + 360) % 360);
3834
+ const c = _chunkVNYRI265cjs.angleToSvgLinearCoords.call(void 0, angle);
3835
+ const len2 = Math.hypot(x2 - x1, y2 - y1);
3836
+ const d = len2 > 0 ? { x: (x2 - x1) / len2, y: (y2 - y1) / len2 } : { x: 0, y: 1 };
3837
+ const along2 = (x, y) => x * d.x + y * d.y;
3838
+ const same = len2 > 0 && Math.abs((c.x2 - c.x1) * d.y - (c.y2 - c.y1) * d.x) < 1e-3 && Math.abs(along2(c.x1, c.y1) - along2(x1, y1)) < 1e-3 && Math.abs(along2(c.x2, c.y2) - along2(x2, y2)) < 1e-3;
3839
+ if (same) return { type: "linear", angle, stops, ...spread };
3840
+ }
3841
+ const start = { x: x1, y: y1 };
3842
+ const end = { x: x2, y: y2 };
3843
+ return {
3844
+ type: "linear",
3845
+ stops,
3846
+ start: map(start),
3847
+ end: map(end),
3848
+ width: map({ x: x1 - (y2 - y1), y: y1 + (x2 - x1) }),
3849
+ units: user ? "userSpaceOnUse" : "objectBoundingBox",
3850
+ ...spread
3851
+ };
3852
+ }
3853
+ var applySvgMatrix = (m, p) => ({ x: m[0] * p.x + m[2] * p.y + m[4], y: m[1] * p.x + m[3] * p.y + m[5] });
2791
3854
  function parseVectorSvg(svg, options = {}) {
2792
3855
  const root = parseXml(svg);
2793
3856
  if (!root) throw new Error("The markup has no <svg> element.");
@@ -2880,7 +3943,7 @@ function parseVectorSvg(svg, options = {}) {
2880
3943
  const part = (name) => g.children.find((c) => c.name === "path" && c.attrs["data-vector-part"] === name);
2881
3944
  const fillEl = part("fill");
2882
3945
  const subpaths = fillEl ? shapeOf(fillEl) : null;
2883
- if (!fillEl || !_optionalChain([subpaths, 'optionalAccess', _73 => _73.length])) return null;
3946
+ if (!fillEl || !_optionalChain([subpaths, 'optionalAccess', _89 => _89.length])) return null;
2884
3947
  const a = g.attrs;
2885
3948
  const fs = readStyle(fillEl, style);
2886
3949
  const strokeEl = part("stroke");
@@ -2888,7 +3951,7 @@ function parseVectorSvg(svg, options = {}) {
2888
3951
  const ss = strokeEl ? readStyle(strokeEl, style) : null;
2889
3952
  const width2 = length(a["data-vector-stroke-width"], ss ? length(ss.strokeWidth, 1) : 1);
2890
3953
  const arrow = (v2) => VECTOR_ARROWHEADS.includes(v2) ? v2 : void 0;
2891
- const scale = (v2) => {
3954
+ const scale3 = (v2) => {
2892
3955
  const n = v2 === void 0 ? NaN : parseFloat(v2);
2893
3956
  return n > 0 ? n : void 0;
2894
3957
  };
@@ -2912,12 +3975,12 @@ function parseVectorSvg(svg, options = {}) {
2912
3975
  ...arrow(a["data-vector-start-arrow"]) ? { startArrow: arrow(a["data-vector-start-arrow"]) } : {},
2913
3976
  ...arrow(a["data-vector-end-arrow"]) ? { endArrow: arrow(a["data-vector-end-arrow"]) } : {},
2914
3977
  ...a["data-vector-arrow-align"] === "tip" ? { arrowAlign: "tip" } : {},
2915
- ...scale(a["data-vector-start-arrow-scale"]) ? { startArrowScale: scale(a["data-vector-start-arrow-scale"]) } : {},
2916
- ...scale(a["data-vector-end-arrow-scale"]) ? { endArrowScale: scale(a["data-vector-end-arrow-scale"]) } : {},
3978
+ ...scale3(a["data-vector-start-arrow-scale"]) ? { startArrowScale: scale3(a["data-vector-start-arrow-scale"]) } : {},
3979
+ ...scale3(a["data-vector-end-arrow-scale"]) ? { endArrowScale: scale3(a["data-vector-end-arrow-scale"]) } : {},
2917
3980
  ...profile.length ? { strokeWidthProfile: profile } : {},
2918
3981
  ...keptCap ? { strokeLinecap: keptCap } : {},
2919
3982
  ...keptJoin ? { strokeLinejoin: keptJoin } : {},
2920
- ..._optionalChain([keptDash, 'optionalAccess', _74 => _74.length]) ? { strokeDasharray: keptDash } : {}
3983
+ ..._optionalChain([keptDash, 'optionalAccess', _90 => _90.length]) ? { strokeDasharray: keptDash } : {}
2921
3984
  };
2922
3985
  return placed(node, m);
2923
3986
  };
@@ -2925,7 +3988,7 @@ function parseVectorSvg(svg, options = {}) {
2925
3988
  let raw = {};
2926
3989
  try {
2927
3990
  raw = _nullishCoalesce(JSON.parse(g.attrs["data-vector-live"]), () => ( {}));
2928
- } catch (e5) {
3991
+ } catch (e6) {
2929
3992
  raw = {};
2930
3993
  }
2931
3994
  const sourceEl = g.children.find((c) => c.name === "g" && c.attrs["data-vector-part"] === "source");
@@ -3006,6 +4069,72 @@ function parseVectorSvg(svg, options = {}) {
3006
4069
  return { width, height, nodes };
3007
4070
  }
3008
4071
 
4072
+ // src/vector/gradientTool.ts
4073
+ var VECTOR_GRADIENT_STOP_GAP = 12;
4074
+ var VECTOR_GRADIENT_MIDPOINT_GAP = 9;
4075
+ var VECTOR_GRADIENT_OFF_BAR = 24;
4076
+ var STOP_HIT = 6;
4077
+ var MIDPOINT_HIT = 5;
4078
+ var HANDLE_HIT = 6;
4079
+ var BAR_HIT = 5;
4080
+ var fmt3 = (n) => Math.round(n * 100) / 100;
4081
+ var apply2 = (m, p) => ({ x: m[0] * p.x + m[2] * p.y + m[4], y: m[1] * p.x + m[3] * p.y + m[5] });
4082
+ function vectorGradientTargetPath(doc, selection, target) {
4083
+ for (const id of selection) {
4084
+ const node = findVectorNode(doc.nodes, id);
4085
+ if (!node) continue;
4086
+ const path = collectPathNodes(node).find((p) => isVectorGradient(p[target]));
4087
+ if (path) return path;
4088
+ }
4089
+ return null;
4090
+ }
4091
+ function getVectorGradientAnnotator(doc, selection, viewport, target) {
4092
+ const path = vectorGradientTargetPath(doc, selection, target);
4093
+ if (!path) return null;
4094
+ const gradient = path[target];
4095
+ const world = _nullishCoalesce(vectorWorldMatrix(doc.nodes, path.id), () => ( IDENTITY_MATRIX));
4096
+ const screen = multiplyMatrices(world, [viewport.zoom, 0, 0, viewport.zoom, viewport.x, viewport.y]);
4097
+ const bounds = vectorPathLocalBounds(path);
4098
+ const h = vectorGradientHandles(gradient, bounds, screen);
4099
+ const v2 = { x: h.end.x - h.start.x, y: h.end.y - h.start.y };
4100
+ const len2 = Math.hypot(v2.x, v2.y);
4101
+ const normal = len2 > 1e-9 ? { x: -v2.y / len2, y: v2.x / len2 } : { x: 0, y: 1 };
4102
+ const at2 = (t, gap) => ({ x: h.start.x + v2.x * t + normal.x * gap, y: h.start.y + v2.y * t + normal.y * gap });
4103
+ const sorted = sortedVectorGradientStops(gradient);
4104
+ const stops = sorted.map((s, index) => ({ ...s, index, ...at2(s.offset, VECTOR_GRADIENT_STOP_GAP) }));
4105
+ const midpoints = sorted.slice(0, -1).flatMap((s, index) => {
4106
+ const pos = vectorGradientMidpointPosition(sorted, index);
4107
+ return pos === null || sorted[index + 1].offset - s.offset < 1e-6 ? [] : [{ index, midpoint: _nullishCoalesce(s.midpoint, () => ( 0.5)), ...at2(pos, -VECTOR_GRADIENT_MIDPOINT_GAP) }];
4108
+ });
4109
+ let extent = null;
4110
+ if (isCentredVectorGradient(gradient.type)) {
4111
+ const frame2 = multiplyMatrices(vectorGradientMatrix(gradient, bounds), screen);
4112
+ const pts = gradient.type === "diamond" ? [{ x: 1, y: 0 }, { x: 0, y: 1 }, { x: -1, y: 0 }, { x: 0, y: -1 }] : Array.from({ length: 48 }, (_, k) => ({ x: Math.cos(k * Math.PI / 24), y: Math.sin(k * Math.PI / 24) }));
4113
+ extent = `M${pts.map((p) => apply2(frame2, p)).map((p) => `${fmt3(p.x)} ${fmt3(p.y)}`).join("L")}Z`;
4114
+ }
4115
+ return { nodeId: path.id, target, gradient, bounds, world, start: h.start, end: h.end, aspect: h.aspect, focal: h.focal, normal, stops, midpoints, extent };
4116
+ }
4117
+ function vectorGradientBarPosition(a, p) {
4118
+ const v2 = { x: a.end.x - a.start.x, y: a.end.y - a.start.y };
4119
+ const l2 = v2.x * v2.x + v2.y * v2.y;
4120
+ const d = { x: p.x - a.start.x, y: p.y - a.start.y };
4121
+ if (l2 < 1e-12) return { offset: 0, across: Math.hypot(d.x, d.y) };
4122
+ return { offset: (d.x * v2.x + d.y * v2.y) / l2, across: d.x * a.normal.x + d.y * a.normal.y };
4123
+ }
4124
+ function hitVectorGradientAnnotator(a, p) {
4125
+ const near2 = (q, r) => !!q && Math.hypot(q.x - p.x, q.y - p.y) <= r;
4126
+ for (let i = a.stops.length - 1; i >= 0; i--) if (near2(a.stops[i], STOP_HIT)) return { kind: "stop", index: a.stops[i].index };
4127
+ for (const m of a.midpoints) if (near2(m, MIDPOINT_HIT)) return { kind: "midpoint", index: m.index };
4128
+ const centred = isCentredVectorGradient(a.gradient.type);
4129
+ if (near2(a.end, HANDLE_HIT)) return { kind: "handle", handle: centred ? "radius" : "end" };
4130
+ if (a.focal && near2(a.focal, HANDLE_HIT) && !near2(a.start, 1)) return { kind: "handle", handle: "focal" };
4131
+ if (near2(a.start, HANDLE_HIT)) return { kind: "handle", handle: "start" };
4132
+ if (near2(a.aspect, HANDLE_HIT)) return { kind: "handle", handle: "aspect" };
4133
+ const { offset, across } = vectorGradientBarPosition(a, p);
4134
+ if (offset >= 0 && offset <= 1 && across >= -BAR_HIT && across <= VECTOR_GRADIENT_STOP_GAP + STOP_HIT) return { kind: "bar", offset };
4135
+ return null;
4136
+ }
4137
+
3009
4138
  // src/vector/blend.ts
3010
4139
  var lerpN = (a, b, t) => a + (b - a) * t;
3011
4140
  var lerpP = (a, b, t) => [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
@@ -3097,10 +4226,10 @@ function refineMorphContour(c, segments) {
3097
4226
  const extra = segments - have;
3098
4227
  const want = lens.map((l) => sum > 1e-12 ? l / sum * extra : extra / have);
3099
4228
  const parts = want.map((w) => 1 + Math.floor(w));
3100
- let left = segments - parts.reduce((a, b) => a + b, 0);
4229
+ let left2 = segments - parts.reduce((a, b) => a + b, 0);
3101
4230
  const frac = want.map((w) => w - Math.floor(w));
3102
4231
  const order = parts.map((_, i) => i).sort((a, b) => Math.abs(frac[b] - frac[a]) > 1e-9 ? frac[b] - frac[a] : a - b);
3103
- for (let i = 0; left > 0; i = (i + 1) % order.length, left--) parts[order[i]]++;
4232
+ for (let i = 0; left2 > 0; i = (i + 1) % order.length, left2--) parts[order[i]]++;
3104
4233
  const pts = [c.pts[0]];
3105
4234
  const anchors = [0];
3106
4235
  let at2 = 0;
@@ -3209,19 +4338,19 @@ function bestStart(fixed, free, useShapeContext) {
3209
4338
  const gsc = useShapeContext ? shapeContexts(ga, free.closed) : null;
3210
4339
  const m = Math.max(1, fn.length);
3211
4340
  const cost = (s) => {
3212
- let dot2 = 0;
4341
+ let dot3 = 0;
3213
4342
  let cross2 = 0;
3214
4343
  let sc = 0;
3215
4344
  for (let i = 0; i < fn.length; i++) {
3216
4345
  const j = free.closed ? (i + s) % n : i;
3217
4346
  const p = fn[i];
3218
4347
  const q = gn[j];
3219
- dot2 += p[0] * q[0] + p[1] * q[1];
4348
+ dot3 += p[0] * q[0] + p[1] * q[1];
3220
4349
  cross2 += p[0] * q[1] - p[1] * q[0];
3221
4350
  if (fsc && gsc) sc += chi2(fsc[i], gsc[j]);
3222
4351
  }
3223
- const residual = Math.max(0, 1 - (dot2 * dot2 + cross2 * cross2) / (m * m));
3224
- const turn = 1 - Math.cos(Math.atan2(cross2, dot2));
4352
+ const residual = Math.max(0, 1 - (dot3 * dot3 + cross2 * cross2) / (m * m));
4353
+ const turn = 1 - Math.cos(Math.atan2(cross2, dot3));
3225
4354
  return residual + TURN_WEIGHT * turn + sc / m;
3226
4355
  };
3227
4356
  if (!free.closed) return { start: 0, cost: cost(0) };
@@ -3366,16 +4495,16 @@ function procrustes(a, b) {
3366
4495
  const ca = frame(pa).c;
3367
4496
  const cb = frame(pb).c;
3368
4497
  let ss = 0;
3369
- let dot2 = 0;
4498
+ let dot3 = 0;
3370
4499
  let cross2 = 0;
3371
4500
  for (let i = 0; i < Math.min(pa.length, pb.length); i++) {
3372
4501
  const p = [pa[i][0] - ca[0], pa[i][1] - ca[1]];
3373
4502
  const q = [pb[i][0] - cb[0], pb[i][1] - cb[1]];
3374
4503
  ss += p[0] * p[0] + p[1] * p[1];
3375
- dot2 += p[0] * q[0] + p[1] * q[1];
4504
+ dot3 += p[0] * q[0] + p[1] * q[1];
3376
4505
  cross2 += p[0] * q[1] - p[1] * q[0];
3377
4506
  }
3378
- return { ca, cb, theta: Math.atan2(cross2, dot2), s: ss > 1e-12 ? Math.hypot(dot2, cross2) / ss : 1 };
4507
+ return { ca, cb, theta: Math.atan2(cross2, dot3), s: ss > 1e-12 ? Math.hypot(dot3, cross2) / ss : 1 };
3379
4508
  }
3380
4509
  function interpolateMorphContours(a, b, u, options = {}) {
3381
4510
  const w = options.linear ? 0 : Math.min(1, Math.max(0, _nullishCoalesce(options.rigidity, () => ( 1))));
@@ -3429,39 +4558,24 @@ function interpolateVectorColor(a, b, t) {
3429
4558
  if (t >= 1) return b;
3430
4559
  return _nullishCoalesce(_chunkMR3Z4OQIcjs.mixColorsOklab.call(void 0, a, b, t), () => ( (t < 0.5 ? a : b)));
3431
4560
  }
3432
- function sortedStops(g) {
3433
- return g.stops.slice().sort((x, y) => x.offset - y.offset);
3434
- }
3435
- function sampleGradient(stops, offset) {
3436
- if (!stops.length) return { color: "transparent", opacity: 1 };
3437
- const first = stops[0];
3438
- const last = stops[stops.length - 1];
3439
- if (offset <= first.offset) return { color: first.color, opacity: _nullishCoalesce(first.opacity, () => ( 1)) };
3440
- if (offset >= last.offset) return { color: last.color, opacity: _nullishCoalesce(last.opacity, () => ( 1)) };
3441
- let i = 0;
3442
- while (i + 1 < stops.length && stops[i + 1].offset < offset) i++;
3443
- const x = stops[i];
3444
- const y = stops[i + 1];
3445
- const u = y.offset - x.offset > 1e-12 ? (offset - x.offset) / (y.offset - x.offset) : 1;
3446
- return { color: interpolateVectorColor(x.color, y.color, u), opacity: lerpN(_nullishCoalesce(x.opacity, () => ( 1)), _nullishCoalesce(y.opacity, () => ( 1)), u) };
3447
- }
3448
4561
  function resampleGradientStops(g, offsets) {
3449
- const stops = sortedStops(g);
3450
4562
  return offsets.map((offset) => {
3451
- const s = sampleGradient(stops, offset);
3452
- return s.opacity < 1 ? { offset, color: s.color, opacity: s.opacity } : { offset, color: s.color };
4563
+ const s = sampleVectorGradient(g, offset);
4564
+ return s.opacity < 1 ? { offset, color: s.color, opacity: Math.round(s.opacity * 1e4) / 1e4 } : { offset, color: s.color };
3453
4565
  });
3454
4566
  }
3455
4567
  function lerpAngle(a, b, t) {
3456
4568
  const d = ((b - a + 180) % 360 + 360) % 360 - 180;
3457
4569
  return a + d * t;
3458
4570
  }
3459
- function lerpGradient(a, b, t) {
3460
- if (a.type !== b.type) return null;
3461
- let stopsA = sortedStops(a);
3462
- let stopsB = sortedStops(b);
3463
- if (stopsA.length !== stopsB.length) {
3464
- const offs = [...stopsA, ...stopsB].map((s) => s.offset).filter(Number.isFinite).sort((x, y) => x - y);
4571
+ var lerpPoint = (a, b, t) => ({ x: lerpN(a.x, b.x, t), y: lerpN(a.y, b.y, t) });
4572
+ var sameColourModel = (a, b) => (_nullishCoalesce(a.interpolation, () => ( "srgb"))) === (_nullishCoalesce(b.interpolation, () => ( "srgb"))) && (_nullishCoalesce(a.smoothness, () => ( 0))) === (_nullishCoalesce(b.smoothness, () => ( 0)));
4573
+ function lerpStops(a, b, t) {
4574
+ let stopsA = sortedVectorGradientStops(a);
4575
+ let stopsB = sortedVectorGradientStops(b);
4576
+ const resampled = stopsA.length !== stopsB.length || !sameColourModel(a, b);
4577
+ if (resampled) {
4578
+ const offs = [...expandVectorGradientStops(a), ...expandVectorGradientStops(b), ...stopsA, ...stopsB].map((s) => s.offset).filter(Number.isFinite).sort((x, y) => x - y);
3465
4579
  const common = offs.filter((o, i) => i === 0 || o - offs[i - 1] > 1e-4);
3466
4580
  stopsA = resampleGradientStops(a, common);
3467
4581
  stopsB = resampleGradientStops(b, common);
@@ -3471,12 +4585,53 @@ function lerpGradient(a, b, t) {
3471
4585
  const opacity = lerpN(_nullishCoalesce(x.opacity, () => ( 1)), _nullishCoalesce(y.opacity, () => ( 1)), t);
3472
4586
  const stop = { offset: lerpN(x.offset, y.offset, t), color: interpolateVectorColor(x.color, y.color, t) };
3473
4587
  if (opacity < 1) stop.opacity = opacity;
4588
+ const mid = lerpN(_nullishCoalesce(x.midpoint, () => ( 0.5)), _nullishCoalesce(y.midpoint, () => ( 0.5)), t);
4589
+ if (Math.abs(mid - 0.5) > 1e-6) stop.midpoint = mid;
3474
4590
  return stop;
3475
4591
  });
3476
- if (a.type === "linear" && b.type === "linear") return { type: "linear", angle: lerpAngle(_nullishCoalesce(a.angle, () => ( 90)), _nullishCoalesce(b.angle, () => ( 90)), t), stops };
3477
- const ra = a;
3478
- const rb = b;
3479
- return { type: "radial", cx: lerpN(_nullishCoalesce(ra.cx, () => ( 0.5)), _nullishCoalesce(rb.cx, () => ( 0.5)), t), cy: lerpN(_nullishCoalesce(ra.cy, () => ( 0.5)), _nullishCoalesce(rb.cy, () => ( 0.5)), t), r: lerpN(_nullishCoalesce(ra.r, () => ( 0.5)), _nullishCoalesce(rb.r, () => ( 0.5)), t), stops };
4592
+ return { stops, resampled };
4593
+ }
4594
+ function lerpGradient(a, b, t) {
4595
+ if (a.type !== b.type) return null;
4596
+ const { stops, resampled } = lerpStops(a, b, t);
4597
+ const near2 = t < 0.5 ? a : b;
4598
+ const out = { type: a.type, stops };
4599
+ if (!resampled) {
4600
+ if (a.interpolation && a.interpolation !== "srgb") out.interpolation = a.interpolation;
4601
+ if (a.smoothness) out.smoothness = a.smoothness;
4602
+ }
4603
+ if (near2.spread && near2.spread !== "pad") out.spread = near2.spread;
4604
+ if (near2.strokeMode && near2.strokeMode !== "within") out.strokeMode = near2.strokeMode;
4605
+ const ea = hasExplicitGradientGeometry(a);
4606
+ const eb = hasExplicitGradientGeometry(b);
4607
+ if (!ea && !eb) {
4608
+ if (a.angle !== void 0 || b.angle !== void 0 || a.type === "linear" || a.type === "reflected") {
4609
+ const fallback = a.type === "linear" || a.type === "reflected" ? 180 : 90;
4610
+ out.angle = lerpAngle(_nullishCoalesce(a.angle, () => ( fallback)), _nullishCoalesce(b.angle, () => ( fallback)), t);
4611
+ }
4612
+ if (a.type !== "linear" && a.type !== "reflected") {
4613
+ out.cx = lerpN(_nullishCoalesce(a.cx, () => ( 0.5)), _nullishCoalesce(b.cx, () => ( 0.5)), t);
4614
+ out.cy = lerpN(_nullishCoalesce(a.cy, () => ( 0.5)), _nullishCoalesce(b.cy, () => ( 0.5)), t);
4615
+ out.r = lerpN(_nullishCoalesce(a.r, () => ( 0.5)), _nullishCoalesce(b.r, () => ( 0.5)), t);
4616
+ }
4617
+ return out;
4618
+ }
4619
+ const pa = vectorGradientPoints(a);
4620
+ const pb = vectorGradientPoints(b);
4621
+ if (pa.units !== pb.units) {
4622
+ const src = explicitVectorGradient(near2);
4623
+ return { ...src, stops: out.stops, ...out.interpolation ? { interpolation: out.interpolation } : {}, ...out.smoothness ? { smoothness: out.smoothness } : {} };
4624
+ }
4625
+ out.start = lerpPoint(pa.start, pb.start, t);
4626
+ out.end = lerpPoint(pa.end, pb.end, t);
4627
+ out.width = lerpPoint(pa.width, pb.width, t);
4628
+ out.units = pa.units;
4629
+ if (a.type === "radial") {
4630
+ if (pa.focal || pb.focal) out.focal = lerpPoint(_nullishCoalesce(pa.focal, () => ( pa.start)), _nullishCoalesce(pb.focal, () => ( pb.start)), t);
4631
+ const fr = lerpN(pa.focalRadius, pb.focalRadius, t);
4632
+ if (fr > 0) out.focalRadius = fr;
4633
+ }
4634
+ return out;
3480
4635
  }
3481
4636
  function solidLike(color, like) {
3482
4637
  return { ...like, stops: like.stops.map((s) => color === null ? { offset: s.offset, color: s.color, opacity: 0 } : { offset: s.offset, color }) };
@@ -3490,15 +4645,15 @@ function interpolateVectorPaint(a, b, t) {
3490
4645
  if (typeof a === "string" && pa && typeof b === "string" && pb) return interpolateVectorColor(a, b, t);
3491
4646
  if (!pa && typeof b === "string") return interpolateVectorColor("transparent", b, t);
3492
4647
  if (!pb && typeof a === "string") return interpolateVectorColor(a, "transparent", t);
3493
- const ga = pa && typeof a === "object" && a ? a : null;
3494
- const gb = pb && typeof b === "object" && b ? b : null;
4648
+ const ga = pa && isVectorGradient(a) ? a : null;
4649
+ const gb = pb && isVectorGradient(b) ? b : null;
3495
4650
  if (ga && gb) return _nullishCoalesce(lerpGradient(ga, gb, t), () => ( (t < 0.5 ? a : b)));
3496
4651
  if (gb) return _nullishCoalesce(lerpGradient(solidLike(pa ? a : null, gb), gb, t), () => ( b));
3497
4652
  if (ga) return _nullishCoalesce(lerpGradient(ga, solidLike(pb ? b : null, ga), t), () => ( a));
3498
4653
  return t < 0.5 ? a : b;
3499
4654
  }
3500
4655
  function interpolateWidthProfiles(a, b, t) {
3501
- if (!_optionalChain([a, 'optionalAccess', _75 => _75.length]) && !_optionalChain([b, 'optionalAccess', _76 => _76.length])) return null;
4656
+ if (!_optionalChain([a, 'optionalAccess', _91 => _91.length]) && !_optionalChain([b, 'optionalAccess', _92 => _92.length])) return null;
3502
4657
  const at2 = [..._nullishCoalesce(a, () => ( [])), ..._nullishCoalesce(b, () => ( [])), { t: 0 }, { t: 1 }].map((p) => p.t).sort((x, y) => x - y);
3503
4658
  const ts = at2.filter((v2, i) => i === 0 || v2 - at2[i - 1] > 1e-6);
3504
4659
  return ts.map((pos) => {
@@ -3508,7 +4663,7 @@ function interpolateWidthProfiles(a, b, t) {
3508
4663
  });
3509
4664
  }
3510
4665
  function lerpDashes(a, b, t) {
3511
- const dashed = (d) => !!_optionalChain([d, 'optionalAccess', _77 => _77.length]) && d.some((v2) => v2 > 0);
4666
+ const dashed = (d) => !!_optionalChain([d, 'optionalAccess', _93 => _93.length]) && d.some((v2) => v2 > 0);
3512
4667
  if (!dashed(a) && !dashed(b)) return t < 0.5 ? a : b;
3513
4668
  const even = (p) => p.length % 2 ? [...p, ...p] : p;
3514
4669
  const solid = (p) => {
@@ -3535,7 +4690,7 @@ function interpolateVectorStyle(a, b, t) {
3535
4690
  for (const key of ["startArrowScale", "endArrowScale"])
3536
4691
  if (a[key] !== void 0 || b[key] !== void 0) out[key] = lerpN(_nullishCoalesce(a[key], () => ( 1)), _nullishCoalesce(b[key], () => ( 1)), t);
3537
4692
  const dash = lerpDashes(a.strokeDasharray, b.strokeDasharray, t);
3538
- if (_optionalChain([dash, 'optionalAccess', _78 => _78.length])) out.strokeDasharray = dash;
4693
+ if (_optionalChain([dash, 'optionalAccess', _94 => _94.length])) out.strokeDasharray = dash;
3539
4694
  const profile = interpolateWidthProfiles(a.strokeWidthProfile, b.strokeWidthProfile, t);
3540
4695
  if (profile) out.strokeWidthProfile = profile;
3541
4696
  return out;
@@ -3546,7 +4701,7 @@ function reversedStyle(s) {
3546
4701
  out.endArrow = s.startArrow;
3547
4702
  out.startArrowScale = s.endArrowScale;
3548
4703
  out.endArrowScale = s.startArrowScale;
3549
- if (_optionalChain([s, 'access', _79 => _79.strokeWidthProfile, 'optionalAccess', _80 => _80.length])) out.strokeWidthProfile = reverseWidthProfile(s.strokeWidthProfile);
4704
+ if (_optionalChain([s, 'access', _95 => _95.strokeWidthProfile, 'optionalAccess', _96 => _96.length])) out.strokeWidthProfile = reverseWidthProfile(s.strokeWidthProfile);
3550
4705
  for (const k of ["startArrow", "endArrow", "startArrowScale", "endArrowScale"]) if (out[k] === void 0) delete out[k];
3551
4706
  return out;
3552
4707
  }
@@ -3628,15 +4783,15 @@ function createVectorMorph(a, b, options = {}) {
3628
4783
  for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
3629
4784
  const x = pa[i];
3630
4785
  const y = pb[i];
3631
- const sub3 = { ...options, id: `${id}-${i}`, starts: void 0 };
4786
+ const sub5 = { ...options, id: `${id}-${i}`, starts: void 0 };
3632
4787
  if (x && y) {
3633
- parts.push(preparePathMorph(x.path, y.path, sub3, x.m, y.m));
4788
+ parts.push(preparePathMorph(x.path, y.path, sub5, x.m, y.m));
3634
4789
  opacities.push([x.opacity, y.opacity]);
3635
4790
  } else if (x) {
3636
- parts.push(preparePathMorph(x.path, collapsedPath(x.path, x.m, ba, bb), sub3, x.m, IDENTITY_MATRIX));
4791
+ parts.push(preparePathMorph(x.path, collapsedPath(x.path, x.m, ba, bb), sub5, x.m, IDENTITY_MATRIX));
3637
4792
  opacities.push([x.opacity, x.opacity]);
3638
4793
  } else if (y) {
3639
- parts.push(preparePathMorph(collapsedPath(y.path, y.m, bb, ba), y.path, sub3, IDENTITY_MATRIX, y.m));
4794
+ parts.push(preparePathMorph(collapsedPath(y.path, y.m, bb, ba), y.path, sub5, IDENTITY_MATRIX, y.m));
3640
4795
  opacities.push([y.opacity, y.opacity]);
3641
4796
  }
3642
4797
  }
@@ -3692,11 +4847,11 @@ function vectorBlendKeys(group) {
3692
4847
  }
3693
4848
  function resolveVectorBlendOptions(options) {
3694
4849
  return {
3695
- spacing: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _81 => _81.spacing]), () => ( "steps")),
3696
- steps: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _82 => _82.steps]), () => ( DEFAULT_VECTOR_BLEND_STEPS)),
3697
- distance: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _83 => _83.distance]), () => ( DEFAULT_VECTOR_BLEND_DISTANCE)),
3698
- orientToSpine: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _84 => _84.orientToSpine]), () => ( false)),
3699
- linear: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _85 => _85.linear]), () => ( false))
4850
+ spacing: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _97 => _97.spacing]), () => ( "steps")),
4851
+ steps: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _98 => _98.steps]), () => ( DEFAULT_VECTOR_BLEND_STEPS)),
4852
+ distance: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _99 => _99.distance]), () => ( DEFAULT_VECTOR_BLEND_DISTANCE)),
4853
+ orientToSpine: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _100 => _100.orientToSpine]), () => ( false)),
4854
+ linear: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _101 => _101.linear]), () => ( false))
3700
4855
  };
3701
4856
  }
3702
4857
  function paintColors(p) {
@@ -3784,7 +4939,7 @@ function vectorBlendRailAt(rail, s) {
3784
4939
  function vectorBlendSpine(group) {
3785
4940
  const keys = vectorBlendKeys(group);
3786
4941
  if (keys.length < 2) return null;
3787
- const stored = _optionalChain([group, 'access', _86 => _86.blend, 'optionalAccess', _87 => _87.spine]);
4942
+ const stored = _optionalChain([group, 'access', _102 => _102.blend, 'optionalAccess', _103 => _103.spine]);
3788
4943
  if (stored && stored.anchors.length >= 2) {
3789
4944
  const rail2 = vectorBlendRail(stored);
3790
4945
  if (rail2) {
@@ -3805,7 +4960,7 @@ function vectorBlendKeyTargets(group) {
3805
4960
  }
3806
4961
  var morphCache = /* @__PURE__ */ new WeakMap();
3807
4962
  function morphFor(a, b, blend) {
3808
- const starts = [_nullishCoalesce(_optionalChain([blend, 'access', _88 => _88.starts, 'optionalAccess', _89 => _89[a.id]]), () => ( null)), _nullishCoalesce(_optionalChain([blend, 'access', _90 => _90.starts, 'optionalAccess', _91 => _91[b.id]]), () => ( null))];
4963
+ const starts = [_nullishCoalesce(_optionalChain([blend, 'access', _104 => _104.starts, 'optionalAccess', _105 => _105[a.id]]), () => ( null)), _nullishCoalesce(_optionalChain([blend, 'access', _106 => _106.starts, 'optionalAccess', _107 => _107[b.id]]), () => ( null))];
3809
4964
  const key = JSON.stringify([!!blend.linear, starts]);
3810
4965
  let inner = morphCache.get(a);
3811
4966
  if (!inner) morphCache.set(a, inner = /* @__PURE__ */ new WeakMap());
@@ -3904,7 +5059,7 @@ function releaseVectorBlend(group, newId) {
3904
5059
  const m = nodeMatrix(group);
3905
5060
  const opacity = group.opacity;
3906
5061
  const keys = vectorBlendKeys(group).map((c) => group.transform ? transformVectorNode(c, m) : c).map((c) => opacity !== void 0 && opacity < 1 ? { ...c, opacity: (_nullishCoalesce(c.opacity, () => ( 1))) * opacity } : c);
3907
- const spine = _optionalChain([group, 'access', _92 => _92.blend, 'optionalAccess', _93 => _93.spine]);
5062
+ const spine = _optionalChain([group, 'access', _108 => _108.blend, 'optionalAccess', _109 => _109.spine]);
3908
5063
  if (!spine || spine.anchors.length < 2) return keys;
3909
5064
  const path = { id: newId(), type: "path", name: "Spine", fill: null, stroke: null, subpaths: [{ closed: spine.closed, anchors: spine.anchors.map((a) => ({ ...a })) }] };
3910
5065
  return [...keys, group.transform ? transformVectorNode(path, m) : path];
@@ -3913,7 +5068,7 @@ function releaseVectorBlend(group, newId) {
3913
5068
  // src/vector/snap.ts
3914
5069
  var VECTOR_CONSTRUCTION_ANGLES = [0, 45, 90, 135];
3915
5070
  function constrainToLeave(leave, p) {
3916
- if (!_optionalChain([leave, 'optionalAccess', _94 => _94.constrain])) return p;
5071
+ if (!_optionalChain([leave, 'optionalAccess', _110 => _110.constrain])) return p;
3917
5072
  const dx = p.x - leave.at.x;
3918
5073
  const dy = p.y - leave.at.y;
3919
5074
  switch (leave.constrain) {
@@ -4024,7 +5179,7 @@ var VectorSnapTargets = (_class3 = class _VectorSnapTargets {
4024
5179
  moving.set(r.nodeId, set);
4025
5180
  }
4026
5181
  const t = _VectorSnapTargets.collect(doc, new Set(moving.keys()), "segments", false);
4027
- t.pushPaths(doc, new Set(moving.keys()), (id, s, i) => !!_optionalChain([moving, 'access', _95 => _95.get, 'call', _96 => _96(id), 'optionalAccess', _97 => _97.has, 'call', _98 => _98(`${s}:${i}`)]), true);
5182
+ t.pushPaths(doc, new Set(moving.keys()), (id, s, i) => !!_optionalChain([moving, 'access', _111 => _111.get, 'call', _112 => _112(id), 'optionalAccess', _113 => _113.has, 'call', _114 => _114(`${s}:${i}`)]), true);
4028
5183
  return t.index();
4029
5184
  }
4030
5185
  /**
@@ -4143,7 +5298,7 @@ var VectorSnapTargets = (_class3 = class _VectorSnapTargets {
4143
5298
  }
4144
5299
  const b = vectorNodeBounds(n, parent);
4145
5300
  const f = nodeFrame(n, parent, b);
4146
- if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess', _99 => _99.centre]), () => ( null)));
5301
+ if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess', _115 => _115.centre]), () => ( null)));
4147
5302
  }
4148
5303
  };
4149
5304
  visit(doc.nodes, IDENTITY_MATRIX);
@@ -4188,7 +5343,7 @@ var VectorSnapTargets = (_class3 = class _VectorSnapTargets {
4188
5343
  lines: lines2,
4189
5344
  labels: [this.label(q, kind)]
4190
5345
  });
4191
- if (_optionalChain([leave, 'optionalAccess', _100 => _100.constrain])) {
5346
+ if (_optionalChain([leave, 'optionalAccess', _116 => _116.constrain])) {
4192
5347
  const q = constrainToLeave(leave, p);
4193
5348
  const hit = this.slide(leave.at, { x: q.x - leave.at.x, y: q.y - leave.at.y }, q, tol, true);
4194
5349
  return hit ? { point: hit.point, x: true, y: true, ...this.linedUp(hit.point, hit.from) } : none(q);
@@ -4204,7 +5359,7 @@ var VectorSnapTargets = (_class3 = class _VectorSnapTargets {
4204
5359
  }
4205
5360
  const on = this.onSegment(near2);
4206
5361
  if (on) return at2(on, "path");
4207
- if (_optionalChain([leave, 'optionalAccess', _101 => _101.angles, 'optionalAccess', _102 => _102.length])) {
5362
+ if (_optionalChain([leave, 'optionalAccess', _117 => _117.angles, 'optionalAccess', _118 => _118.length])) {
4208
5363
  const c = construction(leave, p, tol);
4209
5364
  if (c) {
4210
5365
  const hit = this.slide(leave.at, c.dir, c.point, tol, false);
@@ -4334,7 +5489,7 @@ var VectorSnapTargets = (_class3 = class _VectorSnapTargets {
4334
5489
  * with nothing spaces the box evenly in its row or column. Returns the correction and guides.
4335
5490
  */
4336
5491
  snapRect(r, tol, options = {}) {
4337
- const axes = { x: _optionalChain([options, 'access', _103 => _103.axes, 'optionalAccess', _104 => _104.x]) !== false, y: _optionalChain([options, 'access', _105 => _105.axes, 'optionalAccess', _106 => _106.y]) !== false };
5492
+ const axes = { x: _optionalChain([options, 'access', _119 => _119.axes, 'optionalAccess', _120 => _120.x]) !== false, y: _optionalChain([options, 'access', _121 => _121.axes, 'optionalAccess', _122 => _122.y]) !== false };
4338
5493
  const nearest = (lines, values) => {
4339
5494
  let best = null;
4340
5495
  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 };
@@ -4342,7 +5497,7 @@ var VectorSnapTargets = (_class3 = class _VectorSnapTargets {
4342
5497
  };
4343
5498
  const bx = axes.x ? nearest(this.xs, [r.x, r.x + r.width / 2, r.x + r.width]) : null;
4344
5499
  const by = axes.y ? nearest(this.ys, [r.y, r.y + r.height / 2, r.y + r.height]) : null;
4345
- const out = { dx: _nullishCoalesce(_optionalChain([bx, 'optionalAccess', _107 => _107.d]), () => ( 0)), dy: _nullishCoalesce(_optionalChain([by, 'optionalAccess', _108 => _108.d]), () => ( 0)), x: !!bx, y: !!by, lines: [], labels: [] };
5500
+ const out = { dx: _nullishCoalesce(_optionalChain([bx, 'optionalAccess', _123 => _123.d]), () => ( 0)), dy: _nullishCoalesce(_optionalChain([by, 'optionalAccess', _124 => _124.d]), () => ( 0)), x: !!bx, y: !!by, lines: [], labels: [] };
4346
5501
  const done = { ...r, x: r.x + out.dx, y: r.y + out.dy };
4347
5502
  if (bx) {
4348
5503
  const x = bx.from.x;
@@ -4465,9 +5620,9 @@ var VectorSnapTargets = (_class3 = class _VectorSnapTargets {
4465
5620
  const on = this.nearestPoint(p, tol);
4466
5621
  const tries = [];
4467
5622
  if (on) tries.push({ tx: on.p.x, ty: on.p.y, s: { hx: null, hy: null, on } });
4468
- tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _109 => _109.v]), () => ( null)), ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _110 => _110.v]), () => ( null)), s: { hx, hy, on: null } });
4469
- tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _111 => _111.v]), () => ( null)), ty: null, s: { hx, hy: null, on: null } });
4470
- tries.push({ tx: null, ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _112 => _112.v]), () => ( null)), s: { hx: null, hy, on: null } });
5623
+ tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _125 => _125.v]), () => ( null)), ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _126 => _126.v]), () => ( null)), s: { hx, hy, on: null } });
5624
+ tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _127 => _127.v]), () => ( null)), ty: null, s: { hx, hy: null, on: null } });
5625
+ tries.push({ tx: null, ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _128 => _128.v]), () => ( null)), s: { hx: null, hy, on: null } });
4471
5626
  for (const { tx, ty, s } of tries) {
4472
5627
  if (tx === null && ty === null) continue;
4473
5628
  const q = applyMatrix(toLocal, { x: _nullishCoalesce(tx, () => ( p.x)), y: _nullishCoalesce(ty, () => ( p.y)) });
@@ -4621,16 +5776,16 @@ function solveOnSegment(seg, base, cond) {
4621
5776
  return cond([p[0] - b[0], p[1] - b[1]], _chunkVNYRI265cjs.segmentDerivative.call(void 0, seg, t));
4622
5777
  };
4623
5778
  const vals = [];
4624
- let scale = 0;
5779
+ let scale3 = 0;
4625
5780
  for (let i = 0; i <= CURVE_SAMPLES; i++) {
4626
5781
  const t = i / CURVE_SAMPLES;
4627
5782
  const p = _chunkVNYRI265cjs.segmentPoint.call(void 0, seg, t);
4628
5783
  const d = _chunkVNYRI265cjs.segmentDerivative.call(void 0, seg, t);
4629
5784
  const r = [p[0] - b[0], p[1] - b[1]];
4630
5785
  vals.push(cond(r, d));
4631
- scale = Math.max(scale, Math.hypot(r[0], r[1]) * Math.hypot(d[0], d[1]));
5786
+ scale3 = Math.max(scale3, Math.hypot(r[0], r[1]) * Math.hypot(d[0], d[1]));
4632
5787
  }
4633
- if (!(scale > 0) || vals.every((v2) => Math.abs(v2) <= scale * 1e-12)) return [];
5788
+ if (!(scale3 > 0) || vals.every((v2) => Math.abs(v2) <= scale3 * 1e-12)) return [];
4634
5789
  const out = [];
4635
5790
  for (let i = 0; i < CURVE_SAMPLES; i++) {
4636
5791
  const ga = vals[i];
@@ -4664,7 +5819,7 @@ function solveOnSegment(seg, base, cond) {
4664
5819
 
4665
5820
  // src/vector/pathEdit.ts
4666
5821
  var EPS4 = 1e-9;
4667
- var unit3 = (x, y) => {
5822
+ var unit4 = (x, y) => {
4668
5823
  const l = Math.hypot(x, y);
4669
5824
  return l > 1e-12 && Number.isFinite(l) ? [x / l, y / l] : null;
4670
5825
  };
@@ -4677,14 +5832,14 @@ function cubicOf(sp, i) {
4677
5832
  var isLine = (sp, i) => subpathSegment(sp, i).length === 2;
4678
5833
  function startTangent2(c) {
4679
5834
  for (let k = 1; k < 4; k++) {
4680
- const u = unit3(c[k][0] - c[0][0], c[k][1] - c[0][1]);
5835
+ const u = unit4(c[k][0] - c[0][0], c[k][1] - c[0][1]);
4681
5836
  if (u) return u;
4682
5837
  }
4683
5838
  return [1, 0];
4684
5839
  }
4685
5840
  function endTangent2(c) {
4686
5841
  for (let k = 2; k >= 0; k--) {
4687
- const u = unit3(c[3][0] - c[k][0], c[3][1] - c[k][1]);
5842
+ const u = unit4(c[3][0] - c[k][0], c[3][1] - c[k][1]);
4688
5843
  if (u) return u;
4689
5844
  }
4690
5845
  return [1, 0];
@@ -4748,13 +5903,13 @@ function removeAnchorKeepingShape(sp, index) {
4748
5903
  const pi = (index + n - 1) % n;
4749
5904
  const ni = (index + 1) % n;
4750
5905
  if (!(isLine(sp, pi) && isLine(sp, index))) {
4751
- const left = cubicOf(sp, pi);
5906
+ const left2 = cubicOf(sp, pi);
4752
5907
  const right = cubicOf(sp, index);
4753
5908
  const N = 16;
4754
5909
  const pts = [];
4755
- sample(left, N, pts, true);
5910
+ sample(left2, N, pts, true);
4756
5911
  sample(right, N, pts, false);
4757
- const t0 = startTangent2(left);
5912
+ const t0 = startTangent2(left2);
4758
5913
  const t1 = endTangent2(right);
4759
5914
  const { curve } = fitAcross(pts, N, t0, t1);
4760
5915
  if ([curve.p1, curve.p2].every((p) => Number.isFinite(p[0]) && Number.isFinite(p[1]))) {
@@ -4956,7 +6111,7 @@ function smoothSubpath(sp, amount = 1, only) {
4956
6111
  const prev = orig[(i + n - 1) % n];
4957
6112
  const next = orig[(i + 1) % n];
4958
6113
  const p = orig[i];
4959
- const dir = unit3(next.x - prev.x, next.y - prev.y);
6114
+ const dir = unit4(next.x - prev.x, next.y - prev.y);
4960
6115
  if (!dir) continue;
4961
6116
  const li = dist2(p, prev) / 3;
4962
6117
  const lo = dist2(p, next) / 3;
@@ -5091,9 +6246,9 @@ function facesOf(path, fillRule) {
5091
6246
  function splitIntoFaces(path, fillRule = "nonzero") {
5092
6247
  return facesOf(path, fillRule).map(faceToPath);
5093
6248
  }
5094
- function rootsInUnit(a, b, c, d, scale) {
6249
+ function rootsInUnit(a, b, c, d, scale3) {
5095
6250
  const f = (t) => ((a * t + b) * t + c) * t + d;
5096
- const tol = 1e-12 * Math.max(1, scale);
6251
+ const tol = 1e-12 * Math.max(1, scale3);
5097
6252
  const cuts = [0];
5098
6253
  const qa = 3 * a;
5099
6254
  const qb = 2 * b;
@@ -5150,9 +6305,9 @@ function lineHits(p, q, seg) {
5150
6305
  coeffs = [-h0 + 3 * h1 - 3 * h2 + h3, 3 * h0 - 6 * h1 + 3 * h2, -3 * h0 + 3 * h1, h0];
5151
6306
  }
5152
6307
  if (coeffs.every((c) => Math.abs(c) < 1e-12 * Math.max(1, len2))) return [];
5153
- const scale = Math.sqrt(len2) * Math.max(1, ...seg.map((pt2) => Math.abs(pt2[0]) + Math.abs(pt2[1])));
6308
+ const scale3 = Math.sqrt(len2) * Math.max(1, ...seg.map((pt2) => Math.abs(pt2[0]) + Math.abs(pt2[1])));
5154
6309
  const out = [];
5155
- for (const t of rootsInUnit(...coeffs, scale)) {
6310
+ for (const t of rootsInUnit(...coeffs, scale3)) {
5156
6311
  const point = _chunkVNYRI265cjs.segmentPoint.call(void 0, seg, t);
5157
6312
  const s = ((point[0] - p[0]) * dx + (point[1] - p[1]) * dy) / len2;
5158
6313
  if (s >= -1e-9 && s <= 1 + 1e-9) out.push({ s: Math.min(1, Math.max(0, s)), t, point });
@@ -5897,7 +7052,7 @@ function getEditablePathIds(state) {
5897
7052
  }
5898
7053
  }
5899
7054
  }
5900
- return [...ids].filter((id) => _optionalChain([findVectorNode, 'call', _113 => _113(state.document.nodes, id), 'optionalAccess', _114 => _114.type]) === "path");
7055
+ return [...ids].filter((id) => _optionalChain([findVectorNode, 'call', _129 => _129(state.document.nodes, id), 'optionalAccess', _130 => _130.type]) === "path");
5901
7056
  }
5902
7057
  function getVisibleAnchors(state) {
5903
7058
  const out = [];
@@ -5912,7 +7067,7 @@ function getVisibleHandles(state) {
5912
7067
  const seen = /* @__PURE__ */ new Set();
5913
7068
  const push = (node, s, i, handle) => {
5914
7069
  const sp = node.subpaths[s];
5915
- const a = _optionalChain([sp, 'optionalAccess', _115 => _115.anchors, 'access', _116 => _116[i]]);
7070
+ const a = _optionalChain([sp, 'optionalAccess', _131 => _131.anchors, 'access', _132 => _132[i]]);
5916
7071
  if (!a || !a[handle]) return;
5917
7072
  const key = `${node.id}:${s}:${i}:${handle}`;
5918
7073
  if (seen.has(key)) return;
@@ -5921,8 +7076,8 @@ function getVisibleHandles(state) {
5921
7076
  };
5922
7077
  if (state.tool === "pen" && state.pen) {
5923
7078
  const node = findVectorNode(state.document.nodes, state.pen.nodeId);
5924
- const sp = _optionalChain([node, 'optionalAccess', _117 => _117.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
5925
- if (_optionalChain([node, 'optionalAccess', _118 => _118.type]) === "path" && sp) {
7079
+ const sp = _optionalChain([node, 'optionalAccess', _133 => _133.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
7080
+ if (_optionalChain([node, 'optionalAccess', _134 => _134.type]) === "path" && sp) {
5926
7081
  const last = sp.anchors.length - 1;
5927
7082
  push(node, state.pen.subpath, last, "in");
5928
7083
  push(node, state.pen.subpath, last, "out");
@@ -5960,11 +7115,11 @@ function getVectorCornerWidgets(state) {
5960
7115
  const side = Math.min(c.lu, c.lv);
5961
7116
  if (side * zoom < 3 * VECTOR_CORNER_WIDGET_INSET) continue;
5962
7117
  const a = node.subpaths[c.subpath].anchors[c.index];
5963
- const radius = _nullishCoalesce(_optionalChain([a, 'access', _119 => _119.corner, 'optionalAccess', _120 => _120.radius]), () => ( 0));
7118
+ const radius = _nullishCoalesce(_optionalChain([a, 'access', _135 => _135.corner, 'optionalAccess', _136 => _136.radius]), () => ( 0));
5964
7119
  const drawn = fitCornerRadius(c, radius);
5965
7120
  const k = Math.min(Math.max(drawn / c.sin, VECTOR_CORNER_WIDGET_INSET / (zoom * Math.sqrt(1 + c.cos))), side / 2);
5966
7121
  const p = applyMatrix(m, cornerBisectorPoint(c, k));
5967
- 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', _121 => _121.corner, 'optionalAccess', _122 => _122.kind]), () => ( "round")), corner: c });
7122
+ 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', _137 => _137.corner, 'optionalAccess', _138 => _138.kind]), () => ( "round")), corner: c });
5968
7123
  }
5969
7124
  }
5970
7125
  return out;
@@ -5987,13 +7142,13 @@ function getVectorWidthHandles(state) {
5987
7142
  const n = findVectorNode(state.document.nodes, id);
5988
7143
  if (n) for (const p of collectPathNodes(n)) ids.add(p.id);
5989
7144
  }
5990
- if (_optionalChain([state, 'access', _123 => _123.hover, 'optionalAccess', _124 => _124.nodeId])) ids.add(state.hover.nodeId);
5991
- if (_optionalChain([state, 'access', _125 => _125.interaction, 'optionalAccess', _126 => _126.type]) === "width") ids.add(state.interaction.ref.nodeId);
7145
+ if (_optionalChain([state, 'access', _139 => _139.hover, 'optionalAccess', _140 => _140.nodeId])) ids.add(state.hover.nodeId);
7146
+ if (_optionalChain([state, 'access', _141 => _141.interaction, 'optionalAccess', _142 => _142.type]) === "width") ids.add(state.interaction.ref.nodeId);
5992
7147
  for (const r of state.selectedWidthPoints) ids.add(r.nodeId);
5993
7148
  const out = [];
5994
7149
  for (const id of ids) {
5995
7150
  const node = findVectorNode(state.document.nodes, id);
5996
- if (_optionalChain([node, 'optionalAccess', _127 => _127.type]) !== "path" || node.visible === false || !_optionalChain([node, 'access', _128 => _128.strokeWidthProfile, 'optionalAccess', _129 => _129.length])) continue;
7151
+ if (_optionalChain([node, 'optionalAccess', _143 => _143.type]) !== "path" || node.visible === false || !_optionalChain([node, 'access', _144 => _144.strokeWidthProfile, 'optionalAccess', _145 => _145.length])) continue;
5997
7152
  const g = vectorStrokeWidthGeometry(node);
5998
7153
  if (!g) continue;
5999
7154
  const profile = sanitizeWidthProfile(node.strokeWidthProfile);
@@ -6026,7 +7181,7 @@ function getVectorWidthHandles(state) {
6026
7181
  }
6027
7182
  function getAnchor(doc, ref) {
6028
7183
  const node = findVectorNode(doc.nodes, ref.nodeId);
6029
- return _optionalChain([node, 'optionalAccess', _130 => _130.type]) === "path" ? _optionalChain([node, 'access', _131 => _131.subpaths, 'access', _132 => _132[ref.subpath], 'optionalAccess', _133 => _133.anchors, 'access', _134 => _134[ref.index]]) : void 0;
7184
+ return _optionalChain([node, 'optionalAccess', _146 => _146.type]) === "path" ? _optionalChain([node, 'access', _147 => _147.subpaths, 'access', _148 => _148[ref.subpath], 'optionalAccess', _149 => _149.anchors, 'access', _150 => _150[ref.index]]) : void 0;
6030
7185
  }
6031
7186
  function getAnchorWorld(doc, ref) {
6032
7187
  const a = getAnchor(doc, ref);
@@ -6098,7 +7253,8 @@ var TOOL_LABELS = {
6098
7253
  width: "Width",
6099
7254
  shapeBuilder: "Shape Builder",
6100
7255
  paintBucket: "Paint Bucket",
6101
- blend: "Blend"
7256
+ blend: "Blend",
7257
+ gradient: "Gradient"
6102
7258
  };
6103
7259
  var TOOL_KEYS = {
6104
7260
  v: "select",
@@ -6116,7 +7272,8 @@ var TOOL_KEYS = {
6116
7272
  w: "width",
6117
7273
  m: "shapeBuilder",
6118
7274
  b: "paintBucket",
6119
- j: "blend"
7275
+ j: "blend",
7276
+ g: "gradient"
6120
7277
  };
6121
7278
  function vectorToolLabel(tool) {
6122
7279
  return TOOL_LABELS[tool];
@@ -6137,7 +7294,8 @@ var VECTOR_TOOL_SHORTCUTS = {
6137
7294
  width: "W",
6138
7295
  shapeBuilder: "M",
6139
7296
  paintBucket: "B",
6140
- blend: "J"
7297
+ blend: "J",
7298
+ gradient: "G"
6141
7299
  };
6142
7300
  var VECTOR_TOOL_GLYPHS = {
6143
7301
  select: "\u2196",
@@ -6155,7 +7313,8 @@ var VECTOR_TOOL_GLYPHS = {
6155
7313
  width: "\u21F9",
6156
7314
  shapeBuilder: "\u2295",
6157
7315
  paintBucket: "\u25CD",
6158
- blend: "\u29D3"
7316
+ blend: "\u29D3",
7317
+ gradient: "\u25D0"
6159
7318
  };
6160
7319
  var SHAPE_TOOLS = /* @__PURE__ */ new Set(["rect", "ellipse", "polygon", "star", "line"]);
6161
7320
  var BOOLEAN_OPS = {
@@ -6252,18 +7411,19 @@ var VectorEditorController = (_class5 = class {
6252
7411
  __init30() {this.blendStart = null}
6253
7412
  /** Options new blends get (the toolbars edit them along with the selected blend's). */
6254
7413
  __init31() {this.blendDefaults = {}}
7414
+ __init32() {this.gradientEdit = { target: "fill", stop: null }}
6255
7415
  /** What the highlight shows, to skip notifying when the pointer stays over the same face. */
6256
- __init32() {this.builderHoverKey = null}
7416
+ __init33() {this.builderHoverKey = null}
6257
7417
 
6258
7418
 
6259
- __init33() {this.listeners = /* @__PURE__ */ new Set()}
6260
- __init34() {this.cleanups = []}
6261
- __init35() {this.applying = false}
7419
+ __init34() {this.listeners = /* @__PURE__ */ new Set()}
7420
+ __init35() {this.cleanups = []}
7421
+ __init36() {this.applying = false}
6262
7422
  /** Set while a commit records a change that was already applied, so `restore` still reports it. */
6263
- __init36() {this.committing = false}
6264
- __init37() {this.revision = 0}
7423
+ __init37() {this.committing = false}
7424
+ __init38() {this.revision = 0}
6265
7425
 
6266
- constructor(options = {}) {;_class5.prototype.__init12.call(this);_class5.prototype.__init13.call(this);_class5.prototype.__init14.call(this);_class5.prototype.__init15.call(this);_class5.prototype.__init16.call(this);_class5.prototype.__init17.call(this);_class5.prototype.__init18.call(this);_class5.prototype.__init19.call(this);_class5.prototype.__init20.call(this);_class5.prototype.__init21.call(this);_class5.prototype.__init22.call(this);_class5.prototype.__init23.call(this);_class5.prototype.__init24.call(this);_class5.prototype.__init25.call(this);_class5.prototype.__init26.call(this);_class5.prototype.__init27.call(this);_class5.prototype.__init28.call(this);_class5.prototype.__init29.call(this);_class5.prototype.__init30.call(this);_class5.prototype.__init31.call(this);_class5.prototype.__init32.call(this);_class5.prototype.__init33.call(this);_class5.prototype.__init34.call(this);_class5.prototype.__init35.call(this);_class5.prototype.__init36.call(this);_class5.prototype.__init37.call(this);_class5.prototype.__init38.call(this);_class5.prototype.__init39.call(this);
7426
+ constructor(options = {}) {;_class5.prototype.__init12.call(this);_class5.prototype.__init13.call(this);_class5.prototype.__init14.call(this);_class5.prototype.__init15.call(this);_class5.prototype.__init16.call(this);_class5.prototype.__init17.call(this);_class5.prototype.__init18.call(this);_class5.prototype.__init19.call(this);_class5.prototype.__init20.call(this);_class5.prototype.__init21.call(this);_class5.prototype.__init22.call(this);_class5.prototype.__init23.call(this);_class5.prototype.__init24.call(this);_class5.prototype.__init25.call(this);_class5.prototype.__init26.call(this);_class5.prototype.__init27.call(this);_class5.prototype.__init28.call(this);_class5.prototype.__init29.call(this);_class5.prototype.__init30.call(this);_class5.prototype.__init31.call(this);_class5.prototype.__init32.call(this);_class5.prototype.__init33.call(this);_class5.prototype.__init34.call(this);_class5.prototype.__init35.call(this);_class5.prototype.__init36.call(this);_class5.prototype.__init37.call(this);_class5.prototype.__init38.call(this);_class5.prototype.__init39.call(this);_class5.prototype.__init40.call(this);
6267
7427
  this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkQKLRAA4Tcjs.HistoryManager)()));
6268
7428
  this.doc = options.document ? cloneVectorDocument(options.document) : { width: 800, height: 600, nodes: [] };
6269
7429
  this.doc = this.refreshed(this.doc);
@@ -6276,15 +7436,26 @@ var VectorEditorController = (_class5 = class {
6276
7436
  this.createId = _nullishCoalesce(options.createId, () => ( defaultCreateId));
6277
7437
  this.onChange = options.onChange;
6278
7438
  this.state = this.computeState();
7439
+ this.connect();
7440
+ }
7441
+ /** Listens to the history (once; again after `destroy`). */
7442
+ connect() {
7443
+ if (this.cleanups.length) return;
6279
7444
  this.cleanups.push(
6280
7445
  this.history.subscribe((h) => {
6281
- const s = this.state;
6282
- if (this.applying || s.canUndo === h.canUndo && s.canRedo === h.canRedo && s.undoLabel === h.undoName && s.redoLabel === h.redoName) return;
6283
- this.notify();
7446
+ if (!this.applying && this.historyDiffers(h)) this.notify();
6284
7447
  })
6285
7448
  );
7449
+ if (this.historyDiffers(this.history.getState())) this.state = this.computeState();
6286
7450
  }
6287
- /** Stops listening to the history. */
7451
+ historyDiffers(h) {
7452
+ const s = this.state;
7453
+ return s.canUndo !== h.canUndo || s.canRedo !== h.canRedo || s.undoLabel !== h.undoName || s.redoLabel !== h.redoName;
7454
+ }
7455
+ /**
7456
+ * Stops listening to the history and drops all listeners. A later `subscribe` reconnects, so a
7457
+ * controller torn down by React StrictMode's double effect keeps working when it is reused.
7458
+ */
6288
7459
  destroy() {
6289
7460
  this.cleanups.splice(0).forEach((fn) => fn());
6290
7461
  this.listeners.clear();
@@ -6293,9 +7464,10 @@ var VectorEditorController = (_class5 = class {
6293
7464
  // State
6294
7465
  // -------------------------------------------------------------------------
6295
7466
  /** The current snapshot (stable until the next change). */
6296
- __init38() {this.getState = () => this.state}
7467
+ __init39() {this.getState = () => this.state}
6297
7468
  /** Calls `listener` after every change. Returns an unsubscribe function. */
6298
- __init39() {this.subscribe = (listener) => {
7469
+ __init40() {this.subscribe = (listener) => {
7470
+ this.connect();
6299
7471
  this.listeners.add(listener);
6300
7472
  return () => {
6301
7473
  this.listeners.delete(listener);
@@ -6333,6 +7505,7 @@ var VectorEditorController = (_class5 = class {
6333
7505
  builder: this.builderPreview,
6334
7506
  blendStart: this.blendStart,
6335
7507
  blendDefaults: this.blendDefaults,
7508
+ gradientEdit: this.gradientEdit,
6336
7509
  canUndo: h.canUndo,
6337
7510
  canRedo: h.canRedo,
6338
7511
  undoLabel: h.undoName,
@@ -6351,7 +7524,7 @@ var VectorEditorController = (_class5 = class {
6351
7524
  if (this.blendStart && !this.getNode(this.blendStart.nodeId)) this.blendStart = null;
6352
7525
  this.revision++;
6353
7526
  this.state = this.computeState();
6354
- if (docChanged) _optionalChain([this, 'access', _135 => _135.onChange, 'optionalCall', _136 => _136(this.doc)]);
7527
+ if (docChanged) _optionalChain([this, 'access', _151 => _151.onChange, 'optionalCall', _152 => _152(this.doc)]);
6355
7528
  const state = this.state;
6356
7529
  this.listeners.forEach((listener) => {
6357
7530
  try {
@@ -6381,31 +7554,38 @@ var VectorEditorController = (_class5 = class {
6381
7554
  return applyMatrix(invertMatrix(this.worldOf(id, doc)), p);
6382
7555
  }
6383
7556
  cursorFor() {
6384
- if (_optionalChain([this, 'access', _137 => _137.drag, 'optionalAccess', _138 => _138.kind]) === "pan" || this.spaceHeld) return this.drag ? "grabbing" : "grab";
6385
- if (_optionalChain([this, 'access', _139 => _139.drag, 'optionalAccess', _140 => _140.kind]) === "rotate") return "grabbing";
6386
- if (_optionalChain([this, 'access', _141 => _141.drag, 'optionalAccess', _142 => _142.kind]) === "move") return "move";
7557
+ if (_optionalChain([this, 'access', _153 => _153.drag, 'optionalAccess', _154 => _154.kind]) === "pan" || this.spaceHeld) return this.drag ? "grabbing" : "grab";
7558
+ if (_optionalChain([this, 'access', _155 => _155.drag, 'optionalAccess', _156 => _156.kind]) === "rotate") return "grabbing";
7559
+ if (_optionalChain([this, 'access', _157 => _157.drag, 'optionalAccess', _158 => _158.kind]) === "move") return "move";
6387
7560
  const h = this.hover;
6388
7561
  switch (this.tool) {
6389
7562
  case "select":
6390
- if (_optionalChain([h, 'optionalAccess', _143 => _143.transformHandle])) return vectorResizeCursor(h.transformHandle, _nullishCoalesce(_optionalChain([this, 'access', _144 => _144.frameOf, 'call', _145 => _145(this.selectedRoots()), 'optionalAccess', _146 => _146.angle]), () => ( 0)));
6391
- return _optionalChain([h, 'optionalAccess', _147 => _147.nodeId]) ? "move" : "default";
7563
+ if (_optionalChain([h, 'optionalAccess', _159 => _159.transformHandle])) return vectorResizeCursor(h.transformHandle, _nullishCoalesce(_optionalChain([this, 'access', _160 => _160.frameOf, 'call', _161 => _161(this.selectedRoots()), 'optionalAccess', _162 => _162.angle]), () => ( 0)));
7564
+ return _optionalChain([h, 'optionalAccess', _163 => _163.nodeId]) ? "move" : "default";
6392
7565
  case "direct":
6393
- if (_optionalChain([h, 'optionalAccess', _148 => _148.anchor]) || _optionalChain([h, 'optionalAccess', _149 => _149.handle]) || _optionalChain([h, 'optionalAccess', _150 => _150.corner])) return "pointer";
6394
- if (_optionalChain([h, 'optionalAccess', _151 => _151.segment])) return "copy";
6395
- return _optionalChain([h, 'optionalAccess', _152 => _152.nodeId]) ? "move" : "default";
7566
+ if (_optionalChain([h, 'optionalAccess', _164 => _164.anchor]) || _optionalChain([h, 'optionalAccess', _165 => _165.handle]) || _optionalChain([h, 'optionalAccess', _166 => _166.corner])) return "pointer";
7567
+ if (_optionalChain([h, 'optionalAccess', _167 => _167.segment])) return "copy";
7568
+ return _optionalChain([h, 'optionalAccess', _168 => _168.nodeId]) ? "move" : "default";
6396
7569
  case "pen":
6397
- return _optionalChain([h, 'optionalAccess', _153 => _153.anchor]) ? "pointer" : "crosshair";
7570
+ return _optionalChain([h, 'optionalAccess', _169 => _169.anchor]) ? "pointer" : "crosshair";
6398
7571
  case "width":
6399
- if (_optionalChain([this, 'access', _154 => _154.drag, 'optionalAccess', _155 => _155.kind]) === "width") return this.drag.mode === "width" ? "ew-resize" : "grabbing";
6400
- if (_optionalChain([h, 'optionalAccess', _156 => _156.widthPoint])) return h.widthPoint.part === "center" ? "grab" : "ew-resize";
6401
- return _optionalChain([h, 'optionalAccess', _157 => _157.strokeSpot]) ? "copy" : "default";
7572
+ if (_optionalChain([this, 'access', _170 => _170.drag, 'optionalAccess', _171 => _171.kind]) === "width") return this.drag.mode === "width" ? "ew-resize" : "grabbing";
7573
+ if (_optionalChain([h, 'optionalAccess', _172 => _172.widthPoint])) return h.widthPoint.part === "center" ? "grab" : "ew-resize";
7574
+ return _optionalChain([h, 'optionalAccess', _173 => _173.strokeSpot]) ? "copy" : "default";
6402
7575
  case "shapeBuilder":
6403
- if (!_optionalChain([this, 'access', _158 => _158.builderPreview, 'optionalAccess', _159 => _159.faces, 'access', _160 => _160.length]) && !_optionalChain([this, 'access', _161 => _161.builderPreview, 'optionalAccess', _162 => _162.lines, 'access', _163 => _163.length])) return "crosshair";
7576
+ if (!_optionalChain([this, 'access', _174 => _174.builderPreview, 'optionalAccess', _175 => _175.faces, 'access', _176 => _176.length]) && !_optionalChain([this, 'access', _177 => _177.builderPreview, 'optionalAccess', _178 => _178.lines, 'access', _179 => _179.length])) return "crosshair";
6404
7577
  return this.builderPreview.mode === "erase" ? "not-allowed" : "cell";
6405
7578
  case "paintBucket":
6406
7579
  return this.builderPreview ? "cell" : "crosshair";
6407
7580
  case "blend":
6408
- return _optionalChain([h, 'optionalAccess', _164 => _164.nodeId]) ? this.blendStart && this.blendStart.nodeId !== h.nodeId ? "copy" : "pointer" : "crosshair";
7581
+ return _optionalChain([h, 'optionalAccess', _180 => _180.nodeId]) ? this.blendStart && this.blendStart.nodeId !== h.nodeId ? "copy" : "pointer" : "crosshair";
7582
+ case "gradient": {
7583
+ const part = _optionalChain([this, 'access', _181 => _181.drag, 'optionalAccess', _182 => _182.kind]) === "gradient" ? this.drag.part : _optionalChain([h, 'optionalAccess', _183 => _183.gradient]);
7584
+ if (!part) return "crosshair";
7585
+ if (part.kind === "bar") return this.drag ? "grabbing" : "copy";
7586
+ if (part.kind === "handle" && part.handle === "start") return this.drag ? "grabbing" : "move";
7587
+ return "pointer";
7588
+ }
6409
7589
  default:
6410
7590
  return "crosshair";
6411
7591
  }
@@ -6426,11 +7606,11 @@ var VectorEditorController = (_class5 = class {
6426
7606
  this.selection = s.selection.filter((id) => this.getNode(id));
6427
7607
  this.anchors = s.anchors.filter((r) => getAnchor(this.doc, r));
6428
7608
  this.editingId = s.editingId && this.getNode(s.editingId) ? s.editingId : null;
6429
- this.pen = s.pen && _optionalChain([this, 'access', _165 => _165.getNode, 'call', _166 => _166(s.pen.nodeId), 'optionalAccess', _167 => _167.subpaths, 'optionalAccess', _168 => _168[s.pen.subpath]]) ? s.pen : null;
7609
+ this.pen = s.pen && _optionalChain([this, 'access', _184 => _184.getNode, 'call', _185 => _185(s.pen.nodeId), 'optionalAccess', _186 => _186.subpaths, 'optionalAccess', _187 => _187[s.pen.subpath]]) ? s.pen : null;
6430
7610
  if (this.widthPoints.length) {
6431
7611
  const kept = this.widthPoints.filter((r) => {
6432
7612
  const n = this.getNode(r.nodeId);
6433
- return _optionalChain([n, 'optionalAccess', _169 => _169.type]) === "path" && !!_optionalChain([n, 'access', _170 => _170.strokeWidthProfile, 'optionalAccess', _171 => _171[r.index]]);
7613
+ return _optionalChain([n, 'optionalAccess', _188 => _188.type]) === "path" && !!_optionalChain([n, 'access', _189 => _189.strokeWidthProfile, 'optionalAccess', _190 => _190[r.index]]);
6434
7614
  });
6435
7615
  if (kept.length !== this.widthPoints.length) this.widthPoints = kept;
6436
7616
  }
@@ -6612,7 +7792,7 @@ var VectorEditorController = (_class5 = class {
6612
7792
  if (res.kind) {
6613
7793
  this.snappedToObject = true;
6614
7794
  this.setGuides(res);
6615
- return _optionalChain([leave, 'optionalAccess', _172 => _172.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) };
7795
+ return _optionalChain([leave, 'optionalAccess', _191 => _191.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) };
6616
7796
  }
6617
7797
  }
6618
7798
  this.setGuides(null);
@@ -6630,14 +7810,14 @@ var VectorEditorController = (_class5 = class {
6630
7810
  /** Where the pointer at `p` would place a point with the pen or a shape tool (and the guides showing why). */
6631
7811
  hoverSnap(p, input) {
6632
7812
  if (this.tool === "pen") {
6633
- const sp = this.pen ? _optionalChain([this, 'access', _173 => _173.getNode, 'call', _174 => _174(this.pen.nodeId), 'optionalAccess', _175 => _175.subpaths, 'access', _176 => _176[this.pen.subpath]]) : void 0;
6634
- if (this.pen && sp && _optionalChain([this, 'access', _177 => _177.hover, 'optionalAccess', _178 => _178.anchor])) {
7813
+ const sp = this.pen ? _optionalChain([this, 'access', _192 => _192.getNode, 'call', _193 => _193(this.pen.nodeId), 'optionalAccess', _194 => _194.subpaths, 'access', _195 => _195[this.pen.subpath]]) : void 0;
7814
+ if (this.pen && sp && _optionalChain([this, 'access', _196 => _196.hover, 'optionalAccess', _197 => _197.anchor])) {
6635
7815
  this.setGuides(null);
6636
7816
  return p;
6637
7817
  }
6638
7818
  const last = this.pen && sp ? getAnchorWorld(this.doc, { nodeId: this.pen.nodeId, subpath: this.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
6639
7819
  const leave = last ? { at: { x: last.x, y: last.y }, constrain: input.shift ? "angle" : null } : null;
6640
- return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access', _179 => _179.pen, 'optionalAccess', _180 => _180.nodeId]), () => ( null))));
7820
+ return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access', _198 => _198.pen, 'optionalAccess', _199 => _199.nodeId]), () => ( null))));
6641
7821
  }
6642
7822
  if (SHAPE_TOOLS.has(this.tool)) return this.snapDrawn(p, null, input, () => this.drawingTargets(null));
6643
7823
  this.setGuides(null);
@@ -6785,7 +7965,7 @@ var VectorEditorController = (_class5 = class {
6785
7965
  if (d.fillRule) out.fillRule = d.fillRule;
6786
7966
  if (d.strokeAlign && d.strokeAlign !== "center") out.strokeAlign = d.strokeAlign;
6787
7967
  for (const key of ["startArrow", "endArrow", "arrowAlign", "startArrowScale", "endArrowScale"]) if (d[key]) out[key] = d[key];
6788
- if (_optionalChain([d, 'access', _181 => _181.strokeWidthProfile, 'optionalAccess', _182 => _182.length])) out.strokeWidthProfile = d.strokeWidthProfile.map((p) => ({ ...p }));
7968
+ if (_optionalChain([d, 'access', _200 => _200.strokeWidthProfile, 'optionalAccess', _201 => _201.length])) out.strokeWidthProfile = d.strokeWidthProfile.map((p) => ({ ...p }));
6789
7969
  return out;
6790
7970
  }
6791
7971
  /** Merges `patch` into a node (paths take style and geometry; groups name / visibility / opacity). */
@@ -6831,6 +8011,233 @@ var VectorEditorController = (_class5 = class {
6831
8011
  this.widthEdit = null;
6832
8012
  return this.setStyle({ strokeWidthProfile: points.length ? points : null }, _nullishCoalesce(options.ids, () => ( this.selection)), { label: points.length ? "Width Profile" : "Uniform Width" });
6833
8013
  }
8014
+ // -------------------------------------------------------------------------
8015
+ // Gradients
8016
+ // -------------------------------------------------------------------------
8017
+ /** Which paint (fill or stroke) the gradient tool and editor work on. */
8018
+ setGradientTarget(target) {
8019
+ if (target === this.gradientEdit.target) return;
8020
+ this.gradientEdit = { target, stop: null };
8021
+ this.notify();
8022
+ }
8023
+ /** Selects a stop of the gradient being edited (index into its sorted stops), or none. */
8024
+ selectGradientStop(index) {
8025
+ if (index === this.gradientEdit.stop) return;
8026
+ this.gradientEdit = { ...this.gradientEdit, stop: index };
8027
+ this.notify();
8028
+ }
8029
+ /** The gradient tool's annotator for the selection (screen pixels), or null when its paint is not a gradient. */
8030
+ getGradientAnnotator() {
8031
+ return getVectorGradientAnnotator(this.doc, this.selection, this.viewport, this.gradientEdit.target);
8032
+ }
8033
+ /** Unlocked paths of `ids` (default: the selection). */
8034
+ gradientPaths(ids = this.selection) {
8035
+ const out = /* @__PURE__ */ new Map();
8036
+ for (const id of ids) {
8037
+ const node = this.getNode(id);
8038
+ if (node && !node.locked) {
8039
+ for (const p of collectPathNodes(node)) if (!p.locked) out.set(p.id, p);
8040
+ }
8041
+ }
8042
+ return [...out.values()];
8043
+ }
8044
+ /**
8045
+ * Edits the gradient of the `target` paint (default: the editor's) of the selected (or given)
8046
+ * paths with `patch`: a colour or no paint becomes a gradient (`patch.type`, default linear,
8047
+ * from the colour to white). Consecutive edits with the same `merge` key are one undo step.
8048
+ */
8049
+ setGradient(patch, options = {}) {
8050
+ const target = _nullishCoalesce(options.target, () => ( this.gradientEdit.target));
8051
+ const paths = this.gradientPaths(_nullishCoalesce(options.ids, () => ( this.selection)));
8052
+ if (!paths.length) return { ok: false, message: "Nothing is selected." };
8053
+ const next = /* @__PURE__ */ new Map();
8054
+ for (const path of paths) {
8055
+ const paint = path[target];
8056
+ const base = isVectorGradient(paint) ? paint : defaultVectorGradient(_nullishCoalesce(patch.type, () => ( "linear")), paint);
8057
+ const g = applyVectorGradientPatch(base, patch);
8058
+ if (!g) return { ok: false, message: "That is not a valid gradient: it needs at least one stop with an offset and a colour." };
8059
+ next.set(path.id, { ...path, [target]: g });
8060
+ }
8061
+ this.change(_nullishCoalesce(options.label, () => ( "Gradient")), () => this.setDoc(mapTree(this.doc.nodes, (n) => _nullishCoalesce(next.get(n.id), () => ( n)))), options.merge);
8062
+ const stops = _nullishCoalesce(_optionalChain([next, 'access', _202 => _202.values, 'call', _203 => _203(), 'access', _204 => _204.next, 'call', _205 => _205(), 'access', _206 => _206.value, 'optionalAccess', _207 => _207[target], 'optionalAccess', _208 => _208.stops, 'access', _209 => _209.length]), () => ( 0));
8063
+ if (this.gradientEdit.stop !== null && this.gradientEdit.stop >= stops) this.gradientEdit = { ...this.gradientEdit, stop: null };
8064
+ return { ok: true };
8065
+ }
8066
+ /** Applies `fn` to every target gradient of the selection (paths whose paint is not a gradient are skipped). */
8067
+ editGradients(label, fn, merge) {
8068
+ const target = this.gradientEdit.target;
8069
+ const paths = this.gradientPaths().filter((p) => isVectorGradient(p[target]));
8070
+ if (!paths.length) return { ok: false, message: "The selection has no gradient." };
8071
+ const next = /* @__PURE__ */ new Map();
8072
+ for (const path of paths) {
8073
+ const g = path[target];
8074
+ const stops = fn(sortedVectorGradientStops(g));
8075
+ if (!stops) return { ok: false, message: "A gradient keeps at least two stops." };
8076
+ next.set(path.id, { ...path, [target]: { ...g, stops } });
8077
+ }
8078
+ this.change(label, () => this.setDoc(mapTree(this.doc.nodes, (n) => _nullishCoalesce(next.get(n.id), () => ( n)))), merge);
8079
+ return { ok: true };
8080
+ }
8081
+ /** Changes stop `index` (colour, opacity, offset or midpoint) of the selection's gradients; selects it. */
8082
+ setGradientStop(index, patch, options = {}) {
8083
+ let moved = index;
8084
+ const result = this.editGradients(
8085
+ "Gradient Stop",
8086
+ (stops) => {
8087
+ if (!stops[index]) return stops;
8088
+ let next = stops.map((s, i) => {
8089
+ if (i !== index) return s;
8090
+ const out = { ...s };
8091
+ if (patch.color !== void 0) out.color = patch.color;
8092
+ if (patch.opacity !== void 0 && Number.isFinite(patch.opacity)) {
8093
+ const o = Math.min(1, Math.max(0, patch.opacity));
8094
+ if (o < 1) out.opacity = o;
8095
+ else delete out.opacity;
8096
+ }
8097
+ if (patch.midpoint !== void 0 && Number.isFinite(patch.midpoint)) {
8098
+ const m = Math.min(0.95, Math.max(0.05, patch.midpoint));
8099
+ if (Math.abs(m - 0.5) > 1e-3) out.midpoint = m;
8100
+ else delete out.midpoint;
8101
+ }
8102
+ return out;
8103
+ });
8104
+ if (patch.offset !== void 0 && Number.isFinite(patch.offset)) ({ stops: next, index: moved } = moveVectorGradientStop(next, index, patch.offset));
8105
+ return next;
8106
+ },
8107
+ options.merge
8108
+ );
8109
+ if (result.ok) this.selectGradientStop(moved);
8110
+ return result;
8111
+ }
8112
+ /** Adds a stop at `offset` (painting what the gradient paints there) to the selection's gradients and selects it. */
8113
+ addGradientStop(offset) {
8114
+ let index = 0;
8115
+ const target = this.gradientEdit.target;
8116
+ const result = this.editGradients("Add Gradient Stop", (stops) => {
8117
+ const path = this.gradientPaths().find((p) => isVectorGradient(p[target]));
8118
+ const r = insertVectorGradientStop({ ...path[target], stops }, offset);
8119
+ index = r.index;
8120
+ return r.stops;
8121
+ });
8122
+ if (result.ok) this.selectGradientStop(index);
8123
+ return result;
8124
+ }
8125
+ /** Removes stop `index` of the selection's gradients (never below two stops). */
8126
+ removeGradientStop(index) {
8127
+ const result = this.editGradients("Delete Gradient Stop", (stops) => removeVectorGradientStop(stops, index));
8128
+ if (result.ok) this.selectGradientStop(null);
8129
+ return result;
8130
+ }
8131
+ /** Reverses the selection's gradients (stops and midpoints). */
8132
+ reverseGradient() {
8133
+ return this.setGradient({ reverse: true }, { label: "Reverse Gradient" });
8134
+ }
8135
+ /** `doc` with path `id`'s `target` paint replaced. */
8136
+ withGradient(doc, id, target, g) {
8137
+ return { ...doc, nodes: mapTree(doc.nodes, (n) => n.id === id ? { ...n, [target]: g } : n) };
8138
+ }
8139
+ downGradient(screen, p, input) {
8140
+ const before = this.snapshot();
8141
+ const a = this.getGradientAnnotator();
8142
+ let part = a ? hitVectorGradientAnnotator(a, screen) : null;
8143
+ if (a && part) {
8144
+ if (part.kind === "handle" && part.handle === "start" && input.alt && a.gradient.type === "radial") part = { kind: "handle", handle: "focal" };
8145
+ if (part.kind === "stop") this.gradientEdit = { ...this.gradientEdit, stop: part.index };
8146
+ this.drag = { kind: "gradient", part, nodeId: a.nodeId, targets: [a.nodeId], annotator: a, start: screen, startDoc: p, before, base: this.doc, moved: false, stop: part.kind === "stop" ? part.index : null };
8147
+ this.interaction = { type: "gradient", part: part.kind, ...part.kind === "handle" ? { handle: part.handle } : {} };
8148
+ this.notify();
8149
+ return;
8150
+ }
8151
+ const hit = this.hitPath(p);
8152
+ if (hit && !this.gradientPaths().some((n) => n.id === hit.id)) {
8153
+ this.selection = [hit.id];
8154
+ this.gradientEdit = { ...this.gradientEdit, stop: null };
8155
+ }
8156
+ const targets = this.gradientPaths().map((n) => n.id);
8157
+ if (!targets.length) {
8158
+ if (this.selection.length) this.clearSelection();
8159
+ return;
8160
+ }
8161
+ this.drag = { kind: "gradient", part: null, nodeId: null, targets, annotator: null, start: screen, startDoc: p, before, base: this.doc, moved: false, stop: null };
8162
+ this.interaction = { type: "gradient", part: "create" };
8163
+ this.notify();
8164
+ }
8165
+ moveGradient(drag, screen, p, input) {
8166
+ if (!drag.moved && Math.hypot(screen.x - drag.start.x, screen.y - drag.start.y) < 3) return;
8167
+ drag.moved = true;
8168
+ const target = this.gradientEdit.target;
8169
+ const base = drag.base;
8170
+ const constrained = (from, to) => {
8171
+ if (!input.shift) return to;
8172
+ const ang = Math.round(Math.atan2(to.y - from.y, to.x - from.x) / (Math.PI / 4)) * (Math.PI / 4);
8173
+ const len2 = Math.hypot(to.x - from.x, to.y - from.y);
8174
+ return { x: from.x + Math.cos(ang) * len2, y: from.y + Math.sin(ang) * len2 };
8175
+ };
8176
+ if (!drag.part) {
8177
+ const end = constrained(drag.startDoc, p);
8178
+ let doc = base;
8179
+ for (const id2 of drag.targets) {
8180
+ const node2 = findVectorNode(base.nodes, id2);
8181
+ if (!node2) continue;
8182
+ const paint = node2[target];
8183
+ const g2 = isVectorGradient(paint) ? paint : defaultVectorGradient("linear", paint);
8184
+ doc = this.withGradient(doc, id2, target, setVectorGradientVector(g2, vectorPathLocalBounds(node2), this.toLocal(id2, drag.startDoc, base), this.toLocal(id2, end, base)));
8185
+ }
8186
+ this.doc = doc;
8187
+ this.notify(true);
8188
+ return;
8189
+ }
8190
+ const id = drag.nodeId;
8191
+ const node = findVectorNode(base.nodes, id);
8192
+ const g = node[target];
8193
+ const bounds = vectorPathLocalBounds(node);
8194
+ const local = (q) => this.toLocal(id, q, base);
8195
+ const part = drag.part;
8196
+ let next = g;
8197
+ if (part.kind === "handle") {
8198
+ let to = p;
8199
+ if (part.handle === "end" || part.handle === "radius") to = constrained(this.screenToDocument(drag.annotator.start), p);
8200
+ next = moveVectorGradientHandle(g, bounds, part.handle, local(to), local(drag.startDoc));
8201
+ } else if (part.kind === "bar") {
8202
+ next = moveVectorGradientHandle(g, bounds, "start", local(p), local(drag.startDoc));
8203
+ } else {
8204
+ const a = drag.annotator;
8205
+ const pos = vectorGradientBarPosition(a, screen);
8206
+ const stops = sortedVectorGradientStops(g);
8207
+ if (part.kind === "stop") {
8208
+ const off = Math.abs(pos.across - VECTOR_GRADIENT_STOP_GAP) > VECTOR_GRADIENT_OFF_BAR;
8209
+ const removed = off ? removeVectorGradientStop(stops, part.index) : null;
8210
+ if (removed) {
8211
+ next = { ...g, stops: removed };
8212
+ drag.stop = null;
8213
+ } else {
8214
+ const r = moveVectorGradientStop(stops, part.index, Math.min(1, Math.max(0, pos.offset)));
8215
+ next = { ...g, stops: r.stops };
8216
+ drag.stop = r.index;
8217
+ }
8218
+ this.interaction = { type: "gradient", part: "stop", ...removed ? { removing: true } : {} };
8219
+ this.gradientEdit = { ...this.gradientEdit, stop: drag.stop };
8220
+ } else {
8221
+ const m = vectorGradientMidpointFromPosition(stops, part.index, pos.offset);
8222
+ if (m !== null) next = { ...g, stops: stops.map((s, i) => i !== part.index ? s : Math.abs(m - 0.5) < 1e-3 ? (({ midpoint: _m, ...rest }) => rest)(s) : { ...s, midpoint: m }) };
8223
+ }
8224
+ }
8225
+ this.doc = this.withGradient(base, id, target, next);
8226
+ this.notify(true);
8227
+ }
8228
+ upGradient(drag) {
8229
+ const part = drag.part;
8230
+ if (!drag.moved) {
8231
+ if (_optionalChain([part, 'optionalAccess', _210 => _210.kind]) === "bar") {
8232
+ this.addGradientStop(part.offset);
8233
+ return;
8234
+ }
8235
+ this.notify();
8236
+ return;
8237
+ }
8238
+ const label = !part ? "Gradient" : part.kind === "stop" ? drag.stop === null ? "Delete Gradient Stop" : "Move Gradient Stop" : part.kind === "midpoint" ? "Gradient Midpoint" : part.kind === "bar" || part.kind === "handle" && part.handle === "start" ? "Move Gradient" : "Gradient Handle";
8239
+ return this.commit(label, drag.before);
8240
+ }
6834
8241
  /** Removes one width point of a path's profile. */
6835
8242
  removeWidthPoint(ref) {
6836
8243
  return this.removeWidthPoints([ref]);
@@ -6840,7 +8247,7 @@ var VectorEditorController = (_class5 = class {
6840
8247
  const byNode = /* @__PURE__ */ new Map();
6841
8248
  for (const r of refs) {
6842
8249
  const node = this.getNode(r.nodeId);
6843
- if (_optionalChain([node, 'optionalAccess', _183 => _183.type]) !== "path" || !_optionalChain([node, 'access', _184 => _184.strokeWidthProfile, 'optionalAccess', _185 => _185[r.index]])) continue;
8250
+ if (_optionalChain([node, 'optionalAccess', _211 => _211.type]) !== "path" || !_optionalChain([node, 'access', _212 => _212.strokeWidthProfile, 'optionalAccess', _213 => _213[r.index]])) continue;
6844
8251
  byNode.set(r.nodeId, [..._nullishCoalesce(byNode.get(r.nodeId), () => ( [])), r.index]);
6845
8252
  }
6846
8253
  if (!byNode.size) return { ok: false, message: "There is no such width point." };
@@ -6858,12 +8265,12 @@ var VectorEditorController = (_class5 = class {
6858
8265
  }
6859
8266
  /** Selects width points (width tool); points on other paths than the first are dropped. */
6860
8267
  selectWidthPoints(refs) {
6861
- const first = _optionalChain([refs, 'access', _186 => _186[0], 'optionalAccess', _187 => _187.nodeId]);
8268
+ const first = _optionalChain([refs, 'access', _214 => _214[0], 'optionalAccess', _215 => _215.nodeId]);
6862
8269
  const next = [];
6863
8270
  for (const r of refs) {
6864
8271
  if (r.nodeId !== first || next.some((q) => q.index === r.index)) continue;
6865
8272
  const n = this.getNode(r.nodeId);
6866
- if (_optionalChain([n, 'optionalAccess', _188 => _188.type]) === "path" && _optionalChain([n, 'access', _189 => _189.strokeWidthProfile, 'optionalAccess', _190 => _190[r.index]])) next.push({ nodeId: r.nodeId, index: r.index, subpath: _nullishCoalesce(r.subpath, () => ( 0)) });
8273
+ if (_optionalChain([n, 'optionalAccess', _216 => _216.type]) === "path" && _optionalChain([n, 'access', _217 => _217.strokeWidthProfile, 'optionalAccess', _218 => _218[r.index]])) next.push({ nodeId: r.nodeId, index: r.index, subpath: _nullishCoalesce(r.subpath, () => ( 0)) });
6867
8274
  }
6868
8275
  this.widthPoints = next;
6869
8276
  this.notify();
@@ -6878,14 +8285,14 @@ var VectorEditorController = (_class5 = class {
6878
8285
  */
6879
8286
  setWidthPoint(target, values) {
6880
8287
  const node = this.getNode(target.nodeId);
6881
- if (_optionalChain([node, 'optionalAccess', _191 => _191.type]) !== "path") return { ok: false, message: "There is no such path." };
8288
+ if (_optionalChain([node, 'optionalAccess', _219 => _219.type]) !== "path") return { ok: false, message: "There is no such path." };
6882
8289
  const g = vectorStrokeWidthGeometry(node);
6883
8290
  if (!g || !(g.unit > 0)) return { ok: false, message: "The path has no visible stroke." };
6884
- const num = (v2) => typeof v2 === "number" && Number.isFinite(v2) ? v2 : void 0;
6885
- const t = num(values.t);
6886
- const left = num(values.left);
6887
- const right = num(values.right);
6888
- const scale = (w) => w === void 0 ? void 0 : Math.max(0, w) / g.unit;
8291
+ const num2 = (v2) => typeof v2 === "number" && Number.isFinite(v2) ? v2 : void 0;
8292
+ const t = num2(values.t);
8293
+ const left2 = num2(values.left);
8294
+ const right = num2(values.right);
8295
+ const scale3 = (w) => w === void 0 ? void 0 : Math.max(0, w) / g.unit;
6889
8296
  const profile = sanitizeWidthProfile(node.strokeWidthProfile);
6890
8297
  let points;
6891
8298
  let index;
@@ -6893,11 +8300,11 @@ var VectorEditorController = (_class5 = class {
6893
8300
  if (existing === null) {
6894
8301
  if (t === void 0) return { ok: false, message: "A new width point needs a position t." };
6895
8302
  if (profile.length >= VECTOR_WIDTH_PROFILE_MAX_POINTS) return { ok: false, message: "The profile has too many width points." };
6896
- ({ points, index } = insertWidthPoint(profile.length ? profile : null, t, { left: scale(left), right: scale(right) }, { step: true }));
8303
+ ({ points, index } = insertWidthPoint(profile.length ? profile : null, t, { left: scale3(left2), right: scale3(right) }, { step: true }));
6897
8304
  } else {
6898
8305
  const p = profile[existing];
6899
8306
  if (!p) return { ok: false, message: "There is no such width point." };
6900
- const sides2 = { left: _nullishCoalesce(scale(left), () => ( p.left)), right: _nullishCoalesce(scale(right), () => ( p.right)) };
8307
+ const sides2 = { left: _nullishCoalesce(scale3(left2), () => ( p.left)), right: _nullishCoalesce(scale3(right), () => ( p.right)) };
6901
8308
  const tt = t === void 0 ? p.t : Math.min(Math.max(t, 0), 1);
6902
8309
  if (Math.abs(tt - p.t) <= VECTOR_WIDTH_SAME_T) {
6903
8310
  points = setWidthPoint(profile, existing, sides2);
@@ -6911,11 +8318,11 @@ var VectorEditorController = (_class5 = class {
6911
8318
  }
6912
8319
  }
6913
8320
  }
6914
- const subpath = _optionalChain([this, 'access', _192 => _192.widthEdit, 'optionalAccess', _193 => _193.nodeId]) === node.id ? this.widthEdit.subpath : _nullishCoalesce(_optionalChain([this, 'access', _194 => _194.widthPoints, 'access', _195 => _195.find, 'call', _196 => _196((r) => r.nodeId === node.id), 'optionalAccess', _197 => _197.subpath]), () => ( 0));
8321
+ const subpath = _optionalChain([this, 'access', _220 => _220.widthEdit, 'optionalAccess', _221 => _221.nodeId]) === node.id ? this.widthEdit.subpath : _nullishCoalesce(_optionalChain([this, 'access', _222 => _222.widthPoints, 'access', _223 => _223.find, 'call', _224 => _224((r) => r.nodeId === node.id), 'optionalAccess', _225 => _225.subpath]), () => ( 0));
6915
8322
  const edit = this.widthEdit;
6916
8323
  this.change("Width Point", () => this.replaceNode(node.id, withStyle(node, { strokeWidthProfile: points })));
6917
8324
  this.widthPoints = [{ nodeId: node.id, index, subpath }];
6918
- if (_optionalChain([edit, 'optionalAccess', _198 => _198.nodeId]) === node.id && (edit.index === existing || existing === null)) this.widthEdit = this.widthEditFor(node.id, subpath, index, null);
8325
+ if (_optionalChain([edit, 'optionalAccess', _226 => _226.nodeId]) === node.id && (edit.index === existing || existing === null)) this.widthEdit = this.widthEditFor(node.id, subpath, index, null);
6919
8326
  this.notify();
6920
8327
  return { ok: true };
6921
8328
  }
@@ -6949,7 +8356,7 @@ var VectorEditorController = (_class5 = class {
6949
8356
  /** The editor's view of point `index` (or of the stroke at `t` when index is null) of `nodeId`. */
6950
8357
  widthEditFor(nodeId, subpath, index, t) {
6951
8358
  const node = this.getNode(nodeId);
6952
- if (_optionalChain([node, 'optionalAccess', _199 => _199.type]) !== "path") return null;
8359
+ if (_optionalChain([node, 'optionalAccess', _227 => _227.type]) !== "path") return null;
6953
8360
  const g = vectorStrokeWidthGeometry(node);
6954
8361
  if (!g) return null;
6955
8362
  const profile = sanitizeWidthProfile(node.strokeWidthProfile);
@@ -6983,7 +8390,7 @@ var VectorEditorController = (_class5 = class {
6983
8390
  const refs = this.cornerTargets(options);
6984
8391
  if (!refs.length) return { ok: false, message: "Nothing is selected." };
6985
8392
  this.change("Corner Kind", () => {
6986
- this.doc = this.updateAnchors(this.doc, refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _200 => _200.corner, 'optionalAccess', _201 => _201.radius]), () => ( 0)), kind));
8393
+ this.doc = this.updateAnchors(this.doc, refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _228 => _228.corner, 'optionalAccess', _229 => _229.radius]), () => ( 0)), kind));
6987
8394
  });
6988
8395
  return { ok: true };
6989
8396
  }
@@ -7105,7 +8512,7 @@ var VectorEditorController = (_class5 = class {
7105
8512
  updateEffect(nodeId, effectId, patch, options = {}) {
7106
8513
  const merge = options.merge === true ? `effect:${nodeId}:${effectId}` : options.merge || void 0;
7107
8514
  const node = this.getNode(nodeId);
7108
- const current = _optionalChain([node, 'optionalAccess', _202 => _202.effects, 'optionalAccess', _203 => _203.find, 'call', _204 => _204((e) => e.id === effectId)]);
8515
+ const current = _optionalChain([node, 'optionalAccess', _230 => _230.effects, 'optionalAccess', _231 => _231.find, 'call', _232 => _232((e) => e.id === effectId)]);
7109
8516
  const label = current ? _nullishCoalesce(VECTOR_EFFECT_LABELS[current.type], () => ( "Effect")) : "Effect";
7110
8517
  return this.editEffects(
7111
8518
  nodeId,
@@ -7238,10 +8645,10 @@ var VectorEditorController = (_class5 = class {
7238
8645
  return this.selection.filter((id) => {
7239
8646
  let covered = false;
7240
8647
  const loc = locateVectorNode(this.doc.nodes, id);
7241
- let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _205 => _205.parent]), () => ( null));
8648
+ let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _233 => _233.parent]), () => ( null));
7242
8649
  while (parent) {
7243
8650
  if (this.selection.includes(parent.id)) covered = true;
7244
- parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _206 => _206(this.doc.nodes, parent.id), 'optionalAccess', _207 => _207.parent]), () => ( null));
8651
+ parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _234 => _234(this.doc.nodes, parent.id), 'optionalAccess', _235 => _235.parent]), () => ( null));
7245
8652
  }
7246
8653
  return !covered;
7247
8654
  });
@@ -7424,8 +8831,8 @@ var VectorEditorController = (_class5 = class {
7424
8831
  if (!ids.length) return null;
7425
8832
  const locs = ids.map((id2) => locateVectorNode(this.doc.nodes, id2));
7426
8833
  if (locs.some((l) => !l)) return null;
7427
- const parent = _nullishCoalesce(_optionalChain([locs, 'access', _208 => _208[0], 'access', _209 => _209.parent, 'optionalAccess', _210 => _210.id]), () => ( null));
7428
- if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access', _211 => _211.parent, 'optionalAccess', _212 => _212.id]), () => ( null))) !== parent)) return null;
8834
+ const parent = _nullishCoalesce(_optionalChain([locs, 'access', _236 => _236[0], 'access', _237 => _237.parent, 'optionalAccess', _238 => _238.id]), () => ( null));
8835
+ if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access', _239 => _239.parent, 'optionalAccess', _240 => _240.id]), () => ( null))) !== parent)) return null;
7429
8836
  const id = this.newId();
7430
8837
  this.change("Group", () => {
7431
8838
  const set = new Set(ids);
@@ -7448,7 +8855,7 @@ var VectorEditorController = (_class5 = class {
7448
8855
  }
7449
8856
  /** Replaces selected groups with their children. */
7450
8857
  ungroup(options = {}) {
7451
- const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _213 => _213.type]) === "group");
8858
+ const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _241 => _241.type]) === "group");
7452
8859
  if (!groups.length) return { ok: false, message: "Select a group to ungroup." };
7453
8860
  const set = new Set(groups.map((g) => g.id));
7454
8861
  this.change("Ungroup", () => {
@@ -7548,7 +8955,7 @@ var VectorEditorController = (_class5 = class {
7548
8955
  this.change(label, () => this.replaceNode(lone.id, { ...lone, liveBoolean: op, children }));
7549
8956
  return lone.id;
7550
8957
  }
7551
- const parents = roots.map((id2) => _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _214 => _214(this.doc.nodes, id2), 'optionalAccess', _215 => _215.parent]), () => ( null)));
8958
+ const parents = roots.map((id2) => _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _242 => _242(this.doc.nodes, id2), 'optionalAccess', _243 => _243.parent]), () => ( null)));
7552
8959
  if (parents.every((g) => isVectorLiveBoolean(g))) {
7553
8960
  const set = new Set(roots);
7554
8961
  this.change(
@@ -7569,8 +8976,8 @@ var VectorEditorController = (_class5 = class {
7569
8976
  return parents[0].id;
7570
8977
  }
7571
8978
  if (nodes.length < 2) return null;
7572
- const parent = _nullishCoalesce(_optionalChain([parents, 'access', _216 => _216[0], 'optionalAccess', _217 => _217.id]), () => ( null));
7573
- if (parents.some((g) => (_nullishCoalesce(_optionalChain([g, 'optionalAccess', _218 => _218.id]), () => ( null))) !== parent)) return null;
8979
+ const parent = _nullishCoalesce(_optionalChain([parents, 'access', _244 => _244[0], 'optionalAccess', _245 => _245.id]), () => ( null));
8980
+ if (parents.some((g) => (_nullishCoalesce(_optionalChain([g, 'optionalAccess', _246 => _246.id]), () => ( null))) !== parent)) return null;
7574
8981
  const id = this.newId();
7575
8982
  this.change(label, () => {
7576
8983
  const set = new Set(roots);
@@ -7618,7 +9025,7 @@ var VectorEditorController = (_class5 = class {
7618
9025
  }
7619
9026
  /** Splits selected compound paths into one path per subpath. */
7620
9027
  breakApart(options = {}) {
7621
- const paths = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _219 => _219.type]) === "path" && n.subpaths.length > 1);
9028
+ const paths = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _247 => _247.type]) === "path" && n.subpaths.length > 1);
7622
9029
  if (!paths.length) return { ok: false, message: "Select a compound path to break apart." };
7623
9030
  const created = [];
7624
9031
  const set = new Set(paths.map((p) => p.id));
@@ -7686,7 +9093,7 @@ var VectorEditorController = (_class5 = class {
7686
9093
  const out = /* @__PURE__ */ new Map();
7687
9094
  for (const id of _nullishCoalesce(ids, () => ( this.selection))) {
7688
9095
  const node = this.getNode(id);
7689
- const group = isVectorBlendGroup(node) ? node : _optionalChain([locateVectorNode, 'call', _220 => _220(this.doc.nodes, id), 'optionalAccess', _221 => _221.parent]);
9096
+ const group = isVectorBlendGroup(node) ? node : _optionalChain([locateVectorNode, 'call', _248 => _248(this.doc.nodes, id), 'optionalAccess', _249 => _249.parent]);
7690
9097
  if (isVectorBlendGroup(group)) out.set(group.id, group);
7691
9098
  }
7692
9099
  return [...out.values()];
@@ -7704,8 +9111,8 @@ var VectorEditorController = (_class5 = class {
7704
9111
  });
7705
9112
  if (ids.length < 2) return null;
7706
9113
  const locs = ids.map((id2) => locateVectorNode(this.doc.nodes, id2));
7707
- const parent = _nullishCoalesce(_optionalChain([locs, 'access', _222 => _222[0], 'access', _223 => _223.parent, 'optionalAccess', _224 => _224.id]), () => ( null));
7708
- if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access', _225 => _225.parent, 'optionalAccess', _226 => _226.id]), () => ( null))) !== parent)) return null;
9114
+ const parent = _nullishCoalesce(_optionalChain([locs, 'access', _250 => _250[0], 'access', _251 => _251.parent, 'optionalAccess', _252 => _252.id]), () => ( null));
9115
+ if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access', _253 => _253.parent, 'optionalAccess', _254 => _254.id]), () => ( null))) !== parent)) return null;
7709
9116
  const order = input.keepOrder ? ids : ids.slice().sort((a, b) => locs[ids.indexOf(a)].index - locs[ids.indexOf(b)].index);
7710
9117
  const blend = blendOptionsOf({ ...this.blendDefaults, ...input });
7711
9118
  if (input.starts) {
@@ -7747,7 +9154,7 @@ var VectorEditorController = (_class5 = class {
7747
9154
  const node = this.getNode(nodeId);
7748
9155
  if (!isVectorBlendGroup(group)) return { ok: false, message: `"${blendId}" is not a blend.` };
7749
9156
  if (!node || node.blendStep || node.locked || node.id === blendId) return { ok: false, message: `"${nodeId}" cannot be a key.` };
7750
- const parentOf = (id) => _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _227 => _227(this.doc.nodes, id), 'optionalAccess', _228 => _228.parent, 'optionalAccess', _229 => _229.id]), () => ( null));
9157
+ const parentOf = (id) => _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _255 => _255(this.doc.nodes, id), 'optionalAccess', _256 => _256.parent, 'optionalAccess', _257 => _257.id]), () => ( null));
7751
9158
  if (parentOf(blendId) !== parentOf(nodeId)) return { ok: false, message: "The new key must sit beside the blend (same parent)." };
7752
9159
  const start = options.start;
7753
9160
  this.change("Blend", () => {
@@ -7817,7 +9224,7 @@ var VectorEditorController = (_class5 = class {
7817
9224
  replaceBlendSpine(options = {}) {
7818
9225
  const roots = _nullishCoalesce(options.ids, () => ( this.selectedRoots()));
7819
9226
  const groups = this.blendTargets(roots);
7820
- const paths = roots.map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _230 => _230.type]) === "path" && !n.blendStep && !isVectorBlendGroup(_optionalChain([locateVectorNode, 'call', _231 => _231(this.doc.nodes, n.id), 'optionalAccess', _232 => _232.parent])));
9227
+ const paths = roots.map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _258 => _258.type]) === "path" && !n.blendStep && !isVectorBlendGroup(_optionalChain([locateVectorNode, 'call', _259 => _259(this.doc.nodes, n.id), 'optionalAccess', _260 => _260.parent])));
7821
9228
  if (groups.length !== 1 || paths.length !== 1) return { ok: false, message: "Select one blend and one path to be its spine." };
7822
9229
  const group = groups[0];
7823
9230
  const path = paths[0];
@@ -7848,7 +9255,7 @@ var VectorEditorController = (_class5 = class {
7848
9255
  shapeBuilderMap(ids) {
7849
9256
  const roots = _nullishCoalesce(ids, () => ( this.selectedRoots()));
7850
9257
  const key = roots.join("\0");
7851
- if (_optionalChain([this, 'access', _233 => _233.builderCache, 'optionalAccess', _234 => _234.doc]) === this.doc && this.builderCache.key === key) return this.builderCache.map;
9258
+ if (_optionalChain([this, 'access', _261 => _261.builderCache, 'optionalAccess', _262 => _262.doc]) === this.doc && this.builderCache.key === key) return this.builderCache.map;
7852
9259
  const entries = roots.length ? vectorPathEntries(this.doc, roots, { expand: false }) : [];
7853
9260
  const map = entries.length ? vectorShapeBuilderMap(entries) : null;
7854
9261
  this.builderCache = { doc: this.doc, key, map };
@@ -7908,9 +9315,9 @@ var VectorEditorController = (_class5 = class {
7908
9315
  const list = [];
7909
9316
  const r = plan.replace.get(i);
7910
9317
  if (r === void 0) list.push(node);
7911
- else if (_optionalChain([r, 'optionalAccess', _235 => _235.kind]) === "fill") list.push({ ...node, fillRule: "nonzero", subpaths: bezierToSubpaths(toLocal(r.path)) });
7912
- else if (_optionalChain([r, 'optionalAccess', _236 => _236.kind]) === "outline") list.push({ ...node, subpaths: bezierToSubpaths(toLocal(r.path)) });
7913
- else if (_optionalChain([r, 'optionalAccess', _237 => _237.kind]) === "pieces") {
9318
+ else if (_optionalChain([r, 'optionalAccess', _263 => _263.kind]) === "fill") list.push({ ...node, fillRule: "nonzero", subpaths: bezierToSubpaths(toLocal(r.path)) });
9319
+ else if (_optionalChain([r, 'optionalAccess', _264 => _264.kind]) === "outline") list.push({ ...node, subpaths: bezierToSubpaths(toLocal(r.path)) });
9320
+ else if (_optionalChain([r, 'optionalAccess', _265 => _265.kind]) === "pieces") {
7914
9321
  r.paths.forEach(
7915
9322
  (path, k) => list.push({ ...node, id: k === 0 ? node.id : this.newId(), subpaths: bezierToSubpaths(toLocal(path)).map((sp) => ({ ...sp, closed: false })) })
7916
9323
  );
@@ -7944,7 +9351,7 @@ var VectorEditorController = (_class5 = class {
7944
9351
  }
7945
9352
  /** The paint bucket's map of the visible outlines, cached per document and gap. */
7946
9353
  fillMap(gap) {
7947
- if (_optionalChain([this, 'access', _238 => _238.fillCache, 'optionalAccess', _239 => _239.doc]) === this.doc && this.fillCache.gap === gap) return this.fillCache.map;
9354
+ if (_optionalChain([this, 'access', _266 => _266.fillCache, 'optionalAccess', _267 => _267.doc]) === this.doc && this.fillCache.gap === gap) return this.fillCache.map;
7948
9355
  const map = vectorFillMap(this.doc, gap);
7949
9356
  this.fillCache = { doc: this.doc, gap, map };
7950
9357
  return map;
@@ -7958,8 +9365,8 @@ var VectorEditorController = (_class5 = class {
7958
9365
  */
7959
9366
  fillRegion(point, options = {}) {
7960
9367
  if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) return null;
7961
- const region = _optionalChain([this, 'access', _240 => _240.fillMap, 'call', _241 => _241(_nullishCoalesce(options.gap, () => ( this.editing.fillGap))), 'optionalAccess', _242 => _242.regionAt, 'call', _243 => _243([point.x, point.y])]);
7962
- if (!_optionalChain([region, 'optionalAccess', _244 => _244.length])) return null;
9368
+ const region = _optionalChain([this, 'access', _268 => _268.fillMap, 'call', _269 => _269(_nullishCoalesce(options.gap, () => ( this.editing.fillGap))), 'optionalAccess', _270 => _270.regionAt, 'call', _271 => _271([point.x, point.y])]);
9369
+ if (!_optionalChain([region, 'optionalAccess', _272 => _272.length])) return null;
7963
9370
  let below = null;
7964
9371
  const roots = new Set(this.selectedRoots());
7965
9372
  walkVectorNodes(this.doc.nodes, (n) => {
@@ -7985,7 +9392,7 @@ var VectorEditorController = (_class5 = class {
7985
9392
  /** Updates the Shape Builder / paint bucket highlight for the pointer at `p`; true when it changed. */
7986
9393
  hoverBuilder(p, alt) {
7987
9394
  if (this.tool === "paintBucket") {
7988
- const region = _nullishCoalesce(_optionalChain([this, 'access', _245 => _245.fillMap, 'call', _246 => _246(this.editing.fillGap), 'optionalAccess', _247 => _247.regionAt, 'call', _248 => _248([p.x, p.y])]), () => ( null));
9395
+ const region = _nullishCoalesce(_optionalChain([this, 'access', _273 => _273.fillMap, 'call', _274 => _274(this.editing.fillGap), 'optionalAccess', _275 => _275.regionAt, 'call', _276 => _276([p.x, p.y])]), () => ( null));
7989
9396
  const key2 = region ? _chunkVNYRI265cjs.bezierPathToSvg.call(void 0, region, { precision: 2 }) : null;
7990
9397
  if (key2 === this.builderHoverKey) return false;
7991
9398
  this.builderHoverKey = key2;
@@ -8093,7 +9500,7 @@ var VectorEditorController = (_class5 = class {
8093
9500
  /** `doc` with anchor `ref` moved to document point `to` by a Curvature-tool move. */
8094
9501
  curvatureMoved(doc, ref, to) {
8095
9502
  const node = findVectorNode(doc.nodes, ref.nodeId);
8096
- if (_optionalChain([node, 'optionalAccess', _249 => _249.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
9503
+ if (_optionalChain([node, 'optionalAccess', _277 => _277.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
8097
9504
  const q = this.toLocal(ref.nodeId, to, doc);
8098
9505
  const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? curvatureMoveAnchor(sp, ref.index, q) : sp);
8099
9506
  const nodes = mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n);
@@ -8102,7 +9509,7 @@ var VectorEditorController = (_class5 = class {
8102
9509
  /** `doc` with segment `ref` bent through document point `p` at parameter `t`. */
8103
9510
  bent(doc, ref, t, p) {
8104
9511
  const node = findVectorNode(doc.nodes, ref.nodeId);
8105
- if (_optionalChain([node, 'optionalAccess', _250 => _250.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
9512
+ if (_optionalChain([node, 'optionalAccess', _278 => _278.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
8106
9513
  const q = this.toLocal(ref.nodeId, p, doc);
8107
9514
  const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? bendSegment(sp, ref.segment, t, q) : sp);
8108
9515
  return { ...doc, nodes: mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n) };
@@ -8113,7 +9520,7 @@ var VectorEditorController = (_class5 = class {
8113
9520
  */
8114
9521
  bendSegment(ref, to, t = 0.5) {
8115
9522
  const node = this.getNode(ref.nodeId);
8116
- const sp = _optionalChain([node, 'optionalAccess', _251 => _251.type]) === "path" ? node.subpaths[ref.subpath] : void 0;
9523
+ const sp = _optionalChain([node, 'optionalAccess', _279 => _279.type]) === "path" ? node.subpaths[ref.subpath] : void 0;
8117
9524
  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." };
8118
9525
  if (node.locked) return { ok: false, message: "The path is locked." };
8119
9526
  this.change("Bend Segment", () => {
@@ -8148,7 +9555,7 @@ var VectorEditorController = (_class5 = class {
8148
9555
  /** Adds an anchor on the nearest segment of `nodeId` to document point `point` (shape unchanged). */
8149
9556
  insertAnchorAt(nodeId, point) {
8150
9557
  const node = this.getNode(nodeId);
8151
- if (_optionalChain([node, 'optionalAccess', _252 => _252.type]) !== "path") return null;
9558
+ if (_optionalChain([node, 'optionalAccess', _280 => _280.type]) !== "path") return null;
8152
9559
  const hit = nearestSegment(node, this.toLocal(nodeId, point));
8153
9560
  if (!hit) return null;
8154
9561
  const { subpath, index } = insertAnchor(node.subpaths[hit.subpath], hit.segment, hit.t);
@@ -8192,7 +9599,7 @@ var VectorEditorController = (_class5 = class {
8192
9599
  setClosed(closed, nodeId) {
8193
9600
  const targets = nodeId ? (() => {
8194
9601
  const n = this.getNode(nodeId);
8195
- return _optionalChain([n, 'optionalAccess', _253 => _253.type]) === "path" ? n.subpaths.map((_, s) => ({ nodeId, subpath: s })) : [];
9602
+ return _optionalChain([n, 'optionalAccess', _281 => _281.type]) === "path" ? n.subpaths.map((_, s) => ({ nodeId, subpath: s })) : [];
8196
9603
  })() : this.anchors.map((r) => ({ nodeId: r.nodeId, subpath: r.subpath }));
8197
9604
  if (!targets.length) return { ok: false, message: "Select a path or points." };
8198
9605
  this.change(closed ? "Close Path" : "Open Path", () => {
@@ -8213,7 +9620,7 @@ var VectorEditorController = (_class5 = class {
8213
9620
  this.setDoc(
8214
9621
  mapTree(
8215
9622
  this.doc.nodes,
8216
- (n) => n.type === "path" && set.has(n.id) ? { ...n, subpaths: n.subpaths.map(reverseSubpath), ..._optionalChain([n, 'access', _254 => _254.strokeWidthProfile, 'optionalAccess', _255 => _255.length]) ? { strokeWidthProfile: reverseWidthProfile(n.strokeWidthProfile) } : {} } : n
9623
+ (n) => n.type === "path" && set.has(n.id) ? { ...n, subpaths: n.subpaths.map(reverseSubpath), ..._optionalChain([n, 'access', _282 => _282.strokeWidthProfile, 'optionalAccess', _283 => _283.length]) ? { strokeWidthProfile: reverseWidthProfile(n.strokeWidthProfile) } : {} } : n
8217
9624
  )
8218
9625
  );
8219
9626
  this.anchors = [];
@@ -8351,7 +9758,7 @@ var VectorEditorController = (_class5 = class {
8351
9758
  */
8352
9759
  cutPathAt(nodeId, point) {
8353
9760
  const node = this.getNode(nodeId);
8354
- if (_optionalChain([node, 'optionalAccess', _256 => _256.type]) !== "path" || node.locked) return null;
9761
+ if (_optionalChain([node, 'optionalAccess', _284 => _284.type]) !== "path" || node.locked) return null;
8355
9762
  const local = this.toLocal(nodeId, point);
8356
9763
  const hit = nearestSegment(node, local);
8357
9764
  if (!hit) return null;
@@ -8401,7 +9808,7 @@ var VectorEditorController = (_class5 = class {
8401
9808
  if (n.visible === false || n.locked || parent && blocked.has(parent.id)) blocked.add(n.id);
8402
9809
  });
8403
9810
  const visible = (p) => !blocked.has(p.id);
8404
- if (_optionalChain([ids, 'optionalAccess', _257 => _257.length]) || this.selection.length) return this.pathTargets(_nullishCoalesce(ids, () => ( this.selection))).filter(visible);
9811
+ if (_optionalChain([ids, 'optionalAccess', _285 => _285.length]) || this.selection.length) return this.pathTargets(_nullishCoalesce(ids, () => ( this.selection))).filter(visible);
8405
9812
  const box = polylineBounds(points.map(toPoint2), pad);
8406
9813
  if (!box) return [];
8407
9814
  const out = [];
@@ -8487,7 +9894,7 @@ var VectorEditorController = (_class5 = class {
8487
9894
  finishPath() {
8488
9895
  if (!this.pen) return;
8489
9896
  const node = this.getNode(this.pen.nodeId);
8490
- const sp = _optionalChain([node, 'optionalAccess', _258 => _258.subpaths, 'access', _259 => _259[this.pen.subpath]]);
9897
+ const sp = _optionalChain([node, 'optionalAccess', _286 => _286.subpaths, 'access', _287 => _287[this.pen.subpath]]);
8491
9898
  const pen = this.pen;
8492
9899
  this.pen = null;
8493
9900
  this.pointer = null;
@@ -8531,19 +9938,19 @@ var VectorEditorController = (_class5 = class {
8531
9938
  if (trimmed.startsWith("{")) {
8532
9939
  try {
8533
9940
  const data = JSON.parse(trimmed);
8534
- if (_optionalChain([data, 'optionalAccess', _260 => _260.type]) === VECTOR_CLIPBOARD_TYPE && Array.isArray(data.nodes)) nodes = data.nodes;
8535
- } catch (e6) {
9941
+ if (_optionalChain([data, 'optionalAccess', _288 => _288.type]) === VECTOR_CLIPBOARD_TYPE && Array.isArray(data.nodes)) nodes = data.nodes;
9942
+ } catch (e7) {
8536
9943
  nodes = null;
8537
9944
  }
8538
9945
  } else if (/<svg[\s>]/i.test(trimmed)) {
8539
9946
  try {
8540
9947
  nodes = parseVectorSvg(trimmed).nodes;
8541
- } catch (e7) {
9948
+ } catch (e8) {
8542
9949
  nodes = null;
8543
9950
  }
8544
9951
  }
8545
9952
  } else nodes = this.clipboard;
8546
- if (!_optionalChain([nodes, 'optionalAccess', _261 => _261.length])) return [];
9953
+ if (!_optionalChain([nodes, 'optionalAccess', _289 => _289.length])) return [];
8547
9954
  const offset = _nullishCoalesce(options.offset, () => ( (text && !text.trim().startsWith("{") ? { x: 0, y: 0 } : { x: 10, y: 10 })));
8548
9955
  const copies = nodes.map((n) => transformVectorNode(this.freshIds(n), translateMatrix(offset.x, offset.y)));
8549
9956
  this.change("Paste", () => {
@@ -8594,7 +10001,7 @@ var VectorEditorController = (_class5 = class {
8594
10001
  hitHandle(screen) {
8595
10002
  for (const ref of getVisibleHandles(this.view()).reverse()) {
8596
10003
  const a = getAnchorWorld(this.doc, ref);
8597
- const h = _optionalChain([a, 'optionalAccess', _262 => _262[ref.handle]]);
10004
+ const h = _optionalChain([a, 'optionalAccess', _290 => _290[ref.handle]]);
8598
10005
  if (!h) continue;
8599
10006
  const s = this.documentToScreen(h);
8600
10007
  if (Math.hypot(s.x - screen.x, s.y - screen.y) <= this.hitRadius) return ref;
@@ -8714,6 +10121,13 @@ var VectorEditorController = (_class5 = class {
8714
10121
  const spot = this.strokeSpotAt(p);
8715
10122
  return spot ? { nodeId: spot.nodeId, strokeSpot: spot } : null;
8716
10123
  }
10124
+ case "gradient": {
10125
+ const a = this.getGradientAnnotator();
10126
+ const part = a ? hitVectorGradientAnnotator(a, screen) : null;
10127
+ if (a && part) return { nodeId: a.nodeId, gradient: part };
10128
+ const path = this.hitPath(p);
10129
+ return path ? { nodeId: path.id } : null;
10130
+ }
8717
10131
  default:
8718
10132
  return null;
8719
10133
  }
@@ -8726,7 +10140,7 @@ var VectorEditorController = (_class5 = class {
8726
10140
  const shown = this.hitWidthHandleIn(screen, this.widthView());
8727
10141
  if (shown) return shown;
8728
10142
  const spot = this.strokeSpotAt(this.screenToDocument(screen));
8729
- return spot && spot.nodeId !== _optionalChain([this, 'access', _263 => _263.hover, 'optionalAccess', _264 => _264.nodeId]) ? this.hitWidthHandleIn(screen, { ...this.widthView(), hover: { nodeId: spot.nodeId } }) : null;
10143
+ return spot && spot.nodeId !== _optionalChain([this, 'access', _291 => _291.hover, 'optionalAccess', _292 => _292.nodeId]) ? this.hitWidthHandleIn(screen, { ...this.widthView(), hover: { nodeId: spot.nodeId } }) : null;
8730
10144
  }
8731
10145
  hitWidthHandleIn(screen, view) {
8732
10146
  let best = null;
@@ -8781,9 +10195,9 @@ var VectorEditorController = (_class5 = class {
8781
10195
  downWidth(screen, p, input) {
8782
10196
  const hit = this.hitWidthHandle(screen);
8783
10197
  const spot = hit ? null : this.strokeSpotAt(p);
8784
- const nodeId = _nullishCoalesce(_optionalChain([hit, 'optionalAccess', _265 => _265.ref, 'access', _266 => _266.nodeId]), () => ( _optionalChain([spot, 'optionalAccess', _267 => _267.nodeId])));
10198
+ const nodeId = _nullishCoalesce(_optionalChain([hit, 'optionalAccess', _293 => _293.ref, 'access', _294 => _294.nodeId]), () => ( _optionalChain([spot, 'optionalAccess', _295 => _295.nodeId])));
8785
10199
  const node = nodeId ? this.getNode(nodeId) : void 0;
8786
- const g = _optionalChain([node, 'optionalAccess', _268 => _268.type]) === "path" ? vectorStrokeWidthGeometry(node) : null;
10200
+ const g = _optionalChain([node, 'optionalAccess', _296 => _296.type]) === "path" ? vectorStrokeWidthGeometry(node) : null;
8787
10201
  if (this.widthEdit) this.widthEdit = null;
8788
10202
  if (!node || node.type !== "path" || !g) {
8789
10203
  if (!input.shift) this.widthPoints = [];
@@ -8803,7 +10217,7 @@ var VectorEditorController = (_class5 = class {
8803
10217
  if (hit) {
8804
10218
  profile = sanitizeWidthProfile(node.strokeWidthProfile);
8805
10219
  ref = { nodeId: node.id, index: hit.ref.index, subpath: hit.ref.subpath };
8806
- t = _nullishCoalesce(_optionalChain([profile, 'access', _269 => _269[ref.index], 'optionalAccess', _270 => _270.t]), () => ( 0));
10220
+ t = _nullishCoalesce(_optionalChain([profile, 'access', _297 => _297[ref.index], 'optionalAccess', _298 => _298.t]), () => ( 0));
8807
10221
  subpath = hit.ref.subpath;
8808
10222
  if (hit.ref.part === "center") mode = input.alt && profile.length < VECTOR_WIDTH_PROFILE_MAX_POINTS ? "copy" : "move";
8809
10223
  else if (input.alt) side = hit.ref.part;
@@ -8936,6 +10350,8 @@ var VectorEditorController = (_class5 = class {
8936
10350
  return this.downBuilder(p, input);
8937
10351
  case "blend":
8938
10352
  return this.downBlend(screen, p);
10353
+ case "gradient":
10354
+ return this.downGradient(screen, p, input);
8939
10355
  case "scissors": {
8940
10356
  const seg = this.nearestOutline(p);
8941
10357
  if (seg) this.cutPathAt(seg.nodeId, seg.point);
@@ -8958,7 +10374,7 @@ var VectorEditorController = (_class5 = class {
8958
10374
  let anchor = null;
8959
10375
  if (node.type === "path" && node.subpaths[0]) {
8960
10376
  const refs = node.subpaths[0].anchors.map((_, index) => ({ nodeId: node.id, subpath: 0, index }));
8961
- anchor = _nullishCoalesce(_optionalChain([this, 'access', _271 => _271.hitAnchor, 'call', _272 => _272(screen, refs), 'optionalAccess', _273 => _273.index]), () => ( null));
10377
+ anchor = _nullishCoalesce(_optionalChain([this, 'access', _299 => _299.hitAnchor, 'call', _300 => _300(screen, refs), 'optionalAccess', _301 => _301.index]), () => ( null));
8962
10378
  }
8963
10379
  return { nodeId: node.id, anchor };
8964
10380
  }
@@ -9111,7 +10527,7 @@ var VectorEditorController = (_class5 = class {
9111
10527
  }
9112
10528
  downPen(screen, p, input) {
9113
10529
  const before = this.snapshot();
9114
- const sp = this.pen ? _optionalChain([this, 'access', _274 => _274.getNode, 'call', _275 => _275(this.pen.nodeId), 'optionalAccess', _276 => _276.subpaths, 'access', _277 => _277[this.pen.subpath]]) : void 0;
10530
+ const sp = this.pen ? _optionalChain([this, 'access', _302 => _302.getNode, 'call', _303 => _303(this.pen.nodeId), 'optionalAccess', _304 => _304.subpaths, 'access', _305 => _305[this.pen.subpath]]) : void 0;
9115
10531
  if (this.pen && sp) {
9116
10532
  const pen = this.pen;
9117
10533
  const first = { nodeId: pen.nodeId, subpath: pen.subpath, index: 0 };
@@ -9227,6 +10643,8 @@ var VectorEditorController = (_class5 = class {
9227
10643
  this.viewport = { ...drag.viewport, x: drag.viewport.x + screen.x - drag.start.x, y: drag.viewport.y + screen.y - drag.start.y };
9228
10644
  this.notify();
9229
10645
  return;
10646
+ case "gradient":
10647
+ return this.moveGradient(drag, screen, p, input);
9230
10648
  case "move": {
9231
10649
  let dx = p.x - drag.start.x;
9232
10650
  let dy = p.y - drag.start.y;
@@ -9373,7 +10791,7 @@ var VectorEditorController = (_class5 = class {
9373
10791
  }
9374
10792
  case "shape": {
9375
10793
  const node = this.getNode(drag.nodeId);
9376
- if (_optionalChain([node, 'optionalAccess', _278 => _278.type]) !== "path") return;
10794
+ if (_optionalChain([node, 'optionalAccess', _306 => _306.type]) !== "path") return;
9377
10795
  const leave = { at: drag.start, constrain: input.shift ? drag.tool === "line" ? "angle" : "square" : null, curves: false };
9378
10796
  let end = this.snapDrawn(p, leave, input, () => drag.targets ??= this.drawingTargets(null));
9379
10797
  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);
@@ -9447,14 +10865,14 @@ var VectorEditorController = (_class5 = class {
9447
10865
  for (const q of drag.lazy.push([p.x, p.y])) {
9448
10866
  if (Math.hypot(q[0] - last[0], q[1] - last[1]) * this.viewport.zoom < 1.5) continue;
9449
10867
  drag.points.push(q);
9450
- _optionalChain([drag, 'access', _279 => _279.pressures, 'optionalAccess', _280 => _280.push, 'call', _281 => _281(drag.pressure)]);
10868
+ _optionalChain([drag, 'access', _307 => _307.pressures, 'optionalAccess', _308 => _308.push, 'call', _309 => _309(drag.pressure)]);
9451
10869
  moved = true;
9452
10870
  }
9453
10871
  const string = drag.lazy.string();
9454
10872
  const node = this.getNode(drag.nodeId);
9455
- if (_optionalChain([node, 'optionalAccess', _282 => _282.type]) !== "path") return;
10873
+ if (_optionalChain([node, 'optionalAccess', _310 => _310.type]) !== "path") return;
9456
10874
  if (!moved) {
9457
- if (string && _optionalChain([this, 'access', _283 => _283.interaction, 'optionalAccess', _284 => _284.type]) === "draw") {
10875
+ if (string && _optionalChain([this, 'access', _311 => _311.interaction, 'optionalAccess', _312 => _312.type]) === "draw") {
9458
10876
  this.interaction = { ...this.interaction, string: [{ x: string[0][0], y: string[0][1] }, { x: string[1][0], y: string[1][1] }] };
9459
10877
  this.notify();
9460
10878
  }
@@ -9490,8 +10908,8 @@ var VectorEditorController = (_class5 = class {
9490
10908
  }
9491
10909
  this.setGuides(res);
9492
10910
  return {
9493
- dx: _optionalChain([res, 'optionalAccess', _285 => _285.x]) ? dx + res.dx : axes.x ? this.gridValue(origin.x + dx) - origin.x : dx,
9494
- dy: _optionalChain([res, 'optionalAccess', _286 => _286.y]) ? dy + res.dy : axes.y ? this.gridValue(origin.y + dy) - origin.y : dy
10911
+ dx: _optionalChain([res, 'optionalAccess', _313 => _313.x]) ? dx + res.dx : axes.x ? this.gridValue(origin.x + dx) - origin.x : dx,
10912
+ dy: _optionalChain([res, 'optionalAccess', _314 => _314.y]) ? dy + res.dy : axes.y ? this.gridValue(origin.y + dy) - origin.y : dy
9495
10913
  };
9496
10914
  }
9497
10915
  /** The anchor a point drag snaps by, where it started: the grabbed one, else the moving one nearest the press. */
@@ -9570,7 +10988,7 @@ var VectorEditorController = (_class5 = class {
9570
10988
  if (drag.moved) return this.commit("Corner Radius", drag.before);
9571
10989
  if (drag.alt) {
9572
10990
  const kind = nextVectorCornerKind(drag.widget.kind);
9573
- this.doc = this.updateAnchors(drag.base, drag.refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _287 => _287.corner, 'optionalAccess', _288 => _288.radius]), () => ( 0)), kind));
10991
+ this.doc = this.updateAnchors(drag.base, drag.refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _315 => _315.corner, 'optionalAccess', _316 => _316.radius]), () => ( 0)), kind));
9574
10992
  return this.commit("Corner Kind", drag.before);
9575
10993
  }
9576
10994
  const ref = drag.widget.ref;
@@ -9585,7 +11003,7 @@ var VectorEditorController = (_class5 = class {
9585
11003
  const several = drag.group.length > 0;
9586
11004
  if (drag.mode === "copy") this.widthPoints = [drag.ref];
9587
11005
  else if (drag.mode === "move" && several) {
9588
- this.widthPoints = [drag.source, ...drag.group].map((i) => ({ ...drag.ref, index: _nullishCoalesce(_optionalChain([drag, 'access', _289 => _289.renumber, 'optionalAccess', _290 => _290.get, 'call', _291 => _291(i)]), () => ( i)) }));
11006
+ this.widthPoints = [drag.source, ...drag.group].map((i) => ({ ...drag.ref, index: _nullishCoalesce(_optionalChain([drag, 'access', _317 => _317.renumber, 'optionalAccess', _318 => _318.get, 'call', _319 => _319(i)]), () => ( i)) }));
9589
11007
  } else if (!several) this.widthPoints = [drag.ref];
9590
11008
  const label = drag.mode === "copy" ? "Copy Width Point" : drag.mode === "move" ? several ? "Move Width Points" : "Move Width Point" : several ? "Width Points" : "Width Point";
9591
11009
  return this.commit(label, drag.before);
@@ -9602,6 +11020,8 @@ var VectorEditorController = (_class5 = class {
9602
11020
  }
9603
11021
  case "builder":
9604
11022
  return this.upBuilder(drag);
11023
+ case "gradient":
11024
+ return this.upGradient(drag);
9605
11025
  case "stroke": {
9606
11026
  this.notify();
9607
11027
  if (drag.tool === "knife") this.knife(drag.points);
@@ -9627,14 +11047,14 @@ var VectorEditorController = (_class5 = class {
9627
11047
  }
9628
11048
  case "pencil": {
9629
11049
  const node = this.getNode(drag.nodeId);
9630
- if (_optionalChain([node, 'optionalAccess', _292 => _292.type]) !== "path" || drag.points.length < 2) {
11050
+ if (_optionalChain([node, 'optionalAccess', _320 => _320.type]) !== "path" || drag.points.length < 2) {
9631
11051
  this.doc = drag.before.doc;
9632
11052
  this.notify(true);
9633
11053
  return;
9634
11054
  }
9635
11055
  for (const q of drag.lazy.finish()) {
9636
11056
  drag.points.push(q);
9637
- _optionalChain([drag, 'access', _293 => _293.pressures, 'optionalAccess', _294 => _294.push, 'call', _295 => _295(drag.pressure)]);
11057
+ _optionalChain([drag, 'access', _321 => _321.pressures, 'optionalAccess', _322 => _322.push, 'call', _323 => _323(drag.pressure)]);
9638
11058
  }
9639
11059
  const shape = this.editing.recognizeShapes ? recognizeInkShape(drag.points, { minSize: 6 / this.viewport.zoom }) : null;
9640
11060
  if (shape) {
@@ -9709,7 +11129,7 @@ var VectorEditorController = (_class5 = class {
9709
11129
  this.convertAnchors(a.in || a.out ? "corner" : "smooth", [anchor]);
9710
11130
  return;
9711
11131
  }
9712
- const id = _nullishCoalesce(_optionalChain([this, 'access', _296 => _296.hover, 'optionalAccess', _297 => _297.segment, 'optionalAccess', _298 => _298.nodeId]), () => ( this.editingId));
11132
+ const id = _nullishCoalesce(_optionalChain([this, 'access', _324 => _324.hover, 'optionalAccess', _325 => _325.segment, 'optionalAccess', _326 => _326.nodeId]), () => ( this.editingId));
9713
11133
  if (id) {
9714
11134
  const node = this.getNode(id);
9715
11135
  const seg = node ? nearestSegment(node, this.toLocal(id, p)) : null;
@@ -9806,7 +11226,7 @@ var VectorEditorController = (_class5 = class {
9806
11226
  this.finishPath();
9807
11227
  return true;
9808
11228
  }
9809
- if (this.tool === "select" && this.selection.length === 1 && _optionalChain([this, 'access', _299 => _299.getNode, 'call', _300 => _300(this.selection[0]), 'optionalAccess', _301 => _301.type]) === "path") {
11229
+ if (this.tool === "select" && this.selection.length === 1 && _optionalChain([this, 'access', _327 => _327.getNode, 'call', _328 => _328(this.selection[0]), 'optionalAccess', _329 => _329.type]) === "path") {
9810
11230
  this.tool = "direct";
9811
11231
  this.editingId = this.selection[0];
9812
11232
  this.notify();
@@ -9816,6 +11236,7 @@ var VectorEditorController = (_class5 = class {
9816
11236
  case "Delete":
9817
11237
  case "Backspace":
9818
11238
  if (this.tool === "width" && this.widthPoints.length) return this.removeWidthPoints().ok;
11239
+ if (this.tool === "gradient" && this.gradientEdit.stop !== null) return this.removeGradientStop(this.gradientEdit.stop).ok;
9819
11240
  if (this.pen) {
9820
11241
  const node = this.getNode(this.pen.nodeId);
9821
11242
  const sp = node.subpaths[this.pen.subpath];
@@ -9924,25 +11345,24 @@ function pencilPressureProfile(points, pressures) {
9924
11345
  }
9925
11346
 
9926
11347
  // src/vector/render.ts
9927
- var fmt = (n) => Math.round(n * 100) / 100;
9928
- var fmtMatrix = (m) => `matrix(${m.map((n) => Math.round(n * 1e6) / 1e6).join(" ")})`;
11348
+ var fmt4 = (n) => Math.round(n * 100) / 100;
11349
+ var fmtMatrix2 = (m) => `matrix(${m.map((n) => Math.round(n * 1e6) / 1e6).join(" ")})`;
9929
11350
  var VECTOR_BUILDER_HATCH_GAP = 6;
9930
11351
  function renderNode(stored, defs, idPrefix) {
9931
11352
  if (stored.visible === false) return null;
9932
11353
  const node = expandVectorAppearance(stored);
9933
11354
  if (node.type === "group") {
9934
11355
  const children = node.children.map((c) => renderNode(c, defs, idPrefix)).filter((c) => !!c);
9935
- return { kind: "group", id: node.id, ...node.transform ? { transform: fmtMatrix(node.transform) } : {}, opacity: _nullishCoalesce(node.opacity, () => ( 1)), children };
11356
+ return { kind: "group", id: node.id, ...node.transform ? { transform: fmtMatrix2(node.transform) } : {}, opacity: _nullishCoalesce(node.opacity, () => ( 1)), children };
9936
11357
  }
9937
- const fill = vectorPaintToSvg(node.fill, `${idPrefix}fill-${node.id}`);
9938
- const stroke = vectorPaintToSvg(node.stroke, `${idPrefix}stroke-${node.id}`);
9939
- if (fill.gradient) defs.push(fill.gradient);
9940
- if (stroke.gradient) defs.push(stroke.gradient);
11358
+ const fill = vectorPaintToSvg(node.fill, `${idPrefix}fill-${node.id}`, { node });
11359
+ const stroke = vectorPaintToSvg(node.stroke, `${idPrefix}stroke-${node.id}`, { node, role: "stroke" });
11360
+ defs.push(...fill.defs, ...stroke.defs);
9941
11361
  const strokeParts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${idPrefix}clip-${node.id}`) : void 0;
9942
11362
  return {
9943
11363
  kind: "path",
9944
11364
  id: node.id,
9945
- ...node.transform ? { transform: fmtMatrix(node.transform) } : {},
11365
+ ...node.transform ? { transform: fmtMatrix2(node.transform) } : {},
9946
11366
  ...strokeParts ? { strokeParts } : {},
9947
11367
  d: vectorPathData(node),
9948
11368
  fill: fill.value,
@@ -9950,7 +11370,7 @@ function renderNode(stored, defs, idPrefix) {
9950
11370
  strokeWidth: _nullishCoalesce(node.strokeWidth, () => ( 1)),
9951
11371
  ...node.strokeLinecap ? { strokeLinecap: node.strokeLinecap } : {},
9952
11372
  ...node.strokeLinejoin ? { strokeLinejoin: node.strokeLinejoin } : {},
9953
- ..._optionalChain([node, 'access', _302 => _302.strokeDasharray, 'optionalAccess', _303 => _303.length]) ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
11373
+ ..._optionalChain([node, 'access', _330 => _330.strokeDasharray, 'optionalAccess', _331 => _331.length]) ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
9954
11374
  fillRule: _nullishCoalesce(node.fillRule, () => ( "nonzero")),
9955
11375
  opacity: _nullishCoalesce(node.opacity, () => ( 1))
9956
11376
  };
@@ -9961,7 +11381,7 @@ function getVectorSceneRenderData(state, options = {}) {
9961
11381
  const nodes = state.document.nodes.map((n) => renderNode(n, defs, prefix)).filter((n) => !!n);
9962
11382
  const v2 = state.viewport;
9963
11383
  return {
9964
- transform: `translate(${fmt(v2.x)} ${fmt(v2.y)}) scale(${v2.zoom})`,
11384
+ transform: `translate(${fmt4(v2.x)} ${fmt4(v2.y)}) scale(${v2.zoom})`,
9965
11385
  width: state.document.width,
9966
11386
  height: state.document.height,
9967
11387
  ...state.document.background ? { background: state.document.background } : {},
@@ -10004,9 +11424,9 @@ function getVectorOverlayRenderData(state) {
10004
11424
  };
10005
11425
  for (const id of state.selection) outline(id, true);
10006
11426
  if (state.editingId) outline(state.editingId, true);
10007
- if (_optionalChain([state, 'access', _304 => _304.hover, 'optionalAccess', _305 => _305.nodeId]) && !state.interaction) outline(state.hover.nodeId, false);
11427
+ if (_optionalChain([state, 'access', _332 => _332.hover, 'optionalAccess', _333 => _333.nodeId]) && !state.interaction) outline(state.hover.nodeId, false);
10008
11428
  let selectionBox = null;
10009
- if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access', _306 => _306.interaction, 'optionalAccess', _307 => _307.type]) !== "marquee") {
11429
+ if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access', _334 => _334.interaction, 'optionalAccess', _335 => _335.type]) !== "marquee") {
10010
11430
  const g = getVectorSelectionBoxGeometry(state.selectionFrame, state.viewport);
10011
11431
  selectionBox = { ...g.box, angle: g.angle, transform: g.transform, corners: g.corners, handles: g.handles };
10012
11432
  }
@@ -10016,9 +11436,9 @@ function getVectorOverlayRenderData(state) {
10016
11436
  const a = getAnchorWorld(doc, ref);
10017
11437
  const s = toScreen(state, a);
10018
11438
  const closes = !!state.pen && state.pen.nodeId === ref.nodeId && state.pen.subpath === ref.subpath && ref.index === 0;
10019
- return { ref, x: s.x, y: s.y, type: _nullishCoalesce(a.type, () => ( inferAnchorType(a))), selected: selected(ref), hovered: sameAnchor(_optionalChain([state, 'access', _308 => _308.hover, 'optionalAccess', _309 => _309.anchor]), ref), closes };
11439
+ return { ref, x: s.x, y: s.y, type: _nullishCoalesce(a.type, () => ( inferAnchorType(a))), selected: selected(ref), hovered: sameAnchor(_optionalChain([state, 'access', _336 => _336.hover, 'optionalAccess', _337 => _337.anchor]), ref), closes };
10020
11440
  });
10021
- if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access', _310 => _310.hover, 'optionalAccess', _311 => _311.anchor])) {
11441
+ if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access', _338 => _338.hover, 'optionalAccess', _339 => _339.anchor])) {
10022
11442
  const a = getAnchorWorld(doc, state.hover.anchor);
10023
11443
  if (a) {
10024
11444
  const s = toScreen(state, a);
@@ -10027,15 +11447,15 @@ function getVectorOverlayRenderData(state) {
10027
11447
  }
10028
11448
  const handles = getVisibleHandles(state).flatMap((ref) => {
10029
11449
  const a = getAnchorWorld(doc, ref);
10030
- const h = _optionalChain([a, 'optionalAccess', _312 => _312[ref.handle]]);
11450
+ const h = _optionalChain([a, 'optionalAccess', _340 => _340[ref.handle]]);
10031
11451
  if (!a || !h) return [];
10032
11452
  const s = toScreen(state, h);
10033
11453
  const sa = toScreen(state, a);
10034
- const hovered = !!_optionalChain([state, 'access', _313 => _313.hover, 'optionalAccess', _314 => _314.handle]) && sameAnchor(state.hover.handle, ref) && state.hover.handle.handle === ref.handle;
11454
+ const hovered = !!_optionalChain([state, 'access', _341 => _341.hover, 'optionalAccess', _342 => _342.handle]) && sameAnchor(state.hover.handle, ref) && state.hover.handle.handle === ref.handle;
10035
11455
  return [{ ref, x: s.x, y: s.y, anchorX: sa.x, anchorY: sa.y, hovered }];
10036
11456
  });
10037
11457
  let marquee = null;
10038
- if (_optionalChain([state, 'access', _315 => _315.interaction, 'optionalAccess', _316 => _316.type]) === "marquee") {
11458
+ if (_optionalChain([state, 'access', _343 => _343.interaction, 'optionalAccess', _344 => _344.type]) === "marquee") {
10039
11459
  const b = state.interaction.bounds;
10040
11460
  const tl = toScreen(state, b);
10041
11461
  marquee = { x: tl.x, y: tl.y, width: b.width * z, height: b.height * z };
@@ -10043,70 +11463,70 @@ function getVectorOverlayRenderData(state) {
10043
11463
  let penPreview = null;
10044
11464
  if (state.tool === "pen" && state.pen && state.pointer && !state.interaction) {
10045
11465
  const node = findVectorNode(doc.nodes, state.pen.nodeId);
10046
- const sp = _optionalChain([node, 'optionalAccess', _317 => _317.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
11466
+ const sp = _optionalChain([node, 'optionalAccess', _345 => _345.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
10047
11467
  const last = sp ? getAnchorWorld(doc, { nodeId: state.pen.nodeId, subpath: state.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
10048
11468
  if (last) {
10049
11469
  const a = toScreen(state, last);
10050
11470
  const p = toScreen(state, state.pointer);
10051
11471
  const c = last.out ? toScreen(state, last.out) : null;
10052
- penPreview = c ? `M${fmt(a.x)} ${fmt(a.y)}Q${fmt(c.x)} ${fmt(c.y)} ${fmt(p.x)} ${fmt(p.y)}` : `M${fmt(a.x)} ${fmt(a.y)}L${fmt(p.x)} ${fmt(p.y)}`;
11472
+ penPreview = c ? `M${fmt4(a.x)} ${fmt4(a.y)}Q${fmt4(c.x)} ${fmt4(c.y)} ${fmt4(p.x)} ${fmt4(p.y)}` : `M${fmt4(a.x)} ${fmt4(a.y)}L${fmt4(p.x)} ${fmt4(p.y)}`;
10053
11473
  }
10054
11474
  }
10055
11475
  const origin = toScreen(state, { x: 0, y: 0 });
10056
11476
  const artboard = { x: origin.x, y: origin.y, width: doc.width * z, height: doc.height * z };
10057
11477
  let status = null;
10058
11478
  const it = state.interaction;
10059
- if (_optionalChain([it, 'optionalAccess', _318 => _318.type]) === "rotate") status = `${Math.round(it.angle)}\xB0`;
10060
- else if (_optionalChain([it, 'optionalAccess', _319 => _319.type]) === "move" || _optionalChain([it, 'optionalAccess', _320 => _320.type]) === "anchors") status = `\u0394 ${fmt(it.dx)}, ${fmt(it.dy)}`;
10061
- else if (_optionalChain([it, 'optionalAccess', _321 => _321.type]) === "scale" || _optionalChain([it, 'optionalAccess', _322 => _322.type]) === "draw") {
11479
+ if (_optionalChain([it, 'optionalAccess', _346 => _346.type]) === "rotate") status = `${Math.round(it.angle)}\xB0`;
11480
+ else if (_optionalChain([it, 'optionalAccess', _347 => _347.type]) === "move" || _optionalChain([it, 'optionalAccess', _348 => _348.type]) === "anchors") status = `\u0394 ${fmt4(it.dx)}, ${fmt4(it.dy)}`;
11481
+ else if (_optionalChain([it, 'optionalAccess', _349 => _349.type]) === "scale" || _optionalChain([it, 'optionalAccess', _350 => _350.type]) === "draw") {
10062
11482
  const b = it.type === "draw" ? findVectorNode(doc.nodes, it.nodeId) ? vectorNodeBounds(findVectorNode(doc.nodes, it.nodeId)) : null : state.selectionFrame;
10063
- if (b) status = `W ${fmt(b.width)} \xD7 H ${fmt(b.height)}`;
11483
+ if (b) status = `W ${fmt4(b.width)} \xD7 H ${fmt4(b.height)}`;
10064
11484
  }
10065
11485
  const guides = { lines: [], labels: [] };
10066
11486
  if (state.guides) {
10067
11487
  for (const l of state.guides.lines) {
10068
11488
  const a = toScreen(state, { x: l.x1, y: l.y1 });
10069
11489
  const b = toScreen(state, { x: l.x2, y: l.y2 });
10070
- guides.lines.push({ kind: l.kind, x1: fmt(a.x), y1: fmt(a.y), x2: fmt(b.x), y2: fmt(b.y) });
11490
+ guides.lines.push({ kind: l.kind, x1: fmt4(a.x), y1: fmt4(a.y), x2: fmt4(b.x), y2: fmt4(b.y) });
10071
11491
  }
10072
11492
  for (const l of state.guides.labels) {
10073
11493
  const p = toScreen(state, l);
10074
- guides.labels.push({ kind: l.kind, x: fmt(p.x), y: fmt(p.y), text: l.text });
11494
+ guides.labels.push({ kind: l.kind, x: fmt4(p.x), y: fmt4(p.y), text: l.text });
10075
11495
  }
10076
11496
  }
10077
11497
  let toolStroke = null;
10078
- if (_optionalChain([it, 'optionalAccess', _323 => _323.type]) === "stroke") {
11498
+ if (_optionalChain([it, 'optionalAccess', _351 => _351.type]) === "stroke") {
10079
11499
  const pts = it.points.map((q) => toScreen(state, q));
10080
- 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)}` : "");
10081
- toolStroke = { tool: it.tool, d, width: fmt(it.width * z) };
11500
+ const d = pts.map((q, i) => `${i ? "L" : "M"}${fmt4(q.x)} ${fmt4(q.y)}`).join("") + (pts.length === 1 ? `L${fmt4(pts[0].x)} ${fmt4(pts[0].y)}` : "");
11501
+ toolStroke = { tool: it.tool, d, width: fmt4(it.width * z) };
10082
11502
  }
10083
11503
  let pencilString = null;
10084
- if (_optionalChain([it, 'optionalAccess', _324 => _324.type]) === "draw" && it.string) {
11504
+ if (_optionalChain([it, 'optionalAccess', _352 => _352.type]) === "draw" && it.string) {
10085
11505
  const a = toScreen(state, it.string[0]);
10086
11506
  const b = toScreen(state, it.string[1]);
10087
- pencilString = { x1: fmt(a.x), y1: fmt(a.y), x2: fmt(b.x), y2: fmt(b.y) };
11507
+ pencilString = { x1: fmt4(a.x), y1: fmt4(a.y), x2: fmt4(b.x), y2: fmt4(b.y) };
10088
11508
  }
10089
- const dragged = _optionalChain([it, 'optionalAccess', _325 => _325.type]) === "corner" ? it.ref : null;
11509
+ const dragged = _optionalChain([it, 'optionalAccess', _353 => _353.type]) === "corner" ? it.ref : null;
10090
11510
  const cornerWidgets = getVectorCornerWidgets(state).map((w) => {
10091
11511
  const s = toScreen(state, w);
10092
- return { ref: w.ref, x: fmt(s.x), y: fmt(s.y), radius: w.radius, kind: w.kind, hovered: sameAnchor(_optionalChain([state, 'access', _326 => _326.hover, 'optionalAccess', _327 => _327.corner]), w.ref), active: !!dragged };
11512
+ return { ref: w.ref, x: fmt4(s.x), y: fmt4(s.y), radius: w.radius, kind: w.kind, hovered: sameAnchor(_optionalChain([state, 'access', _354 => _354.hover, 'optionalAccess', _355 => _355.corner]), w.ref), active: !!dragged };
10093
11513
  });
10094
- if (_optionalChain([it, 'optionalAccess', _328 => _328.type]) === "corner") status = `R ${fmt(it.radius)}`;
10095
- const cutPoint = state.tool === "scissors" && _optionalChain([state, 'access', _329 => _329.hover, 'optionalAccess', _330 => _330.segment]) && !it ? (({ x, y }) => ({ x: fmt(x), y: fmt(y) }))(toScreen(state, state.hover.segment.point)) : null;
11514
+ if (_optionalChain([it, 'optionalAccess', _356 => _356.type]) === "corner") status = `R ${fmt4(it.radius)}`;
11515
+ const cutPoint = state.tool === "scissors" && _optionalChain([state, 'access', _357 => _357.hover, 'optionalAccess', _358 => _358.segment]) && !it ? (({ x, y }) => ({ x: fmt4(x), y: fmt4(y) }))(toScreen(state, state.hover.segment.point)) : null;
10096
11516
  const sc = (p) => {
10097
11517
  const q = toScreen(state, p);
10098
- return { x: fmt(q.x), y: fmt(q.y) };
11518
+ return { x: fmt4(q.x), y: fmt4(q.y) };
10099
11519
  };
10100
11520
  const sameWidth = (a, b) => !!a && a.nodeId === b.nodeId && a.index === b.index;
10101
11521
  const widthPoints = getVectorWidthHandles(state).map((h) => {
10102
11522
  const c = sc(h);
10103
- const hw = _optionalChain([state, 'access', _331 => _331.hover, 'optionalAccess', _332 => _332.widthPoint]);
11523
+ const hw = _optionalChain([state, 'access', _359 => _359.hover, 'optionalAccess', _360 => _360.widthPoint]);
10104
11524
  const hovered = hw && sameWidth(hw, h.ref) && hw.subpath === h.ref.subpath ? hw.part : null;
10105
- return { ref: h.ref, x: c.x, y: c.y, left: sc(h.left), right: sc(h.right), selected: h.selected, hovered, active: _optionalChain([it, 'optionalAccess', _333 => _333.type]) === "width" && sameWidth(it.ref, h.ref) };
11525
+ return { ref: h.ref, x: c.x, y: c.y, left: sc(h.left), right: sc(h.right), selected: h.selected, hovered, active: _optionalChain([it, 'optionalAccess', _361 => _361.type]) === "width" && sameWidth(it.ref, h.ref) };
10106
11526
  });
10107
- const spot = state.tool === "width" && !it ? _optionalChain([state, 'access', _334 => _334.hover, 'optionalAccess', _335 => _335.strokeSpot]) : null;
11527
+ const spot = state.tool === "width" && !it ? _optionalChain([state, 'access', _362 => _362.hover, 'optionalAccess', _363 => _363.strokeSpot]) : null;
10108
11528
  const widthSpot = spot ? { ...sc(spot.point), left: sc(spot.left), right: sc(spot.right) } : null;
10109
- if (_optionalChain([it, 'optionalAccess', _336 => _336.type]) === "width") status = it.mode === "move" ? "Move width point" : it.mode === "copy" ? "Copy width point" : `W ${fmt(it.width)}`;
11529
+ if (_optionalChain([it, 'optionalAccess', _364 => _364.type]) === "width") status = it.mode === "move" ? "Move width point" : it.mode === "copy" ? "Copy width point" : `W ${fmt4(it.width)}`;
10110
11530
  let builder = null;
10111
11531
  if (state.builder) {
10112
11532
  const v2 = state.viewport;
@@ -10122,20 +11542,56 @@ function getVectorOverlayRenderData(state) {
10122
11542
  lines: state.builder.lines.map((l) => svg(_chunkVNYRI265cjs.transformBezierPath.call(void 0, l, view))),
10123
11543
  trail
10124
11544
  };
10125
- if (state.builder.mode !== "fill" && _optionalChain([it, 'optionalAccess', _337 => _337.type]) === "builder") {
11545
+ if (state.builder.mode !== "fill" && _optionalChain([it, 'optionalAccess', _365 => _365.type]) === "builder") {
10126
11546
  const n = state.builder.faces.length;
10127
11547
  status = state.builder.mode === "erase" ? `Delete ${n} ${n === 1 ? "face" : "faces"}` : `Merge ${n} ${n === 1 ? "face" : "faces"}`;
10128
11548
  }
10129
11549
  }
10130
11550
  const blend = blendOverlay(state);
10131
11551
  if (state.tool === "blend" && !it) status = state.blendStart ? "Click another shape to blend into" : "Click a shape to blend from";
10132
- return { outlines, selectionBox, anchors, handles, marquee, penPreview, artboard, guides, status, toolStroke, pencilString, cutPoint, cornerWidgets, widthPoints, widthSpot, builder, blend };
11552
+ let gradient = null;
11553
+ if (state.tool === "gradient") {
11554
+ gradient = gradientOverlay(state);
11555
+ if (!gradient && !it) status = "Drag across a shape to set a gradient";
11556
+ else if (_optionalChain([it, 'optionalAccess', _366 => _366.type]) === "gradient" && it.removing) status = "Release to delete the stop";
11557
+ }
11558
+ return { outlines, selectionBox, anchors, handles, marquee, penPreview, artboard, guides, status, toolStroke, pencilString, cutPoint, cornerWidgets, widthPoints, widthSpot, builder, blend, gradient };
11559
+ }
11560
+ function gradientOverlay(state) {
11561
+ const a = getVectorGradientAnnotator(state.document, state.selection, state.viewport, state.gradientEdit.target);
11562
+ if (!a) return null;
11563
+ const hover = state.interaction ? null : _optionalChain([state, 'access', _367 => _367.hover, 'optionalAccess', _368 => _368.gradient]);
11564
+ const handleHovered = (h) => _optionalChain([hover, 'optionalAccess', _369 => _369.kind]) === "handle" && hover.handle === h;
11565
+ const pt2 = (p) => ({ x: fmt4(p.x), y: fmt4(p.y) });
11566
+ const centred = isCentredVectorGradient(a.gradient.type);
11567
+ const removing = _optionalChain([state, 'access', _370 => _370.interaction, 'optionalAccess', _371 => _371.type]) === "gradient" && !!state.interaction.removing;
11568
+ return {
11569
+ nodeId: a.nodeId,
11570
+ type: a.gradient.type,
11571
+ start: { ...pt2(a.start), hovered: handleHovered("start") },
11572
+ end: { ...pt2(a.end), hovered: handleHovered(centred ? "radius" : "end"), handle: centred ? "radius" : "end" },
11573
+ aspect: a.aspect ? { ...pt2(a.aspect), hovered: handleHovered("aspect") } : null,
11574
+ focal: a.focal ? { ...pt2(a.focal), hovered: handleHovered("focal") } : null,
11575
+ stops: a.stops.map((s) => ({
11576
+ index: s.index,
11577
+ ...pt2(s),
11578
+ barX: fmt4(s.x - a.normal.x * VECTOR_GRADIENT_STOP_GAP),
11579
+ barY: fmt4(s.y - a.normal.y * VECTOR_GRADIENT_STOP_GAP),
11580
+ color: s.color,
11581
+ opacity: _nullishCoalesce(s.opacity, () => ( 1)),
11582
+ selected: state.gradientEdit.stop === s.index,
11583
+ hovered: _optionalChain([hover, 'optionalAccess', _372 => _372.kind]) === "stop" && hover.index === s.index
11584
+ })),
11585
+ midpoints: a.midpoints.map((m) => ({ index: m.index, ...pt2(m), hovered: _optionalChain([hover, 'optionalAccess', _373 => _373.kind]) === "midpoint" && hover.index === m.index })),
11586
+ extent: a.extent,
11587
+ removing
11588
+ };
10133
11589
  }
10134
11590
  function selectedBlends(state) {
10135
11591
  const out = /* @__PURE__ */ new Map();
10136
11592
  for (const id of state.selection) {
10137
11593
  const node = findVectorNode(state.document.nodes, id);
10138
- const group = isVectorBlendGroup(node) ? node : _optionalChain([locateVectorNode, 'call', _338 => _338(state.document.nodes, id), 'optionalAccess', _339 => _339.parent]);
11594
+ const group = isVectorBlendGroup(node) ? node : _optionalChain([locateVectorNode, 'call', _374 => _374(state.document.nodes, id), 'optionalAccess', _375 => _375.parent]);
10139
11595
  if (isVectorBlendGroup(group)) out.set(group.id, group);
10140
11596
  }
10141
11597
  return [...out.values()];
@@ -10146,7 +11602,7 @@ function blendOverlay(state) {
10146
11602
  const doc = state.document;
10147
11603
  const sc = (p, m) => {
10148
11604
  const q = { x: m[0] * p.x + m[2] * p.y + m[4], y: m[1] * p.x + m[3] * p.y + m[5] };
10149
- return { x: fmt(q.x), y: fmt(q.y) };
11605
+ return { x: fmt4(q.x), y: fmt4(q.y) };
10150
11606
  };
10151
11607
  const spines = selectedBlends(state).flatMap((g) => {
10152
11608
  const s = vectorBlendSpine(g);
@@ -10167,11 +11623,11 @@ function blendOverlay(state) {
10167
11623
  const a = state.blendStart.anchor === null ? void 0 : getAnchorWorld(doc, { nodeId: from.id, subpath: 0, index: state.blendStart.anchor });
10168
11624
  start = { d: screenOutline(state, from, /* @__PURE__ */ new Set()), anchor: a ? sc(a, view) : null };
10169
11625
  }
10170
- const hovered = _optionalChain([state, 'access', _340 => _340.hover, 'optionalAccess', _341 => _341.nodeId]) ? findVectorNode(doc.nodes, state.hover.nodeId) : void 0;
10171
- if (_optionalChain([hovered, 'optionalAccess', _342 => _342.type]) === "path" && hovered.subpaths[0]) {
11626
+ const hovered = _optionalChain([state, 'access', _376 => _376.hover, 'optionalAccess', _377 => _377.nodeId]) ? findVectorNode(doc.nodes, state.hover.nodeId) : void 0;
11627
+ if (_optionalChain([hovered, 'optionalAccess', _378 => _378.type]) === "path" && hovered.subpaths[0]) {
10172
11628
  hovered.subpaths[0].anchors.forEach((_, index) => {
10173
11629
  const a = getAnchorWorld(doc, { nodeId: hovered.id, subpath: 0, index });
10174
- if (a) anchors.push({ ...sc(a, view), hovered: _optionalChain([state, 'access', _343 => _343.hover, 'optionalAccess', _344 => _344.anchor, 'optionalAccess', _345 => _345.index]) === index });
11630
+ if (a) anchors.push({ ...sc(a, view), hovered: _optionalChain([state, 'access', _379 => _379.hover, 'optionalAccess', _380 => _380.anchor, 'optionalAccess', _381 => _381.index]) === index });
10175
11631
  });
10176
11632
  }
10177
11633
  }
@@ -10182,17 +11638,17 @@ function getVectorBlendControls(state) {
10182
11638
  const doc = state.document;
10183
11639
  const roots = state.selection.filter((id) => {
10184
11640
  const node = findVectorNode(doc.nodes, id);
10185
- return !!node && !node.blendStep && !node.locked && !state.selection.includes(_nullishCoalesce(_optionalChain([locateVectorNode, 'call', _346 => _346(doc.nodes, id), 'optionalAccess', _347 => _347.parent, 'optionalAccess', _348 => _348.id]), () => ( "")));
11641
+ return !!node && !node.blendStep && !node.locked && !state.selection.includes(_nullishCoalesce(_optionalChain([locateVectorNode, 'call', _382 => _382(doc.nodes, id), 'optionalAccess', _383 => _383.parent, 'optionalAccess', _384 => _384.id]), () => ( "")));
10186
11642
  });
10187
- const parents = new Set(roots.map((id) => _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _349 => _349(doc.nodes, id), 'optionalAccess', _350 => _350.parent, 'optionalAccess', _351 => _351.id]), () => ( null))));
10188
- const paths = roots.filter((id) => _optionalChain([findVectorNode, 'call', _352 => _352(doc.nodes, id), 'optionalAccess', _353 => _353.type]) === "path" && !isVectorBlendGroup(_optionalChain([locateVectorNode, 'call', _354 => _354(doc.nodes, id), 'optionalAccess', _355 => _355.parent])));
11643
+ const parents = new Set(roots.map((id) => _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _385 => _385(doc.nodes, id), 'optionalAccess', _386 => _386.parent, 'optionalAccess', _387 => _387.id]), () => ( null))));
11644
+ const paths = roots.filter((id) => _optionalChain([findVectorNode, 'call', _388 => _388(doc.nodes, id), 'optionalAccess', _389 => _389.type]) === "path" && !isVectorBlendGroup(_optionalChain([locateVectorNode, 'call', _390 => _390(doc.nodes, id), 'optionalAccess', _391 => _391.parent])));
10189
11645
  const first = blends[0];
10190
11646
  return {
10191
11647
  ids: blends.map((g) => g.id),
10192
11648
  options: resolveVectorBlendOptions(first ? first.blend : state.blendDefaults),
10193
11649
  canBlend: roots.length >= 2 && parents.size === 1,
10194
11650
  canReplaceSpine: blends.length === 1 && paths.length === 1,
10195
- hasSpine: !!_optionalChain([first, 'optionalAccess', _356 => _356.blend, 'optionalAccess', _357 => _357.spine])
11651
+ hasSpine: !!_optionalChain([first, 'optionalAccess', _392 => _392.blend, 'optionalAccess', _393 => _393.spine])
10196
11652
  };
10197
11653
  }
10198
11654
  function applyVectorBlendOptions(controller, patch) {
@@ -10224,7 +11680,7 @@ var VECTOR_OVERLAY_THEME = {
10224
11680
  builderHatchOpacity: 0.6
10225
11681
  };
10226
11682
  function vectorDiamondPath(x, y, r = VECTOR_OVERLAY_THEME.widthPointSize) {
10227
- return `M${fmt(x)} ${fmt(y - r)}L${fmt(x + r)} ${fmt(y)}L${fmt(x)} ${fmt(y + r)}L${fmt(x - r)} ${fmt(y)}Z`;
11683
+ return `M${fmt4(x)} ${fmt4(y - r)}L${fmt4(x + r)} ${fmt4(y)}L${fmt4(x)} ${fmt4(y + r)}L${fmt4(x - r)} ${fmt4(y)}Z`;
10228
11684
  }
10229
11685
  function toVectorModifiers(event) {
10230
11686
  return { shift: !!event.shiftKey, alt: !!event.altKey, mod: !!(event.metaKey || event.ctrlKey) };
@@ -10245,7 +11701,7 @@ function isVectorTextTarget(target) {
10245
11701
  if (!el || typeof el !== "object") return false;
10246
11702
  const tag = (_nullishCoalesce(el.tagName, () => ( ""))).toUpperCase();
10247
11703
  if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
10248
- return !!el.isContentEditable || _optionalChain([el, 'access', _358 => _358.getAttribute, 'optionalCall', _359 => _359("contenteditable")]) === "true";
11704
+ return !!el.isContentEditable || _optionalChain([el, 'access', _394 => _394.getAttribute, 'optionalCall', _395 => _395("contenteditable")]) === "true";
10249
11705
  }
10250
11706
  function getVectorSelectionStyle(state) {
10251
11707
  const paths = [];
@@ -10253,12 +11709,12 @@ function getVectorSelectionStyle(state) {
10253
11709
  const node = findVectorNode(state.document.nodes, id);
10254
11710
  if (node) paths.push(...collect(node));
10255
11711
  }
10256
- const anchorMode = state.tool === "direct" && !!_optionalChain([state, 'access', _360 => _360.selectedAnchors, 'optionalAccess', _361 => _361.length]);
11712
+ const anchorMode = state.tool === "direct" && !!_optionalChain([state, 'access', _396 => _396.selectedAnchors, 'optionalAccess', _397 => _397.length]);
10257
11713
  if (anchorMode) {
10258
11714
  for (const r of state.selectedAnchors) {
10259
11715
  if (paths.some((p) => p.id === r.nodeId)) continue;
10260
11716
  const node = findVectorNode(state.document.nodes, r.nodeId);
10261
- if (_optionalChain([node, 'optionalAccess', _362 => _362.type]) === "path") paths.push(node);
11717
+ if (_optionalChain([node, 'optionalAccess', _398 => _398.type]) === "path") paths.push(node);
10262
11718
  }
10263
11719
  }
10264
11720
  if (!paths.length) return null;
@@ -10269,7 +11725,7 @@ function getVectorSelectionStyle(state) {
10269
11725
  for (const c of vectorCorners(p.subpaths)) {
10270
11726
  if (anchorMode && !state.selectedAnchors.some((r) => r.nodeId === p.id && r.subpath === c.subpath && r.index === c.index)) continue;
10271
11727
  const corner = p.subpaths[c.subpath].anchors[c.index].corner;
10272
- corners2.push({ radius: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _363 => _363.radius]), () => ( 0)), kind: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _364 => _364.kind]), () => ( "round")) });
11728
+ corners2.push({ radius: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _399 => _399.radius]), () => ( 0)), kind: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _400 => _400.kind]), () => ( "round")) });
10273
11729
  }
10274
11730
  }
10275
11731
  const c0 = _nullishCoalesce(corners2[0], () => ( { radius: 0, kind: "round" }));
@@ -10329,6 +11785,45 @@ function vectorPaintToHex(paint) {
10329
11785
  const hex = m[1].length === 3 ? m[1].replace(/./g, (c) => c + c) : m[1];
10330
11786
  return `#${hex.toLowerCase()}`;
10331
11787
  }
11788
+ var VECTOR_GRADIENT_TYPE_LABELS = { linear: "Linear", radial: "Radial", conic: "Conic", reflected: "Reflected", diamond: "Diamond" };
11789
+ var VECTOR_GRADIENT_SPREAD_LABELS = { pad: "Pad", reflect: "Reflect", repeat: "Repeat" };
11790
+ var VECTOR_GRADIENT_INTERPOLATION_LABELS = { srgb: "sRGB", linear: "Linear light", oklab: "Oklab" };
11791
+ var VECTOR_STROKE_GRADIENT_MODE_LABELS = { within: "Within stroke", along: "Along stroke", across: "Across stroke" };
11792
+ function getVectorGradientEditor(state) {
11793
+ const paths = [];
11794
+ for (const id of state.selection) {
11795
+ const node = findVectorNode(state.document.nodes, id);
11796
+ if (node) paths.push(...collect(node));
11797
+ }
11798
+ if (!paths.length) return null;
11799
+ const target = state.gradientEdit.target;
11800
+ const paint = paths[0][target];
11801
+ const gradient = isVectorGradient(paint) ? paint : null;
11802
+ const sorted = gradient ? sortedVectorGradientStops(gradient) : [];
11803
+ const stopIndex = state.gradientEdit.stop;
11804
+ const stops = sorted.map((s, index) => ({ ...s, index, selected: index === stopIndex }));
11805
+ const sel = stopIndex !== null && sorted[stopIndex] ? { ...sorted[stopIndex], index: stopIndex, last: stopIndex === sorted.length - 1 } : null;
11806
+ const preview = gradient ? `linear-gradient(to right, ${expandVectorGradientStops(gradient).map((s) => {
11807
+ const c = parseVectorColor(s.color);
11808
+ const a = (_nullishCoalesce(_optionalChain([c, 'optionalAccess', _401 => _401[3]]), () => ( 1))) * (_nullishCoalesce(s.opacity, () => ( 1)));
11809
+ const color = c ? `rgba(${Math.round(c[0] * 255)}, ${Math.round(c[1] * 255)}, ${Math.round(c[2] * 255)}, ${Math.round(a * 1e3) / 1e3})` : s.color;
11810
+ return `${color} ${Math.round(s.offset * 1e4) / 100}%`;
11811
+ }).join(", ")})` : null;
11812
+ return {
11813
+ target,
11814
+ gradient,
11815
+ color: vectorPaintToHex(paint),
11816
+ stops,
11817
+ selected: sel,
11818
+ preview,
11819
+ type: _nullishCoalesce(_optionalChain([gradient, 'optionalAccess', _402 => _402.type]), () => ( null)),
11820
+ spread: _nullishCoalesce(_optionalChain([gradient, 'optionalAccess', _403 => _403.spread]), () => ( "pad")),
11821
+ interpolation: _nullishCoalesce(_optionalChain([gradient, 'optionalAccess', _404 => _404.interpolation]), () => ( "srgb")),
11822
+ smoothness: _nullishCoalesce(_optionalChain([gradient, 'optionalAccess', _405 => _405.smoothness]), () => ( 0)),
11823
+ strokeMode: _nullishCoalesce(_optionalChain([gradient, 'optionalAccess', _406 => _406.strokeMode]), () => ( "within")),
11824
+ mixed: paths.some((p) => JSON.stringify(_nullishCoalesce(p[target], () => ( null))) !== JSON.stringify(_nullishCoalesce(paint, () => ( null))))
11825
+ };
11826
+ }
10332
11827
  var VECTOR_PATH_ACTIONS = [
10333
11828
  { id: "join", label: "\u22C8", title: "Join", shortcut: "J", enabled: (s, p) => s + p > 0, run: (c) => c.joinPaths() },
10334
11829
  { id: "average", label: "\u22B9", title: "Average Points", shortcut: "Alt+J", enabled: (_s, p) => p > 1, run: (c) => c.averageAnchors("both") },
@@ -10356,9 +11851,9 @@ function getVectorEffectsPanel(state) {
10356
11851
  if (!nodes.length) return null;
10357
11852
  const one = nodes.length === 1 ? nodes[0] : null;
10358
11853
  return {
10359
- nodeId: _nullishCoalesce(_optionalChain([one, 'optionalAccess', _365 => _365.id]), () => ( null)),
11854
+ nodeId: _nullishCoalesce(_optionalChain([one, 'optionalAccess', _407 => _407.id]), () => ( null)),
10360
11855
  count: nodes.length,
10361
- effects: (_nullishCoalesce(_optionalChain([one, 'optionalAccess', _366 => _366.effects]), () => ( []))).map((e) => ({
11856
+ effects: (_nullishCoalesce(_optionalChain([one, 'optionalAccess', _408 => _408.effects]), () => ( []))).map((e) => ({
10362
11857
  id: e.id,
10363
11858
  type: e.type,
10364
11859
  label: _nullishCoalesce(VECTOR_EFFECT_LABELS[e.type], () => ( e.type)),
@@ -10412,12 +11907,45 @@ var BLEND_PROPS = {
10412
11907
  linear: { type: "boolean", description: "Move anchors in straight lines instead of the rotation-aware morph." }
10413
11908
  };
10414
11909
  var checkBlendOptions = (params) => {
10415
- if (_optionalChain([params, 'optionalAccess', _367 => _367.spacing]) !== void 0 && !VECTOR_BLEND_SPACINGS.includes(params.spacing)) fail(`Unknown blend spacing "${params.spacing}".`);
10416
- if (_optionalChain([params, 'optionalAccess', _368 => _368.steps]) !== void 0 && !(Number.isFinite(params.steps) && params.steps >= 1)) fail("steps must be at least 1.");
10417
- if (_optionalChain([params, 'optionalAccess', _369 => _369.distance]) !== void 0 && !(Number.isFinite(params.distance) && params.distance > 0)) fail("distance must be positive.");
11910
+ if (_optionalChain([params, 'optionalAccess', _409 => _409.spacing]) !== void 0 && !VECTOR_BLEND_SPACINGS.includes(params.spacing)) fail(`Unknown blend spacing "${params.spacing}".`);
11911
+ if (_optionalChain([params, 'optionalAccess', _410 => _410.steps]) !== void 0 && !(Number.isFinite(params.steps) && params.steps >= 1)) fail("steps must be at least 1.");
11912
+ if (_optionalChain([params, 'optionalAccess', _411 => _411.distance]) !== void 0 && !(Number.isFinite(params.distance) && params.distance > 0)) fail("distance must be positive.");
11913
+ };
11914
+ var POINT = { type: "object", properties: { x: { type: "number" }, y: { type: "number" } }, required: ["x", "y"] };
11915
+ var GRADIENT_PROPS = {
11916
+ type: { type: "string", enum: VECTOR_GRADIENT_TYPES, description: "linear, radial, conic (an angle sweep), reflected (linear mirrored about the start) or diamond." },
11917
+ stops: {
11918
+ type: "array",
11919
+ minItems: 1,
11920
+ items: {
11921
+ type: "object",
11922
+ properties: {
11923
+ offset: { type: "number", minimum: 0, maximum: 1 },
11924
+ color: { type: "string", description: "A CSS colour." },
11925
+ opacity: { type: "number", minimum: 0, maximum: 1 },
11926
+ midpoint: { type: "number", minimum: 0.05, maximum: 0.95, description: "Where between this stop and the next the colours are half-mixed (default 0.5)." }
11927
+ },
11928
+ required: ["offset", "color"]
11929
+ }
11930
+ },
11931
+ angle: { type: "number", description: "Shorthand direction in degrees (0 up, clockwise): linear / reflected direction across the box, conic start angle, diamond turn." },
11932
+ cx: { type: "number", description: "Shorthand centre x of radial / conic / diamond gradients, a fraction of the box (default 0.5)." },
11933
+ cy: { type: "number" },
11934
+ r: { type: "number", description: "Shorthand radius, a fraction of the box (default 0.5)." },
11935
+ start: { ...POINT, description: "Explicit start (centre of radial / conic / diamond), in `units`. Wins over the shorthand." },
11936
+ end: { ...POINT, description: "Explicit end of the main axis (the radius of radial / conic / diamond)." },
11937
+ width: { ...POINT, description: "End of the secondary axis (aspect and turn of radial / conic / diamond); default square to the main axis, as long." },
11938
+ focal: { ...POINT, description: "Radial focal point (where t = 0), in `units`." },
11939
+ focalRadius: { type: "number", minimum: 0, maximum: 0.99, description: "Radial focal circle radius as a fraction of the radius (SVG fr)." },
11940
+ units: { type: "string", enum: ["objectBoundingBox", "userSpaceOnUse"], description: "Space of start / end / width / focal: fractions of the shape box (default) or the node's own coordinates." },
11941
+ spread: { type: "string", enum: VECTOR_GRADIENT_SPREADS, description: "What paints past the ends: pad (default), reflect or repeat." },
11942
+ interpolation: { type: "string", enum: VECTOR_GRADIENT_INTERPOLATIONS, description: "Colour mixing space: srgb (default), linear (light) or oklab." },
11943
+ smoothness: { type: "number", minimum: 0, maximum: 1, description: "Bends the ramp along a smooth spline through the stops (0 = straight)." },
11944
+ strokeMode: { type: "string", enum: VECTOR_STROKE_GRADIENT_MODES, description: "On strokes: within (default, like a fill), along the path or across it." }
10418
11945
  };
10419
11946
  var PAINT = {
10420
- description: 'A CSS colour, `null` for none, or a gradient `{ type: "linear", angle, stops: [{ offset, color }] }`.'
11947
+ description: 'A CSS colour, `null` for none, or a gradient object: `{ type: "linear" | "radial" | "conic" | "reflected" | "diamond", stops: [{ offset, color, opacity?, midpoint? }] }` plus the shorthand `angle` / `cx, cy, r` or explicit `start`, `end`, `width`, `focal`, `focalRadius`, `units`, and `spread`, `interpolation`, `smoothness`, `strokeMode` (see vector.setGradient).',
11948
+ anyOf: [{ type: "string" }, { type: "null" }, { type: "object", properties: GRADIENT_PROPS, required: ["type", "stops"] }]
10421
11949
  };
10422
11950
  var STYLE_PROPS = {
10423
11951
  fill: PAINT,
@@ -10438,6 +11966,10 @@ var STYLE_PROPS = {
10438
11966
  var pickStyle = (params) => {
10439
11967
  const out = {};
10440
11968
  for (const key of Object.keys(STYLE_PROPS)) if (params[key] !== void 0) out[key] = params[key];
11969
+ for (const key of ["fill", "stroke"]) {
11970
+ const paint = out[key];
11971
+ if (paint && typeof paint === "object") out[key] = _nullishCoalesce(sanitizeVectorGradient(paint), () => ( fail(`${key} is not a valid gradient: give a type and at least one stop { offset, color }.`)));
11972
+ }
10441
11973
  return out;
10442
11974
  };
10443
11975
  var check = (result) => {
@@ -10449,17 +11981,17 @@ function createVectorCommands(controller, options = {}) {
10449
11981
  const base = { group, ...options.scope ? { scope: options.scope } : {} };
10450
11982
  const hasSelection = () => controller.getState().selection.length ? true : "Nothing is selected.";
10451
11983
  const ids = (params) => {
10452
- const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _370 => _370.ids]), () => ( controller.getState().selection));
11984
+ const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _412 => _412.ids]), () => ( controller.getState().selection));
10453
11985
  for (const id of list2) if (!controller.getNode(id)) fail(`There is no node "${id}".`);
10454
11986
  if (!list2.length) fail("Nothing is selected; pass ids.");
10455
11987
  return list2;
10456
11988
  };
10457
- const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess', _371 => _371.ids, 'optionalAccess', _372 => _372.length]) ? true : hasSelection();
11989
+ const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess', _413 => _413.ids, 'optionalAccess', _414 => _414.length]) ? true : hasSelection();
10458
11990
  const anchorsOf = (refs) => {
10459
11991
  if (!refs) return [...controller.getState().selectedAnchors];
10460
11992
  for (const r of refs) {
10461
11993
  const node = controller.getNode(r.nodeId);
10462
- if (_optionalChain([node, 'optionalAccess', _373 => _373.type]) !== "path" || !_optionalChain([node, 'access', _374 => _374.subpaths, 'access', _375 => _375[r.subpath], 'optionalAccess', _376 => _376.anchors, 'access', _377 => _377[r.index]])) fail(`There is no anchor ${r.index} on subpath ${r.subpath} of "${r.nodeId}".`);
11994
+ if (_optionalChain([node, 'optionalAccess', _415 => _415.type]) !== "path" || !_optionalChain([node, 'access', _416 => _416.subpaths, 'access', _417 => _417[r.subpath], 'optionalAccess', _418 => _418.anchors, 'access', _419 => _419[r.index]])) fail(`There is no anchor ${r.index} on subpath ${r.subpath} of "${r.nodeId}".`);
10463
11995
  }
10464
11996
  return refs;
10465
11997
  };
@@ -10483,7 +12015,7 @@ function createVectorCommands(controller, options = {}) {
10483
12015
  ...n.type === "path" ? { fill: _nullishCoalesce(n.fill, () => ( null)), stroke: _nullishCoalesce(n.stroke, () => ( null)), strokeWidth: _nullishCoalesce(n.strokeWidth, () => ( 1)), d: vectorPathData(n, 2) } : { children: n.children.length },
10484
12016
  ...n.type === "group" && n.blend ? { blend: { ...resolveVectorBlendOptions(n.blend), spine: !!n.blend.spine, keys: vectorBlendKeys(n).map((k) => k.id) } } : {},
10485
12017
  ...n.blendStep ? { blendStep: true } : {},
10486
- ..._optionalChain([n, 'access', _378 => _378.effects, 'optionalAccess', _379 => _379.length]) ? { effects: n.effects } : {},
12018
+ ..._optionalChain([n, 'access', _420 => _420.effects, 'optionalAccess', _421 => _421.length]) ? { effects: n.effects } : {},
10487
12019
  ...n.type === "group" && n.liveBoolean ? { liveBoolean: n.liveBoolean } : {},
10488
12020
  ...n.booleanOperation ? { booleanOperation: n.booleanOperation } : {}
10489
12021
  });
@@ -10555,7 +12087,7 @@ function createVectorCommands(controller, options = {}) {
10555
12087
  run: (_ctx, params) => {
10556
12088
  const id = controller.addPath(params.d, { ...pickStyle(params), ...params.name ? { name: params.name } : {} });
10557
12089
  const node = controller.getNode(id);
10558
- if (_optionalChain([node, 'optionalAccess', _380 => _380.type]) === "path" && !node.subpaths.length) {
12090
+ if (_optionalChain([node, 'optionalAccess', _422 => _422.type]) === "path" && !node.subpaths.length) {
10559
12091
  controller.undo();
10560
12092
  fail("The path data has no points.");
10561
12093
  }
@@ -10578,6 +12110,26 @@ function createVectorCommands(controller, options = {}) {
10578
12110
  return { ok: true };
10579
12111
  }
10580
12112
  },
12113
+ {
12114
+ ...base,
12115
+ id: `${prefix}setGradient`,
12116
+ label: "Gradient\u2026",
12117
+ keywords: ["gradient", "linear", "radial", "conic", "angle", "reflected", "diamond", "stops", "midpoint", "oklab", "spread", "focal", "along stroke", "across stroke", "reverse"],
12118
+ description: "Edits the gradient of the fill (default) or stroke of paths (groups apply to every path inside): any gradient field (missing ones keep their value; a colour becomes a gradient from it to white), `aspect` (secondary axis over main axis), `reverse` (flip the stops and midpoints), `focal` / `width` null to remove them. Midpoints, smoothness and oklab / linear interpolation export to SVG as extra stops; conic and diamond gradients as patterns.",
12119
+ params: { type: "object", properties: { ids: IDS, target: { type: "string", enum: ["fill", "stroke"] }, ...GRADIENT_PROPS, aspect: { type: "number" }, reverse: { type: "boolean" } } },
12120
+ enabled: enabledFor,
12121
+ undoable: true,
12122
+ run: (_ctx, params) => {
12123
+ const { ids: _i, target, ...patch } = _nullishCoalesce(params, () => ( {}));
12124
+ if (patch.type !== void 0 && !VECTOR_GRADIENT_TYPES.includes(patch.type)) fail(`Unknown gradient type "${patch.type}".`);
12125
+ if (patch.stops !== void 0 && !(Array.isArray(patch.stops) && patch.stops.length)) fail("stops needs at least one { offset, color }.");
12126
+ check(controller.setGradient(patch, { ids: ids(params), target: _nullishCoalesce(target, () => ( "fill")) }));
12127
+ const id = ids(params)[0];
12128
+ const node = id ? controller.getNode(id) : void 0;
12129
+ const path = _optionalChain([node, 'optionalAccess', _423 => _423.type]) === "path" ? node : void 0;
12130
+ return { ok: true, ...path ? { gradient: path[_nullishCoalesce(target, () => ( "fill"))] } : {} };
12131
+ }
12132
+ },
10581
12133
  {
10582
12134
  ...base,
10583
12135
  id: `${prefix}setCorners`,
@@ -10587,10 +12139,10 @@ function createVectorCommands(controller, options = {}) {
10587
12139
  params: { type: "object", properties: { radius: { type: "number", minimum: 0 }, kind: { type: "string", enum: VECTOR_CORNER_KINDS }, ids: IDS, anchors: ANCHORS } },
10588
12140
  undoable: true,
10589
12141
  run: (_ctx, params) => {
10590
- const refs = _optionalChain([params, 'optionalAccess', _381 => _381.anchors]) ? anchorsOf(params.anchors) : void 0;
10591
- const target = { refs, ids: _optionalChain([params, 'optionalAccess', _382 => _382.ids]) ? ids(params) : void 0 };
10592
- if (_optionalChain([params, 'optionalAccess', _383 => _383.radius]) !== void 0) check(controller.setCornerRadius(params.radius, { ...target, kind: params.kind }));
10593
- else if (_optionalChain([params, 'optionalAccess', _384 => _384.kind])) check(controller.setCornerKind(params.kind, target));
12142
+ const refs = _optionalChain([params, 'optionalAccess', _424 => _424.anchors]) ? anchorsOf(params.anchors) : void 0;
12143
+ const target = { refs, ids: _optionalChain([params, 'optionalAccess', _425 => _425.ids]) ? ids(params) : void 0 };
12144
+ if (_optionalChain([params, 'optionalAccess', _426 => _426.radius]) !== void 0) check(controller.setCornerRadius(params.radius, { ...target, kind: params.kind }));
12145
+ else if (_optionalChain([params, 'optionalAccess', _427 => _427.kind])) check(controller.setCornerKind(params.kind, target));
10594
12146
  else fail("Pass `radius` and / or `kind`.");
10595
12147
  return { ok: true };
10596
12148
  }
@@ -10625,12 +12177,12 @@ function createVectorCommands(controller, options = {}) {
10625
12177
  undoable: true,
10626
12178
  run: (_ctx, params) => {
10627
12179
  const target = { ids: ids(params) };
10628
- if (_optionalChain([params, 'optionalAccess', _385 => _385.preset])) check(controller.setWidthProfile(params.preset, target));
10629
- else if (_optionalChain([params, 'optionalAccess', _386 => _386.points]) !== void 0) check(controller.setWidthProfile(_nullishCoalesce(params.points, () => ( null)), target));
10630
- else if (_optionalChain([params, 'optionalAccess', _387 => _387.taper]) || _optionalChain([params, 'optionalAccess', _388 => _388.wave])) check(controller.setWidthProfile({ taper: params.taper, wave: params.wave }, target));
12180
+ if (_optionalChain([params, 'optionalAccess', _428 => _428.preset])) check(controller.setWidthProfile(params.preset, target));
12181
+ else if (_optionalChain([params, 'optionalAccess', _429 => _429.points]) !== void 0) check(controller.setWidthProfile(_nullishCoalesce(params.points, () => ( null)), target));
12182
+ else if (_optionalChain([params, 'optionalAccess', _430 => _430.taper]) || _optionalChain([params, 'optionalAccess', _431 => _431.wave])) check(controller.setWidthProfile({ taper: params.taper, wave: params.wave }, target));
10631
12183
  else fail("Pass `preset`, `points`, `taper` or `wave`.");
10632
12184
  const node = controller.getNode(target.ids[0]);
10633
- return { ok: true, points: _optionalChain([node, 'optionalAccess', _389 => _389.type]) === "path" ? _nullishCoalesce(node.strokeWidthProfile, () => ( [])) : [] };
12185
+ return { ok: true, points: _optionalChain([node, 'optionalAccess', _432 => _432.type]) === "path" ? _nullishCoalesce(node.strokeWidthProfile, () => ( [])) : [] };
10634
12186
  }
10635
12187
  },
10636
12188
  {
@@ -10652,10 +12204,10 @@ function createVectorCommands(controller, options = {}) {
10652
12204
  },
10653
12205
  undoable: true,
10654
12206
  run: (_ctx, params) => {
10655
- if (!_optionalChain([params, 'optionalAccess', _390 => _390.id])) fail("Pass the path `id`.");
12207
+ if (!_optionalChain([params, 'optionalAccess', _433 => _433.id])) fail("Pass the path `id`.");
10656
12208
  check(controller.setWidthPoint({ nodeId: params.id, index: _nullishCoalesce(params.index, () => ( null)) }, { t: params.t, left: params.left, right: params.right }));
10657
12209
  const node = controller.getNode(params.id);
10658
- return { ok: true, points: _optionalChain([node, 'optionalAccess', _391 => _391.type]) === "path" ? _nullishCoalesce(node.strokeWidthProfile, () => ( [])) : [] };
12210
+ return { ok: true, points: _optionalChain([node, 'optionalAccess', _434 => _434.type]) === "path" ? _nullishCoalesce(node.strokeWidthProfile, () => ( [])) : [] };
10659
12211
  }
10660
12212
  },
10661
12213
  {
@@ -10668,7 +12220,7 @@ function createVectorCommands(controller, options = {}) {
10668
12220
  enabled: enabledFor,
10669
12221
  undoable: true,
10670
12222
  run: (_ctx, params) => {
10671
- const created = controller.outlineStroke({ ids: _optionalChain([params, 'optionalAccess', _392 => _392.ids]) ? ids(params) : void 0 });
12223
+ const created = controller.outlineStroke({ ids: _optionalChain([params, 'optionalAccess', _435 => _435.ids]) ? ids(params) : void 0 });
10672
12224
  if (!created.length) fail("No selected path has a stroke.");
10673
12225
  return { ids: created };
10674
12226
  }
@@ -10785,7 +12337,7 @@ function createVectorCommands(controller, options = {}) {
10785
12337
  run: (_ctx, params) => {
10786
12338
  if (params.params !== void 0 || params.enabled !== void 0) check(controller.updateEffect(params.nodeId, params.effectId, { params: params.params, enabled: params.enabled }));
10787
12339
  if (params.index !== void 0) check(controller.moveEffect(params.nodeId, params.effectId, params.index));
10788
- return { effects: _nullishCoalesce(_optionalChain([controller, 'access', _393 => _393.getNode, 'call', _394 => _394(params.nodeId), 'optionalAccess', _395 => _395.effects]), () => ( [])) };
12340
+ return { effects: _nullishCoalesce(_optionalChain([controller, 'access', _436 => _436.getNode, 'call', _437 => _437(params.nodeId), 'optionalAccess', _438 => _438.effects]), () => ( [])) };
10789
12341
  }
10790
12342
  },
10791
12343
  {
@@ -10849,7 +12401,7 @@ function createVectorCommands(controller, options = {}) {
10849
12401
  undoable: true,
10850
12402
  run: (_ctx, params) => {
10851
12403
  checkBlendOptions(params);
10852
- const r = controller.setBlendOptions(_nullishCoalesce(params, () => ( {})), { ids: _optionalChain([params, 'optionalAccess', _396 => _396.ids]) });
12404
+ const r = controller.setBlendOptions(_nullishCoalesce(params, () => ( {})), { ids: _optionalChain([params, 'optionalAccess', _439 => _439.ids]) });
10853
12405
  if (!r.ok) fail(r.message);
10854
12406
  return { ok: true };
10855
12407
  }
@@ -10863,7 +12415,7 @@ function createVectorCommands(controller, options = {}) {
10863
12415
  params: { type: "object", properties: { ids: IDS } },
10864
12416
  undoable: true,
10865
12417
  run: (_ctx, params) => {
10866
- const groups = controller.expandBlend({ ids: _optionalChain([params, 'optionalAccess', _397 => _397.ids]) });
12418
+ const groups = controller.expandBlend({ ids: _optionalChain([params, 'optionalAccess', _440 => _440.ids]) });
10867
12419
  if (!groups.length) fail("Select a blend to expand.");
10868
12420
  return { ids: groups };
10869
12421
  }
@@ -10877,7 +12429,7 @@ function createVectorCommands(controller, options = {}) {
10877
12429
  params: { type: "object", properties: { ids: IDS } },
10878
12430
  undoable: true,
10879
12431
  run: (_ctx, params) => {
10880
- const released = controller.releaseBlend({ ids: _optionalChain([params, 'optionalAccess', _398 => _398.ids]) });
12432
+ const released = controller.releaseBlend({ ids: _optionalChain([params, 'optionalAccess', _441 => _441.ids]) });
10881
12433
  if (!released.length) fail("Select a blend to release.");
10882
12434
  return { ids: released };
10883
12435
  }
@@ -10891,7 +12443,7 @@ function createVectorCommands(controller, options = {}) {
10891
12443
  params: { type: "object", properties: { ids: IDS } },
10892
12444
  undoable: true,
10893
12445
  run: (_ctx, params) => {
10894
- const r = controller.replaceBlendSpine({ ids: _optionalChain([params, 'optionalAccess', _399 => _399.ids]) });
12446
+ const r = controller.replaceBlendSpine({ ids: _optionalChain([params, 'optionalAccess', _442 => _442.ids]) });
10895
12447
  if (!r.ok) fail(r.message);
10896
12448
  return { ok: true };
10897
12449
  }
@@ -11005,7 +12557,7 @@ function createVectorCommands(controller, options = {}) {
11005
12557
  enabled: enabledFor,
11006
12558
  undoable: true,
11007
12559
  run: (_ctx, params) => {
11008
- const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess', _400 => _400.name]) });
12560
+ const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess', _443 => _443.name]) });
11009
12561
  if (!id) fail("Only nodes that share a parent can be grouped.");
11010
12562
  return { id };
11011
12563
  }
@@ -11033,7 +12585,7 @@ function createVectorCommands(controller, options = {}) {
11033
12585
  params: { type: "object", properties: { anchors: ANCHORS, keepShape: { type: "boolean" } } },
11034
12586
  undoable: true,
11035
12587
  run: (_ctx, params) => {
11036
- check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _401 => _401.anchors])), { keepShape: _optionalChain([params, 'optionalAccess', _402 => _402.keepShape]) }));
12588
+ check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _444 => _444.anchors])), { keepShape: _optionalChain([params, 'optionalAccess', _445 => _445.keepShape]) }));
11037
12589
  return { ok: true };
11038
12590
  }
11039
12591
  },
@@ -11088,7 +12640,7 @@ function createVectorCommands(controller, options = {}) {
11088
12640
  params: { type: "object", properties: { ids: IDS, tolerance: { type: "number", minimum: 0 } } },
11089
12641
  undoable: true,
11090
12642
  run: (_ctx, params) => {
11091
- check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess', _403 => _403.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _404 => _404.tolerance]) }));
12643
+ check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess', _446 => _446.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _447 => _447.tolerance]) }));
11092
12644
  return { selection: controller.getState().selection };
11093
12645
  }
11094
12646
  },
@@ -11102,7 +12654,7 @@ function createVectorCommands(controller, options = {}) {
11102
12654
  params: { type: "object", properties: { axis: { type: "string", enum: ["horizontal", "vertical", "both"] }, anchors: ANCHORS } },
11103
12655
  undoable: true,
11104
12656
  run: (_ctx, params) => {
11105
- check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _405 => _405.axis]), () => ( "both")), anchorsOf(_optionalChain([params, 'optionalAccess', _406 => _406.anchors]))));
12657
+ check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _448 => _448.axis]), () => ( "both")), anchorsOf(_optionalChain([params, 'optionalAccess', _449 => _449.anchors]))));
11106
12658
  return { ok: true };
11107
12659
  }
11108
12660
  },
@@ -11116,7 +12668,7 @@ function createVectorCommands(controller, options = {}) {
11116
12668
  enabled: enabledFor,
11117
12669
  undoable: true,
11118
12670
  run: (_ctx, params) => {
11119
- check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess', _407 => _407.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _408 => _408.tolerance]) }));
12671
+ check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess', _450 => _450.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _451 => _451.tolerance]) }));
11120
12672
  return { ok: true };
11121
12673
  }
11122
12674
  },
@@ -11129,7 +12681,7 @@ function createVectorCommands(controller, options = {}) {
11129
12681
  params: { type: "object", properties: { ids: IDS, amount: { type: "number", minimum: 0, maximum: 1 }, anchors: ANCHORS } },
11130
12682
  undoable: true,
11131
12683
  run: (_ctx, params) => {
11132
- check(controller.smoothPaths({ ids: _optionalChain([params, 'optionalAccess', _409 => _409.ids]) ? ids(params) : void 0, amount: _optionalChain([params, 'optionalAccess', _410 => _410.amount]), refs: _optionalChain([params, 'optionalAccess', _411 => _411.anchors]) }));
12684
+ check(controller.smoothPaths({ ids: _optionalChain([params, 'optionalAccess', _452 => _452.ids]) ? ids(params) : void 0, amount: _optionalChain([params, 'optionalAccess', _453 => _453.amount]), refs: _optionalChain([params, 'optionalAccess', _454 => _454.anchors]) }));
11133
12685
  return { ok: true };
11134
12686
  }
11135
12687
  },
@@ -11142,14 +12694,14 @@ function createVectorCommands(controller, options = {}) {
11142
12694
  params: { type: "object", properties: { nodeId: { type: "string" }, x: { type: "number" }, y: { type: "number" }, anchors: ANCHORS } },
11143
12695
  undoable: true,
11144
12696
  run: (_ctx, params) => {
11145
- if (_optionalChain([params, 'optionalAccess', _412 => _412.nodeId]) !== void 0) {
12697
+ if (_optionalChain([params, 'optionalAccess', _455 => _455.nodeId]) !== void 0) {
11146
12698
  if (!controller.getNode(params.nodeId)) fail(`There is no node "${params.nodeId}".`);
11147
12699
  if (params.x === void 0 || params.y === void 0) fail("Pass x and y with nodeId.");
11148
12700
  const created = controller.cutPathAt(params.nodeId, { x: params.x, y: params.y });
11149
12701
  if (!created) fail("Nothing to cut there (the end of an open path, or a locked path).");
11150
12702
  return { ids: created };
11151
12703
  }
11152
- check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _413 => _413.anchors]))));
12704
+ check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _456 => _456.anchors]))));
11153
12705
  return { ids: controller.getState().selection };
11154
12706
  }
11155
12707
  },
@@ -11200,7 +12752,7 @@ function createVectorCommands(controller, options = {}) {
11200
12752
  },
11201
12753
  run: (_ctx, params) => {
11202
12754
  const patch = {};
11203
- for (const key of ["keepShapeOnDelete", "curvatureMoves", "pencilSmoothing", "recognizeShapes", "eraserSize", "pencilPressure"]) if (_optionalChain([params, 'optionalAccess', _414 => _414[key]]) !== void 0) patch[key] = params[key];
12755
+ for (const key of ["keepShapeOnDelete", "curvatureMoves", "pencilSmoothing", "recognizeShapes", "eraserSize", "pencilPressure"]) if (_optionalChain([params, 'optionalAccess', _457 => _457[key]]) !== void 0) patch[key] = params[key];
11204
12756
  controller.setEditing(patch);
11205
12757
  return { editing: controller.getState().editing };
11206
12758
  }
@@ -11225,7 +12777,7 @@ function createVectorCommands(controller, options = {}) {
11225
12777
  keywords: ["download", "save", "markup"],
11226
12778
  description: "Returns the document as SVG markup.",
11227
12779
  params: { type: "object", properties: { precision: { type: "integer", minimum: 0, maximum: 8 } } },
11228
- run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess', _415 => _415.precision]) }) })
12780
+ run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess', _458 => _458.precision]) }) })
11229
12781
  },
11230
12782
  {
11231
12783
  ...base,
@@ -11519,5 +13071,70 @@ function createVectorCommands(controller, options = {}) {
11519
13071
 
11520
13072
 
11521
13073
 
11522
- exports.DEFAULT_VECTOR_BLEND_DISTANCE = DEFAULT_VECTOR_BLEND_DISTANCE; exports.DEFAULT_VECTOR_BLEND_STEPS = DEFAULT_VECTOR_BLEND_STEPS; 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_BLEND_MAX_SMOOTH_STEPS = VECTOR_BLEND_MAX_SMOOTH_STEPS; exports.VECTOR_BLEND_MAX_STEPS = VECTOR_BLEND_MAX_STEPS; exports.VECTOR_BLEND_SPACINGS = VECTOR_BLEND_SPACINGS; exports.VECTOR_BLEND_SPACING_LABELS = VECTOR_BLEND_SPACING_LABELS; exports.VECTOR_BUILDER_HATCH_GAP = VECTOR_BUILDER_HATCH_GAP; exports.VECTOR_CLIPBOARD_TYPE = VECTOR_CLIPBOARD_TYPE; exports.VECTOR_CONSTRUCTION_ANGLES = VECTOR_CONSTRUCTION_ANGLES; exports.VECTOR_CORNER_KINDS = VECTOR_CORNER_KINDS; exports.VECTOR_CORNER_KIND_LABELS = VECTOR_CORNER_KIND_LABELS; exports.VECTOR_CORNER_WIDGET_INSET = VECTOR_CORNER_WIDGET_INSET; exports.VECTOR_EDIT_TOGGLES = VECTOR_EDIT_TOGGLES; exports.VECTOR_EFFECT_DEFAULTS = VECTOR_EFFECT_DEFAULTS; exports.VECTOR_EFFECT_LABELS = VECTOR_EFFECT_LABELS; exports.VECTOR_EFFECT_MENU = VECTOR_EFFECT_MENU; exports.VECTOR_EFFECT_PARAM_SPECS = VECTOR_EFFECT_PARAM_SPECS; exports.VECTOR_EFFECT_TYPES = VECTOR_EFFECT_TYPES; exports.VECTOR_FILL_GAP = VECTOR_FILL_GAP; exports.VECTOR_INK_MAX_CORNERS = VECTOR_INK_MAX_CORNERS; exports.VECTOR_MERGE_MODES = VECTOR_MERGE_MODES; exports.VECTOR_OVERLAY_THEME = VECTOR_OVERLAY_THEME; exports.VECTOR_PATHFINDER_ACTIONS = VECTOR_PATHFINDER_ACTIONS; exports.VECTOR_PATHFINDER_LABELS = VECTOR_PATHFINDER_LABELS; exports.VECTOR_PATHFINDER_OPERATIONS = VECTOR_PATHFINDER_OPERATIONS; exports.VECTOR_PATH_ACTIONS = VECTOR_PATH_ACTIONS; exports.VECTOR_PENCIL_STRING_PX = VECTOR_PENCIL_STRING_PX; exports.VECTOR_PRESSURE_NOMINAL = VECTOR_PRESSURE_NOMINAL; exports.VECTOR_ROTATE_HANDLE_OFFSET = VECTOR_ROTATE_HANDLE_OFFSET; exports.VECTOR_STROKE_ALIGN_LABELS = VECTOR_STROKE_ALIGN_LABELS; exports.VECTOR_STYLE_KEYS = VECTOR_STYLE_KEYS; exports.VECTOR_TOOLS = VECTOR_TOOLS; exports.VECTOR_TOOL_GLYPHS = VECTOR_TOOL_GLYPHS; exports.VECTOR_TOOL_SHORTCUTS = VECTOR_TOOL_SHORTCUTS; exports.VECTOR_TRANSFORM_HANDLES = VECTOR_TRANSFORM_HANDLES; exports.VECTOR_WIDTH_PRESETS = VECTOR_WIDTH_PRESETS; exports.VECTOR_WIDTH_PRESET_IDS = VECTOR_WIDTH_PRESET_IDS; exports.VECTOR_WIDTH_PROFILE_MAX_POINTS = VECTOR_WIDTH_PROFILE_MAX_POINTS; exports.VECTOR_WIDTH_PROFILE_MAX_SCALE = VECTOR_WIDTH_PROFILE_MAX_SCALE; exports.VECTOR_WIDTH_SAME_T = VECTOR_WIDTH_SAME_T; exports.VectorEditorController = VectorEditorController; exports.VectorSnapTargets = VectorSnapTargets; exports.activeVectorEffects = activeVectorEffects; exports.addBuilderHit = addBuilderHit; exports.applyMatrix = applyMatrix; exports.applyMatrixLinear = applyMatrixLinear; exports.applyVectorBlendOptions = applyVectorBlendOptions; exports.applyVectorCornersAttribute = applyVectorCornersAttribute; exports.applyVectorEffects = applyVectorEffects; exports.arrowheadInset = arrowheadInset; exports.arrowheadPath = arrowheadPath; exports.arrowheadWeight = arrowheadWeight; exports.bendSegment = bendSegment; exports.bezierToSubpaths = bezierToSubpaths; exports.boundsFromPoints = boundsFromPoints; exports.boundsIntersect = boundsIntersect; exports.clearVectorLiveBooleanCache = clearVectorLiveBooleanCache; 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.correspondMorphContours = correspondMorphContours; exports.createVectorCommands = createVectorCommands; exports.createVectorEffect = createVectorEffect; exports.createVectorMorph = createVectorMorph; exports.curvatureMoveAnchor = curvatureMoveAnchor; exports.cutSubpathAt = cutSubpathAt; exports.dashIntervals = dashIntervals; exports.decomposeRigid = decomposeRigid; exports.effectPathLength = effectPathLength; exports.effectPathVertexCount = effectPathVertexCount; exports.effectiveStrokeAlign = effectiveStrokeAlign; exports.ellipseSubpath = ellipseSubpath; exports.eraseFromFill = eraseFromFill; exports.eraseFromOutline = eraseFromOutline; exports.eraserRegion = eraserRegion; exports.evaluateVectorPathKeyframes = evaluateVectorPathKeyframes; exports.expandVectorAppearance = expandVectorAppearance; exports.expandVectorAppearances = expandVectorAppearances; exports.expandVectorBlend = expandVectorBlend; exports.expandVectorCorners = expandVectorCorners; exports.expandVectorCornersOf = expandVectorCornersOf; exports.findVectorNode = findVectorNode; exports.fitCornerRadius = fitCornerRadius; exports.foldVectorBooleanOperands = foldVectorBooleanOperands; exports.generateVectorBlendChildren = generateVectorBlendChildren; exports.generateWidthProfile = generateWidthProfile; exports.getAnchor = getAnchor; exports.getAnchorWorld = getAnchorWorld; exports.getEditablePathIds = getEditablePathIds; exports.getTransformHandlePositions = getTransformHandlePositions; exports.getVectorBlendControls = getVectorBlendControls; exports.getVectorCornerWidgets = getVectorCornerWidgets; exports.getVectorEffectsPanel = getVectorEffectsPanel; exports.getVectorEnteredGroups = getVectorEnteredGroups; exports.getVectorOverlayRenderData = getVectorOverlayRenderData; exports.getVectorSceneRenderData = getVectorSceneRenderData; exports.getVectorSelectionBoxGeometry = getVectorSelectionBoxGeometry; exports.getVectorSelectionFrame = getVectorSelectionFrame; exports.getVectorSelectionStyle = getVectorSelectionStyle; exports.getVectorWidthHandles = getVectorWidthHandles; exports.getVisibleAnchors = getVisibleAnchors; exports.getVisibleHandles = getVisibleHandles; exports.hasOwnVectorAppearance = hasOwnVectorAppearance; exports.hasPaint = hasPaint; exports.hasStrokeExtras = hasStrokeExtras; exports.hasVectorAppearance = hasVectorAppearance; exports.hasVectorCorners = hasVectorCorners; exports.hitTestVectorNode = hitTestVectorNode; exports.hitTestVectorNodes = hitTestVectorNodes; exports.hitTestVectorStroke = hitTestVectorStroke; exports.inferAnchorType = inferAnchorType; exports.insertAnchor = insertAnchor; exports.insertWidthPoint = insertWidthPoint; exports.interpolateMorphContours = interpolateMorphContours; exports.interpolateRigidMatrix = interpolateRigidMatrix; exports.interpolateVectorColor = interpolateVectorColor; exports.interpolateVectorPaint = interpolateVectorPaint; exports.interpolateVectorPaths = interpolateVectorPaths; exports.interpolateVectorStyle = interpolateVectorStyle; exports.interpolateWidthProfiles = interpolateWidthProfiles; exports.invertMatrix = invertMatrix; exports.isClosedBezierShape = isClosedBezierShape; exports.isIdentityMatrix = isIdentityMatrix; exports.isUniformWidthProfile = isUniformWidthProfile; exports.isVectorBlendGroup = isVectorBlendGroup; exports.isVectorLiveBoolean = isVectorLiveBoolean; exports.isVectorTextTarget = isVectorTextTarget; exports.joinSubpaths = joinSubpaths; exports.knifePath = knifePath; exports.lineSubpath = lineSubpath; exports.locateVectorNode = locateVectorNode; exports.matrixRotation = matrixRotation; exports.mergePaths = mergePaths; exports.mirrorWidthProfile = mirrorWidthProfile; exports.morphContourToSubpath = morphContourToSubpath; exports.morphPathsCross = morphPathsCross; exports.moveAnchorHandle = moveAnchorHandle; exports.multiplyMatrices = multiplyMatrices; exports.nearestSegment = nearestSegment; exports.nearestStrokePosition = nearestStrokePosition; exports.nextVectorCornerKind = nextVectorCornerKind; exports.nodeMatrix = nodeMatrix; exports.normalizeVectorEffectParams = normalizeVectorEffectParams; exports.offsetPaths = offsetPaths; exports.parseSvgTransform = parseSvgTransform; exports.parseVectorSvg = parseVectorSvg; exports.pathsCenter = pathsCenter; exports.planShapeBuilder = planShapeBuilder; exports.polygonSubpath = polygonSubpath; exports.polylineBounds = polylineBounds; exports.pressureWidthProfile = pressureWidthProfile; exports.puckerBloatPaths = puckerBloatPaths; exports.recognizeInkShape = recognizeInkShape; exports.rectSubpath = rectSubpath; exports.refineMorphContour = refineMorphContour; exports.refreshVectorBlends = refreshVectorBlends; exports.releaseVectorBlend = releaseVectorBlend; exports.removeAnchorKeepingShape = removeAnchorKeepingShape; exports.removeAnchors = removeAnchors; exports.removeAnchorsKeepingShape = removeAnchorsKeepingShape; exports.removeRedundantPoints = removeRedundantPoints; exports.removeWidthPoints = removeWidthPoints; exports.repeatPaths = repeatPaths; exports.resampleGradientStops = resampleGradientStops; exports.resolveVectorBlendOptions = resolveVectorBlendOptions; exports.reverseMorphContour = reverseMorphContour; exports.reverseSubpath = reverseSubpath; exports.reverseWidthProfile = reverseWidthProfile; exports.rotateMatrix = rotateMatrix; exports.roundCornerPaths = roundCornerPaths; exports.roundSubpaths = roundSubpaths; exports.samplePolyline = samplePolyline; exports.sanitizeVectorEffects = sanitizeVectorEffects; exports.sanitizeWidthProfile = sanitizeWidthProfile; exports.scaleFor = scaleFor; exports.scaleMatrix = scaleMatrix; exports.setAnchorCorner = setAnchorCorner; exports.setSubpathCorners = setSubpathCorners; exports.setWidthPoint = setWidthPoint; exports.simplifyPolyline = simplifyPolyline; exports.smoothSubpath = smoothSubpath; exports.splitIntoFaces = splitIntoFaces; exports.splitSubpathAt = splitSubpathAt; exports.starSubpath = starSubpath; exports.strokePositionPoint = strokePositionPoint; exports.strokeWidthProfileOf = strokeWidthProfileOf; exports.subpathEnclosesArea = subpathEnclosesArea; exports.subpathPointAt = subpathPointAt; exports.subpathSegment = subpathSegment; exports.subpathSegmentCount = subpathSegmentCount; exports.subpathToMorphContour = subpathToMorphContour; exports.subpathsToBezier = subpathsToBezier; exports.taperWaveFactor = taperWaveFactor; exports.toVectorKeyInput = toVectorKeyInput; exports.toVectorModifiers = toVectorModifiers; exports.toVectorPointerInput = toVectorPointerInput; exports.transformVectorEffects = transformVectorEffects; exports.transformVectorNode = transformVectorNode; exports.translateAnchor = translateAnchor; exports.translateMatrix = translateMatrix; exports.trimIntervals = trimIntervals; exports.trimPaths = trimPaths; exports.trimPathsIndividually = trimPathsIndividually; exports.turnMorphContour = turnMorphContour; exports.twistPaths = twistPaths; exports.unionBounds = unionBounds; exports.variableStrokeOutline = variableStrokeOutline; exports.variableStrokeRings = variableStrokeRings; exports.vectorAppearanceNodes = vectorAppearanceNodes; exports.vectorAppearanceOutline = vectorAppearanceOutline; exports.vectorAppearanceShapes = vectorAppearanceShapes; exports.vectorBlendColorDistance = vectorBlendColorDistance; exports.vectorBlendKeyTargets = vectorBlendKeyTargets; exports.vectorBlendKeys = vectorBlendKeys; exports.vectorBlendRail = vectorBlendRail; exports.vectorBlendRailAt = vectorBlendRailAt; exports.vectorBlendSpine = vectorBlendSpine; exports.vectorBlendStepCount = vectorBlendStepCount; exports.vectorBuilderErasedEdges = vectorBuilderErasedEdges; exports.vectorCornerAt = vectorCornerAt; exports.vectorCorners = vectorCorners; exports.vectorCornersAttribute = vectorCornersAttribute; exports.vectorDiamondPath = vectorDiamondPath; exports.vectorDocumentToSvg = vectorDocumentToSvg; exports.vectorEnteredGroups = vectorEnteredGroups; exports.vectorFillMap = vectorFillMap; exports.vectorInkShapeName = vectorInkShapeName; exports.vectorLiveBooleanSteps = vectorLiveBooleanSteps; exports.vectorNodeBounds = vectorNodeBounds; exports.vectorNodeRotation = vectorNodeRotation; exports.vectorNodeToBezier = vectorNodeToBezier; exports.vectorOperandOperation = vectorOperandOperation; exports.vectorPaintToHex = vectorPaintToHex; exports.vectorPaintToSvg = vectorPaintToSvg; exports.vectorParentMatrix = vectorParentMatrix; exports.vectorPathData = vectorPathData; exports.vectorPathEntries = vectorPathEntries; exports.vectorPathWorldBezier = vectorPathWorldBezier; exports.vectorPathfinderShape = vectorPathfinderShape; exports.vectorRepeaterCopies = vectorRepeaterCopies; exports.vectorResizeCursor = vectorResizeCursor; exports.vectorResultStyle = vectorResultStyle; exports.vectorShapeBuilderHit = vectorShapeBuilderHit; exports.vectorShapeBuilderMap = vectorShapeBuilderMap; exports.vectorStrokeLineArea = vectorStrokeLineArea; exports.vectorStrokeOutline = vectorStrokeOutline; exports.vectorStrokeOutlinePath = vectorStrokeOutlinePath; exports.vectorStrokeParts = vectorStrokeParts; exports.vectorStrokeReach = vectorStrokeReach; exports.vectorStrokeSvgParts = vectorStrokeSvgParts; exports.vectorStrokeWidthGeometry = vectorStrokeWidthGeometry; exports.vectorToolLabel = vectorToolLabel; exports.vectorWorldMatrix = vectorWorldMatrix; exports.walkVectorNodes = walkVectorNodes; exports.widthProfileAt = widthProfileAt; exports.widthProfileFromAttribute = widthProfileFromAttribute; exports.widthProfileMaxScale = widthProfileMaxScale; exports.widthProfilePreset = widthProfilePreset; exports.widthProfilePresetId = widthProfilePresetId; exports.widthProfileToAttribute = widthProfileToAttribute; exports.wigglePaths = wigglePaths; exports.zigzagPaths = zigzagPaths;
13074
+
13075
+
13076
+
13077
+
13078
+
13079
+
13080
+
13081
+
13082
+
13083
+
13084
+
13085
+
13086
+
13087
+
13088
+
13089
+
13090
+
13091
+
13092
+
13093
+
13094
+
13095
+
13096
+
13097
+
13098
+
13099
+
13100
+
13101
+
13102
+
13103
+
13104
+
13105
+
13106
+
13107
+
13108
+
13109
+
13110
+
13111
+
13112
+
13113
+
13114
+
13115
+
13116
+
13117
+
13118
+
13119
+
13120
+
13121
+
13122
+
13123
+
13124
+
13125
+
13126
+
13127
+
13128
+
13129
+
13130
+
13131
+
13132
+
13133
+
13134
+
13135
+
13136
+
13137
+
13138
+
13139
+ exports.DEFAULT_VECTOR_BLEND_DISTANCE = DEFAULT_VECTOR_BLEND_DISTANCE; exports.DEFAULT_VECTOR_BLEND_STEPS = DEFAULT_VECTOR_BLEND_STEPS; 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_BLEND_MAX_SMOOTH_STEPS = VECTOR_BLEND_MAX_SMOOTH_STEPS; exports.VECTOR_BLEND_MAX_STEPS = VECTOR_BLEND_MAX_STEPS; exports.VECTOR_BLEND_SPACINGS = VECTOR_BLEND_SPACINGS; exports.VECTOR_BLEND_SPACING_LABELS = VECTOR_BLEND_SPACING_LABELS; exports.VECTOR_BUILDER_HATCH_GAP = VECTOR_BUILDER_HATCH_GAP; exports.VECTOR_CLIPBOARD_TYPE = VECTOR_CLIPBOARD_TYPE; exports.VECTOR_CONSTRUCTION_ANGLES = VECTOR_CONSTRUCTION_ANGLES; exports.VECTOR_CORNER_KINDS = VECTOR_CORNER_KINDS; exports.VECTOR_CORNER_KIND_LABELS = VECTOR_CORNER_KIND_LABELS; exports.VECTOR_CORNER_WIDGET_INSET = VECTOR_CORNER_WIDGET_INSET; exports.VECTOR_EDIT_TOGGLES = VECTOR_EDIT_TOGGLES; exports.VECTOR_EFFECT_DEFAULTS = VECTOR_EFFECT_DEFAULTS; exports.VECTOR_EFFECT_LABELS = VECTOR_EFFECT_LABELS; exports.VECTOR_EFFECT_MENU = VECTOR_EFFECT_MENU; exports.VECTOR_EFFECT_PARAM_SPECS = VECTOR_EFFECT_PARAM_SPECS; exports.VECTOR_EFFECT_TYPES = VECTOR_EFFECT_TYPES; exports.VECTOR_FILL_GAP = VECTOR_FILL_GAP; exports.VECTOR_GRADIENT_INTERPOLATIONS = VECTOR_GRADIENT_INTERPOLATIONS; exports.VECTOR_GRADIENT_INTERPOLATION_LABELS = VECTOR_GRADIENT_INTERPOLATION_LABELS; exports.VECTOR_GRADIENT_MAX_PERIODS = VECTOR_GRADIENT_MAX_PERIODS; exports.VECTOR_GRADIENT_MIDPOINT_GAP = VECTOR_GRADIENT_MIDPOINT_GAP; exports.VECTOR_GRADIENT_MIN_STOPS = VECTOR_GRADIENT_MIN_STOPS; exports.VECTOR_GRADIENT_OFF_BAR = VECTOR_GRADIENT_OFF_BAR; exports.VECTOR_GRADIENT_SPREADS = VECTOR_GRADIENT_SPREADS; exports.VECTOR_GRADIENT_SPREAD_LABELS = VECTOR_GRADIENT_SPREAD_LABELS; exports.VECTOR_GRADIENT_STOP_GAP = VECTOR_GRADIENT_STOP_GAP; exports.VECTOR_GRADIENT_TYPES = VECTOR_GRADIENT_TYPES; exports.VECTOR_GRADIENT_TYPE_LABELS = VECTOR_GRADIENT_TYPE_LABELS; exports.VECTOR_INK_MAX_CORNERS = VECTOR_INK_MAX_CORNERS; exports.VECTOR_MERGE_MODES = VECTOR_MERGE_MODES; exports.VECTOR_OVERLAY_THEME = VECTOR_OVERLAY_THEME; exports.VECTOR_PATHFINDER_ACTIONS = VECTOR_PATHFINDER_ACTIONS; exports.VECTOR_PATHFINDER_LABELS = VECTOR_PATHFINDER_LABELS; exports.VECTOR_PATHFINDER_OPERATIONS = VECTOR_PATHFINDER_OPERATIONS; exports.VECTOR_PATH_ACTIONS = VECTOR_PATH_ACTIONS; exports.VECTOR_PENCIL_STRING_PX = VECTOR_PENCIL_STRING_PX; exports.VECTOR_PRESSURE_NOMINAL = VECTOR_PRESSURE_NOMINAL; exports.VECTOR_ROTATE_HANDLE_OFFSET = VECTOR_ROTATE_HANDLE_OFFSET; exports.VECTOR_STROKE_ALIGN_LABELS = VECTOR_STROKE_ALIGN_LABELS; exports.VECTOR_STROKE_GRADIENT_MODES = VECTOR_STROKE_GRADIENT_MODES; exports.VECTOR_STROKE_GRADIENT_MODE_LABELS = VECTOR_STROKE_GRADIENT_MODE_LABELS; exports.VECTOR_STYLE_KEYS = VECTOR_STYLE_KEYS; exports.VECTOR_TOOLS = VECTOR_TOOLS; exports.VECTOR_TOOL_GLYPHS = VECTOR_TOOL_GLYPHS; exports.VECTOR_TOOL_SHORTCUTS = VECTOR_TOOL_SHORTCUTS; exports.VECTOR_TRANSFORM_HANDLES = VECTOR_TRANSFORM_HANDLES; exports.VECTOR_WIDTH_PRESETS = VECTOR_WIDTH_PRESETS; exports.VECTOR_WIDTH_PRESET_IDS = VECTOR_WIDTH_PRESET_IDS; exports.VECTOR_WIDTH_PROFILE_MAX_POINTS = VECTOR_WIDTH_PROFILE_MAX_POINTS; exports.VECTOR_WIDTH_PROFILE_MAX_SCALE = VECTOR_WIDTH_PROFILE_MAX_SCALE; exports.VECTOR_WIDTH_SAME_T = VECTOR_WIDTH_SAME_T; exports.VectorEditorController = VectorEditorController; exports.VectorSnapTargets = VectorSnapTargets; exports.activeVectorEffects = activeVectorEffects; exports.addBuilderHit = addBuilderHit; exports.applyMatrix = applyMatrix; exports.applyMatrixLinear = applyMatrixLinear; exports.applyVectorBlendOptions = applyVectorBlendOptions; exports.applyVectorCornersAttribute = applyVectorCornersAttribute; exports.applyVectorEffects = applyVectorEffects; exports.applyVectorGradientPatch = applyVectorGradientPatch; exports.applyVectorGradientSpread = applyVectorGradientSpread; exports.arrowheadInset = arrowheadInset; exports.arrowheadPath = arrowheadPath; exports.arrowheadWeight = arrowheadWeight; exports.bendSegment = bendSegment; exports.bezierToSubpaths = bezierToSubpaths; exports.boundsFromPoints = boundsFromPoints; exports.boundsIntersect = boundsIntersect; exports.clearVectorLiveBooleanCache = clearVectorLiveBooleanCache; exports.cloneAnchor = cloneAnchor; exports.cloneSubpath = cloneSubpath; exports.cloneVectorDocument = cloneVectorDocument; exports.cloneVectorNode = cloneVectorNode; exports.collectPathNodes = collectPathNodes; exports.constrainToLeave = constrainToLeave; exports.convertAnchor = convertAnchor; exports.convertVectorGradient = convertVectorGradient; exports.cornerBisectorPoint = cornerBisectorPoint; exports.cornerRadiusChange = cornerRadiusChange; exports.cornerSetback = cornerSetback; exports.correspondMorphContours = correspondMorphContours; exports.createVectorCommands = createVectorCommands; exports.createVectorEffect = createVectorEffect; exports.createVectorMorph = createVectorMorph; exports.curvatureMoveAnchor = curvatureMoveAnchor; exports.cutSubpathAt = cutSubpathAt; exports.dashIntervals = dashIntervals; exports.decomposeRigid = decomposeRigid; exports.defaultVectorGradient = defaultVectorGradient; exports.effectPathLength = effectPathLength; exports.effectPathVertexCount = effectPathVertexCount; exports.effectiveStrokeAlign = effectiveStrokeAlign; exports.ellipseSubpath = ellipseSubpath; exports.eraseFromFill = eraseFromFill; exports.eraseFromOutline = eraseFromOutline; exports.eraserRegion = eraserRegion; exports.evaluateVectorPathKeyframes = evaluateVectorPathKeyframes; exports.expandVectorAppearance = expandVectorAppearance; exports.expandVectorAppearances = expandVectorAppearances; exports.expandVectorBlend = expandVectorBlend; exports.expandVectorCorners = expandVectorCorners; exports.expandVectorCornersOf = expandVectorCornersOf; exports.expandVectorGradientSpread = expandVectorGradientSpread; exports.expandVectorGradientStops = expandVectorGradientStops; exports.explicitVectorGradient = explicitVectorGradient; exports.findVectorNode = findVectorNode; exports.fitCornerRadius = fitCornerRadius; exports.foldVectorBooleanOperands = foldVectorBooleanOperands; exports.generateVectorBlendChildren = generateVectorBlendChildren; exports.generateWidthProfile = generateWidthProfile; exports.getAnchor = getAnchor; exports.getAnchorWorld = getAnchorWorld; exports.getEditablePathIds = getEditablePathIds; exports.getTransformHandlePositions = getTransformHandlePositions; exports.getVectorBlendControls = getVectorBlendControls; exports.getVectorCornerWidgets = getVectorCornerWidgets; exports.getVectorEffectsPanel = getVectorEffectsPanel; exports.getVectorEnteredGroups = getVectorEnteredGroups; exports.getVectorGradientAnnotator = getVectorGradientAnnotator; exports.getVectorGradientEditor = getVectorGradientEditor; exports.getVectorOverlayRenderData = getVectorOverlayRenderData; exports.getVectorSceneRenderData = getVectorSceneRenderData; exports.getVectorSelectionBoxGeometry = getVectorSelectionBoxGeometry; exports.getVectorSelectionFrame = getVectorSelectionFrame; exports.getVectorSelectionStyle = getVectorSelectionStyle; exports.getVectorWidthHandles = getVectorWidthHandles; exports.getVisibleAnchors = getVisibleAnchors; exports.getVisibleHandles = getVisibleHandles; exports.gradientMidpointRemap = gradientMidpointRemap; exports.hasExplicitGradientGeometry = hasExplicitGradientGeometry; exports.hasOwnVectorAppearance = hasOwnVectorAppearance; exports.hasPaint = hasPaint; exports.hasStrokeExtras = hasStrokeExtras; exports.hasVectorAppearance = hasVectorAppearance; exports.hasVectorCorners = hasVectorCorners; exports.hitTestVectorNode = hitTestVectorNode; exports.hitTestVectorNodes = hitTestVectorNodes; exports.hitTestVectorStroke = hitTestVectorStroke; exports.hitVectorGradientAnnotator = hitVectorGradientAnnotator; exports.inferAnchorType = inferAnchorType; exports.insertAnchor = insertAnchor; exports.insertVectorGradientStop = insertVectorGradientStop; exports.insertWidthPoint = insertWidthPoint; exports.interpolateMorphContours = interpolateMorphContours; exports.interpolateRigidMatrix = interpolateRigidMatrix; exports.interpolateVectorColor = interpolateVectorColor; exports.interpolateVectorPaint = interpolateVectorPaint; exports.interpolateVectorPaths = interpolateVectorPaths; exports.interpolateVectorStyle = interpolateVectorStyle; exports.interpolateWidthProfiles = interpolateWidthProfiles; exports.invertMatrix = invertMatrix; exports.isCentredVectorGradient = isCentredVectorGradient; exports.isClosedBezierShape = isClosedBezierShape; exports.isIdentityMatrix = isIdentityMatrix; exports.isUniformWidthProfile = isUniformWidthProfile; exports.isVectorBlendGroup = isVectorBlendGroup; exports.isVectorGradient = isVectorGradient; exports.isVectorLiveBoolean = isVectorLiveBoolean; exports.isVectorTextTarget = isVectorTextTarget; exports.joinSubpaths = joinSubpaths; exports.knifePath = knifePath; exports.lineSubpath = lineSubpath; exports.locateVectorNode = locateVectorNode; exports.matrixRotation = matrixRotation; exports.mergePaths = mergePaths; exports.mirrorWidthProfile = mirrorWidthProfile; exports.morphContourToSubpath = morphContourToSubpath; exports.morphPathsCross = morphPathsCross; exports.moveAnchorHandle = moveAnchorHandle; exports.moveVectorGradientHandle = moveVectorGradientHandle; exports.moveVectorGradientStop = moveVectorGradientStop; exports.multiplyMatrices = multiplyMatrices; exports.nearestSegment = nearestSegment; exports.nearestStrokePosition = nearestStrokePosition; exports.nextVectorCornerKind = nextVectorCornerKind; exports.nodeMatrix = nodeMatrix; exports.normalizeVectorEffectParams = normalizeVectorEffectParams; exports.offsetPaths = offsetPaths; exports.parseSvgTransform = parseSvgTransform; exports.parseVectorColor = parseVectorColor; exports.parseVectorSvg = parseVectorSvg; exports.pathsCenter = pathsCenter; exports.patternAttrs = patternAttrs; exports.periodicVectorGradientStops = periodicVectorGradientStops; exports.pinVectorGradient = pinVectorGradient; exports.planShapeBuilder = planShapeBuilder; exports.polygonSubpath = polygonSubpath; exports.polylineBounds = polylineBounds; exports.pressureWidthProfile = pressureWidthProfile; exports.puckerBloatPaths = puckerBloatPaths; exports.recognizeInkShape = recognizeInkShape; exports.rectSubpath = rectSubpath; exports.refineMorphContour = refineMorphContour; exports.refreshVectorBlends = refreshVectorBlends; exports.releaseVectorBlend = releaseVectorBlend; exports.removeAnchorKeepingShape = removeAnchorKeepingShape; exports.removeAnchors = removeAnchors; exports.removeAnchorsKeepingShape = removeAnchorsKeepingShape; exports.removeRedundantPoints = removeRedundantPoints; exports.removeVectorGradientStop = removeVectorGradientStop; exports.removeWidthPoints = removeWidthPoints; exports.repeatPaths = repeatPaths; exports.resampleGradientStops = resampleGradientStops; exports.resolveVectorBlendOptions = resolveVectorBlendOptions; exports.reverseMorphContour = reverseMorphContour; exports.reverseSubpath = reverseSubpath; exports.reverseVectorGradient = reverseVectorGradient; exports.reverseWidthProfile = reverseWidthProfile; exports.rotateMatrix = rotateMatrix; exports.roundCornerPaths = roundCornerPaths; exports.roundSubpaths = roundSubpaths; exports.samplePlainStops = samplePlainStops; exports.samplePolyline = samplePolyline; exports.sampleVectorGradient = sampleVectorGradient; exports.sampleVectorGradientAt = sampleVectorGradientAt; exports.sampleVectorPaintDefAt = sampleVectorPaintDefAt; exports.sanitizeVectorEffects = sanitizeVectorEffects; exports.sanitizeVectorGradient = sanitizeVectorGradient; exports.sanitizeWidthProfile = sanitizeWidthProfile; exports.scaleFor = scaleFor; exports.scaleMatrix = scaleMatrix; exports.setAnchorCorner = setAnchorCorner; exports.setSubpathCorners = setSubpathCorners; exports.setVectorGradientVector = setVectorGradientVector; exports.setWidthPoint = setWidthPoint; exports.simplifyPolyline = simplifyPolyline; exports.smoothSubpath = smoothSubpath; exports.sortedVectorGradientStops = sortedVectorGradientStops; exports.spanVectorGradientStops = spanVectorGradientStops; exports.splitIntoFaces = splitIntoFaces; exports.splitSubpathAt = splitSubpathAt; exports.starSubpath = starSubpath; exports.strokePositionPoint = strokePositionPoint; exports.strokeWidthProfileOf = strokeWidthProfileOf; exports.subpathEnclosesArea = subpathEnclosesArea; exports.subpathPointAt = subpathPointAt; exports.subpathSegment = subpathSegment; exports.subpathSegmentCount = subpathSegmentCount; exports.subpathToMorphContour = subpathToMorphContour; exports.subpathsToBezier = subpathsToBezier; exports.taperWaveFactor = taperWaveFactor; exports.toVectorKeyInput = toVectorKeyInput; exports.toVectorModifiers = toVectorModifiers; exports.toVectorPointerInput = toVectorPointerInput; exports.transformVectorEffects = transformVectorEffects; exports.transformVectorGradient = transformVectorGradient; exports.transformVectorNode = transformVectorNode; exports.transformVectorPaint = transformVectorPaint; exports.translateAnchor = translateAnchor; exports.translateMatrix = translateMatrix; exports.trimIntervals = trimIntervals; exports.trimPaths = trimPaths; exports.trimPathsIndividually = trimPathsIndividually; exports.turnMorphContour = turnMorphContour; exports.twistPaths = twistPaths; exports.unionBounds = unionBounds; exports.variableStrokeOutline = variableStrokeOutline; exports.variableStrokeRings = variableStrokeRings; exports.vectorAppearanceNodes = vectorAppearanceNodes; exports.vectorAppearanceOutline = vectorAppearanceOutline; exports.vectorAppearanceShapes = vectorAppearanceShapes; exports.vectorBlendColorDistance = vectorBlendColorDistance; exports.vectorBlendKeyTargets = vectorBlendKeyTargets; exports.vectorBlendKeys = vectorBlendKeys; exports.vectorBlendRail = vectorBlendRail; exports.vectorBlendRailAt = vectorBlendRailAt; exports.vectorBlendSpine = vectorBlendSpine; exports.vectorBlendStepCount = vectorBlendStepCount; exports.vectorBoxMatrix = vectorBoxMatrix; exports.vectorBuilderErasedEdges = vectorBuilderErasedEdges; exports.vectorCornerAt = vectorCornerAt; exports.vectorCorners = vectorCorners; exports.vectorCornersAttribute = vectorCornersAttribute; exports.vectorDiamondPath = vectorDiamondPath; exports.vectorDocumentToSvg = vectorDocumentToSvg; exports.vectorEnteredGroups = vectorEnteredGroups; exports.vectorFillMap = vectorFillMap; exports.vectorGradientArea = vectorGradientArea; exports.vectorGradientAspect = vectorGradientAspect; exports.vectorGradientBarPosition = vectorGradientBarPosition; exports.vectorGradientFocalParam = vectorGradientFocalParam; exports.vectorGradientHandles = vectorGradientHandles; exports.vectorGradientMatrix = vectorGradientMatrix; exports.vectorGradientMidpointFromPosition = vectorGradientMidpointFromPosition; exports.vectorGradientMidpointPosition = vectorGradientMidpointPosition; exports.vectorGradientNeedsExpansion = vectorGradientNeedsExpansion; exports.vectorGradientParam = vectorGradientParam; exports.vectorGradientPoints = vectorGradientPoints; exports.vectorGradientRamp = vectorGradientRamp; exports.vectorGradientSvgDefs = vectorGradientSvgDefs; exports.vectorGradientTAt = vectorGradientTAt; exports.vectorGradientTargetPath = vectorGradientTargetPath; exports.vectorGradientUnitsMatrix = vectorGradientUnitsMatrix; exports.vectorInkShapeName = vectorInkShapeName; exports.vectorLiveBooleanSteps = vectorLiveBooleanSteps; exports.vectorNodeBounds = vectorNodeBounds; exports.vectorNodeRotation = vectorNodeRotation; exports.vectorNodeToBezier = vectorNodeToBezier; exports.vectorOperandOperation = vectorOperandOperation; exports.vectorPaintToHex = vectorPaintToHex; exports.vectorPaintToSvg = vectorPaintToSvg; exports.vectorParentMatrix = vectorParentMatrix; exports.vectorPathData = vectorPathData; exports.vectorPathEntries = vectorPathEntries; exports.vectorPathLocalBounds = vectorPathLocalBounds; exports.vectorPathWorldBezier = vectorPathWorldBezier; exports.vectorPathfinderShape = vectorPathfinderShape; exports.vectorRepeaterCopies = vectorRepeaterCopies; exports.vectorResizeCursor = vectorResizeCursor; exports.vectorResultStyle = vectorResultStyle; exports.vectorShapeBuilderHit = vectorShapeBuilderHit; exports.vectorShapeBuilderMap = vectorShapeBuilderMap; exports.vectorStrokeGradientDefs = vectorStrokeGradientDefs; exports.vectorStrokeLineArea = vectorStrokeLineArea; exports.vectorStrokeOutline = vectorStrokeOutline; exports.vectorStrokeOutlinePath = vectorStrokeOutlinePath; exports.vectorStrokeParts = vectorStrokeParts; exports.vectorStrokeReach = vectorStrokeReach; exports.vectorStrokeSvgParts = vectorStrokeSvgParts; exports.vectorStrokeWidthGeometry = vectorStrokeWidthGeometry; exports.vectorToolLabel = vectorToolLabel; exports.vectorWorldMatrix = vectorWorldMatrix; exports.walkVectorNodes = walkVectorNodes; exports.widthProfileAt = widthProfileAt; exports.widthProfileFromAttribute = widthProfileFromAttribute; exports.widthProfileMaxScale = widthProfileMaxScale; exports.widthProfilePreset = widthProfilePreset; exports.widthProfilePresetId = widthProfilePresetId; exports.widthProfileToAttribute = widthProfileToAttribute; exports.wigglePaths = wigglePaths; exports.zigzagPaths = zigzagPaths;
11523
13140
  //# sourceMappingURL=index.cjs.map