@pixerate/editor 0.266.2 → 0.267.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/dist/agent/index.cjs +2 -2
  2. package/dist/agent/index.js +1 -1
  3. package/dist/animation-editor/controller.d.cts +8 -0
  4. package/dist/animation-editor/controller.d.ts +8 -0
  5. package/dist/animation-editor/effect-tools.d.cts +1 -1
  6. package/dist/animation-editor/effect-tools.d.ts +1 -1
  7. package/dist/animation-editor/index.cjs +172 -105
  8. package/dist/animation-editor/index.js +91 -24
  9. package/dist/brushes/index.cjs +59 -59
  10. package/dist/brushes/index.js +5 -5
  11. package/dist/brushes/profiles.d.cts +6 -1
  12. package/dist/brushes/profiles.d.ts +6 -1
  13. package/dist/{brushstrokeRender-FSUWXUZY.cjs → brushstrokeRender-L7JYRR2R.cjs} +16 -16
  14. package/dist/{brushstrokeRender-KD453OBX.js → brushstrokeRender-NLFPEBGI.js} +4 -4
  15. package/dist/canvas/brushTypes.d.cts +12 -0
  16. package/dist/canvas/brushTypes.d.ts +12 -0
  17. package/dist/canvas/brushstroke.d.cts +1 -1
  18. package/dist/canvas/brushstroke.d.ts +1 -1
  19. package/dist/canvas/drawController.d.cts +19 -4
  20. package/dist/canvas/drawController.d.ts +19 -4
  21. package/dist/canvas/index.cjs +144 -105
  22. package/dist/canvas/index.js +46 -7
  23. package/dist/{chunk-XORJ7UZO.cjs → chunk-3VHH6V67.cjs} +13 -12
  24. package/dist/{chunk-T2LDTEXK.js → chunk-4IDUYB45.js} +2217 -56
  25. package/dist/chunk-7UKFXXWL.js +802 -0
  26. package/dist/chunk-ADMRNWR7.js +340 -0
  27. package/dist/{chunk-CIJBSUM5.js → chunk-AGNUD5BI.js} +2 -2
  28. package/dist/{chunk-WLUVZKVD.cjs → chunk-DT4YQUGT.cjs} +32 -32
  29. package/dist/{chunk-DXYHQ2VN.js → chunk-EF7LZAXQ.js} +1 -1
  30. package/dist/chunk-EXB2EGBE.cjs +802 -0
  31. package/dist/{chunk-4ODRXQRW.js → chunk-EYSHTYLG.js} +1 -1
  32. package/dist/{chunk-R44Y3EFS.js → chunk-FGJHJIMN.js} +8 -336
  33. package/dist/{chunk-AQZQWXEU.cjs → chunk-G7NHE2VD.cjs} +35 -35
  34. package/dist/{chunk-XC6DPRYI.js → chunk-GSTSZPSR.js} +154 -949
  35. package/dist/{chunk-VRK322ZC.cjs → chunk-HIO6E7LC.cjs} +2 -2
  36. package/dist/{chunk-TOID45I2.cjs → chunk-J2TNIZ55.cjs} +1 -1
  37. package/dist/{chunk-YHDHFJUC.cjs → chunk-J5VQYK7T.cjs} +217 -1012
  38. package/dist/{chunk-3C33YTNN.js → chunk-JGWVZPT6.js} +2 -2
  39. package/dist/{chunk-ACFSFUQH.js → chunk-JL6YH5WD.js} +4 -0
  40. package/dist/{chunk-3SBLVGOM.cjs → chunk-LA5Y4ETA.cjs} +23 -23
  41. package/dist/{chunk-MVUQSK5J.cjs → chunk-LHPEG5AU.cjs} +107 -435
  42. package/dist/{chunk-DOZ6F2K2.js → chunk-NSETJI74.js} +3 -2
  43. package/dist/{chunk-XRXFVRGY.cjs → chunk-OKHKA43L.cjs} +2364 -203
  44. package/dist/{chunk-LK4SXL3V.js → chunk-OSEXT7JL.js} +1 -1
  45. package/dist/{chunk-PE6YJMQT.cjs → chunk-PFHJS4HE.cjs} +20 -19
  46. package/dist/{chunk-7KNW3WXZ.cjs → chunk-QEVLRLUN.cjs} +63 -0
  47. package/dist/{chunk-OTN6VB5S.cjs → chunk-RDFHWAHL.cjs} +5 -1
  48. package/dist/{chunk-6JL2D5SG.js → chunk-VE6UKYQ2.js} +3 -2
  49. package/dist/{chunk-RFJZGTHA.cjs → chunk-XVOP3CGQ.cjs} +9 -9
  50. package/dist/{chunk-EZOJBP4D.js → chunk-YIC6MAJ2.js} +1 -1
  51. package/dist/chunk-YZWM6ZIP.cjs +340 -0
  52. package/dist/{chunk-472INMWH.js → chunk-ZA7L5YJB.js} +63 -0
  53. package/dist/commands/builtins.d.cts +12 -1
  54. package/dist/commands/builtins.d.ts +12 -1
  55. package/dist/commands/index.cjs +2 -2
  56. package/dist/commands/index.js +1 -1
  57. package/dist/image-editor/controller.d.cts +1 -1
  58. package/dist/image-editor/controller.d.ts +1 -1
  59. package/dist/image-editor/index.cjs +837 -833
  60. package/dist/image-editor/index.js +105 -101
  61. package/dist/paint/index.cjs +5 -3
  62. package/dist/paint/index.js +46 -44
  63. package/dist/paint-editor/controller.d.cts +5 -3
  64. package/dist/paint-editor/controller.d.ts +5 -3
  65. package/dist/paint-editor/index.cjs +832 -2295
  66. package/dist/paint-editor/index.js +285 -1748
  67. package/dist/paint-editor/path-samples.d.cts +2 -0
  68. package/dist/paint-editor/path-samples.d.ts +2 -0
  69. package/dist/paint-editor/presence.d.cts +20 -1
  70. package/dist/paint-editor/presence.d.ts +20 -1
  71. package/dist/stickers/helpers.d.cts +2 -0
  72. package/dist/stickers/helpers.d.ts +2 -0
  73. package/dist/stickers/index.cjs +41 -35
  74. package/dist/stickers/index.js +22 -16
  75. package/dist/stickers/types.d.cts +4 -2
  76. package/dist/stickers/types.d.ts +4 -2
  77. package/dist/vector/backdrop.d.cts +13 -0
  78. package/dist/vector/backdrop.d.ts +13 -0
  79. package/dist/vector/brushstroke.d.cts +54 -7
  80. package/dist/vector/brushstroke.d.ts +54 -7
  81. package/dist/vector/brushstrokeMedia.d.cts +96 -0
  82. package/dist/vector/brushstrokeMedia.d.ts +96 -0
  83. package/dist/vector/controller.d.cts +17 -3
  84. package/dist/vector/controller.d.ts +17 -3
  85. package/dist/vector/index.cjs +1846 -1386
  86. package/dist/vector/index.d.cts +2 -0
  87. package/dist/vector/index.d.ts +2 -0
  88. package/dist/vector/index.js +517 -57
  89. package/dist/vector/render.d.cts +6 -0
  90. package/dist/vector/render.d.ts +6 -0
  91. package/dist/vector/types.d.cts +14 -0
  92. package/dist/vector/types.d.ts +14 -0
  93. package/package.json +1 -1
  94. package/skills/pixerate-animation-editor/SKILL.md +1 -0
  95. package/skills/pixerate-brushes/SKILL.md +2 -2
  96. package/skills/pixerate-brushes-design/SKILL.md +7 -4
  97. package/skills/pixerate-brushes-design/evals/port-to-vector.json +8 -8
  98. package/skills/pixerate-canvas/SKILL.md +11 -1
  99. package/skills/pixerate-image-editor/SKILL.md +19 -64
  100. package/skills/pixerate-image-editor/references/color-management.md +2 -1
  101. package/skills/pixerate-image-editor/references/free-transform.md +12 -0
  102. package/skills/pixerate-image-editor/references/layer-effects.md +10 -0
  103. package/skills/pixerate-image-editor/references/painting.md +17 -0
  104. package/skills/pixerate-image-editor/references/photoshop-files.md +1 -1
  105. package/skills/pixerate-image-editor/references/shapes-and-type.md +14 -0
  106. package/skills/pixerate-image-editor/references/smart-objects.md +10 -0
  107. package/skills/pixerate-paint-editor/SKILL.md +8 -4
  108. package/skills/pixerate-paint-editor/references/wet-media.md +9 -0
  109. package/skills/pixerate-vector/SKILL.md +16 -6
  110. package/skills/pixerate-vector/references/best-practices.md +1 -0
  111. package/skills/pixerate-vector/references/commands.md +19 -0
@@ -35,7 +35,10 @@ import {
35
35
  traceField,
36
36
  traceVectorImage,
37
37
  vectorTraceContours
38
- } from "../chunk-EZOJBP4D.js";
38
+ } from "../chunk-YIC6MAJ2.js";
39
+ import {
40
+ rasterizeCoverage
41
+ } from "../chunk-ADMRNWR7.js";
39
42
  import {
40
43
  applyHomography,
41
44
  homographyFromPoints,
@@ -63,7 +66,7 @@ import {
63
66
  reverseMorphContour,
64
67
  subpathToMorphContour,
65
68
  turnMorphContour
66
- } from "../chunk-LK4SXL3V.js";
69
+ } from "../chunk-OSEXT7JL.js";
67
70
  import {
68
71
  DEFAULT_VECTOR_FONT,
69
72
  IDENTITY_MATRIX,
@@ -119,6 +122,7 @@ import {
119
122
  VECTOR_TRANSFORM_HANDLES,
120
123
  VECTOR_VERTICAL_RESIZING,
121
124
  VECTOR_VERTICAL_RESIZING_LABELS,
125
+ VECTOR_WET_STEPS,
122
126
  VECTOR_WIDTH_PRESETS,
123
127
  VECTOR_WIDTH_PRESET_IDS,
124
128
  VECTOR_WIDTH_PROFILE_MAX_POINTS,
@@ -128,6 +132,7 @@ import {
128
132
  VectorTextError,
129
133
  WARP_STYLES,
130
134
  WARP_STYLE_LABELS,
135
+ WetBrushstrokeJob,
131
136
  activeVectorEffects,
132
137
  activeVectorFilters,
133
138
  applyMatrix,
@@ -220,6 +225,7 @@ import {
220
225
  isUniformWidthProfile,
221
226
  isValidWarpMesh,
222
227
  isVectorBlendMode,
228
+ isVectorBrushstrokeEngine,
223
229
  isVectorBrushstrokeLayer,
224
230
  isVectorConnector,
225
231
  isVectorFilterType,
@@ -270,6 +276,7 @@ import {
270
276
  normalizeVectorPaintStacks,
271
277
  normalizeVectorTextRuns,
272
278
  offsetPaths,
279
+ onVectorBrushstrokesSettled,
273
280
  parseSvgTransform,
274
281
  parseVectorColor,
275
282
  parseVectorSvg,
@@ -287,6 +294,7 @@ import {
287
294
  puckerBloatPaths,
288
295
  pullWarpMesh,
289
296
  randomVectorBrushstrokeSeed,
297
+ readVectorBrushstrokePaint,
290
298
  rectSubpath,
291
299
  removeAnchors,
292
300
  removeVectorFreeformPoint,
@@ -294,6 +302,8 @@ import {
294
302
  removeWarpMeshSplitU,
295
303
  removeWarpMeshSplitV,
296
304
  removeWidthPoints,
305
+ renderBackdropBrushstroke,
306
+ renderWetBrushstroke,
297
307
  repeatPaths,
298
308
  replaceVectorText,
299
309
  rerouteVectorConnectors,
@@ -335,9 +345,11 @@ import {
335
345
  scaleVectorStrokeWidths,
336
346
  setAnchorCorner,
337
347
  setSubpathCorners,
348
+ setVectorBackdropSource,
338
349
  setVectorGradientVector,
339
350
  setVectorTextShaper,
340
351
  setWidthPoint,
352
+ settleVectorBrushstrokes,
341
353
  shadeVectorColor,
342
354
  simplifyPolyline,
343
355
  skewMatrix,
@@ -374,12 +386,14 @@ import {
374
386
  trimPathsIndividually,
375
387
  twistPaths,
376
388
  unionBounds,
389
+ usesVectorBackdrop,
377
390
  variableStrokeOutline,
378
391
  variableStrokeRings,
379
392
  vectorAppearanceNodes,
380
393
  vectorAppearanceOutline,
381
394
  vectorAppearanceShapes,
382
395
  vectorArtboards,
396
+ vectorBackdropRect,
383
397
  vectorBevelFaces,
384
398
  vectorBlendModeCss,
385
399
  vectorBlendModeFromCss,
@@ -392,7 +406,9 @@ import {
392
406
  vectorBrushstrokeCharacterError,
393
407
  vectorBrushstrokeDefaultColour,
394
408
  vectorBrushstrokeImages,
409
+ vectorBrushstrokePaintError,
395
410
  vectorBrushstrokeReach,
411
+ vectorBrushstrokesSettling,
396
412
  vectorChildAppearanceShapes,
397
413
  vectorConnectionSites,
398
414
  vectorConnectorTargetAt,
@@ -455,6 +471,7 @@ import {
455
471
  vectorMaskedShapes,
456
472
  vectorMasterArt,
457
473
  vectorMasterPages,
474
+ vectorMediaEngine,
458
475
  vectorMeshColorAt,
459
476
  vectorMeshFromOutline,
460
477
  vectorMeshLines,
@@ -539,11 +556,11 @@ import {
539
556
  withVectorStrokes,
540
557
  withVectorTargetPaint,
541
558
  zigzagPaths
542
- } from "../chunk-T2LDTEXK.js";
559
+ } from "../chunk-4IDUYB45.js";
543
560
  import {
544
561
  snapshotBrushLibrary
545
- } from "../chunk-CIJBSUM5.js";
546
- import "../chunk-ACFSFUQH.js";
562
+ } from "../chunk-AGNUD5BI.js";
563
+ import "../chunk-JL6YH5WD.js";
547
564
  import {
548
565
  createProvenance
549
566
  } from "../chunk-M6DNZNVY.js";
@@ -580,6 +597,7 @@ import {
580
597
  resolveEffectValues
581
598
  } from "../chunk-MV7CYFMJ.js";
582
599
  import "../chunk-V2ZCJRDM.js";
600
+ import "../chunk-GSTSZPSR.js";
583
601
  import {
584
602
  GRAIN_PATTERNS
585
603
  } from "../chunk-TXFWZ5ZJ.js";
@@ -1240,6 +1258,245 @@ function releaseVectorBlend(group, newId) {
1240
1258
  return [...keys, group.transform ? transformVectorNode(path, m) : path];
1241
1259
  }
1242
1260
 
1261
+ // src/vector/backdrop.ts
1262
+ var IDENTITY = [1, 0, 0, 1, 0, 0];
1263
+ var mul = (a, b) => [a[0] * b[0] + a[2] * b[1], a[1] * b[0] + a[3] * b[1], a[0] * b[2] + a[2] * b[3], a[1] * b[2] + a[3] * b[3], a[0] * b[4] + a[2] * b[5] + a[4], a[1] * b[4] + a[3] * b[5] + a[5]];
1264
+ var inv = (m) => {
1265
+ const det = m[0] * m[3] - m[1] * m[2];
1266
+ if (!det || !Number.isFinite(det)) return null;
1267
+ return [m[3] / det, -m[1] / det, -m[2] / det, m[0] / det, (m[2] * m[5] - m[3] * m[4]) / det, (m[1] * m[4] - m[0] * m[5]) / det];
1268
+ };
1269
+ function parseVectorTransform(value) {
1270
+ if (!value) return IDENTITY;
1271
+ let m = IDENTITY;
1272
+ for (const [, fn, args] of value.matchAll(/(matrix|translate|scale|rotate)\s*\(([^)]*)\)/g)) {
1273
+ const n2 = args.split(/[\s,]+/).filter(Boolean).map(Number);
1274
+ if (n2.some((v) => !Number.isFinite(v))) continue;
1275
+ let t = IDENTITY;
1276
+ if (fn === "matrix" && n2.length === 6) t = n2;
1277
+ else if (fn === "translate") t = [1, 0, 0, 1, n2[0] ?? 0, n2[1] ?? 0];
1278
+ else if (fn === "scale") t = [n2[0] ?? 1, 0, 0, n2[1] ?? n2[0] ?? 1, 0, 0];
1279
+ else if (fn === "rotate") {
1280
+ const a = (n2[0] ?? 0) * Math.PI / 180;
1281
+ const [cx, cy] = [n2[1] ?? 0, n2[2] ?? 0];
1282
+ t = mul(mul([1, 0, 0, 1, cx, cy], [Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0]), [1, 0, 0, 1, -cx, -cy]);
1283
+ }
1284
+ m = mul(m, t);
1285
+ }
1286
+ return m;
1287
+ }
1288
+ function flatten(d, m, tolerance = 0.25) {
1289
+ const out = [];
1290
+ let cur = null;
1291
+ let x = 0, y = 0, sx = 0, sy = 0;
1292
+ const push = (px, py) => cur.pts.push(m[0] * px + m[2] * py + m[4], m[1] * px + m[3] * py + m[5]);
1293
+ const scale = Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])) || 1;
1294
+ const steps = (len2) => Math.max(2, Math.min(64, Math.ceil(Math.sqrt(len2 * scale / tolerance))));
1295
+ for (const c of parseSvgPath(d)) {
1296
+ if (c.type === "M") {
1297
+ cur = { pts: [], closed: false };
1298
+ out.push(cur);
1299
+ x = sx = c.x;
1300
+ y = sy = c.y;
1301
+ push(x, y);
1302
+ continue;
1303
+ }
1304
+ if (!cur) continue;
1305
+ if (c.type === "Z") {
1306
+ cur.closed = true;
1307
+ x = sx;
1308
+ y = sy;
1309
+ cur = { pts: [], closed: false };
1310
+ out.push(cur);
1311
+ push(x, y);
1312
+ continue;
1313
+ }
1314
+ if (c.type === "C") {
1315
+ const n2 = steps(Math.hypot(c.x1 - x, c.y1 - y) + Math.hypot(c.x2 - c.x1, c.y2 - c.y1) + Math.hypot(c.x - c.x2, c.y - c.y2));
1316
+ for (let i = 1; i <= n2; i++) {
1317
+ const t = i / n2, u = 1 - t;
1318
+ push(u * u * u * x + 3 * u * u * t * c.x1 + 3 * u * t * t * c.x2 + t * t * t * c.x, u * u * u * y + 3 * u * u * t * c.y1 + 3 * u * t * t * c.y2 + t * t * t * c.y);
1319
+ }
1320
+ } else if (c.type === "Q") {
1321
+ const n2 = steps(Math.hypot(c.x1 - x, c.y1 - y) + Math.hypot(c.x - c.x1, c.y - c.y1));
1322
+ for (let i = 1; i <= n2; i++) {
1323
+ const t = i / n2, u = 1 - t;
1324
+ push(u * u * x + 2 * u * t * c.x1 + t * t * c.x, u * u * y + 2 * u * t * c.y1 + t * t * c.y);
1325
+ }
1326
+ } else push(c.x, c.y);
1327
+ x = c.x;
1328
+ y = c.y;
1329
+ }
1330
+ return out.filter((p) => p.pts.length >= 4);
1331
+ }
1332
+ function strokeCoverage(lines, width, rect) {
1333
+ const r = width / 2;
1334
+ if (!(r > 0)) return null;
1335
+ const w = rect.width, h = rect.height;
1336
+ let out = null;
1337
+ for (const line2 of lines) {
1338
+ const p = line2.pts;
1339
+ const n2 = p.length / 2;
1340
+ const segs = line2.closed ? n2 : n2 - 1;
1341
+ for (let s = 0; s < segs; s++) {
1342
+ const ax = p[2 * s] - rect.x, ay = p[2 * s + 1] - rect.y;
1343
+ const k = (s + 1) % n2;
1344
+ const bx = p[2 * k] - rect.x, by = p[2 * k + 1] - rect.y;
1345
+ const x0 = Math.max(0, Math.floor(Math.min(ax, bx) - r - 1)), x1 = Math.min(w - 1, Math.ceil(Math.max(ax, bx) + r + 1));
1346
+ const y0 = Math.max(0, Math.floor(Math.min(ay, by) - r - 1)), y1 = Math.min(h - 1, Math.ceil(Math.max(ay, by) + r + 1));
1347
+ if (x0 > x1 || y0 > y1) continue;
1348
+ const dx = bx - ax, dy = by - ay;
1349
+ const l2 = dx * dx + dy * dy;
1350
+ for (let yy = y0; yy <= y1; yy++) {
1351
+ for (let xx = x0; xx <= x1; xx++) {
1352
+ const px = xx + 0.5 - ax, py = yy + 0.5 - ay;
1353
+ const t = l2 > 0 ? Math.min(1, Math.max(0, (px * dx + py * dy) / l2)) : 0;
1354
+ const dist3 = Math.hypot(px - t * dx, py - t * dy);
1355
+ const c = Math.min(1, Math.max(0, r + 0.5 - dist3));
1356
+ if (c <= 0) continue;
1357
+ out ??= new Float32Array(w * h);
1358
+ const i = yy * w + xx;
1359
+ if (c > out[i]) out[i] = c;
1360
+ }
1361
+ }
1362
+ }
1363
+ }
1364
+ return out;
1365
+ }
1366
+ var pngs = /* @__PURE__ */ new Map();
1367
+ function decoded(href) {
1368
+ if (pngs.has(href)) return pngs.get(href);
1369
+ let img = null;
1370
+ try {
1371
+ const parsed = parseDataUrl(href);
1372
+ const png = parsed ? decodePng(parsed.bytes) : null;
1373
+ if (png && png.channels === 4) img = { width: png.width, height: png.height, data: png.data };
1374
+ } catch {
1375
+ img = null;
1376
+ }
1377
+ if (pngs.size >= 64) pngs.delete(pngs.keys().next().value);
1378
+ pngs.set(href, img);
1379
+ return img;
1380
+ }
1381
+ var Canvas = class {
1382
+ constructor(rect) {
1383
+ this.rect = rect;
1384
+ this.px = new Float32Array(rect.width * rect.height * 4);
1385
+ }
1386
+ rect;
1387
+ px;
1388
+ over(i, c, a) {
1389
+ const k = Math.min(1, Math.max(0, c[3] * a));
1390
+ if (k <= 0) return;
1391
+ const o = i * 4, px = this.px, rest = 1 - k;
1392
+ px[o] = c[0] * k + px[o] * rest;
1393
+ px[o + 1] = c[1] * k + px[o + 1] * rest;
1394
+ px[o + 2] = c[2] * k + px[o + 2] * rest;
1395
+ px[o + 3] = k + px[o + 3] * rest;
1396
+ }
1397
+ bytes() {
1398
+ const out = new Uint8ClampedArray(this.px.length);
1399
+ for (let i = 0; i < this.px.length; i += 4) {
1400
+ const a = this.px[i + 3];
1401
+ if (a <= 0) continue;
1402
+ out[i] = Math.round(this.px[i] / a * 255);
1403
+ out[i + 1] = Math.round(this.px[i + 1] / a * 255);
1404
+ out[i + 2] = Math.round(this.px[i + 2] / a * 255);
1405
+ out[i + 3] = Math.round(a * 255);
1406
+ }
1407
+ return out;
1408
+ }
1409
+ };
1410
+ function paintCoverage(canvas, coverage, paint, defs, toLocal, alpha) {
1411
+ if (!coverage || paint === "none") return;
1412
+ const url = /^url\(#(.+)\)$/.exec(paint);
1413
+ const solid = url ? null : parseVectorColor(paint);
1414
+ if (!url && !solid) return;
1415
+ const { x: rx, y: ry, width: w } = canvas.rect;
1416
+ for (let i = 0; i < coverage.length; i++) {
1417
+ const c = coverage[i];
1418
+ if (c <= 0) continue;
1419
+ let col = solid;
1420
+ if (url) {
1421
+ const px = rx + i % w + 0.5, py = ry + Math.floor(i / w) + 0.5;
1422
+ col = sampleVectorPaintDefAt(defs, url[1], { x: toLocal[0] * px + toLocal[2] * py + toLocal[4], y: toLocal[1] * px + toLocal[3] * py + toLocal[5] });
1423
+ }
1424
+ if (col) canvas.over(i, col, c * alpha);
1425
+ }
1426
+ }
1427
+ function drawImage(canvas, im, toLocal, alpha) {
1428
+ const img = decoded(im.href);
1429
+ if (!img || !(im.width > 0 && im.height > 0)) return;
1430
+ const { x: rx, y: ry, width: w, height: h } = canvas.rect;
1431
+ const c = [0, 0, 0, 0];
1432
+ for (let yy = 0; yy < h; yy++) {
1433
+ for (let xx = 0; xx < w; xx++) {
1434
+ const px = rx + xx + 0.5, py = ry + yy + 0.5;
1435
+ const lx = toLocal[0] * px + toLocal[2] * py + toLocal[4], ly = toLocal[1] * px + toLocal[3] * py + toLocal[5];
1436
+ const u = (lx - im.x) / im.width * img.width - 0.5, v = (ly - im.y) / im.height * img.height - 0.5;
1437
+ if (u < -1 || v < -1 || u > img.width || v > img.height) continue;
1438
+ const u0 = Math.floor(u), v0 = Math.floor(v), fu = u - u0, fv = v - v0;
1439
+ c[0] = c[1] = c[2] = c[3] = 0;
1440
+ for (let k = 0; k < 4; k++) {
1441
+ const sx = u0 + (k & 1), sy = v0 + (k >> 1);
1442
+ if (sx < 0 || sy < 0 || sx >= img.width || sy >= img.height) continue;
1443
+ const wgt = (k & 1 ? fu : 1 - fu) * (k >> 1 ? fv : 1 - fv);
1444
+ const o = (sy * img.width + sx) * 4, a = img.data[o + 3] / 255 * wgt;
1445
+ c[0] += img.data[o] / 255 * a;
1446
+ c[1] += img.data[o + 1] / 255 * a;
1447
+ c[2] += img.data[o + 2] / 255 * a;
1448
+ c[3] += a;
1449
+ }
1450
+ if (c[3] <= 0) continue;
1451
+ canvas.over(yy * w + xx, [c[0] / c[3], c[1] / c[3], c[2] / c[3], c[3]], alpha);
1452
+ }
1453
+ }
1454
+ }
1455
+ function drawPath(canvas, p, defs, m, alpha) {
1456
+ const toLocal = inv(m);
1457
+ if (!toLocal) return;
1458
+ const scale = Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2]));
1459
+ const rule = p.fillRule === "evenodd" ? "evenodd" : "nonzero";
1460
+ const layers = p.layers ?? [
1461
+ { kind: "fill", index: 0, fill: p.fill, stroke: "none", strokeWidth: 0, opacity: 1 },
1462
+ { kind: "stroke", index: 0, fill: "none", stroke: p.stroke, strokeWidth: p.strokeWidth, opacity: 1, strokeParts: p.strokeParts }
1463
+ ];
1464
+ let lines = null;
1465
+ const shape = () => lines ??= flatten(p.d, m);
1466
+ const fill = (d, rl) => {
1467
+ const flat = (d ? flatten(d, m) : shape()).map((l) => Float64Array.from(l.pts));
1468
+ return rasterizeCoverage(flat, canvas.rect, rl);
1469
+ };
1470
+ for (const l of layers) {
1471
+ const a = alpha * l.opacity;
1472
+ if (a <= 0) continue;
1473
+ if (l.kind === "brushstroke") {
1474
+ for (const im of l.images ?? []) drawImage(canvas, im, toLocal, a);
1475
+ } else if (l.kind === "fill") {
1476
+ if (l.fill !== "none") paintCoverage(canvas, fill(null, rule), l.fill, defs, toLocal, a);
1477
+ } else if (l.stroke !== "none" && l.strokeWidth > 0) {
1478
+ const parts = l.strokeParts;
1479
+ const cov = parts?.outline ? fill(parts.outline, "nonzero") : strokeCoverage(parts ? flatten(parts.d, m) : shape(), (parts ? parts.align === "center" ? parts.width : parts.width / 2 : l.strokeWidth) * scale, canvas.rect);
1480
+ paintCoverage(canvas, cov, l.stroke, defs, toLocal, a);
1481
+ }
1482
+ }
1483
+ }
1484
+ function drawNodes(canvas, nodes, defs, m, alpha) {
1485
+ for (const n2 of nodes) {
1486
+ if (n2.kind === "text") continue;
1487
+ const mm = mul(m, parseVectorTransform(n2.transform));
1488
+ const a = alpha * (n2.opacity ?? 1);
1489
+ if (a <= 0) continue;
1490
+ if (n2.kind === "group") drawNodes(canvas, n2.children, defs, mm, a);
1491
+ else drawPath(canvas, n2, defs, mm, a);
1492
+ }
1493
+ }
1494
+ function rasterizeVectorBackdrop(nodes, defs, toPixels, rect) {
1495
+ const canvas = new Canvas(rect);
1496
+ if (rect.width > 0 && rect.height > 0) drawNodes(canvas, nodes, defs, toPixels, 1);
1497
+ return canvas.bytes();
1498
+ }
1499
+
1243
1500
  // src/vector/perspective.ts
1244
1501
  function vectorPerspectivePlanes(p) {
1245
1502
  return p.vanishingPoints.length <= 1 ? ["floor", "side", "front"] : ["floor", "left", "right"];
@@ -1295,11 +1552,11 @@ function vectorPerspectiveFlatToPlane(p, plane) {
1295
1552
  }
1296
1553
  function vectorPerspectivePlaneToFlat(p, plane) {
1297
1554
  const h = vectorPerspectivePlaneMap(p, plane);
1298
- const inv = h ? invertHomography(h) : null;
1299
- if (!inv) return null;
1555
+ const inv2 = h ? invertHomography(h) : null;
1556
+ if (!inv2) return null;
1300
1557
  const o = p.origin;
1301
1558
  return (q) => {
1302
- const f = applyHomography(inv, q);
1559
+ const f = applyHomography(inv2, q);
1303
1560
  if (!f) return null;
1304
1561
  const w = h[6] * f.x + h[7] * f.y + h[8];
1305
1562
  if (!(w > 1e-6)) return null;
@@ -1890,8 +2147,8 @@ var Model = class {
1890
2147
  const changed = /* @__PURE__ */ new Map();
1891
2148
  for (const [nodeId, slots] of byNode) {
1892
2149
  const loc = this.located.get(nodeId);
1893
- const inv = invertMatrix(loc.world);
1894
- const toLocal = (p) => applyMatrix(inv, p);
2150
+ const inv2 = invertMatrix(loc.world);
2151
+ const toLocal = (p) => applyMatrix(inv2, p);
1895
2152
  let subpaths = loc.node.subpaths;
1896
2153
  const circle = slots.find((s) => s.kind === "circle");
1897
2154
  if (circle) {
@@ -2678,7 +2935,7 @@ function renderLayers(node, defs, idPrefix) {
2678
2935
  const out = [];
2679
2936
  for (const l of vectorPaintLayerNodes(node)) {
2680
2937
  if (l.kind === "stroke" && isVectorBrushstrokeLayer(l.layer) && l.layer.visible !== false && (l.layer.opacity ?? 1) > 0) {
2681
- const images = vectorBrushstrokeImages(node, l.layer);
2938
+ const images = vectorBrushstrokeImages(node, l.layer, scene ?? {});
2682
2939
  const blend2 = vectorBlendModeCss(l.layer.blendMode);
2683
2940
  if (images.length) out.push({ kind: "brushstroke", index: l.index, fill: "none", stroke: "none", strokeWidth: 0, opacity: l.layer.opacity ?? 1, ...blend2 ? { blendMode: blend2 } : {}, images });
2684
2941
  continue;
@@ -2852,6 +3109,68 @@ function resolvedLensNodes(doc) {
2852
3109
  return nodes;
2853
3110
  }
2854
3111
  function getVectorSceneRenderData(state, options = {}) {
3112
+ const outer = scene;
3113
+ scene = { document: state.document, settle: options.brushstrokes === "settled" ? "now" : "async" };
3114
+ try {
3115
+ return sceneRenderData(state, options);
3116
+ } finally {
3117
+ scene = outer;
3118
+ }
3119
+ }
3120
+ var scene = null;
3121
+ function nodeChain(nodes, id) {
3122
+ for (let i = 0; i < nodes.length; i++) {
3123
+ const n2 = nodes[i];
3124
+ if (n2.id === id) return [{ list: nodes, at: i }];
3125
+ if (n2.type === "group") {
3126
+ const inner = nodeChain(n2.children, id);
3127
+ if (inner) return [{ list: nodes, at: i }, ...inner];
3128
+ }
3129
+ }
3130
+ return null;
3131
+ }
3132
+ function nodesBelow(chain, last) {
3133
+ const [level, ...rest] = chain;
3134
+ const before = level.list.slice(0, level.at);
3135
+ if (!rest.length) return [...before, last];
3136
+ const group = level.list[level.at];
3137
+ return [...before, { ...group, children: nodesBelow(rest, last) }];
3138
+ }
3139
+ setVectorBackdropSource((document, node, index, settle) => {
3140
+ const nodes = document ? resolvedLensNodes(document) : [];
3141
+ const chain = document ? nodeChain(nodes, node.id) : null;
3142
+ const deps = [];
3143
+ if (chain) {
3144
+ for (let k = 0; k < chain.length; k++) {
3145
+ const { list, at } = chain[k];
3146
+ deps.push(...list.slice(0, at));
3147
+ const n2 = list[at];
3148
+ if (k < chain.length - 1) deps.push(JSON.stringify([n2.transform ?? null, n2.opacity ?? 1, n2.visible ?? true]));
3149
+ }
3150
+ if (document?.symbols) deps.push(document.symbols);
3151
+ }
3152
+ deps.push(...vectorFills(node), ...vectorStrokes(node).slice(0, index));
3153
+ return {
3154
+ deps,
3155
+ backdrop: (rect, scale) => {
3156
+ const lower = withVectorStrokes(node, vectorStrokes(node).slice(0, index));
3157
+ const below = chain ? nodesBelow(chain, lower) : [lower];
3158
+ const world = (chain ? vectorWorldMatrix(nodes, node.id) : null) ?? nodeMatrix(node);
3159
+ const defs = [];
3160
+ const outer = scene;
3161
+ scene = { document: document ?? outer?.document ?? { width: 0, height: 0, nodes: [] }, settle };
3162
+ let items;
3163
+ try {
3164
+ items = below.map((n2) => renderNode(n2, defs, [], "ve-backdrop-")).filter((n2) => !!n2);
3165
+ } finally {
3166
+ scene = outer;
3167
+ }
3168
+ const toPixels = multiplyMatrices(invertMatrix(world), [scale, 0, 0, scale, -rect.x, -rect.y]);
3169
+ return rasterizeVectorBackdrop(items, defs, toPixels, { x: 0, y: 0, width: rect.width, height: rect.height });
3170
+ }
3171
+ };
3172
+ });
3173
+ function sceneRenderData(state, options) {
2855
3174
  const defs = [];
2856
3175
  const filters = [];
2857
3176
  const prefix = options.idPrefix ?? "ve-";
@@ -2974,13 +3293,13 @@ function symmetryOverlay(state) {
2974
3293
  if (!isVectorSymmetryGroup(n2)) continue;
2975
3294
  const world = vectorWorldMatrix(state.document.nodes, id);
2976
3295
  if (!world) continue;
2977
- const inv = invertMatrix(world);
3296
+ const inv2 = invertMatrix(world);
2978
3297
  const corners2 = [
2979
3298
  { x: 0, y: 0 },
2980
3299
  { x: state.document.width, y: 0 },
2981
3300
  { x: 0, y: state.document.height },
2982
3301
  { x: state.document.width, y: state.document.height }
2983
- ].map((p) => applyMatrix(inv, p));
3302
+ ].map((p) => applyMatrix(inv2, p));
2984
3303
  const { transform: _t, ...own } = n2;
2985
3304
  const b = unionBoxes([vectorNodeBounds(own), boundsFromCorners(corners2)]);
2986
3305
  if (!b) continue;
@@ -3793,7 +4112,7 @@ var BLEND = {
3793
4112
  function matrixOf(t) {
3794
4113
  if (!t) return [1, 0, 0, 1, 0, 0];
3795
4114
  let m = [1, 0, 0, 1, 0, 0];
3796
- const mul = (a, b) => [a[0] * b[0] + a[2] * b[1], a[1] * b[0] + a[3] * b[1], a[0] * b[2] + a[2] * b[3], a[1] * b[2] + a[3] * b[3], a[0] * b[4] + a[2] * b[5] + a[4], a[1] * b[4] + a[3] * b[5] + a[5]];
4115
+ const mul2 = (a, b) => [a[0] * b[0] + a[2] * b[1], a[1] * b[0] + a[3] * b[1], a[0] * b[2] + a[2] * b[3], a[1] * b[2] + a[3] * b[3], a[0] * b[4] + a[2] * b[5] + a[4], a[1] * b[4] + a[3] * b[5] + a[5]];
3797
4116
  for (const [, name, args] of t.matchAll(/(\w+)\s*\(([^)]*)\)/g)) {
3798
4117
  const v = args.split(/[\s,]+/).filter(Boolean).map(Number);
3799
4118
  let k = [1, 0, 0, 1, 0, 0];
@@ -3803,9 +4122,9 @@ function matrixOf(t) {
3803
4122
  else if (name === "rotate") {
3804
4123
  const r = (v[0] ?? 0) * Math.PI / 180;
3805
4124
  const [cx, cy] = [v[1] ?? 0, v[2] ?? 0];
3806
- k = mul(mul([1, 0, 0, 1, cx, cy], [Math.cos(r), Math.sin(r), -Math.sin(r), Math.cos(r), 0, 0]), [1, 0, 0, 1, -cx, -cy]);
4125
+ k = mul2(mul2([1, 0, 0, 1, cx, cy], [Math.cos(r), Math.sin(r), -Math.sin(r), Math.cos(r), 0, 0]), [1, 0, 0, 1, -cx, -cy]);
3807
4126
  }
3808
- m = mul(m, k);
4127
+ m = mul2(m, k);
3809
4128
  }
3810
4129
  return m;
3811
4130
  }
@@ -4150,8 +4469,8 @@ function vectorDocumentToPdf(doc, options = {}) {
4150
4469
  const pageIds = [];
4151
4470
  docs.forEach((d, k) => {
4152
4471
  w.pageIndex = k;
4153
- const scene = getVectorSceneRenderData({ document: d, viewport: { x: 0, y: 0, zoom: 1 } }, { idPrefix: `p${k}-` });
4154
- const defs = new Map(scene.defs.map((x) => [x.id, x]));
4472
+ const scene2 = getVectorSceneRenderData({ document: d, viewport: { x: 0, y: 0, zoom: 1 } }, { idPrefix: `p${k}-`, brushstrokes: "settled" });
4473
+ const defs = new Map(scene2.defs.map((x) => [x.id, x]));
4155
4474
  const page = new Page(w, defs, notes);
4156
4475
  const W = d.width * scale;
4157
4476
  const H = d.height * scale;
@@ -4160,7 +4479,7 @@ function vectorDocumentToPdf(doc, options = {}) {
4160
4479
  const c = parseVectorColor(d.background);
4161
4480
  if (c) page.content.push(`${n(c[0])} ${n(c[1])} ${n(c[2])} rg`, `0 0 ${n(d.width)} ${n(d.height)} re`, "f");
4162
4481
  }
4163
- for (const node of scene.nodes) page.node(node, 1);
4482
+ for (const node of scene2.nodes) page.node(node, 1);
4164
4483
  const content = w.add(w.stream("", new TextEncoder().encode(page.content.join("\n")), compress));
4165
4484
  const gsEntries = [...page.states.values()].map((name) => `/${name} ${w.add(w.gsDicts.get(`${k}:${name}`))} 0 R`);
4166
4485
  const shEntries = [...page.shadings.values()].map((name) => `/${name} ${w.add(w.shadingDicts.get(`${k}:${name}`))} 0 R`);
@@ -4988,8 +5307,8 @@ function mapEffects(node, f) {
4988
5307
  }
4989
5308
  function distortVectorNode(node, parentToDoc, f, tolerance = VECTOR_DISTORT_TOLERANCE) {
4990
5309
  const toDoc = multiplyMatrices(nodeMatrix(node), parentToDoc);
4991
- const inv = invertMatrix(toDoc);
4992
- const local = (p) => applyMatrix(inv, f(applyMatrix(toDoc, p)));
5310
+ const inv2 = invertMatrix(toDoc);
5311
+ const local = (p) => applyMatrix(inv2, f(applyMatrix(toDoc, p)));
4993
5312
  const effects = mapEffects(node, local);
4994
5313
  if (node.type === "group") {
4995
5314
  const group = node;
@@ -6838,8 +7157,10 @@ var VectorEditorController = class _VectorEditorController {
6838
7157
  revision = 0;
6839
7158
  state;
6840
7159
  colorScheme;
7160
+ colorLocked;
6841
7161
  constructor(options = {}) {
6842
- this.colorScheme = options.colorScheme ?? "light";
7162
+ this.colorScheme = options.colorScheme === "dark" ? "dark" : "light";
7163
+ this.colorLocked = !!options.colorLocked;
6843
7164
  this.brushLibrary = options.brushLibrary ?? null;
6844
7165
  this.brushPreview = options.brushPreview ?? null;
6845
7166
  this.brushes = options.brushLibrary ? snapshotBrushLibrary(options.brushLibrary, () => this.notify()) : null;
@@ -6866,7 +7187,9 @@ var VectorEditorController = class _VectorEditorController {
6866
7187
  this.cleanups.push(
6867
7188
  this.history.subscribe((h) => {
6868
7189
  if (!this.applying && this.historyDiffers(h)) this.notify();
6869
- })
7190
+ }),
7191
+ // A wet brushstroke settled: views redraw with its final image.
7192
+ onVectorBrushstrokesSettled(() => this.notify())
6870
7193
  );
6871
7194
  if (this.historyDiffers(this.history.getState())) this.state = this.computeState();
6872
7195
  }
@@ -6949,6 +7272,8 @@ var VectorEditorController = class _VectorEditorController {
6949
7272
  blendDefaults: this.blendDefaults,
6950
7273
  gradientEdit: this.gradientEdit,
6951
7274
  freeDistort: this.freeDistort,
7275
+ colorScheme: this.colorScheme,
7276
+ colorLocked: this.colorLocked,
6952
7277
  textEdit: this.textEdit ? { nodeId: this.textEdit.nodeId, anchor: this.textEdit.anchor, focus: this.textEdit.focus } : null,
6953
7278
  textDefaults: this.textDefaults,
6954
7279
  symbolEdit: this.symbolEditState(),
@@ -7507,8 +7832,8 @@ var VectorEditorController = class _VectorEditorController {
7507
7832
  const node = this.getNode(id);
7508
7833
  if (!node || node.type !== "group" || !node.generation) return { ok: false, message: `"${id}" is not generated art.` };
7509
7834
  if (node.locked) return { ok: false, message: `"${id}" is locked; unlock it first.` };
7510
- const inv = invertMatrix(this.worldOf(id));
7511
- const placed = children.map((c) => this.freshIds(isIdentityMatrix(inv) ? c : transformVectorNode(c, inv)));
7835
+ const inv2 = invertMatrix(this.worldOf(id));
7836
+ const placed = children.map((c) => this.freshIds(isIdentityMatrix(inv2) ? c : transformVectorNode(c, inv2)));
7512
7837
  this.change("Regenerate Art", () => {
7513
7838
  this.replaceNode(id, { ...node, generation, children: placed });
7514
7839
  this.anchors = [];
@@ -8208,7 +8533,7 @@ var VectorEditorController = class _VectorEditorController {
8208
8533
  * Adds a brushstroke to the selected (or given) paths: a stroke layer that paints the path with
8209
8534
  * brush `brushId` (a preset, or a saved brush of the brush library: `listBrushstrokeBrushes`), `size` document
8210
8535
  * units across (default the brush's own), an optional `pressure` profile along the length, in
8211
- * `color` (default the brush's own default colour, unless `keepColour`; else the top stroke's
8536
+ * `color` (default the brush's own default colour, unless `keepColour` or the colour is locked; else the top stroke's
8212
8537
  * paint, else black) at `opacity`. At the top of the stroke
8213
8538
  * stack or at `index`. A `seed` gives it the brush's character (jitter and the grain of `paper`,
8214
8539
  * `null` for none); without one it renders plain. One undo step.
@@ -8221,9 +8546,11 @@ var VectorEditorController = class _VectorEditorController {
8221
8546
  if (brush.size !== void 0 && !(brush.size > 0 && brush.size <= 500)) return { ok: false, message: "size must be above 0 and at most 500 (document units)." };
8222
8547
  const characterError = vectorBrushstrokeCharacterError(brush);
8223
8548
  if (characterError) return { ok: false, message: characterError };
8549
+ const paintError = brush.paint !== void 0 ? vectorBrushstrokePaintError(brush.paint) : null;
8550
+ if (paintError) return { ok: false, message: paintError };
8224
8551
  if (options.color !== void 0 && !parseVectorColor(options.color)) return { ok: false, message: `"${options.color}" is not a colour.` };
8225
8552
  if (options.opacity !== void 0 && !(options.opacity >= 0 && options.opacity <= 1)) return { ok: false, message: "opacity must be from 0 to 1." };
8226
- const own = options.keepColour ? null : vectorBrushstrokeDefaultColour(clean.brushId, user, this.colorScheme);
8553
+ const own = options.keepColour || this.colorLocked ? null : vectorBrushstrokeDefaultColour(clean.brushId, user, this.colorScheme);
8227
8554
  return this.editStacks("stroke", options.ids, "Add Brushstroke", (stack, path) => {
8228
8555
  const top = [...stack].reverse().find((l) => (l.kind ?? "paint") === "paint");
8229
8556
  const paint = options.color ?? own ?? (typeof top?.paint === "string" && top.paint !== "none" ? top.paint : typeof path.stroke === "string" && path.stroke !== "none" ? path.stroke : "#000000");
@@ -8237,7 +8564,7 @@ var VectorEditorController = class _VectorEditorController {
8237
8564
  * given) paths: another brush, size, pressure profile (null removes it), colour or opacity, the
8238
8565
  * character `seed` (null removes it: plain dabs) or the `paper` (replaced whole: `null` for no
8239
8566
  * grain, `{}` for the default paper). Another brush without a `color` takes that brush's default
8240
- * colour, if it has one, unless `keepColour`. Fails, changing nothing, when that layer is not a
8567
+ * colour, if it has one, unless `keepColour` or the colour is locked. Fails, changing nothing, when that layer is not a
8241
8568
  * brushstroke. With `merge`, consecutive edits fold into one undo step.
8242
8569
  */
8243
8570
  setBrushstroke(index, patch, options = {}) {
@@ -8249,6 +8576,8 @@ var VectorEditorController = class _VectorEditorController {
8249
8576
  if (patch.opacity !== void 0 && !(patch.opacity >= 0 && patch.opacity <= 1)) return { ok: false, message: "opacity must be from 0 to 1." };
8250
8577
  const characterError = vectorBrushstrokeCharacterError({ ...patch.seed !== null && patch.seed !== void 0 ? { seed: patch.seed } : {}, ...patch.paper !== void 0 ? { paper: patch.paper } : {} });
8251
8578
  if (characterError) return { ok: false, message: characterError };
8579
+ const paintError = patch.paint !== void 0 && patch.paint !== null ? vectorBrushstrokePaintError(patch.paint) : null;
8580
+ if (paintError) return { ok: false, message: paintError };
8252
8581
  const merge = options.merge === true ? `brushstroke:${index}` : options.merge || void 0;
8253
8582
  return this.editStacks(
8254
8583
  "stroke",
@@ -8265,7 +8594,9 @@ var VectorEditorController = class _VectorEditorController {
8265
8594
  if (patch.seed === null) delete brush.seed;
8266
8595
  else if (patch.seed !== void 0) brush.seed = patch.seed;
8267
8596
  if (patch.paper !== void 0) brush.paper = patch.paper;
8268
- const own = patch.brushId && patch.brushId !== layer.brush.brushId && !patch.keepColour ? vectorBrushstrokeDefaultColour(patch.brushId, user, this.colorScheme) : null;
8597
+ if (patch.paint === null) delete brush.paint;
8598
+ else if (patch.paint !== void 0) brush.paint = patch.paint;
8599
+ const own = patch.brushId && patch.brushId !== layer.brush.brushId && !patch.keepColour && !this.colorLocked ? vectorBrushstrokeDefaultColour(patch.brushId, user, this.colorScheme) : null;
8269
8600
  const colour = patch.color ?? own;
8270
8601
  const next = { ...layer, brush: sanitizeVectorBrushstroke(brush, user) ?? layer.brush, ...colour ? { paint: colour } : {}, ...patch.opacity !== void 0 ? { opacity: patch.opacity } : {} };
8271
8602
  return stack.map((l, i) => i === index ? next : l);
@@ -8275,7 +8606,20 @@ var VectorEditorController = class _VectorEditorController {
8275
8606
  }
8276
8607
  /** Sets the colour scheme that picks the side of a brush's light / dark default colour. Existing strokes keep their colour. */
8277
8608
  setColorScheme(scheme) {
8278
- this.colorScheme = scheme;
8609
+ const next = scheme === "dark" ? "dark" : "light";
8610
+ if (this.colorScheme === next) return;
8611
+ this.colorScheme = next;
8612
+ this.notify();
8613
+ }
8614
+ /**
8615
+ * Locks the brushstroke colour ("Keep my colour"): `addBrushstroke` and `setBrushstroke` with
8616
+ * another brush keep the colour instead of taking the brush's default; an explicit `color` still
8617
+ * applies. Existing strokes are unchanged. Editor state, not an undo step.
8618
+ */
8619
+ setColorLocked(locked) {
8620
+ if (this.colorLocked === !!locked) return;
8621
+ this.colorLocked = !!locked;
8622
+ this.notify();
8279
8623
  }
8280
8624
  /** Removes layer `index` of the fill or stroke stack of the selected (or given) paths. */
8281
8625
  removePaintLayer(kind, index, options = {}) {
@@ -9098,7 +9442,7 @@ var VectorEditorController = class _VectorEditorController {
9098
9442
  if (!groups.length) return { ok: false, message: "Select a group to ungroup." };
9099
9443
  const set = new Set(groups.map((g) => g.id));
9100
9444
  this.change("Ungroup", () => {
9101
- const flatten = (list) => {
9445
+ const flatten2 = (list) => {
9102
9446
  const out = [];
9103
9447
  let changed = false;
9104
9448
  for (const n2 of list) {
@@ -9110,14 +9454,14 @@ var VectorEditorController = class _VectorEditorController {
9110
9454
  ...n2.children.map((c) => c.blendStep ? (({ blendStep: _s, locked: _l, ...rest }) => rest)(c) : c).map((c) => c.booleanOperation ? (({ booleanOperation: _b, ...rest }) => rest)(c) : c).map((c) => n2.transform ? transformVectorNode(c, m) : c).map((c) => opacity !== void 0 && opacity < 1 ? { ...c, opacity: (c.opacity ?? 1) * opacity } : c)
9111
9455
  );
9112
9456
  } else if (n2.type === "group") {
9113
- const children = flatten(n2.children);
9457
+ const children = flatten2(n2.children);
9114
9458
  if (children !== n2.children) changed = true;
9115
9459
  out.push(children !== n2.children ? { ...n2, children } : n2);
9116
9460
  } else out.push(n2);
9117
9461
  }
9118
9462
  return changed ? out : list;
9119
9463
  };
9120
- this.setDoc(flatten(this.doc.nodes));
9464
+ this.setDoc(flatten2(this.doc.nodes));
9121
9465
  this.selection = groups.flatMap((g) => g.children.map((c) => c.id));
9122
9466
  });
9123
9467
  return { ok: true };
@@ -9702,8 +10046,8 @@ var VectorEditorController = class _VectorEditorController {
9702
10046
  const parentM = this.parentMatrixOf(id);
9703
10047
  const r = project(node, parentM);
9704
10048
  if (typeof r === "string") return { ok: false, message: r };
9705
- const inv = invertMatrix(parentM);
9706
- next.set(id, isIdentityMatrix(inv) ? r : transformVectorNode(r, inv));
10049
+ const inv2 = invertMatrix(parentM);
10050
+ next.set(id, isIdentityMatrix(inv2) ? r : transformVectorNode(r, inv2));
9707
10051
  }
9708
10052
  this.change(`Attach to ${plane[0].toUpperCase()}${plane.slice(1)} Plane`, () => {
9709
10053
  this.setDoc(mapTree(this.doc.nodes, (n2) => next.get(n2.id) ?? n2));
@@ -11663,11 +12007,11 @@ var VectorEditorController = class _VectorEditorController {
11663
12007
  const nodes = mapTree(base.nodes, (n2) => {
11664
12008
  if (!targets.has(n2.id) || n2.type !== "path" || n2.locked) return n2;
11665
12009
  const world = vectorWorldMatrix(base.nodes, n2.id) ?? IDENTITY_MATRIX;
11666
- const inv = invertMatrix(world);
12010
+ const inv2 = invertMatrix(world);
11667
12011
  const k = 1 / Math.sqrt(Math.abs(world[0] * world[3] - world[1] * world[2]) || 1);
11668
12012
  const local = dabs.map((d) => {
11669
- const c = applyMatrix(inv, d);
11670
- const moved = d.dx !== void 0 || d.dy !== void 0 ? applyMatrix(inv, { x: d.x + (d.dx ?? 0), y: d.y + (d.dy ?? 0) }) : null;
12013
+ const c = applyMatrix(inv2, d);
12014
+ const moved = d.dx !== void 0 || d.dy !== void 0 ? applyMatrix(inv2, { x: d.x + (d.dx ?? 0), y: d.y + (d.dy ?? 0) }) : null;
11671
12015
  return { ...d, x: c.x, y: c.y, radius: d.radius * k, ...moved ? { dx: moved.x - c.x, dy: moved.y - c.y } : {} };
11672
12016
  });
11673
12017
  const path = subpathsToBezier(n2.subpaths);
@@ -11712,8 +12056,8 @@ var VectorEditorController = class _VectorEditorController {
11712
12056
  }
11713
12057
  /** A stroke's points in node `id`'s own space. */
11714
12058
  localPoints(id, points) {
11715
- const inv = invertMatrix(this.worldOf(id));
11716
- return points.map((p) => toPoint2(applyMatrix(inv, p)));
12059
+ const inv2 = invertMatrix(this.worldOf(id));
12060
+ return points.map((p) => toPoint2(applyMatrix(inv2, p)));
11717
12061
  }
11718
12062
  /**
11719
12063
  * Knife: cuts closed paths along the polyline `points` (document space) wherever it crosses
@@ -12682,8 +13026,8 @@ var VectorEditorController = class _VectorEditorController {
12682
13026
  if (!drag.moved && Math.hypot(p.x - drag.start.x, p.y - drag.start.y) * this.viewport.zoom < 3) return;
12683
13027
  drag.moved = true;
12684
13028
  const c = drag.widget.corner;
12685
- const inv = invertMatrix(this.worldOf(drag.widget.ref.nodeId, drag.base));
12686
- const d = applyMatrixLinear(inv, { x: p.x - drag.start.x, y: p.y - drag.start.y });
13029
+ const inv2 = invertMatrix(this.worldOf(drag.widget.ref.nodeId, drag.base));
13030
+ const d = applyMatrixLinear(inv2, { x: p.x - drag.start.x, y: p.y - drag.start.y });
12687
13031
  const radius = Math.max(0, Math.min(c.maxRadius, drag.widget.drawnRadius + cornerRadiusChange(c, d)));
12688
13032
  this.doc = this.updateAnchors(drag.base, drag.refs, (a) => setAnchorCorner(a, radius));
12689
13033
  this.interaction = { type: "corner", ref: drag.widget.ref, radius };
@@ -15932,6 +16276,65 @@ function createVectorCommands(controller, options = {}) {
15932
16276
  }
15933
16277
  }
15934
16278
  ];
16279
+ const BRUSH_PAINT = {
16280
+ anyOf: [
16281
+ {
16282
+ type: "object",
16283
+ additionalProperties: false,
16284
+ required: ["type", "color"],
16285
+ description: "One colour instead of the layer colour (the layer colour still gives the alpha).",
16286
+ properties: {
16287
+ type: { type: "string", enum: ["color"], description: 'Always "color".' },
16288
+ color: { type: "string", pattern: "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$", description: "The colour as #rrggbb." }
16289
+ }
16290
+ },
16291
+ {
16292
+ type: "object",
16293
+ additionalProperties: false,
16294
+ required: ["type", "stops"],
16295
+ description: "Gradient paint: the brush samples it dab by dab along the path.",
16296
+ properties: {
16297
+ type: { type: "string", enum: ["gradient"], description: 'Always "gradient".' },
16298
+ stops: {
16299
+ type: "array",
16300
+ minItems: 2,
16301
+ maxItems: 16,
16302
+ description: "2..16 stops sorted by offset.",
16303
+ items: {
16304
+ type: "object",
16305
+ additionalProperties: false,
16306
+ required: ["offset", "color"],
16307
+ properties: {
16308
+ offset: { type: "number", minimum: 0, maximum: 1, description: "Position 0..1 along the gradient." },
16309
+ color: { type: "string", description: 'The stop colour as #rrggbb, or "paint" (the layer colour) or "secondary" (white).', anyOf: [{ pattern: "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" }, { enum: ["paint", "secondary"] }] },
16310
+ opacity: { type: "number", minimum: 0, maximum: 1, description: "Multiplies the brush flow where this colour is laid, 0..1 (default 1)." },
16311
+ midpoint: { type: "number", minimum: 0.05, maximum: 0.95, description: "Where between this stop and the next the colours are half mixed, 0.05..0.95 (default 0.5)." }
16312
+ }
16313
+ }
16314
+ },
16315
+ space: { type: "string", enum: ["pigment", "oklab", "srgb"], description: "How neighbouring stops mix: pigment (default; like paint, blue to yellow passes green), oklab (perceptual) or srgb (plain numbers)." },
16316
+ map: {
16317
+ type: "object",
16318
+ additionalProperties: false,
16319
+ required: ["by"],
16320
+ description: "What moves the stroke along the gradient (default distance, 300 document units, mirror).",
16321
+ properties: {
16322
+ by: {
16323
+ type: "string",
16324
+ enum: ["distance", "pressure", "tilt", "direction", "across-tip", "random"],
16325
+ description: "distance: document units (or dabs) travelled along the path; pressure: the pressure profile 0..1; tilt: no tilt on paths, so the start of the gradient; direction: direction of travel, once round per 360\xB0; across-tip: left to right across the tip (a brush loaded with several colours); random: a seeded random point per dab (the layer seed). Paths have no timing, so velocity and time are not available."
16326
+ },
16327
+ length: { type: "number", minimum: 1, maximum: 1e5, description: 'distance: document units (or dabs, with unit "dabs") per run through the gradient (default 300).' },
16328
+ repeat: { type: "string", enum: ["clamp", "repeat", "mirror"], description: "distance: past the end, clamp (hold the last colour), repeat (start again) or mirror (run back; default)." },
16329
+ unit: { type: "string", enum: ["px", "dabs"], description: 'distance: document units ("px", default) or dabs.' },
16330
+ every: { type: "integer", minimum: 1, maximum: 500, description: "random: dabs per random pick (default 1)." }
16331
+ }
16332
+ },
16333
+ softness: { type: "number", minimum: 0, maximum: 1, description: "How softly neighbouring stops blend: 1 smooth (default) .. 0 hard steps." }
16334
+ }
16335
+ }
16336
+ ]
16337
+ };
15935
16338
  const BRUSH_SEED = { type: "integer", minimum: 0, maximum: VECTOR_BRUSHSTROKE_MAX_SEED };
15936
16339
  const BRUSH_PAPER = {
15937
16340
  type: "object",
@@ -15943,6 +16346,10 @@ function createVectorCommands(controller, options = {}) {
15943
16346
  },
15944
16347
  additionalProperties: false
15945
16348
  };
16349
+ const brushColourResult = () => {
16350
+ const st2 = controller.getState();
16351
+ return { colorScheme: st2.colorScheme, colorLocked: st2.colorLocked };
16352
+ };
15946
16353
  const brushstrokeCommands = () => [
15947
16354
  {
15948
16355
  ...base,
@@ -15950,16 +16357,16 @@ function createVectorCommands(controller, options = {}) {
15950
16357
  label: "Brushstroke Brushes",
15951
16358
  readOnly: true,
15952
16359
  keywords: ["brush", "brushstroke", "pencil", "pastel", "charcoal", "ink", "marker", "natural media"],
15953
- description: "Lists the brushes a brushstroke can use (the dry presets, then saved brushes from the brush library the vector profile admits): { brushes: [{ id, name, category, size, builtin }] } (size: the brush's own diameter in document units).",
16360
+ description: "Lists the brushes a brushstroke can use (the dry presets, wet paint (watercolour, gouache, wet oils), the Mixer Brush, smudge and blender, then saved brushes from the brush library the vector profile admits): { brushes: [{ id, name, category, size, builtin }], colorScheme, colorLocked } (size: the brush's own diameter in document units; colorScheme picks the side of light / dark default colours; colorLocked: brushstrokes keep their colour, vector.setColorLocked).",
15954
16361
  params: { type: "object", properties: {}, additionalProperties: false },
15955
- run: () => ({ brushes: controller.listBrushstrokeBrushes().map((b) => ({ id: b.id, name: b.name, category: b.category, size: b.tip.size, builtin: vectorBrushstrokeBrush(b.id) !== null })) })
16362
+ run: () => ({ brushes: controller.listBrushstrokeBrushes().map((b) => ({ id: b.id, name: b.name, category: b.category, size: b.tip.size, builtin: vectorBrushstrokeBrush(b.id) !== null })), ...brushColourResult() })
15956
16363
  },
15957
16364
  {
15958
16365
  ...base,
15959
16366
  id: `${prefix}addBrushstroke`,
15960
16367
  label: "Add Brushstroke\u2026",
15961
16368
  keywords: ["brushstroke", "brush", "natural media", "pencil line", "chalk", "sketchy", "hand-drawn", "ink stroke"],
15962
- description: "Paints paths (`ids`, default the selection; groups: every path inside) with a natural-media brush along their outline, as a stroke layer of kind brushstroke: `brushId` from vector.listBrushes, `size` (document units), an optional `pressure` profile (0\u20131 values spread from start to end, e.g. [0.2, 1, 0.2] for a tapered stroke), `color` and `opacity`. Pass a `seed` for the brush's character (seeded size, angle, opacity and scatter jitter plus a paper grain: what makes pencil, pastel and charcoal look real), with `paper` to choose the grain or null for none; without a seed the brush paints plain, even dabs. The path stays editable; the strokes follow it. One undo step. Returns { paths: [{ id, strokes }] } (the brushstroke is a stroke layer; change it with vector.setBrushstroke, remove it with vector.removeStroke).",
16369
+ description: "Paints paths (`ids`, default the selection; groups: every path inside) with a natural-media brush along their outline, as a stroke layer of kind brushstroke: `brushId` from vector.listBrushes, `size` (document units), an optional `pressure` profile (0\u20131 values spread from start to end, e.g. [0.2, 1, 0.2] for a tapered stroke), `color`, `opacity` and gradient `paint` sampled dab by dab along the path. Wet paint (watercolour, gouache, wet oils) bleeds and dries; the scene shows it as laid until it settles, exports wait for it. The Mixer Brush, smudge and blender act on the shapes beneath the stroke (fills, strokes and brushstrokes; not text, images or effects). Pass a `seed` for the brush's character (seeded size, angle, opacity and scatter jitter plus a paper grain: what makes pencil, pastel and charcoal look real), with `paper` to choose the grain or null for none; without a seed the brush paints plain, even dabs. The path stays editable; the strokes follow it. One undo step. Returns { paths: [{ id, strokes }] } (the brushstroke is a stroke layer; change it with vector.setBrushstroke, remove it with vector.removeStroke).",
15963
16370
  params: {
15964
16371
  type: "object",
15965
16372
  properties: {
@@ -15972,7 +16379,8 @@ function createVectorCommands(controller, options = {}) {
15972
16379
  opacity: { type: "number", minimum: 0, maximum: 1, description: "Layer opacity 0\u20131 (default 1)." },
15973
16380
  index: { type: "integer", minimum: 0, description: "Position in the stroke stack, 0 the bottom (default: on top)." },
15974
16381
  seed: { ...BRUSH_SEED, description: "Character seed 0\u20132147483647: turns on the brush's jitter and paper grain; the same seed always renders the same stroke (default: none, plain dabs)." },
15975
- paper: { anyOf: [BRUSH_PAPER, { type: "null" }], description: "With `seed`: the paper the grain comes from { pattern, seed, scale, depth }, or null for jitter without grain (default: the sanded paper)." }
16382
+ paper: { anyOf: [BRUSH_PAPER, { type: "null" }], description: "With `seed`: the paper the grain comes from { pattern, seed, scale, depth }, or null for jitter without grain (default: the sanded paper)." },
16383
+ paint: { ...BRUSH_PAINT, description: 'Gradient paint sampled dab by dab along the path instead of the colour, e.g. { type: "gradient", stops: [{ offset: 0, color: "#e11d48" }, { offset: 1, color: "#2563eb" }], map: { by: "distance", length: 200, repeat: "clamp" } } (default: the colour; a gradient layer colour or a gradient brush paints its own gradient).' }
15976
16384
  },
15977
16385
  required: ["brushId"],
15978
16386
  additionalProperties: false
@@ -15988,7 +16396,8 @@ function createVectorCommands(controller, options = {}) {
15988
16396
  ...params.size !== void 0 ? { size: params.size } : {},
15989
16397
  ...params.pressure ? { pressure: params.pressure } : {},
15990
16398
  ...params.seed !== void 0 ? { seed: params.seed } : {},
15991
- ...params.paper !== void 0 ? { paper: params.paper } : {}
16399
+ ...params.paper !== void 0 ? { paper: params.paper } : {},
16400
+ ...params.paint !== void 0 ? { paint: params.paint } : {}
15992
16401
  },
15993
16402
  { ...params.ids ? { ids: params.ids } : {}, ...params.index !== void 0 ? { index: params.index } : {}, ...params.color ? { color: params.color } : {}, ...params.keepColour ? { keepColour: true } : {}, ...params.opacity !== void 0 ? { opacity: params.opacity } : {} }
15994
16403
  )
@@ -16006,7 +16415,7 @@ function createVectorCommands(controller, options = {}) {
16006
16415
  id: `${prefix}setBrushstroke`,
16007
16416
  label: "Edit Brushstroke\u2026",
16008
16417
  keywords: ["brushstroke", "brush", "size", "pressure", "taper"],
16009
- description: "Changes the brushstroke at stroke layer `index` of path `id` (vector.getAppearance lists strokes; 0 is the bottom): `brushId` (the same id again takes that saved brush as the library has it now), `size` (null: the brush's own), `pressure` (null: full pressure), `color`, `opacity`, `seed` (turns the brush's character on; null: plain dabs) and `paper` (replaced whole; null: no grain; {}: the default paper). Pass at least one. One undo step. Returns { id, strokes }.",
16418
+ description: "Changes the brushstroke at stroke layer `index` of path `id` (vector.getAppearance lists strokes; 0 is the bottom): `brushId` (the same id again takes that saved brush as the library has it now), `size` (null: the brush's own), `pressure` (null: full pressure), `color`, `opacity`, `seed` (turns the brush's character on; null: plain dabs), `paper` (replaced whole; null: no grain; {}: the default paper) and gradient `paint` (null: the colour). Pass at least one. One undo step. Returns { id, strokes }.",
16010
16419
  params: {
16011
16420
  type: "object",
16012
16421
  properties: {
@@ -16019,7 +16428,8 @@ function createVectorCommands(controller, options = {}) {
16019
16428
  keepColour: { type: "boolean", description: "With another brushId: keep the layer's colour instead of taking the new brush's default colour (default false)." },
16020
16429
  opacity: { type: "number", minimum: 0, maximum: 1, description: "Layer opacity 0\u20131." },
16021
16430
  seed: { anyOf: [BRUSH_SEED, { type: "null" }], description: "Character seed 0\u20132147483647 (jitter and paper grain; a new seed gives a new take on the same stroke), or null for plain dabs." },
16022
- paper: { anyOf: [BRUSH_PAPER, { type: "null" }], description: "The paper the grain comes from { pattern, seed, scale, depth } (applies with a seed), null for no grain, or {} for the default sanded paper." }
16431
+ paper: { anyOf: [BRUSH_PAPER, { type: "null" }], description: "The paper the grain comes from { pattern, seed, scale, depth } (applies with a seed), null for no grain, or {} for the default sanded paper." },
16432
+ paint: { anyOf: [...BRUSH_PAINT.anyOf, { type: "null" }], description: "Gradient paint sampled dab by dab along the path (replaced whole), or null to paint the colour again." }
16023
16433
  },
16024
16434
  required: ["id", "index"],
16025
16435
  additionalProperties: false
@@ -16027,11 +16437,45 @@ function createVectorCommands(controller, options = {}) {
16027
16437
  undoable: true,
16028
16438
  run: (_ctx, params) => {
16029
16439
  const { id, index, ...patch } = params;
16030
- if (!Object.keys(patch).length) fail("Pass at least one of brushId, size, pressure, color, opacity, seed, paper.");
16440
+ if (!Object.keys(patch).length) fail("Pass at least one of brushId, size, pressure, color, opacity, seed, paper, paint.");
16031
16441
  layerIndex(id, "stroke", index);
16032
16442
  check2(controller.setBrushstroke(index, patch, { ids: [id] }));
16033
16443
  return stackResult(id, "stroke");
16034
16444
  }
16445
+ },
16446
+ {
16447
+ ...base,
16448
+ id: `${prefix}setColorLocked`,
16449
+ label: "Keep My Colour",
16450
+ keywords: ["lock colour", "keep colour", "brush colour", "color lock", "brushstroke"],
16451
+ description: `Locks or unlocks the brushstroke colour ("Keep my colour"): while locked, vector.addBrushstroke and vector.setBrushstroke with another brushId keep the colour (the path's stroke colour, or the layer's) instead of taking the brush's default colour; an explicit color still applies. Strokes already made are unchanged. Editor state, not an undo step. Returns { colorScheme, colorLocked }.`,
16452
+ params: {
16453
+ type: "object",
16454
+ properties: { locked: { type: "boolean", description: "true locks the colour, false unlocks it (the editor starts unlocked)." } },
16455
+ required: ["locked"],
16456
+ additionalProperties: false
16457
+ },
16458
+ run: (_ctx, params) => {
16459
+ controller.setColorLocked(params.locked);
16460
+ return brushColourResult();
16461
+ }
16462
+ },
16463
+ {
16464
+ ...base,
16465
+ id: `${prefix}setColorScheme`,
16466
+ label: "Brush Colours for Light / Dark",
16467
+ keywords: ["colour scheme", "dark mode", "light mode", "theme", "brush colour"],
16468
+ description: "Sets the colour scheme that picks the side of a brush's light / dark default colour for brushstrokes made from now on (white chalk in dark, dark chalk in light). Strokes already made keep their colour. The host usually sets it to match its theme. Editor state, not an undo step. Returns { colorScheme, colorLocked }.",
16469
+ params: {
16470
+ type: "object",
16471
+ properties: { scheme: { type: "string", enum: ["light", "dark"], description: "light (dark chalk on a light background) or dark (light chalk on a dark one)." } },
16472
+ required: ["scheme"],
16473
+ additionalProperties: false
16474
+ },
16475
+ run: (_ctx, params) => {
16476
+ controller.setColorScheme(params.scheme);
16477
+ return brushColourResult();
16478
+ }
16035
16479
  }
16036
16480
  ];
16037
16481
  const pdfCommands = () => [
@@ -16139,13 +16583,13 @@ function createVectorCommands(controller, options = {}) {
16139
16583
  const { image, ...options2 } = params;
16140
16584
  const parsed = parseDataUrl(image);
16141
16585
  if (!parsed || !isPng(parsed.bytes)) fail("image must be a PNG data URL (data:image/png;base64,\u2026).");
16142
- let decoded;
16586
+ let decoded2;
16143
16587
  try {
16144
- decoded = decodePng(parsed.bytes, { maxPixels: VECTOR_TRACE_MAX_PIXELS });
16588
+ decoded2 = decodePng(parsed.bytes, { maxPixels: VECTOR_TRACE_MAX_PIXELS });
16145
16589
  } catch (e) {
16146
16590
  return fail(`The PNG could not be read: ${e.message}`);
16147
16591
  }
16148
- const r = controller.traceImage(decoded, options2);
16592
+ const r = controller.traceImage(decoded2, options2);
16149
16593
  check2(r);
16150
16594
  return {
16151
16595
  id: r.id,
@@ -18184,13 +18628,13 @@ function getVectorSymbolsPanel(state, options = {}) {
18184
18628
  const symbols = (host.symbols ?? []).map((s) => {
18185
18629
  const b = unionBounds(s.nodes.map((n2) => vectorNodeBounds(n2, IDENTITY_MATRIX))) ?? { x: -10, y: -10, width: 20, height: 20 };
18186
18630
  const pad = Math.max(b.width, b.height) * 0.08 + 1;
18187
- const scene = getVectorSceneRenderData({ document: { width: 0, height: 0, nodes: s.nodes }, viewport: { x: 0, y: 0, zoom: 1 } }, { idPrefix: `${prefix}${s.id}-` });
18631
+ const scene2 = getVectorSceneRenderData({ document: { width: 0, height: 0, nodes: s.nodes }, viewport: { x: 0, y: 0, zoom: 1 } }, { idPrefix: `${prefix}${s.id}-` });
18188
18632
  return {
18189
18633
  id: s.id,
18190
18634
  name: s.name,
18191
18635
  instances: counts.get(s.id) ?? 0,
18192
18636
  viewBox: `${fmt3(b.x - pad)} ${fmt3(b.y - pad)} ${fmt3(b.width + 2 * pad)} ${fmt3(b.height + 2 * pad)}`,
18193
- scene,
18637
+ scene: scene2,
18194
18638
  editing: state.symbolEdit?.symbolId === s.id
18195
18639
  };
18196
18640
  });
@@ -18207,9 +18651,9 @@ function getVectorSymbolEditBackground(state, options = {}) {
18207
18651
  const e = state.symbolEdit;
18208
18652
  if (!e || !e.hostTransform) return null;
18209
18653
  const drop = (list) => list.filter((n2) => n2.id !== e.instanceId).map((n2) => n2.type === "group" ? { ...n2, children: drop(n2.children) } : n2);
18210
- const scene = getVectorSceneRenderData({ document: { ...e.host, nodes: drop(e.host.nodes) }, viewport: state.viewport }, { idPrefix: options.idPrefix ?? "ve-host-" });
18654
+ const scene2 = getVectorSceneRenderData({ document: { ...e.host, nodes: drop(e.host.nodes) }, viewport: state.viewport }, { idPrefix: options.idPrefix ?? "ve-host-" });
18211
18655
  const m = e.hostTransform;
18212
- return { ...scene, transform: `${scene.transform} matrix(${m.map((v) => Math.round(v * 1e6) / 1e6).join(" ")})`, name: e.name };
18656
+ return { ...scene2, transform: `${scene2.transform} matrix(${m.map((v) => Math.round(v * 1e6) / 1e6).join(" ")})`, name: e.name };
18213
18657
  }
18214
18658
 
18215
18659
  // src/vector/effect-tools.ts
@@ -18705,6 +19149,7 @@ export {
18705
19149
  VECTOR_VERTICAL_RESIZING_LABELS,
18706
19150
  WARP_STYLES as VECTOR_WARP_STYLES,
18707
19151
  WARP_STYLE_LABELS as VECTOR_WARP_STYLE_LABELS,
19152
+ VECTOR_WET_STEPS,
18708
19153
  VECTOR_WIDTH_PRESETS,
18709
19154
  VECTOR_WIDTH_PRESET_IDS,
18710
19155
  VECTOR_WIDTH_PROFILE_MAX_POINTS,
@@ -18717,6 +19162,7 @@ export {
18717
19162
  VectorRTree,
18718
19163
  VectorSnapTargets,
18719
19164
  VectorTextError,
19165
+ WetBrushstrokeJob,
18720
19166
  activeVectorEffects,
18721
19167
  activeVectorFilters,
18722
19168
  addBuilderHit,
@@ -18877,6 +19323,7 @@ export {
18877
19323
  isValidVectorParameterName,
18878
19324
  isVectorBlendGroup,
18879
19325
  isVectorBlendMode,
19326
+ isVectorBrushstrokeEngine,
18880
19327
  isVectorBrushstrokeLayer,
18881
19328
  isVectorConnector,
18882
19329
  isVectorFilterType,
@@ -18933,12 +19380,14 @@ export {
18933
19380
  normalizeVectorPaintStacks,
18934
19381
  normalizeVectorTextRuns,
18935
19382
  offsetPaths,
19383
+ onVectorBrushstrokesSettled,
18936
19384
  orderQuad as orderVectorQuad,
18937
19385
  parseOpenTypeFont,
18938
19386
  parseSvgTransform,
18939
19387
  parseVectorColor,
18940
19388
  parseVectorLottie,
18941
19389
  parseVectorSvg,
19390
+ parseVectorTransform,
18942
19391
  patchVectorCharacterStyle,
18943
19392
  patchVectorParagraphStyle,
18944
19393
  pathTextFrameFromBezier,
@@ -18956,6 +19405,8 @@ export {
18956
19405
  purgeVectorConstraints,
18957
19406
  quantizeVectorTraceColors,
18958
19407
  randomVectorBrushstrokeSeed,
19408
+ rasterizeVectorBackdrop,
19409
+ readVectorBrushstrokePaint,
18959
19410
  recognizeInkShape,
18960
19411
  rectSubpath,
18961
19412
  refineMorphContour,
@@ -18971,6 +19422,8 @@ export {
18971
19422
  removeWarpMeshSplitU as removeVectorMeshSplitU,
18972
19423
  removeWarpMeshSplitV as removeVectorMeshSplitV,
18973
19424
  removeWidthPoints,
19425
+ renderBackdropBrushstroke,
19426
+ renderWetBrushstroke,
18974
19427
  repeatPaths,
18975
19428
  replaceVectorText,
18976
19429
  rerouteVectorConnectors,
@@ -19018,9 +19471,11 @@ export {
19018
19471
  scaleVectorStrokeWidths,
19019
19472
  setAnchorCorner,
19020
19473
  setSubpathCorners,
19474
+ setVectorBackdropSource,
19021
19475
  setVectorGradientVector,
19022
19476
  setVectorTextShaper,
19023
19477
  setWidthPoint,
19478
+ settleVectorBrushstrokes,
19024
19479
  shadeVectorColor,
19025
19480
  simplifyPolyline,
19026
19481
  smoothSubpath,
@@ -19069,6 +19524,7 @@ export {
19069
19524
  turnMorphContour,
19070
19525
  twistPaths,
19071
19526
  unionBounds,
19527
+ usesVectorBackdrop,
19072
19528
  validateVectorConstraint,
19073
19529
  variableStrokeOutline,
19074
19530
  variableStrokeRings,
@@ -19078,6 +19534,7 @@ export {
19078
19534
  vectorAppearanceShapes,
19079
19535
  vectorArtModels,
19080
19536
  vectorArtboards,
19537
+ vectorBackdropRect,
19081
19538
  vectorBevelFaces,
19082
19539
  vectorBlendColorDistance,
19083
19540
  vectorBlendKeyTargets,
@@ -19098,7 +19555,9 @@ export {
19098
19555
  vectorBrushstrokeCharacterError,
19099
19556
  vectorBrushstrokeDefaultColour,
19100
19557
  vectorBrushstrokeImages,
19558
+ vectorBrushstrokePaintError,
19101
19559
  vectorBrushstrokeReach,
19560
+ vectorBrushstrokesSettling,
19102
19561
  vectorBuilderErasedEdges,
19103
19562
  vectorChildAppearanceShapes,
19104
19563
  vectorConnectionSites,
@@ -19191,6 +19650,7 @@ export {
19191
19650
  vectorMaskedShapes,
19192
19651
  vectorMasterArt,
19193
19652
  vectorMasterPages,
19653
+ vectorMediaEngine,
19194
19654
  vectorMeshColorAt,
19195
19655
  warpMeshColumns as vectorMeshColumns,
19196
19656
  warpMeshControlBounds as vectorMeshControlBounds,