@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
@@ -6,7 +6,7 @@ import type { CropBox } from './types.cjs';
6
6
  export declare function maskSummary(mask: RasterMask | undefined): Record<string, unknown> | null;
7
7
  /**
8
8
  * A plain description of a layer (and its children) for agents: ids, types, names, document
9
- * bounds, visibility, opacity, fill, blend, clipping, locks, mask and effects summaries and
9
+ * bounds, visibility, opacity, fill, blend, clipping, locks, mask, vector mask and effects summaries and
10
10
  * whether it is active. `boundsOf` gives a pixel layer's document bounds.
11
11
  */
12
12
  export declare function describeLayer(layer: ImageLayer, activeId: string | null, boundsOf: (layer: ImageLayer) => CropBox | null): Record<string, unknown>;
@@ -6,7 +6,7 @@ import type { CropBox } from './types.js';
6
6
  export declare function maskSummary(mask: RasterMask | undefined): Record<string, unknown> | null;
7
7
  /**
8
8
  * A plain description of a layer (and its children) for agents: ids, types, names, document
9
- * bounds, visibility, opacity, fill, blend, clipping, locks, mask and effects summaries and
9
+ * bounds, visibility, opacity, fill, blend, clipping, locks, mask, vector mask and effects summaries and
10
10
  * whether it is active. `boundsOf` gives a pixel layer's document bounds.
11
11
  */
12
12
  export declare function describeLayer(layer: ImageLayer, activeId: string | null, boundsOf: (layer: ImageLayer) => CropBox | null): Record<string, unknown>;
@@ -42,6 +42,9 @@ export declare function runLayerFiltersFloat(data: Float32Array, width: number,
42
42
  /** The per-tile compositor for image editor layer stacks (see the raster module for the caches). */
43
43
  export declare class LayerCompositor extends RasterCompositor<ImageLayer, CompositorSources> {
44
44
  constructor(options?: CompositorOptions);
45
+ composite(layers: readonly ImageLayer[], rect: RasterRect, sources: CompositorSources, options?: CompositeCallOptions): Uint8ClampedArray;
46
+ compositeTiles(layers: readonly ImageLayer[], rect: RasterRect, sources: CompositorSources, onTile: (tx: number, ty: number, data: Uint8ClampedArray, changed: boolean) => void, options?: CompositeCallOptions): void;
47
+ tile(layers: readonly ImageLayer[], tx: number, ty: number, sources: CompositorSources): Uint8ClampedArray;
45
48
  }
46
49
  /**
47
50
  * The composite of an image editor stack at 16 or 32 bits (see `raster/deep-compositor`): float
@@ -42,6 +42,9 @@ export declare function runLayerFiltersFloat(data: Float32Array, width: number,
42
42
  /** The per-tile compositor for image editor layer stacks (see the raster module for the caches). */
43
43
  export declare class LayerCompositor extends RasterCompositor<ImageLayer, CompositorSources> {
44
44
  constructor(options?: CompositorOptions);
45
+ composite(layers: readonly ImageLayer[], rect: RasterRect, sources: CompositorSources, options?: CompositeCallOptions): Uint8ClampedArray;
46
+ compositeTiles(layers: readonly ImageLayer[], rect: RasterRect, sources: CompositorSources, onTile: (tx: number, ty: number, data: Uint8ClampedArray, changed: boolean) => void, options?: CompositeCallOptions): void;
47
+ tile(layers: readonly ImageLayer[], tx: number, ty: number, sources: CompositorSources): Uint8ClampedArray;
45
48
  }
46
49
  /**
47
50
  * The composite of an image editor stack at 16 or 32 bits (see `raster/deep-compositor`): float
@@ -28,6 +28,29 @@ export type { LayerLocks };
28
28
  export type RasterMask = BaseRasterMask<DepthMaskOptions>;
29
29
  export interface LayerBase extends RasterLayerBase {
30
30
  mask?: RasterMask;
31
+ /** A vector mask (Photoshop's): a path that reveals the layer, on top of `mask`. Not on shape layers. */
32
+ vectorMask?: VectorMask;
33
+ }
34
+ /**
35
+ * A vector mask: shape components in document pixels (the same paths shape layers use) that
36
+ * reveal the layer inside them (outside with `inverted`). It stays sharp at any size; the
37
+ * compositor multiplies it with the layer's raster mask. No components hides everything
38
+ * (reveals everything when inverted).
39
+ */
40
+ export interface VectorMask {
41
+ components: ShapeComponent[];
42
+ /** How overlapping subpaths fill (default nonzero). */
43
+ fillRule?: ShapeFillRule;
44
+ /** Reveal outside the path instead (default false). */
45
+ inverted?: boolean;
46
+ /** Disabled masks are kept but ignored (default true). */
47
+ enabled?: boolean;
48
+ /** `false`: the path stays put when the layer moves or transforms (default true). */
49
+ linked?: boolean;
50
+ /** 0..1 (default 1): at 0.5 the hidden area still shows half. */
51
+ density?: number;
52
+ /** Gaussian feather σ in px (default 0). */
53
+ feather?: number;
31
54
  }
32
55
  /** The base sliders of the old document-level adjustments, as a filter on one layer. */
33
56
  export interface DevelopSettings {
@@ -28,6 +28,29 @@ export type { LayerLocks };
28
28
  export type RasterMask = BaseRasterMask<DepthMaskOptions>;
29
29
  export interface LayerBase extends RasterLayerBase {
30
30
  mask?: RasterMask;
31
+ /** A vector mask (Photoshop's): a path that reveals the layer, on top of `mask`. Not on shape layers. */
32
+ vectorMask?: VectorMask;
33
+ }
34
+ /**
35
+ * A vector mask: shape components in document pixels (the same paths shape layers use) that
36
+ * reveal the layer inside them (outside with `inverted`). It stays sharp at any size; the
37
+ * compositor multiplies it with the layer's raster mask. No components hides everything
38
+ * (reveals everything when inverted).
39
+ */
40
+ export interface VectorMask {
41
+ components: ShapeComponent[];
42
+ /** How overlapping subpaths fill (default nonzero). */
43
+ fillRule?: ShapeFillRule;
44
+ /** Reveal outside the path instead (default false). */
45
+ inverted?: boolean;
46
+ /** Disabled masks are kept but ignored (default true). */
47
+ enabled?: boolean;
48
+ /** `false`: the path stays put when the layer moves or transforms (default true). */
49
+ linked?: boolean;
50
+ /** 0..1 (default 1): at 0.5 the hidden area still shows half. */
51
+ density?: number;
52
+ /** Gaussian feather σ in px (default 0). */
53
+ feather?: number;
31
54
  }
32
55
  /** The base sliders of the old document-level adjustments, as a filter on one layer. */
33
56
  export interface DevelopSettings {
@@ -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' | 'add-vector-mask-selection' | '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;
@@ -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' | 'add-vector-mask-selection' | '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 the area a vector mask's paths enclose (document pixels, the curves themselves, not their handles), or `null` when it has none. */
42
+ export declare function vectorMaskBounds(vm: Pick<VectorMask, 'components'>): 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 the area a vector mask's paths enclose (document pixels, the curves themselves, not their handles), or `null` when it has none. */
42
+ export declare function vectorMaskBounds(vm: Pick<VectorMask, 'components'>): 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';
@@ -11,3 +11,4 @@ export * from './segmentation.cjs';
11
11
  export * from './stroke.cjs';
12
12
  export * from './channels.cjs';
13
13
  export * from './mask-files.cjs';
14
+ export * from './path.cjs';
@@ -11,3 +11,4 @@ export * from './segmentation.js';
11
11
  export * from './stroke.js';
12
12
  export * from './channels.js';
13
13
  export * from './mask-files.js';
14
+ export * from './path.js';
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Selections ⇄ paths (Photoshop's Make Work Path and Make Selection): a selection's outline traced
3
+ * into Bézier subpaths, and a path filled into a selection.
4
+ */
5
+ import type { VectorSubpath } from '../../vector/types.cjs';
6
+ import type { ShapeComponent, ShapeFillRule } from '../layers/model.cjs';
7
+ import { type SelectionMask } from './mask.cjs';
8
+ export interface SelectionToPathOptions {
9
+ /** How far (px) the path may stray from the outline (default 0.5; Photoshop's Make Work Path tolerance). */
10
+ tolerance?: number;
11
+ /** 0: straight sides and sharp corners; 1: as smooth as the outline allows (default 0.5). */
12
+ cornerRoundness?: number;
13
+ /** Islands and holes smaller than this many square pixels are left out (default 4). */
14
+ minArea?: number;
15
+ /** Coverage (0..255) where the outline runs (default 128, half selected). */
16
+ threshold?: number;
17
+ }
18
+ /**
19
+ * The outline of a selection as closed subpaths in document pixels (holes included, so fill them
20
+ * even-odd). Traced in the selection's bounds only; an empty selection gives none.
21
+ */
22
+ export declare function selectionToSubpaths(mask: SelectionMask, options?: SelectionToPathOptions): VectorSubpath[];
23
+ /** Subpaths as compact SVG path data (absolute, `precision` decimals). */
24
+ export declare function subpathsToSvgPath(subpaths: readonly VectorSubpath[], precision?: number): string;
25
+ export interface PathToSelectionOptions {
26
+ /** Feather radius in px applied after filling (default 0). */
27
+ feather?: number;
28
+ /** Soft edges (default true); false makes every pixel fully in or out. */
29
+ antiAlias?: boolean;
30
+ /** How overlapping subpaths fill (default nonzero). */
31
+ fillRule?: ShapeFillRule;
32
+ }
33
+ /**
34
+ * Make Selection: the area of `components` (document pixels, combined by their ops) over a
35
+ * `width × height` image, computed in the path's bounds only.
36
+ */
37
+ export declare function pathToSelection(components: readonly ShapeComponent[], width: number, height: number, options?: PathToSelectionOptions): SelectionMask;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Selections ⇄ paths (Photoshop's Make Work Path and Make Selection): a selection's outline traced
3
+ * into Bézier subpaths, and a path filled into a selection.
4
+ */
5
+ import type { VectorSubpath } from '../../vector/types.js';
6
+ import type { ShapeComponent, ShapeFillRule } from '../layers/model.js';
7
+ import { type SelectionMask } from './mask.js';
8
+ export interface SelectionToPathOptions {
9
+ /** How far (px) the path may stray from the outline (default 0.5; Photoshop's Make Work Path tolerance). */
10
+ tolerance?: number;
11
+ /** 0: straight sides and sharp corners; 1: as smooth as the outline allows (default 0.5). */
12
+ cornerRoundness?: number;
13
+ /** Islands and holes smaller than this many square pixels are left out (default 4). */
14
+ minArea?: number;
15
+ /** Coverage (0..255) where the outline runs (default 128, half selected). */
16
+ threshold?: number;
17
+ }
18
+ /**
19
+ * The outline of a selection as closed subpaths in document pixels (holes included, so fill them
20
+ * even-odd). Traced in the selection's bounds only; an empty selection gives none.
21
+ */
22
+ export declare function selectionToSubpaths(mask: SelectionMask, options?: SelectionToPathOptions): VectorSubpath[];
23
+ /** Subpaths as compact SVG path data (absolute, `precision` decimals). */
24
+ export declare function subpathsToSvgPath(subpaths: readonly VectorSubpath[], precision?: number): string;
25
+ export interface PathToSelectionOptions {
26
+ /** Feather radius in px applied after filling (default 0). */
27
+ feather?: number;
28
+ /** Soft edges (default true); false makes every pixel fully in or out. */
29
+ antiAlias?: boolean;
30
+ /** How overlapping subpaths fill (default nonzero). */
31
+ fillRule?: ShapeFillRule;
32
+ }
33
+ /**
34
+ * Make Selection: the area of `components` (document pixels, combined by their ops) over a
35
+ * `width × height` image, computed in the path's bounds only.
36
+ */
37
+ export declare function pathToSelection(components: readonly ShapeComponent[], width: number, height: number, options?: PathToSelectionOptions): SelectionMask;
@@ -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 _chunk2UR4OX3Bcjs = require('../chunk-2UR4OX3B.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 } = _chunk2UR4OX3Bcjs.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 _chunk2UR4OX3Bcjs.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(_chunk2UR4OX3Bcjs.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 _chunk2UR4OX3Bcjs.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 = _chunk2UR4OX3Bcjs.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-TXYXF32N.js";
11
11
  import "../chunk-3CI7FRMK.js";
12
12
  import {
13
13
  EffectRendererPool