@pixerate/editor 0.189.0 → 0.191.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 (41) hide show
  1. package/dist/{chunk-ED4L4UD6.cjs → chunk-2UR4OX3B.cjs} +1002 -787
  2. package/dist/{chunk-M7XIC2FC.js → chunk-RQ2EM5TY.js} +333 -0
  3. package/dist/{chunk-ZEHTUR7Z.js → chunk-TXYXF32N.js} +968 -753
  4. package/dist/{chunk-5UEFNX24.cjs → chunk-ZQGZWFPU.cjs} +344 -11
  5. package/dist/image-editor/controller.d.cts +73 -1
  6. package/dist/image-editor/controller.d.ts +73 -1
  7. package/dist/image-editor/free-transform.d.cts +3 -6
  8. package/dist/image-editor/free-transform.d.ts +3 -6
  9. package/dist/image-editor/index.cjs +1382 -904
  10. package/dist/image-editor/index.d.cts +1 -0
  11. package/dist/image-editor/index.d.ts +1 -0
  12. package/dist/image-editor/index.js +485 -7
  13. package/dist/image-editor/layer-tools.d.cts +1 -1
  14. package/dist/image-editor/layer-tools.d.ts +1 -1
  15. package/dist/image-editor/layers/compositor.d.cts +3 -0
  16. package/dist/image-editor/layers/compositor.d.ts +3 -0
  17. package/dist/image-editor/layers/model.d.cts +23 -0
  18. package/dist/image-editor/layers/model.d.ts +23 -0
  19. package/dist/image-editor/layers/panel.d.cts +11 -1
  20. package/dist/image-editor/layers/panel.d.ts +11 -1
  21. package/dist/image-editor/layers/serialize.d.cts +11 -0
  22. package/dist/image-editor/layers/serialize.d.ts +11 -0
  23. package/dist/image-editor/layers/shape.d.cts +5 -0
  24. package/dist/image-editor/layers/shape.d.ts +5 -0
  25. package/dist/image-editor/layers/vector-mask.d.cts +51 -0
  26. package/dist/image-editor/layers/vector-mask.d.ts +51 -0
  27. package/dist/image-editor/psd/import.d.cts +0 -7
  28. package/dist/image-editor/psd/import.d.ts +0 -7
  29. package/dist/image-editor/selection/index.d.cts +1 -0
  30. package/dist/image-editor/selection/index.d.ts +1 -0
  31. package/dist/image-editor/selection/path.d.cts +37 -0
  32. package/dist/image-editor/selection/path.d.ts +37 -0
  33. package/dist/paint-editor/index.cjs +6 -6
  34. package/dist/paint-editor/index.js +1 -1
  35. package/dist/vector/index.cjs +21 -303
  36. package/dist/vector/index.js +15 -297
  37. package/dist/vector/trace.d.cts +8 -1
  38. package/dist/vector/trace.d.ts +8 -1
  39. package/package.json +1 -1
  40. package/skills/pixerate-image-editor/SKILL.md +8 -0
  41. package/skills/pixerate-image-selection-masking/SKILL.md +10 -2
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  loadOpenTypeFont,
3
- parseOpenTypeFont
4
- } from "../chunk-M7XIC2FC.js";
3
+ parseOpenTypeFont,
4
+ traceField
5
+ } from "../chunk-RQ2EM5TY.js";
5
6
  import {
6
7
  ADJUSTMENT_KEYS,
7
8
  ADJUSTMENT_KINDS,
@@ -99,6 +100,7 @@ import {
99
100
  combineSelections,
100
101
  compileAdjustmentLayer,
101
102
  componentPath,
103
+ componentsCoverage,
102
104
  compositeDeep,
103
105
  computeSelectionBounds,
104
106
  contentsBox,
@@ -137,6 +139,7 @@ import {
137
139
  distanceTransform,
138
140
  documentContents,
139
141
  duplicateLayer,
142
+ effectiveLayerMask,
140
143
  encodeDeepSurface,
141
144
  encodeRle,
142
145
  encodeSelectionRle,
@@ -148,6 +151,7 @@ import {
148
151
  filterMaskCoverage,
149
152
  flattenPath,
150
153
  flipCorners,
154
+ followLayerMove,
151
155
  fullSelection,
152
156
  geometryHomography,
153
157
  getAnnotationBounds,
@@ -177,6 +181,7 @@ import {
177
181
  invertMatrix3,
178
182
  invertSelection,
179
183
  isActiveFilter,
184
+ isActiveVectorMask,
180
185
  isAdjustmentKind,
181
186
  isContentsDecoded,
182
187
  isDefaultDepthMask,
@@ -237,6 +242,7 @@ import {
237
242
  offsetLayer,
238
243
  offsetShape,
239
244
  offsetSmartObject,
245
+ offsetVectorMask,
240
246
  parseAdjustmentBlock,
241
247
  parseBlend,
242
248
  parseLfx2,
@@ -290,6 +296,7 @@ import {
290
296
  rerenderText,
291
297
  resizeSelection,
292
298
  resolveExternalAssets,
299
+ resolveVectorMasks,
293
300
  rgbToGray,
294
301
  rgbToHsl,
295
302
  runLayerFilters,
@@ -341,6 +348,7 @@ import {
341
348
  transformMap,
342
349
  transformShapeComponents,
343
350
  transformTypeLayer,
351
+ transformVectorMask,
344
352
  transformedBox,
345
353
  transformedPoint,
346
354
  translateLayer,
@@ -359,6 +367,9 @@ import {
359
367
  updateTypeLayer,
360
368
  userErrorMessage,
361
369
  uvToXy,
370
+ vectorMaskBounds,
371
+ vectorMaskCoverage,
372
+ vectorMaskThumbnail,
362
373
  versionedDescriptorBytes,
363
374
  vibranceLinear,
364
375
  vibrancePixel,
@@ -373,7 +384,7 @@ import {
373
384
  writePsd,
374
385
  xyToTempTint,
375
386
  xyToUv
376
- } from "../chunk-ZEHTUR7Z.js";
387
+ } from "../chunk-TXYXF32N.js";
377
388
  import {
378
389
  parseCssColor
379
390
  } from "../chunk-3CI7FRMK.js";
@@ -620,6 +631,7 @@ import {
620
631
  roundSubpaths,
621
632
  spliceRuns,
622
633
  styleAt,
634
+ subpathsToBezier,
623
635
  textIndexAt,
624
636
  translateMatrix,
625
637
  warpMeshColumns,
@@ -631,7 +643,9 @@ import {
631
643
  } from "../chunk-LPIKXPBM.js";
632
644
  import "../chunk-YXKLVD56.js";
633
645
  import "../chunk-M6DNZNVY.js";
634
- import "../chunk-WEIK3UYB.js";
646
+ import {
647
+ bezierPathToSvg
648
+ } from "../chunk-WEIK3UYB.js";
635
649
  import "../chunk-PWBZH3FK.js";
636
650
  import {
637
651
  EffectUnsupportedError,
@@ -2416,6 +2430,67 @@ function hexRgb(hex) {
2416
2430
  return [v >> 16 & 255, v >> 8 & 255, v & 255];
2417
2431
  }
2418
2432
 
2433
+ // src/image-editor/selection/path.ts
2434
+ function selectionToSubpaths(mask, options = {}) {
2435
+ const b = mask.bounds;
2436
+ if (!b) return [];
2437
+ const x0 = Math.max(0, b.x - 1);
2438
+ const y0 = Math.max(0, b.y - 1);
2439
+ const x1 = Math.min(mask.width, b.x + b.width + 1);
2440
+ const y1 = Math.min(mask.height, b.y + b.height + 1);
2441
+ const w = x1 - x0;
2442
+ const h = y1 - y0;
2443
+ const data = mask.data;
2444
+ const field = new Float32Array(w * h);
2445
+ for (let y = 0; y < h; y++) {
2446
+ const row = (y0 + y) * mask.width + x0;
2447
+ for (let x = 0; x < w; x++) field[y * w + x] = data[row + x] / 255;
2448
+ }
2449
+ const threshold = Math.min(254, Math.max(1, options.threshold ?? 128)) / 255;
2450
+ const subpaths = traceField(field, w, h, threshold, {
2451
+ tolerance: Math.max(0.1, options.tolerance ?? 0.5),
2452
+ cornerRoundness: options.cornerRoundness ?? 0.5,
2453
+ minArea: options.minArea ?? 4,
2454
+ squareCorners: true
2455
+ });
2456
+ return subpaths.map((sp) => mapSubpaths([sp], (p) => ({ x: p.x + x0, y: p.y + y0 }))[0]);
2457
+ }
2458
+ function subpathsToSvgPath(subpaths, precision = 2) {
2459
+ return bezierPathToSvg(subpathsToBezier(subpaths), { precision });
2460
+ }
2461
+ function pathToSelection(components, width, height, options = {}) {
2462
+ let x0 = Infinity;
2463
+ let y0 = Infinity;
2464
+ let x1 = -Infinity;
2465
+ let y1 = -Infinity;
2466
+ for (const c of components) {
2467
+ for (const sp of c.subpaths) {
2468
+ for (const a of sp.anchors) {
2469
+ for (const p of [a, a.in, a.out]) {
2470
+ if (!p) continue;
2471
+ x0 = Math.min(x0, p.x);
2472
+ y0 = Math.min(y0, p.y);
2473
+ x1 = Math.max(x1, p.x);
2474
+ y1 = Math.max(y1, p.y);
2475
+ }
2476
+ }
2477
+ }
2478
+ }
2479
+ const rx0 = Math.max(0, Math.floor(x0));
2480
+ const ry0 = Math.max(0, Math.floor(y0));
2481
+ const rx1 = Math.min(width, Math.ceil(x1));
2482
+ const ry1 = Math.min(height, Math.ceil(y1));
2483
+ if (!(rx1 > rx0 && ry1 > ry0)) return createSelectionMask(width, height);
2484
+ const rect = { x: rx0, y: ry0, width: rx1 - rx0, height: ry1 - ry0 };
2485
+ const cov = componentsCoverage(components, rect, options.fillRule ?? "nonzero");
2486
+ const hard = options.antiAlias === false;
2487
+ const bytes = new Uint8Array(cov.length);
2488
+ for (let i = 0; i < cov.length; i++) bytes[i] = hard ? cov[i] >= 0.5 ? 255 : 0 : coverageToByte(cov[i]);
2489
+ const mask = selectionFromRegion(width, height, rect, bytes);
2490
+ const feather = options.feather ?? 0;
2491
+ return feather > 0 && mask.bounds ? modifySelection(mask, "feather", feather) : mask;
2492
+ }
2493
+
2419
2494
  // src/image-editor/layers/shape-type-ui.ts
2420
2495
  var DEFAULT_SHAPE_TOOL_OPTIONS = {
2421
2496
  kind: "rect",
@@ -4164,6 +4239,24 @@ var FreeTransformSession = class {
4164
4239
  }
4165
4240
  /** Resamples every part for the current geometry and shows it (or returns it, for commit). */
4166
4241
  resample(final, preview = {}) {
4242
+ const out = this.resampleContent(final, preview);
4243
+ return { ...out, layers: this.moveVectorMask(out.layers) };
4244
+ }
4245
+ /** A linked vector mask follows the layer's content through the same document-space map. */
4246
+ moveVectorMask(layers) {
4247
+ const g = this.geometryValue;
4248
+ if (!this.layerId || this.copyId || !this.moves.includes("pixels") || isIdentityTransform(g)) return layers;
4249
+ const vm = findLayer(layers, this.layerId)?.layer.vectorMask;
4250
+ if (!vm || vm.linked === false) return layers;
4251
+ const warp = hasWarp(g);
4252
+ const f = warp ? transformMap(g) : null;
4253
+ const h = warp ? null : geometryHomography(g);
4254
+ const affine = !warp && h && Math.abs(h[6]) < 1e-12 && Math.abs(h[7]) < 1e-12 && Math.abs(h[8] - 1) < 1e-12 ? [h[0], h[3], h[1], h[4], h[2], h[5]] : void 0;
4255
+ const map = (p) => (f ? f(p) : h ? applyHomography(h, p) : p) ?? p;
4256
+ const moved = transformVectorMask(vm, map, affine);
4257
+ return mapLayer(layers, this.layerId, (l) => ({ ...l, vectorMask: moved }));
4258
+ }
4259
+ resampleContent(final, preview = {}) {
4167
4260
  if (this.smart) return { layers: this.resampleSmart(final, preview), selection: this.startSelection };
4168
4261
  if (this.typeTarget) return { layers: this.resampleType(), selection: this.startSelection };
4169
4262
  const g = this.geometryValue;
@@ -6167,6 +6260,7 @@ var ImageEditorController = class {
6167
6260
  let moved = { ...transformTypeLayer(layer, [1, 0, 0, 1, mx, my]), surface: translateSurface(layer.surface, mx, my) };
6168
6261
  const mask = layer.mask;
6169
6262
  if (mask && mask.linked !== false && !mask.source && (mx || my)) moved = { ...moved, mask: { ...mask, surface: translateSurface(mask.surface, mx, my) } };
6263
+ moved = followLayerMove(moved, mx, my);
6170
6264
  this.commitLayers(mapLayer(this.state.layers, id, () => moved), void 0, commitToHistory);
6171
6265
  const t = moved.transform ?? [1, 0, 0, 1, 0, 0];
6172
6266
  return { offset: { x: Math.round(t[4]), y: Math.round(t[5]) }, bounds: this.getLayerBounds(id) };
@@ -6218,6 +6312,179 @@ var ImageEditorController = class {
6218
6312
  if (this.state.activeLayerId === id) this.state.activeTarget = "pixels";
6219
6313
  });
6220
6314
  }
6315
+ // --- Vector masks ---
6316
+ /** The layer for a vector mask command: not a shape layer (its path is its vector mask), not a depth-masked one. */
6317
+ vectorMaskLayer(id) {
6318
+ const layer = requireLayer(this.state.layers, id).layer;
6319
+ const name = layer.name || id;
6320
+ if (layer.type === "shape") throw new LayerOperationError(`Layer "${name}" is a shape layer, whose path already is its vector mask; change it with set_shape_component, or rasterize the layer first.`);
6321
+ if (layer.mask?.source) throw new LayerOperationError(`Layer "${name}" has a depth mask, which cannot be combined with a vector mask; remove the depth mask first.`);
6322
+ return layer;
6323
+ }
6324
+ requireVectorMask(id) {
6325
+ const layer = this.vectorMaskLayer(id);
6326
+ if (!layer.vectorMask) throw new LayerOperationError(`Layer "${layer.name || id}" has no vector mask; add one with add_vector_mask.`);
6327
+ return { layer, vm: layer.vectorMask };
6328
+ }
6329
+ /** Components from a path spec: `components`, or one `shape` / `subpaths`. */
6330
+ vectorMaskComponents(path) {
6331
+ if (path.components?.length && (path.shape !== void 0 || path.subpaths !== void 0)) throw new LayerOperationError("Give components, or shape / subpaths, not both.");
6332
+ if (path.fillRule !== void 0 && path.fillRule !== "nonzero" && path.fillRule !== "evenodd") throw new LayerOperationError("fillRule must be nonzero or evenodd.");
6333
+ const specs = path.components?.length ? path.components : [path];
6334
+ return specs.map((c, i) => this.componentFrom(c, path.components?.length ? `components[${i}].` : ""));
6335
+ }
6336
+ /**
6337
+ * Layer › Vector Mask: `reveal-all` (an empty path showing everything), `hide-all` (an empty path
6338
+ * hiding everything) or `path` (reveals inside the path given). Replaces an existing vector mask;
6339
+ * the raster mask is kept and both apply. One undo step. Returns the vector mask.
6340
+ */
6341
+ addVectorMask(id, from = "reveal-all", path = {}, trace2 = {}) {
6342
+ this.vectorMaskLayer(id);
6343
+ let vm;
6344
+ if (from === "selection") {
6345
+ const subpaths = this.traceSelection(trace2);
6346
+ vm = { components: [{ op: "combine", subpaths }], fillRule: "evenodd" };
6347
+ } else if (from === "path") {
6348
+ vm = { components: this.vectorMaskComponents(path) };
6349
+ if (path.fillRule === "evenodd") vm.fillRule = "evenodd";
6350
+ } else if (from === "reveal-all" || from === "hide-all") {
6351
+ if (path.components?.length || path.shape !== void 0 || path.subpaths !== void 0) throw new LayerOperationError(`A ${from} vector mask starts empty; use from path to give a path.`);
6352
+ vm = { components: [], ...from === "reveal-all" ? { inverted: true } : {} };
6353
+ } else {
6354
+ throw new LayerOperationError(`Unknown vector mask source "${String(from)}". Use reveal-all, hide-all, path or selection.`);
6355
+ }
6356
+ this.commitLayers(mapLayer(this.state.layers, id, (l) => ({ ...l, vectorMask: vm })));
6357
+ return vm;
6358
+ }
6359
+ /** The selection's outline as subpaths; throws when nothing is selected or nothing is left to trace. */
6360
+ traceSelection(options) {
6361
+ const sel = this.state.selection;
6362
+ if (!sel?.bounds) throw new LayerOperationError("Nothing is selected; select first, then make a path from the selection.");
6363
+ if (options.tolerance !== void 0 && !(options.tolerance >= 0.1 && options.tolerance <= 10)) throw new LayerOperationError("tolerance must be from 0.1 to 10 px.");
6364
+ const subpaths = selectionToSubpaths(sel, options);
6365
+ if (!subpaths.length) throw new LayerOperationError("The selection is too small or too faint to trace (below minArea, or under half selected); lower minArea or threshold.");
6366
+ return subpaths;
6367
+ }
6368
+ /**
6369
+ * Make Work Path: the selection's outline as closed Bézier subpaths in document pixels, holes
6370
+ * included (fill them even-odd), within `tolerance` px. Changes nothing. Throws without a selection.
6371
+ */
6372
+ selectionToPath(options = {}) {
6373
+ const subpaths = this.traceSelection(options);
6374
+ const b = vectorMaskBounds({ components: [{ op: "combine", subpaths }] });
6375
+ const r = (n) => Math.round(n * 100) / 100;
6376
+ return {
6377
+ subpaths: roundSubpaths(subpaths),
6378
+ svg: subpathsToSvgPath(subpaths),
6379
+ fillRule: "evenodd",
6380
+ bounds: b ? { x: r(b.x), y: r(b.y), width: r(b.width), height: r(b.height) } : null,
6381
+ anchors: subpaths.reduce((n, sp) => n + sp.anchors.length, 0)
6382
+ };
6383
+ }
6384
+ /**
6385
+ * Make Selection: fills a path (`shape`, `subpaths` or `components`, document pixels) into a
6386
+ * selection combined with the current one by `mode`, with `feather` px and `antiAlias`. One
6387
+ * undo step. Returns whether anything is selected.
6388
+ */
6389
+ pathToSelection(path, options = {}) {
6390
+ if (options.feather !== void 0 && !(options.feather >= 0 && options.feather <= 1e3)) throw new LayerOperationError("feather must be from 0 to 1000 px.");
6391
+ const components = this.vectorMaskComponents(path);
6392
+ const { width, height } = this.naturalSize;
6393
+ const mask = pathToSelection(components, width, height, { ...options, fillRule: path.fillRule ?? options.fillRule });
6394
+ return this.setSelection(mask, options.mode ?? "replace");
6395
+ }
6396
+ /** Replaces a vector mask's path (its properties are kept). One undo step. Returns the vector mask. */
6397
+ setVectorMaskPath(id, path) {
6398
+ const { vm } = this.requireVectorMask(id);
6399
+ const next = { ...vm, components: this.vectorMaskComponents(path) };
6400
+ if (path.fillRule === "evenodd") next.fillRule = "evenodd";
6401
+ else if (path.fillRule === "nonzero") delete next.fillRule;
6402
+ this.commitLayers(mapLayer(this.state.layers, id, (l) => ({ ...l, vectorMask: next })));
6403
+ return next;
6404
+ }
6405
+ /** Changes a vector mask's enabled, linked, inverted, density or feather (the path is kept). One undo step. */
6406
+ setVectorMaskProps(id, props) {
6407
+ const { vm } = this.requireVectorMask(id);
6408
+ if (props.density !== void 0 && !(props.density >= 0 && props.density <= 1)) throw new LayerOperationError("density must be from 0 to 1.");
6409
+ if (props.feather !== void 0 && !(props.feather >= 0 && props.feather <= 1e3)) throw new LayerOperationError("feather must be from 0 to 1000 px.");
6410
+ const next = { ...vm };
6411
+ for (const k of ["enabled", "linked", "inverted"]) {
6412
+ if (props[k] === void 0) continue;
6413
+ if (typeof props[k] !== "boolean") throw new LayerOperationError(`${k} must be true or false.`);
6414
+ if (k === "inverted" ? props[k] : !props[k]) next[k] = k === "inverted";
6415
+ else delete next[k];
6416
+ }
6417
+ if (props.density !== void 0) {
6418
+ if (props.density === 1) delete next.density;
6419
+ else next.density = props.density;
6420
+ }
6421
+ if (props.feather !== void 0) {
6422
+ if (props.feather === 0) delete next.feather;
6423
+ else next.feather = props.feather;
6424
+ }
6425
+ this.commitLayers(mapLayer(this.state.layers, id, (l) => ({ ...l, vectorMask: next })));
6426
+ return next;
6427
+ }
6428
+ /** Deletes a layer's vector mask (one undo step). */
6429
+ removeVectorMask(id) {
6430
+ this.requireVectorMask(id);
6431
+ this.commitLayers(
6432
+ mapLayer(this.state.layers, id, (l) => {
6433
+ const next = { ...l };
6434
+ delete next.vectorMask;
6435
+ return next;
6436
+ })
6437
+ );
6438
+ }
6439
+ /**
6440
+ * Layer › Rasterize › Vector Mask: multiplies the path (with its density and feather) into the
6441
+ * layer's raster mask, adding one when there is none, and removes the vector mask. The raster
6442
+ * mask keeps its own properties. One undo step. Returns the raster mask.
6443
+ */
6444
+ rasterizeVectorMask(id) {
6445
+ const { layer } = this.requireVectorMask(id);
6446
+ const { width, height } = this.canvasSize;
6447
+ const raster = layer.mask;
6448
+ const plain3 = { ...layer, vectorMask: { ...layer.vectorMask, enabled: true }, mask: raster ? { surface: raster.surface, defaultValue: raster.defaultValue } : void 0 };
6449
+ const baked = effectiveLayerMask(plain3, width, height);
6450
+ const mask = raster ? { ...raster, surface: baked.surface, defaultValue: baked.defaultValue } : baked;
6451
+ this.commitLayers(
6452
+ mapLayer(this.state.layers, id, (l) => {
6453
+ const next = { ...l, mask };
6454
+ delete next.vectorMask;
6455
+ return next;
6456
+ })
6457
+ );
6458
+ return mask;
6459
+ }
6460
+ /** Load Selection from a vector mask's path (density and feather are not applied), inverted with `invert`. */
6461
+ selectionFromVectorMask(id, mode = "replace", invert2 = false) {
6462
+ const { vm } = this.requireVectorMask(id);
6463
+ const { width, height } = this.naturalSize;
6464
+ const cov = componentsCoverage(vm.components, { x: 0, y: 0, width, height }, vm.fillRule ?? "nonzero", vm.inverted === true !== invert2);
6465
+ const bytes = new Uint8Array(cov.length);
6466
+ for (let i = 0; i < cov.length; i++) bytes[i] = Math.round(Math.min(1, Math.max(0, cov[i])) * 255);
6467
+ return this.setSelection(createSelectionMask(width, height, bytes), mode);
6468
+ }
6469
+ /** A layer's vector mask with its paths (rounded), or `null` without one. */
6470
+ getVectorMask(id) {
6471
+ const layer = requireLayer(this.state.layers, id).layer;
6472
+ const vm = layer.vectorMask;
6473
+ if (!vm) return null;
6474
+ const b = vectorMaskBounds(vm);
6475
+ const r = (n) => Math.round(n * 1e3) / 1e3;
6476
+ return {
6477
+ layerId: id,
6478
+ enabled: vm.enabled !== false,
6479
+ linked: vm.linked !== false,
6480
+ inverted: vm.inverted === true,
6481
+ fillRule: vm.fillRule ?? "nonzero",
6482
+ density: vm.density ?? 1,
6483
+ feather: vm.feather ?? 0,
6484
+ bounds: b ? { x: r(b.x), y: r(b.y), width: r(b.width), height: r(b.height) } : null,
6485
+ components: vm.components.map((c, index) => ({ index, op: c.op, live: c.live ? JSON.parse(JSON.stringify(c.live)) : null, subpaths: roundSubpaths(c.subpaths) }))
6486
+ };
6487
+ }
6221
6488
  /** Changes any layer mask's density, feather, enabled or linked flag (pixels kept). Returns the mask. */
6222
6489
  setLayerMaskProps(id, props, commitToHistory = true) {
6223
6490
  const layer = requireLayer(this.state.layers, id).layer;
@@ -10988,7 +11255,7 @@ var DEPTH_MASK_CONTROLS = [
10988
11255
  // src/image-editor/playback.ts
10989
11256
  var IMAGE_PLAYBACK_KIND = "layer";
10990
11257
  var PAINT = /^(paint_stroke|clone_stamp|heal_brush|spot_heal|dodge|burn|sponge|smudge|blur_stroke|sharpen_stroke|history_brush|set_clone_source|set_clone_transform|fill_pixels|clear_pixels|set_paint|set_retouch_options|load_mixer_brush|clean_mixer_brush)$/;
10991
- var SELECT = /^(select_region|select_subject|select_by_color|select_all|deselect|invert_selection|select_rect|select_ellipse|select_polygon|select_line|magic_wand|grow_selection|select_similar|reselect|select_layer_transparency|select_color_range|load_selection_from_image|load_selection_from_mask|set_selection_display|modify_selection|refine_selection|transform_selection|apply_selection_as_mask|layer_via_copy|stroke_selection|content_aware_fill|copy_pixels|cut_pixels|paste_pixels|save_selection|load_selection_from_channel|set_channel_properties|duplicate_channel|delete_channel|load_channel_from_image|set_quick_mask)$/;
11258
+ var SELECT = /^(select_region|select_subject|select_by_color|select_all|deselect|invert_selection|select_rect|select_ellipse|select_polygon|select_line|magic_wand|grow_selection|select_similar|reselect|select_layer_transparency|select_color_range|load_selection_from_image|load_selection_from_mask|set_selection_display|modify_selection|refine_selection|transform_selection|apply_selection_as_mask|layer_via_copy|stroke_selection|content_aware_fill|copy_pixels|cut_pixels|paste_pixels|save_selection|load_selection_from_channel|set_channel_properties|duplicate_channel|delete_channel|load_channel_from_image|set_quick_mask|path_to_selection)$/;
10992
11259
  var ANNOTATE = /^(add_shape_layer|update_shape|add_shape_component|set_shape_component|remove_shape_component|merge_shape_components|add_text_layer|set_text|set_text_style|set_paragraph_style|set_text_frame|convert_text_to_shape)$/;
10993
11260
  var CROP = /^(set_crop|crop_to_selection|reset_crop|set_rotation|flip_image|transform_layer|warp_layer|flip_layer|offset_layer)$/;
10994
11261
  var ADJUST = /^(set_adjustments|add_adjustment_layer|update_adjustment_layer|remove_adjustment_layer|move_adjustment_layer)$/;
@@ -11658,11 +11925,12 @@ function createImageSelectionTools(controller) {
11658
11925
  const loadFromMask = {
11659
11926
  name: "load_selection_from_mask",
11660
11927
  title: "Load a selection from a layer mask",
11661
- description: "Load Selection from a layer's mask (Ctrl/\u2318-click its mask thumbnail): the mask's pixels become the selection, combined by mode (density and feather are not applied). Use it to reuse a mask on another layer or to edit it as a selection. Fails when the layer has no painted mask. One undoable step. Returns the selection.",
11928
+ description: "Load Selection from a layer's mask (Ctrl/\u2318-click its mask thumbnail): the raster mask's pixels, or with source vector the vector mask's path, become the selection, combined by mode (density and feather are not applied). Use it to reuse a mask on another layer or to edit it as a selection. Fails when the layer has no such mask. One undoable step. Returns the selection.",
11662
11929
  inputSchema: {
11663
11930
  type: "object",
11664
11931
  properties: {
11665
11932
  layerId: { type: "string", minLength: 1, description: "The layer whose mask to load (see list_layers)." },
11933
+ source: { type: "string", enum: ["pixels", "vector"], description: "pixels: the raster (layer) mask (default); vector: the vector mask's path." },
11666
11934
  invert: { type: "boolean", description: "Select what the mask hides instead (default false)." },
11667
11935
  mode: modeSchema
11668
11936
  },
@@ -11674,6 +11942,11 @@ function createImageSelectionTools(controller) {
11674
11942
  const id = String(args.layerId ?? "");
11675
11943
  const layer = controller.getLayer(id);
11676
11944
  if (!layer) throw new Error(`No layer "${id}". Call list_layers for ids.`);
11945
+ if (args.source === "vector") {
11946
+ if (!layer.vectorMask) throw new Error(`Layer "${id}" has no vector mask. Add one with add_vector_mask, or load its pixels mask (source pixels).`);
11947
+ controller.selectionFromVectorMask(id, parseSelectionMode(args.mode), args.invert === true);
11948
+ return summary(controller);
11949
+ }
11677
11950
  if (!layer.mask) throw new Error(`Layer "${id}" has no mask. Add one with add_layer_mask, or pick a layer with a mask.`);
11678
11951
  if (layer.mask.source) throw new Error(`Layer "${id}" has a computed (depth) mask, not a painted one, so it cannot be loaded as a selection.`);
11679
11952
  controller.selectionFromLayerMask(id, parseSelectionMode(args.mode), args.invert === true);
@@ -12540,6 +12813,7 @@ function describeLayer(layer, activeId, boundsOf) {
12540
12813
  clipped: Boolean(layer.clipped),
12541
12814
  locks: layer.locks ? { ...layer.locks } : {},
12542
12815
  mask: maskSummary(layer.mask),
12816
+ vectorMask: layer.vectorMask ? { enabled: layer.vectorMask.enabled !== false, linked: layer.vectorMask.linked !== false, inverted: layer.vectorMask.inverted === true, components: layer.vectorMask.components.length, bounds: vectorMaskBounds(layer.vectorMask) } : null,
12543
12817
  effects: layer.effects ? { enabled: layer.effects.enabled, scale: layer.effects.scale ?? 1, items: layer.effects.items.map((e) => ({ id: e.id, kind: e.kind, enabled: e.enabled })) } : null,
12544
12818
  active: layer.id === activeId
12545
12819
  };
@@ -15726,7 +16000,170 @@ function createImageShapeTools(controller) {
15726
16000
  annotations: { title: "Merge shape components", ...DESTRUCTIVE6 },
15727
16001
  handler: ({ layerId }) => controller.mergeShapeComponents(shapeId(layerId))
15728
16002
  };
15729
- return [get, add2, update, setComponent, addComponent, removeComponent, merge];
16003
+ const layerArg = (v) => {
16004
+ const id = String(v ?? "");
16005
+ if (!controller.getLayer(id)) throw new Error(`No layer "${id}". Call list_layers for ids.`);
16006
+ return id;
16007
+ };
16008
+ const pathSpec = (args) => {
16009
+ const spec = { ...geometry(args) };
16010
+ if (args.components !== void 0) spec.components = args.components.map(geometry);
16011
+ if (args.fillRule !== void 0) spec.fillRule = args.fillRule;
16012
+ return spec;
16013
+ };
16014
+ const PATH_PROPS = {
16015
+ ...GEOMETRY2,
16016
+ components: {
16017
+ type: "array",
16018
+ minItems: 1,
16019
+ description: "Several components instead of shape / subpaths, bottom first, each with its path operation (combine, subtract, intersect, exclude).",
16020
+ items: { type: "object", description: "One component.", properties: { ...GEOMETRY2, op: OP }, additionalProperties: false }
16021
+ },
16022
+ fillRule: { type: "string", enum: ["nonzero", "evenodd"], description: "Winding rule within a component (default nonzero)." }
16023
+ };
16024
+ const LAYER = { type: "string", minLength: 1, description: "The layer (see list_layers). Not a shape layer: its path already is its vector mask." };
16025
+ const TRACE_PROPS = {
16026
+ tolerance: { type: "number", minimum: 0.1, maximum: 10, description: "How far (px) the path may stray from the selection edge (default 0.5; lower is tighter, more anchors)." },
16027
+ cornerRoundness: { type: "number", minimum: 0, maximum: 1, description: "0: straight sides and sharp corners; 1: as smooth as the outline allows (default 0.5)." },
16028
+ minArea: { type: "number", minimum: 0, description: "Islands and holes smaller than this many square px are left out (default 4)." }
16029
+ };
16030
+ const TRACE_KEYS = ["tolerance", "cornerRoundness", "minArea"];
16031
+ const traceOptions = (args) => {
16032
+ const o = {};
16033
+ for (const k of TRACE_KEYS) if (typeof args[k] === "number") o[k] = args[k];
16034
+ return o;
16035
+ };
16036
+ const selectionToPath = {
16037
+ name: "selection_to_path",
16038
+ title: "Make a path from the selection",
16039
+ description: "Make Work Path: traces the selection's outline (where it is at least half selected) into closed B\xE9zier subpaths in document pixels, holes included, within tolerance px. Changes nothing. Returns svg (compact SVG path data) and fillRule evenodd, ready for add_vector_mask, set_vector_mask_path or add_shape_layer (pass the svg as subpaths and fillRule evenodd), plus bounds and the anchor count. Use add_vector_mask from selection to do it in one step.",
16040
+ inputSchema: { type: "object", properties: { ...TRACE_PROPS, anchors: { type: "boolean", description: "Also return the subpaths as anchors with handles (default false: svg only)." } }, additionalProperties: false },
16041
+ annotations: { title: "Make a path from the selection", ...READ10 },
16042
+ handler: (args) => {
16043
+ const out = controller.selectionToPath(traceOptions(args));
16044
+ const { subpaths, ...rest } = out;
16045
+ return args.anchors === true ? out : rest;
16046
+ }
16047
+ };
16048
+ const pathToSelection2 = {
16049
+ name: "path_to_selection",
16050
+ title: "Make a selection from a path",
16051
+ description: "Make Selection: fills a path (a live shape, free subpaths or SVG path data, or components, in document pixels, as add_shape_layer takes) into a selection, combined with the current one by mode, with feather px and antiAlias. Use it to select along B\xE9zier curves, or to turn a path from selection_to_path or get_vector_mask back into a selection. One undo step. Returns the selection.",
16052
+ inputSchema: {
16053
+ type: "object",
16054
+ $defs: DEFS2,
16055
+ properties: {
16056
+ ...PATH_PROPS,
16057
+ feather: { type: "number", minimum: 0, maximum: 1e3, description: "Feather radius in px (default 0)." },
16058
+ antiAlias: { type: "boolean", description: "Soft edges (default true); false makes every pixel fully in or out." },
16059
+ mode: { type: "string", enum: ["replace", "add", "subtract", "intersect"], description: "How it combines with the current selection (default replace)." }
16060
+ },
16061
+ additionalProperties: false
16062
+ },
16063
+ annotations: { title: "Make a selection from a path", ...WRITE8 },
16064
+ handler: (args) => {
16065
+ const mode = args.mode;
16066
+ controller.pathToSelection(pathSpec(args), { feather: args.feather, antiAlias: args.antiAlias, mode });
16067
+ const sel = controller.getSelection();
16068
+ return { empty: !sel, bounds: sel?.bounds ?? null, area: Math.round(sel ? selectionArea(sel) : 0) };
16069
+ }
16070
+ };
16071
+ const getVectorMask = {
16072
+ name: "get_vector_mask",
16073
+ title: "Read a vector mask",
16074
+ description: "Reads a layer's vector mask: enabled, linked, inverted, fill rule, density, feather, the bounds of its path and its components (op, live shape, subpaths in document pixels). Use it before set_vector_mask_path to edit the path. Returns null when the layer has none.",
16075
+ inputSchema: { type: "object", properties: { layerId: LAYER }, required: ["layerId"], additionalProperties: false },
16076
+ annotations: { title: "Read a vector mask", ...READ10 },
16077
+ handler: ({ layerId }) => ({ vectorMask: controller.getVectorMask(layerArg(layerId)) })
16078
+ };
16079
+ const addVectorMask = {
16080
+ name: "add_vector_mask",
16081
+ title: "Add a vector mask",
16082
+ description: "Layer \u203A Vector Mask: a path that reveals the layer inside it and stays sharp at any size, on top of any raster mask (both apply). from path takes a live shape, free subpaths or components like add_shape_layer; from selection traces the current selection (within tolerance px); reveal-all and hide-all start with an empty path. Use it for hard-edged geometric or traced cutouts that must stay editable; use add_layer_mask for soft or painted ones. A linked vector mask moves and transforms with the layer. Replaces an existing vector mask. One undo step. Returns the vector mask.",
16083
+ inputSchema: {
16084
+ type: "object",
16085
+ $defs: DEFS2,
16086
+ properties: {
16087
+ layerId: LAYER,
16088
+ from: { type: "string", enum: ["path", "selection", "reveal-all", "hide-all"], description: "path (default when a shape, subpaths or components are given), selection (trace the current selection), reveal-all or hide-all (an empty path)." },
16089
+ ...PATH_PROPS,
16090
+ ...TRACE_PROPS
16091
+ },
16092
+ required: ["layerId"],
16093
+ additionalProperties: false
16094
+ },
16095
+ annotations: { title: "Add a vector mask", ...WRITE8 },
16096
+ handler: (args) => {
16097
+ const id = layerArg(args.layerId);
16098
+ const spec = pathSpec(args);
16099
+ const hasPath = spec.components?.length || spec.shape !== void 0 || spec.subpaths !== void 0;
16100
+ const from = args.from ?? (hasPath ? "path" : "reveal-all");
16101
+ if (from !== "selection" && TRACE_KEYS.some((k) => args[k] !== void 0)) throw new Error("tolerance, cornerRoundness and minArea apply only to from selection.");
16102
+ controller.addVectorMask(id, from, spec, traceOptions(args));
16103
+ return { vectorMask: controller.getVectorMask(id) };
16104
+ }
16105
+ };
16106
+ const setVectorMaskPath = {
16107
+ name: "set_vector_mask_path",
16108
+ title: "Replace a vector mask path",
16109
+ description: "Replaces the path of a layer's vector mask (shape, subpaths or components, document pixels); enabled, linked, inverted, density and feather are kept. Read the current path with get_vector_mask first to change part of it. One undo step. Returns the vector mask.",
16110
+ inputSchema: { type: "object", $defs: DEFS2, properties: { layerId: LAYER, ...PATH_PROPS }, required: ["layerId"], additionalProperties: false },
16111
+ annotations: { title: "Replace a vector mask path", ...WRITE_IDEMPOTENT5 },
16112
+ handler: (args) => {
16113
+ const id = layerArg(args.layerId);
16114
+ controller.setVectorMaskPath(id, pathSpec(args));
16115
+ return { vectorMask: controller.getVectorMask(id) };
16116
+ }
16117
+ };
16118
+ const setVectorMaskProps = {
16119
+ name: "set_vector_mask_properties",
16120
+ title: "Set vector mask properties",
16121
+ description: "Changes a layer's vector mask without touching its path: enabled (false ignores it), linked (false: it stays put when the layer moves or transforms), inverted (reveal outside the path), density 0..1 and feather in px (soften the edge without losing the path). One undo step. Returns the vector mask.",
16122
+ inputSchema: {
16123
+ type: "object",
16124
+ properties: {
16125
+ layerId: LAYER,
16126
+ enabled: { type: "boolean", description: "false ignores the vector mask but keeps it." },
16127
+ linked: { type: "boolean", description: "false keeps the path in place when the layer moves or transforms." },
16128
+ inverted: { type: "boolean", description: "true reveals outside the path instead." },
16129
+ density: { type: "number", minimum: 0, maximum: 1, description: "Density 0..1 (default 1): at 0.5 the hidden area still shows half." },
16130
+ feather: { type: "number", minimum: 0, maximum: 1e3, description: "Feather \u03C3 in px (default 0)." }
16131
+ },
16132
+ required: ["layerId"],
16133
+ additionalProperties: false
16134
+ },
16135
+ annotations: { title: "Set vector mask properties", ...WRITE_IDEMPOTENT5 },
16136
+ handler: ({ layerId, ...props }) => {
16137
+ const id = layerArg(layerId);
16138
+ controller.setVectorMaskProps(id, props);
16139
+ return { vectorMask: controller.getVectorMask(id) };
16140
+ }
16141
+ };
16142
+ const rasterizeVectorMask = {
16143
+ name: "rasterize_vector_mask",
16144
+ title: "Rasterize a vector mask",
16145
+ description: "Layer \u203A Rasterize \u203A Vector Mask: multiplies the path (with its density and feather) into the layer's raster mask, adding one if needed, and removes the vector mask, so the mask can be painted. The path can no longer be edited (undo_image brings it back). One undo step. Returns the layer's raster mask summary.",
16146
+ inputSchema: { type: "object", properties: { layerId: LAYER }, required: ["layerId"], additionalProperties: false },
16147
+ annotations: { title: "Rasterize a vector mask", ...DESTRUCTIVE6 },
16148
+ handler: ({ layerId }) => {
16149
+ const id = layerArg(layerId);
16150
+ const mask = controller.rasterizeVectorMask(id);
16151
+ return { layerId: id, mask: { defaultValue: mask.defaultValue, density: mask.density ?? 1, feather: mask.feather ?? 0, enabled: mask.enabled !== false } };
16152
+ }
16153
+ };
16154
+ const removeVectorMask = {
16155
+ name: "remove_vector_mask",
16156
+ title: "Delete a vector mask",
16157
+ description: "Deletes a layer's vector mask without applying it (the raster mask, if any, stays). Disable it instead with set_vector_mask_properties enabled false to keep it. One undo step. Returns the layer id.",
16158
+ inputSchema: { type: "object", properties: { layerId: LAYER }, required: ["layerId"], additionalProperties: false },
16159
+ annotations: { title: "Delete a vector mask", ...DESTRUCTIVE6 },
16160
+ handler: ({ layerId }) => {
16161
+ const id = layerArg(layerId);
16162
+ controller.removeVectorMask(id);
16163
+ return { layerId: id, vectorMask: null };
16164
+ }
16165
+ };
16166
+ return [get, add2, update, setComponent, addComponent, removeComponent, merge, getVectorMask, addVectorMask, setVectorMaskPath, setVectorMaskProps, rasterizeVectorMask, removeVectorMask, selectionToPath, pathToSelection2];
15730
16167
  }
15731
16168
 
15732
16169
  // src/image-editor/type-tools.ts
@@ -16393,6 +16830,11 @@ function maskThumbnailClick(controller, layer, e) {
16393
16830
  controller.setActiveLayer(layer.id, "mask");
16394
16831
  }
16395
16832
  }
16833
+ function vectorMaskThumbnailClick(controller, layer, e) {
16834
+ if (!layer.vectorMask) return;
16835
+ if (e.ctrlKey || e.metaKey) controller.selectionFromVectorMask(layer.id, selectionModeFromModifiers(e, "replace"));
16836
+ else if (e.shiftKey) controller.setVectorMaskProps(layer.id, { enabled: layer.vectorMask.enabled === false });
16837
+ }
16396
16838
  function layerThumbnailClick(controller, layer, e) {
16397
16839
  if (!(e.ctrlKey || e.metaKey)) return false;
16398
16840
  controller.selectLayerTransparency(layer.id, { mode: selectionModeFromModifiers(e, "replace") });
@@ -16491,6 +16933,12 @@ function layerMenu(controller) {
16491
16933
  { action: "delete-mask", label: "Delete Layer Mask", enabled: Boolean(active?.mask) },
16492
16934
  { action: "toggle-mask", label: active?.mask?.enabled === false ? "Enable Layer Mask" : "Disable Layer Mask", enabled: Boolean(active?.mask) },
16493
16935
  { action: "toggle-mask-link", label: active?.mask?.linked === false ? "Link Layer Mask" : "Unlink Layer Mask", enabled: Boolean(active?.mask && !active.mask.source) },
16936
+ { action: "add-vector-mask", label: "Add Vector Mask (Reveal All)", enabled: Boolean(active && active.type !== "shape" && !active.mask?.source) },
16937
+ { action: "add-vector-mask-hide", label: "Add Vector Mask (Hide All)", enabled: Boolean(active && active.type !== "shape" && !active.mask?.source) },
16938
+ { action: "add-vector-mask-selection", label: "Add Vector Mask from Selection", enabled: Boolean(active && active.type !== "shape" && !active.mask?.source) && hasSel },
16939
+ { action: "delete-vector-mask", label: "Delete Vector Mask", enabled: Boolean(active?.vectorMask) },
16940
+ { action: "toggle-vector-mask", label: active?.vectorMask?.enabled === false ? "Enable Vector Mask" : "Disable Vector Mask", enabled: Boolean(active?.vectorMask) },
16941
+ { action: "rasterize-vector-mask", label: "Rasterize Vector Mask", enabled: Boolean(active?.vectorMask) },
16494
16942
  { action: "rasterize", label: "Rasterize Layer", enabled: active?.type === "annotation" || active?.type === "smart" || active?.type === "shape" || active?.type === "type" || isPixel && Boolean(active?.type === "pixel" && active.filters?.length) },
16495
16943
  { action: "convert-smart", label: "Convert to Smart Object", enabled: Boolean(active && active.type !== "annotation") },
16496
16944
  { action: "edit-contents", label: "Edit Contents", enabled: active?.type === "smart" },
@@ -16562,6 +17010,22 @@ async function runLayerMenuAction(controller, action) {
16562
17010
  case "toggle-mask-link":
16563
17011
  if (id && active?.mask) controller.setLayerMaskProps(id, { linked: active.mask.linked === false });
16564
17012
  break;
17013
+ case "add-vector-mask":
17014
+ case "add-vector-mask-hide":
17015
+ if (id) controller.addVectorMask(id, action === "add-vector-mask" ? "reveal-all" : "hide-all");
17016
+ break;
17017
+ case "add-vector-mask-selection":
17018
+ if (id) controller.addVectorMask(id, "selection");
17019
+ break;
17020
+ case "delete-vector-mask":
17021
+ if (id) controller.removeVectorMask(id);
17022
+ break;
17023
+ case "toggle-vector-mask":
17024
+ if (id && active?.vectorMask) controller.setVectorMaskProps(id, { enabled: active.vectorMask.enabled === false });
17025
+ break;
17026
+ case "rasterize-vector-mask":
17027
+ if (id) controller.rasterizeVectorMask(id);
17028
+ break;
16565
17029
  case "rasterize":
16566
17030
  if (id) await controller.rasterizeLayer(id);
16567
17031
  break;
@@ -18154,6 +18618,7 @@ export {
18154
18618
  combineSelections,
18155
18619
  compileAdjustmentLayer,
18156
18620
  componentPath,
18621
+ componentsCoverage,
18157
18622
  compositeDeep,
18158
18623
  compositeSurfaceOver,
18159
18624
  computeHistogram,
@@ -18273,6 +18738,7 @@ export {
18273
18738
  effectLayerTiming,
18274
18739
  effectPixelsToSurface,
18275
18740
  effectReach,
18741
+ effectiveLayerMask,
18276
18742
  effectiveProof,
18277
18743
  effectsFingerprint,
18278
18744
  effectsMargin,
@@ -18309,6 +18775,7 @@ export {
18309
18775
  flattenPath,
18310
18776
  flipCorners,
18311
18777
  flowField,
18778
+ followLayerMove,
18312
18779
  fontOf,
18313
18780
  formatControlValue,
18314
18781
  fullSelection,
@@ -18373,6 +18840,7 @@ export {
18373
18840
  invertMatrix3,
18374
18841
  invertSelection,
18375
18842
  isActiveFilter,
18843
+ isActiveVectorMask,
18376
18844
  isAdjustmentKind,
18377
18845
  isBitDepth,
18378
18846
  isBlendMode,
@@ -18512,6 +18980,7 @@ export {
18512
18980
  offsetLayer,
18513
18981
  offsetShape,
18514
18982
  offsetSmartObject,
18983
+ offsetVectorMask,
18515
18984
  openMaskFile,
18516
18985
  openPsdFile,
18517
18986
  packRgba,
@@ -18538,6 +19007,7 @@ export {
18538
19007
  patchTextStyle,
18539
19008
  pathStrokeCoverage,
18540
19009
  pathStrokeDabs,
19010
+ pathToSelection,
18541
19011
  perDabStrength,
18542
19012
  photoFilterMatrix,
18543
19013
  pigment8,
@@ -18632,6 +19102,7 @@ export {
18632
19102
  resolveMosaicRegionBounds,
18633
19103
  resolvePaintGradient,
18634
19104
  resolveProfile,
19105
+ resolveVectorMasks,
18635
19106
  restyleRange,
18636
19107
  retouchFilter,
18637
19108
  reversePaintGradient,
@@ -18665,6 +19136,7 @@ export {
18665
19136
  selectionRing,
18666
19137
  selectionToCoverage,
18667
19138
  selectionToPixels,
19139
+ selectionToSubpaths,
18668
19140
  selectionValueAt,
18669
19141
  serializeChannels,
18670
19142
  serializeColorMode,
@@ -18722,6 +19194,7 @@ export {
18722
19194
  stepSlotTs,
18723
19195
  strokeGrainCatch,
18724
19196
  styleAt,
19197
+ subpathsToSvgPath,
18725
19198
  sumHistograms,
18726
19199
  surfaceBytes,
18727
19200
  surfaceDepth,
@@ -18761,6 +19234,7 @@ export {
18761
19234
  transformShapeComponents,
18762
19235
  transformSurface,
18763
19236
  transformTypeLayer,
19237
+ transformVectorMask,
18764
19238
  transformedBounds,
18765
19239
  transformedBox,
18766
19240
  transformedPoint,
@@ -18787,6 +19261,10 @@ export {
18787
19261
  validateEffectGradient,
18788
19262
  validateLayerEffect,
18789
19263
  validateLayerEffects,
19264
+ vectorMaskBounds,
19265
+ vectorMaskCoverage,
19266
+ vectorMaskThumbnail,
19267
+ vectorMaskThumbnailClick,
18790
19268
  versionedDescriptorBytes,
18791
19269
  vibranceLinear,
18792
19270
  vibrancePixel,