@pixerate/editor 0.189.0 → 0.190.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 (31) hide show
  1. package/dist/{chunk-ZEHTUR7Z.js → chunk-7ZWLDVFH.js} +971 -753
  2. package/dist/{chunk-ED4L4UD6.cjs → chunk-T4U4O6DS.cjs} +1005 -787
  3. package/dist/image-editor/controller.d.cts +51 -1
  4. package/dist/image-editor/controller.d.ts +51 -1
  5. package/dist/image-editor/free-transform.d.cts +3 -6
  6. package/dist/image-editor/free-transform.d.ts +3 -6
  7. package/dist/image-editor/index.cjs +1219 -901
  8. package/dist/image-editor/index.d.cts +1 -0
  9. package/dist/image-editor/index.d.ts +1 -0
  10. package/dist/image-editor/index.js +321 -3
  11. package/dist/image-editor/layer-tools.d.cts +1 -1
  12. package/dist/image-editor/layer-tools.d.ts +1 -1
  13. package/dist/image-editor/layers/compositor.d.cts +3 -0
  14. package/dist/image-editor/layers/compositor.d.ts +3 -0
  15. package/dist/image-editor/layers/model.d.cts +23 -0
  16. package/dist/image-editor/layers/model.d.ts +23 -0
  17. package/dist/image-editor/layers/panel.d.cts +11 -1
  18. package/dist/image-editor/layers/panel.d.ts +11 -1
  19. package/dist/image-editor/layers/serialize.d.cts +11 -0
  20. package/dist/image-editor/layers/serialize.d.ts +11 -0
  21. package/dist/image-editor/layers/shape.d.cts +5 -0
  22. package/dist/image-editor/layers/shape.d.ts +5 -0
  23. package/dist/image-editor/layers/vector-mask.d.cts +51 -0
  24. package/dist/image-editor/layers/vector-mask.d.ts +51 -0
  25. package/dist/image-editor/psd/import.d.cts +0 -7
  26. package/dist/image-editor/psd/import.d.ts +0 -7
  27. package/dist/paint-editor/index.cjs +6 -6
  28. package/dist/paint-editor/index.js +1 -1
  29. package/package.json +1 -1
  30. package/skills/pixerate-image-editor/SKILL.md +6 -0
  31. package/skills/pixerate-image-selection-masking/SKILL.md +6 -2
@@ -18,6 +18,16 @@ export declare function maskThumbnailClick(controller: ImageEditorController, la
18
18
  shiftKey?: boolean;
19
19
  altKey?: boolean;
20
20
  }): void;
21
+ /**
22
+ * A click on a layer's vector mask thumbnail: Ctrl/⌘-click loads the path as the selection (Shift
23
+ * adds, Alt subtracts, both intersect) and Shift-click turns the vector mask on or off.
24
+ */
25
+ export declare function vectorMaskThumbnailClick(controller: ImageEditorController, layer: ImageLayer, e: {
26
+ ctrlKey?: boolean;
27
+ metaKey?: boolean;
28
+ shiftKey?: boolean;
29
+ altKey?: boolean;
30
+ }): void;
21
31
  /**
22
32
  * A click on a layer's pixel thumbnail: Ctrl/⌘-click loads the layer's transparency as the
23
33
  * selection (Shift adds, Alt subtracts, both intersect) and returns true; other clicks are left
@@ -57,7 +67,7 @@ export type DropZone = 'above' | 'below' | 'inside';
57
67
  export declare function dropZone(fraction: number, isGroup: boolean): DropZone;
58
68
  /** The placement for `move_layer` when `dragId` is dropped on row `targetId` in `zone`, or `null` for a no-op. */
59
69
  export declare function dropPlacement(layers: readonly ImageLayer[], dragId: string, targetId: string, zone: DropZone): LayerPlacement | null;
60
- export type LayerMenuAction = 'new-layer' | 'new-group' | 'duplicate' | 'delete' | 'group-selected' | 'ungroup' | 'merge-down' | 'merge-visible' | 'flatten' | 'via-copy' | 'via-cut' | 'add-mask' | 'add-mask-selection' | 'add-mask-hide-selection' | 'add-mask-transparency' | 'select-transparency' | 'apply-mask' | 'delete-mask' | 'toggle-mask' | 'toggle-mask-link' | 'toggle-clip' | 'rasterize' | 'convert-smart' | 'edit-contents' | 'smart-via-copy' | 'add-filter-mask';
70
+ export type LayerMenuAction = 'new-layer' | 'new-group' | 'duplicate' | 'delete' | 'group-selected' | 'ungroup' | 'merge-down' | 'merge-visible' | 'flatten' | 'via-copy' | 'via-cut' | 'add-mask' | 'add-vector-mask' | 'add-vector-mask-hide' | 'delete-vector-mask' | 'toggle-vector-mask' | 'rasterize-vector-mask' | 'add-mask-selection' | 'add-mask-hide-selection' | 'add-mask-transparency' | 'select-transparency' | 'apply-mask' | 'delete-mask' | 'toggle-mask' | 'toggle-mask-link' | 'toggle-clip' | 'rasterize' | 'convert-smart' | 'edit-contents' | 'smart-via-copy' | 'add-filter-mask';
61
71
  export interface LayerMenuItem {
62
72
  action: LayerMenuAction;
63
73
  label: string;
@@ -40,9 +40,20 @@ interface SerializedLayerBase {
40
40
  blend: string;
41
41
  clipped?: boolean;
42
42
  mask?: SerializedRasterMask;
43
+ vectorMask?: SerializedVectorMask;
43
44
  locks?: LayerLocks;
44
45
  effects?: SerializedLayerEffects;
45
46
  }
47
+ /** A stored vector mask: its components as JSON (document pixels) and its properties. */
48
+ export interface SerializedVectorMask {
49
+ components: unknown[];
50
+ fillRule?: 'evenodd';
51
+ inverted?: true;
52
+ enabled?: false;
53
+ linked?: false;
54
+ density?: number;
55
+ feather?: number;
56
+ }
46
57
  /** A layer's effects: the effect list as JSON plus the PSD block they came from (base64). */
47
58
  export interface SerializedLayerEffects {
48
59
  enabled: boolean;
@@ -40,9 +40,20 @@ interface SerializedLayerBase {
40
40
  blend: string;
41
41
  clipped?: boolean;
42
42
  mask?: SerializedRasterMask;
43
+ vectorMask?: SerializedVectorMask;
43
44
  locks?: LayerLocks;
44
45
  effects?: SerializedLayerEffects;
45
46
  }
47
+ /** A stored vector mask: its components as JSON (document pixels) and its properties. */
48
+ export interface SerializedVectorMask {
49
+ components: unknown[];
50
+ fillRule?: 'evenodd';
51
+ inverted?: true;
52
+ enabled?: false;
53
+ linked?: false;
54
+ density?: number;
55
+ feather?: number;
56
+ }
46
57
  /** A layer's effects: the effect list as JSON plus the PSD block they came from (base64). */
47
58
  export interface SerializedLayerEffects {
48
59
  enabled: boolean;
@@ -50,6 +50,11 @@ export declare function shapeGeometryBounds(layer: Pick<ShapeLayer, 'components'
50
50
  width: number;
51
51
  height: number;
52
52
  } | null;
53
+ /**
54
+ * Coverage (0..1) of `components` over `rect`, filled with `rule` and combined by their ops;
55
+ * `inverted` covers the outside instead. Used for vector masks.
56
+ */
57
+ export declare function componentsCoverage(components: readonly ShapeComponent[], rect: CropBox, rule?: ShapeFillRule, inverted?: boolean): Float32Array;
53
58
  /** Where a shape renders: the canvas grown by the margin. */
54
59
  export declare function shapeRenderClip(env: Pick<ShapeEnv, 'width' | 'height'>): CropBox;
55
60
  /**
@@ -50,6 +50,11 @@ export declare function shapeGeometryBounds(layer: Pick<ShapeLayer, 'components'
50
50
  width: number;
51
51
  height: number;
52
52
  } | null;
53
+ /**
54
+ * Coverage (0..1) of `components` over `rect`, filled with `rule` and combined by their ops;
55
+ * `inverted` covers the outside instead. Used for vector masks.
56
+ */
57
+ export declare function componentsCoverage(components: readonly ShapeComponent[], rect: CropBox, rule?: ShapeFillRule, inverted?: boolean): Float32Array;
53
58
  /** Where a shape renders: the canvas grown by the margin. */
54
59
  export declare function shapeRenderClip(env: Pick<ShapeEnv, 'width' | 'height'>): CropBox;
55
60
  /**
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Vector masks: a path on a layer that reveals it (Photoshop's Layer › Vector Mask). The paths are
3
+ * shape components in document pixels; the compositor sees them through `resolveVectorMasks`, which
4
+ * gives each layer with a vector mask a raster mask that is its raster mask times the path's
5
+ * coverage, so the tile engine and its caches need nothing new.
6
+ */
7
+ import type { CropBox } from '../types.cjs';
8
+ import type { AffineMatrix } from '../../geom/types.cjs';
9
+ import type { ImageLayer, RasterMask, VectorMask } from './model.cjs';
10
+ /** Whether a vector mask takes part in compositing. */
11
+ export declare function isActiveVectorMask(vm: VectorMask | undefined): vm is VectorMask;
12
+ /**
13
+ * The vector mask's coverage (0..1) over the `width × height` canvas, with its feather and
14
+ * density applied.
15
+ */
16
+ export declare function vectorMaskCoverage(vm: VectorMask, width: number, height: number): Float32Array;
17
+ /**
18
+ * The raster mask a layer composites with: its raster mask times its vector mask (layer space,
19
+ * feather and density baked in), or the raster mask alone when there is no active vector mask.
20
+ * A depth (parametric) mask cannot be combined, so it wins.
21
+ */
22
+ export declare function effectiveLayerMask(layer: ImageLayer, width: number, height: number): RasterMask | undefined;
23
+ /**
24
+ * The stack as the compositor should see it: each layer with an active vector mask replaced by a
25
+ * copy whose `mask` is `effectiveLayerMask` (cached per layer object and canvas size, so the
26
+ * engine's tile caches keep working). Returns `layers` itself when there are none.
27
+ */
28
+ export declare function resolveVectorMasks(layers: readonly ImageLayer[], width: number, height: number): readonly ImageLayer[];
29
+ /** A vector mask moved by (dx, dy) document pixels. */
30
+ export declare function offsetVectorMask(vm: VectorMask, dx: number, dy: number): VectorMask;
31
+ /** A vector mask with its paths mapped by `f` (document space); `affine` keeps live shapes live. */
32
+ export declare function transformVectorMask(vm: VectorMask, f: (p: {
33
+ x: number;
34
+ y: number;
35
+ }) => {
36
+ x: number;
37
+ y: number;
38
+ }, affine?: AffineMatrix): VectorMask;
39
+ /** A linked vector mask moved with its layer by (dx, dy); unlinked ones stay. */
40
+ export declare function followLayerMove<T extends ImageLayer>(layer: T, dx: number, dy: number): T;
41
+ /** Bounds of a vector mask's paths (document pixels), or `null` when it has none. */
42
+ export declare function vectorMaskBounds(vm: VectorMask): CropBox | null;
43
+ /**
44
+ * A vector mask's thumbnail over the `width × height` canvas, at most `maxSide` px: grey RGBA,
45
+ * white where the path reveals (cached per mask object).
46
+ */
47
+ export declare function vectorMaskThumbnail(vm: VectorMask, width: number, height: number, maxSide?: number): {
48
+ width: number;
49
+ height: number;
50
+ data: Uint8ClampedArray;
51
+ };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Vector masks: a path on a layer that reveals it (Photoshop's Layer › Vector Mask). The paths are
3
+ * shape components in document pixels; the compositor sees them through `resolveVectorMasks`, which
4
+ * gives each layer with a vector mask a raster mask that is its raster mask times the path's
5
+ * coverage, so the tile engine and its caches need nothing new.
6
+ */
7
+ import type { CropBox } from '../types.js';
8
+ import type { AffineMatrix } from '../../geom/types.js';
9
+ import type { ImageLayer, RasterMask, VectorMask } from './model.js';
10
+ /** Whether a vector mask takes part in compositing. */
11
+ export declare function isActiveVectorMask(vm: VectorMask | undefined): vm is VectorMask;
12
+ /**
13
+ * The vector mask's coverage (0..1) over the `width × height` canvas, with its feather and
14
+ * density applied.
15
+ */
16
+ export declare function vectorMaskCoverage(vm: VectorMask, width: number, height: number): Float32Array;
17
+ /**
18
+ * The raster mask a layer composites with: its raster mask times its vector mask (layer space,
19
+ * feather and density baked in), or the raster mask alone when there is no active vector mask.
20
+ * A depth (parametric) mask cannot be combined, so it wins.
21
+ */
22
+ export declare function effectiveLayerMask(layer: ImageLayer, width: number, height: number): RasterMask | undefined;
23
+ /**
24
+ * The stack as the compositor should see it: each layer with an active vector mask replaced by a
25
+ * copy whose `mask` is `effectiveLayerMask` (cached per layer object and canvas size, so the
26
+ * engine's tile caches keep working). Returns `layers` itself when there are none.
27
+ */
28
+ export declare function resolveVectorMasks(layers: readonly ImageLayer[], width: number, height: number): readonly ImageLayer[];
29
+ /** A vector mask moved by (dx, dy) document pixels. */
30
+ export declare function offsetVectorMask(vm: VectorMask, dx: number, dy: number): VectorMask;
31
+ /** A vector mask with its paths mapped by `f` (document space); `affine` keeps live shapes live. */
32
+ export declare function transformVectorMask(vm: VectorMask, f: (p: {
33
+ x: number;
34
+ y: number;
35
+ }) => {
36
+ x: number;
37
+ y: number;
38
+ }, affine?: AffineMatrix): VectorMask;
39
+ /** A linked vector mask moved with its layer by (dx, dy); unlinked ones stay. */
40
+ export declare function followLayerMove<T extends ImageLayer>(layer: T, dx: number, dy: number): T;
41
+ /** Bounds of a vector mask's paths (document pixels), or `null` when it has none. */
42
+ export declare function vectorMaskBounds(vm: VectorMask): CropBox | null;
43
+ /**
44
+ * A vector mask's thumbnail over the `width × height` canvas, at most `maxSide` px: grey RGBA,
45
+ * white where the path reveals (cached per mask object).
46
+ */
47
+ export declare function vectorMaskThumbnail(vm: VectorMask, width: number, height: number, maxSide?: number): {
48
+ width: number;
49
+ height: number;
50
+ data: Uint8ClampedArray;
51
+ };
@@ -1,10 +1,3 @@
1
- /**
2
- * PSD / PSB → our layer tree. Everything the model holds maps directly (smart objects with their
3
- * embedded contents, placement, warp, smart filters and filter mask; shape layers with their
4
- * paths, fill, stroke and live shapes; type layers with their text, styles and Photoshop's pixels
5
- * until edited); everything else comes in as close as it can (fill layers as their stored pixels)
6
- * with a warning naming the layers.
7
- */
8
1
  import { type PsdAlphaChannel } from './channels.cjs';
9
2
  import type { ImageLayer, LayerBlendMode } from '../layers/model.cjs';
10
3
  import { type PsdFile } from './file.cjs';
@@ -1,10 +1,3 @@
1
- /**
2
- * PSD / PSB → our layer tree. Everything the model holds maps directly (smart objects with their
3
- * embedded contents, placement, warp, smart filters and filter mask; shape layers with their
4
- * paths, fill, stroke and live shapes; type layers with their text, styles and Photoshop's pixels
5
- * until edited); everything else comes in as close as it can (fill layers as their stored pixels)
6
- * with a warning naming the layers.
7
- */
8
1
  import { type PsdAlphaChannel } from './channels.js';
9
2
  import type { ImageLayer, LayerBlendMode } from '../layers/model.js';
10
3
  import { type PsdFile } from './file.js';
@@ -7,7 +7,7 @@ var _chunkZ64NRDEHcjs = require('../chunk-Z64NRDEH.cjs');
7
7
 
8
8
 
9
9
 
10
- var _chunkED4L4UD6cjs = require('../chunk-ED4L4UD6.cjs');
10
+ var _chunkT4U4O6DScjs = require('../chunk-T4U4O6DS.cjs');
11
11
  require('../chunk-A2IS7EHE.cjs');
12
12
 
13
13
 
@@ -1653,11 +1653,11 @@ function header(state, options) {
1653
1653
  return out;
1654
1654
  }
1655
1655
  function serializePaintDocument(state, options = {}) {
1656
- const { layers, assets } = _chunkED4L4UD6cjs.serializeLayers.call(void 0, state.layers);
1656
+ const { layers, assets } = _chunkT4U4O6DScjs.serializeLayers.call(void 0, state.layers);
1657
1657
  return { ...header(state, options), layers, assets };
1658
1658
  }
1659
1659
  async function serializePaintDocumentExternal(state, store, options = {}) {
1660
- const { layers, assets } = await _chunkED4L4UD6cjs.serializeLayersExternal.call(void 0, state.layers, store);
1660
+ const { layers, assets } = await _chunkT4U4O6DScjs.serializeLayersExternal.call(void 0, state.layers, store);
1661
1661
  return { ...header(state, options), layers, assets };
1662
1662
  }
1663
1663
  function paintLayersOnly(layers, problems) {
@@ -1694,7 +1694,7 @@ function deserializePaintDocument(raw) {
1694
1694
  const canvas = normalizeCanvas({ width: r.width, height: r.height, dpi: _nullishCoalesce(r.dpi, () => ( void 0)) });
1695
1695
  const paper = _chunkYQ5C5BVKcjs.normalizePaper.call(void 0, _nullishCoalesce(r.paper, () => ( {})));
1696
1696
  const assets = r.assets && typeof r.assets === "object" ? r.assets : {};
1697
- const layers = paintLayersOnly(_chunkED4L4UD6cjs.deserializeLayers.call(void 0, r.layers, { assets, problems }), problems);
1697
+ const layers = paintLayersOnly(_chunkT4U4O6DScjs.deserializeLayers.call(void 0, r.layers, { assets, problems }), problems);
1698
1698
  const wanted = typeof r.activeLayerId === "string" ? r.activeLayerId : null;
1699
1699
  const activeLayerId = wanted && _chunkHGTDMKNEcjs.findLayer.call(void 0, layers, wanted) ? wanted : _nullishCoalesce(_optionalChain([_chunkHGTDMKNEcjs.flattenLayers.call(void 0, layers), 'access', _70 => _70.filter, 'call', _71 => _71((l) => l.type === "pixel"), 'access', _72 => _72.pop, 'call', _73 => _73(), 'optionalAccess', _74 => _74.id]), () => ( null));
1700
1700
  const wetEvents = Array.isArray(r.wetEvents) ? r.wetEvents.filter((e) => e && typeof e === "object" && Number.isInteger(e.clock) && typeof e.kind === "string") : [];
@@ -1710,7 +1710,7 @@ async function deserializePaintDocumentAsync(raw, store) {
1710
1710
  if (!raw || typeof raw !== "object") throw new Error("Not a paint document: expected a JSON object.");
1711
1711
  const r = raw;
1712
1712
  const problems = [];
1713
- const assets = await _chunkED4L4UD6cjs.resolveExternalAssets.call(void 0, _nullishCoalesce(r.assets, () => ( {})), store, problems);
1713
+ const assets = await _chunkT4U4O6DScjs.resolveExternalAssets.call(void 0, _nullishCoalesce(r.assets, () => ( {})), store, problems);
1714
1714
  const loaded = deserializePaintDocument({ ...r, assets });
1715
1715
  return { ...loaded, problems: [...problems, ...loaded.problems] };
1716
1716
  }
@@ -5409,7 +5409,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5409
5409
  return next.type === "group" ? { ...next, children: toStandard(next.children) } : next;
5410
5410
  });
5411
5411
  const layers = toStandard(dried);
5412
- const result = _chunkED4L4UD6cjs.exportPsd.call(void 0, { width, height, layers: [paperLayer, ...layers], merged, resolution: dpi });
5412
+ const result = _chunkT4U4O6DScjs.exportPsd.call(void 0, { width, height, layers: [paperLayer, ...layers], merged, resolution: dpi });
5413
5413
  for (const [mode, names] of changed) {
5414
5414
  const label = _nullishCoalesce(_optionalChain([_chunkCWM3CUUXcjs.BLEND_MODES, 'access', _271 => _271.find, 'call', _272 => _272((m) => m.id === mode), 'optionalAccess', _273 => _273.label]), () => ( mode));
5415
5415
  const std = _nullishCoalesce(_optionalChain([_chunkCWM3CUUXcjs.BLEND_MODES, 'access', _274 => _274.find, 'call', _275 => _275((m) => m.id === _chunkCWM3CUUXcjs.standardBlendMode.call(void 0, mode)), 'optionalAccess', _276 => _276.label]), () => ( _chunkCWM3CUUXcjs.standardBlendMode.call(void 0, mode)));
@@ -7,7 +7,7 @@ import {
7
7
  resolveExternalAssets,
8
8
  serializeLayers,
9
9
  serializeLayersExternal
10
- } from "../chunk-ZEHTUR7Z.js";
10
+ } from "../chunk-7ZWLDVFH.js";
11
11
  import "../chunk-3CI7FRMK.js";
12
12
  import {
13
13
  EffectRendererPool
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor",
3
- "version": "0.189.0",
3
+ "version": "0.190.0",
4
4
  "description": "UI-agnostic rich text and media editing core library",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -313,6 +313,12 @@ in the source frame at the document's bit depth, colour mode and profile. Read
313
313
  | `add_shape_component` | Add a live shape or path to a shape layer with `op` (`combine`, `subtract`, `intersect`, `exclude`), on top unless `index`. Use it for cut-outs, rings, badges. Returns the shape. |
314
314
  | `remove_shape_component` | Delete one component (not the last: delete the layer instead). Returns the shape. |
315
315
  | `merge_shape_components` | Merge Shape Components: the combined area becomes one plain component (live shapes become paths); the look does not change. Returns the shape. |
316
+ | `get_vector_mask` | Read-only. A layer's vector mask: enabled, linked, inverted, fill rule, density, feather, bounds and components (subpaths in document px), or null. Read it before editing the path. |
317
+ | `add_vector_mask` | Layer › Vector Mask on any layer but a shape: `from` `path` (a `shape`, `subpaths` or `components`, like `add_shape_layer`), `reveal-all` or `hide-all` (empty path). Reveals inside the path, sharp at any size; it combines with the raster mask. Returns the vector mask. |
318
+ | `set_vector_mask_path` | Replace the vector mask's path (properties kept). Returns the vector mask. |
319
+ | `set_vector_mask_properties` | `enabled`, `linked` (false: stays put when the layer moves), `inverted`, `density`, `feather` px; the path is kept. Returns the vector mask. |
320
+ | `rasterize_vector_mask` | Multiply the path into the raster mask (adding one) and drop the vector mask, so it can be painted. Returns the mask summary. |
321
+ | `remove_vector_mask` | Delete the vector mask (the raster mask stays). |
316
322
  | `convert_to_smart_object` | Convert sibling `layerIds` (default the active layer) to one smart object at the top one's position, optional `name`. One pixel layer keeps its id, look and properties (filters become smart filters); several layers, groups or adjustments nest as a layer document. Returns the `layer` and the `smartObject` summary. |
317
323
  | `rasterize_smart_object` | Turn a smart object into a pixel layer holding what it shows now (smart filters baked; same id, mask, effects, opacity, blend). Destructive: the contents and filters are gone. Returns the layer. |
318
324
  | `set_smart_object_transform` | Set a smart object's transform absolutely: `corners`, `matrix` (contents → document), or `scaleX` / `scaleY` (percent of the contents), `rotate`, `skewX`, `skewY`, `x`, `y` (where the contents' centre goes; unset numbers keep their values); `warp` preset or mesh in contents px, `null` removes it; `interpolation`. Re-renders from the contents. Returns `placement`, `masksMoved` and `bounds`. |
@@ -66,7 +66,9 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
66
66
  by it), or `apply_selection_as_mask` with `kind`. Filters (`apply_effect`,
67
67
  `add_smart_filter`) are also limited to an active selection.
68
68
  - A cutout: `layer_via_copy` (the original stays), or `add_layer_mask` `from: "selection"` on
69
- the layer itself.
69
+ the layer itself. For a hard geometric or traced edge that must stay sharp and editable
70
+ (a product, a screen, a window), `add_vector_mask` with the path instead; it moves and
71
+ transforms with the layer, and `rasterize_vector_mask` turns it into paintable pixels.
70
72
  - Painting or fills inside it: keep the selection active and paint; `stroke_selection` for
71
73
  an outline that follows it.
72
74
  - Removing what it covers: `content_aware_fill` `output: "new-layer"`.
@@ -98,7 +100,7 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
98
100
  | Tones (shadows, highlights, a luminosity mask) | `select_color_range` with `preset` `shadows` / `midtones` / `highlights` |
99
101
  | Skin for retouching | `select_color_range` `preset: "skinTones"`, then `intersect` with a box around the face |
100
102
  | An AI matte or saved mask from the host | `load_selection_from_image` (soft: no `threshold`), then `refine_selection`; `load_channel_from_image` to keep it for later |
101
- | An existing layer mask | `load_selection_from_mask` |
103
+ | An existing layer mask | `load_selection_from_mask` (`source: "vector"` for a vector mask) |
102
104
  | A cutout, shape or text already on its own layer | `select_layer_transparency` (exact, soft where the layer is) |
103
105
  | A dead pixel row or column | `select_line`, then fill or heal it |
104
106
 
@@ -195,6 +197,8 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
195
197
  - `load_selection_from_mask`: a layer mask back to a selection
196
198
  - `set_selection_display`: ants, Quick Mask tint or hidden
197
199
  - `set_mask_view`: a layer mask alone in grey or as a red overlay, to check it
200
+ - `add_vector_mask`, `set_vector_mask_path`, `set_vector_mask_properties`, `get_vector_mask`: sharp, editable path masks
201
+ - `rasterize_vector_mask`, `remove_vector_mask`: turn a vector mask into pixels, or drop it
198
202
  - `modify_selection`: expand, contract, feather, border, smooth
199
203
  - `refine_selection`: snap the edge to the image
200
204
  - `transform_selection`: move or scale the outline only