@pixerate/editor 0.183.0 → 0.186.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 (50) hide show
  1. package/dist/{chunk-Y46OMERN.js → chunk-FW7N64XK.js} +298 -189
  2. package/dist/{chunk-UHMSMUHK.cjs → chunk-X2KQQZBT.cjs} +358 -249
  3. package/dist/image-editor/controller.d.cts +208 -8
  4. package/dist/image-editor/controller.d.ts +208 -8
  5. package/dist/image-editor/index.cjs +2610 -1025
  6. package/dist/image-editor/index.js +1672 -87
  7. package/dist/image-editor/layers/model.d.cts +5 -1
  8. package/dist/image-editor/layers/model.d.ts +5 -1
  9. package/dist/image-editor/layers/paint-ui.d.cts +2 -1
  10. package/dist/image-editor/layers/paint-ui.d.ts +2 -1
  11. package/dist/image-editor/layers/panel.d.cts +23 -1
  12. package/dist/image-editor/layers/panel.d.ts +23 -1
  13. package/dist/image-editor/layers/serialize.d.cts +3 -0
  14. package/dist/image-editor/layers/serialize.d.ts +3 -0
  15. package/dist/image-editor/psd/channels.d.cts +24 -0
  16. package/dist/image-editor/psd/channels.d.ts +24 -0
  17. package/dist/image-editor/psd/export.d.cts +3 -0
  18. package/dist/image-editor/psd/export.d.ts +3 -0
  19. package/dist/image-editor/psd/import.d.cts +10 -0
  20. package/dist/image-editor/psd/import.d.ts +10 -0
  21. package/dist/image-editor/renderer.d.cts +6 -0
  22. package/dist/image-editor/renderer.d.ts +6 -0
  23. package/dist/image-editor/selection/channels.d.cts +52 -0
  24. package/dist/image-editor/selection/channels.d.ts +52 -0
  25. package/dist/image-editor/selection/gesture.d.cts +47 -2
  26. package/dist/image-editor/selection/gesture.d.ts +47 -2
  27. package/dist/image-editor/selection/index.d.cts +2 -0
  28. package/dist/image-editor/selection/index.d.ts +2 -0
  29. package/dist/image-editor/selection/modify.d.cts +3 -2
  30. package/dist/image-editor/selection/modify.d.ts +3 -2
  31. package/dist/image-editor/selection/overlay.d.cts +2 -0
  32. package/dist/image-editor/selection/overlay.d.ts +2 -0
  33. package/dist/image-editor/selection/stroke.d.cts +8 -0
  34. package/dist/image-editor/selection/stroke.d.ts +8 -0
  35. package/dist/image-editor/selection/tool-options.d.cts +12 -2
  36. package/dist/image-editor/selection/tool-options.d.ts +12 -2
  37. package/dist/image-editor/selection/wand.d.cts +37 -0
  38. package/dist/image-editor/selection/wand.d.ts +37 -0
  39. package/dist/image-editor/selection-tools.d.cts +8 -3
  40. package/dist/image-editor/selection-tools.d.ts +8 -3
  41. package/dist/image-editor/tools.d.cts +10 -2
  42. package/dist/image-editor/tools.d.ts +10 -2
  43. package/dist/image-editor/types.d.cts +12 -0
  44. package/dist/image-editor/types.d.ts +12 -0
  45. package/dist/paint-editor/index.cjs +6 -6
  46. package/dist/paint-editor/index.js +1 -1
  47. package/package.json +1 -1
  48. package/skills/pixerate-image-editor/SKILL.md +19 -21
  49. package/skills/pixerate-image-editor/references/free-transform.md +22 -0
  50. package/skills/pixerate-image-selection-masking/SKILL.md +36 -3
@@ -10,6 +10,8 @@ export interface QuickMaskStyle {
10
10
  color?: readonly [number, number, number];
11
11
  /** 0..1 (default 0.5): opacity over fully unselected pixels. */
12
12
  opacity?: number;
13
+ /** Tint the unselected area (`masked`, default) or the selected area (`selected`). */
14
+ indicates?: 'masked' | 'selected';
13
15
  }
14
16
  /**
15
17
  * Quick Mask view: RGBA pixels (natural image size) tinting the *unselected* area, so the
@@ -0,0 +1,8 @@
1
+ import { type SelectionMask } from './mask.cjs';
2
+ /**
3
+ * The band Edit › Stroke paints along a selection's edge, `width` pixels wide: `inside` is the
4
+ * selection minus the selection contracted by `width`, `outside` the selection expanded by
5
+ * `width` minus the selection, and `center` straddles the edge by `width / 2` each way. Built
6
+ * from the exact distance transform the Modify commands use, so the band keeps soft edges.
7
+ */
8
+ export declare function selectionRing(sel: SelectionMask, width: number, location: 'inside' | 'center' | 'outside'): SelectionMask;
@@ -0,0 +1,8 @@
1
+ import { type SelectionMask } from './mask.js';
2
+ /**
3
+ * The band Edit › Stroke paints along a selection's edge, `width` pixels wide: `inside` is the
4
+ * selection minus the selection contracted by `width`, `outside` the selection expanded by
5
+ * `width` minus the selection, and `center` straddles the edge by `width / 2` each way. Built
6
+ * from the exact distance transform the Modify commands use, so the band keeps soft edges.
7
+ */
8
+ export declare function selectionRing(sel: SelectionMask, width: number, location: 'inside' | 'center' | 'outside'): SelectionMask;
@@ -1,12 +1,14 @@
1
1
  import type { SelectionMask, SelectionMode } from './mask.cjs';
2
- import type { ColorDistance } from './wand.cjs';
2
+ import type { ColorDistance, ColorRangePreset } from './wand.cjs';
3
3
  import type { RefineEdgeParams } from './refine.cjs';
4
4
  /** Selection tools of the image editor UI. */
5
- export type SelectionToolKind = 'wand' | 'rect' | 'ellipse' | 'lasso';
5
+ export type SelectionToolKind = 'wand' | 'rect' | 'ellipse' | 'row' | 'column' | 'lasso' | 'polygon' | 'range';
6
6
  export declare const SELECTION_TOOLS: readonly {
7
7
  id: SelectionToolKind;
8
8
  label: string;
9
9
  }[];
10
+ /** Labels of the Color Range presets, in menu order. */
11
+ export declare const COLOR_RANGE_PRESET_LABELS: Record<ColorRangePreset, string>;
10
12
  export declare const SELECTION_MODE_LABELS: Record<SelectionMode, string>;
11
13
  /** The options bar of the selection tools (shared by the React and Svelte UIs). */
12
14
  export interface SelectionToolOptions {
@@ -21,6 +23,14 @@ export interface SelectionToolOptions {
21
23
  /** Sample the whole image (`composite`, default) or only the active pixel layer. */
22
24
  sample?: 'composite' | 'active-layer';
23
25
  distance: ColorDistance;
26
+ /** Feather new marquee, lasso, wand and Color Range selections by this many pixels (default 0). */
27
+ feather?: number;
28
+ /** Color Range: levels over which a sampled colour's coverage fades out (0..200, default 40). */
29
+ fuzziness?: number;
30
+ /** Color Range: limit each click to this radius in pixels (Localized Color Clusters; 0 = off). */
31
+ localized?: number;
32
+ /** Color Range: select everything except the sampled colours. */
33
+ rangeInvert?: boolean;
24
34
  }
25
35
  export declare const DEFAULT_SELECTION_TOOL_OPTIONS: SelectionToolOptions;
26
36
  /**
@@ -1,12 +1,14 @@
1
1
  import type { SelectionMask, SelectionMode } from './mask.js';
2
- import type { ColorDistance } from './wand.js';
2
+ import type { ColorDistance, ColorRangePreset } from './wand.js';
3
3
  import type { RefineEdgeParams } from './refine.js';
4
4
  /** Selection tools of the image editor UI. */
5
- export type SelectionToolKind = 'wand' | 'rect' | 'ellipse' | 'lasso';
5
+ export type SelectionToolKind = 'wand' | 'rect' | 'ellipse' | 'row' | 'column' | 'lasso' | 'polygon' | 'range';
6
6
  export declare const SELECTION_TOOLS: readonly {
7
7
  id: SelectionToolKind;
8
8
  label: string;
9
9
  }[];
10
+ /** Labels of the Color Range presets, in menu order. */
11
+ export declare const COLOR_RANGE_PRESET_LABELS: Record<ColorRangePreset, string>;
10
12
  export declare const SELECTION_MODE_LABELS: Record<SelectionMode, string>;
11
13
  /** The options bar of the selection tools (shared by the React and Svelte UIs). */
12
14
  export interface SelectionToolOptions {
@@ -21,6 +23,14 @@ export interface SelectionToolOptions {
21
23
  /** Sample the whole image (`composite`, default) or only the active pixel layer. */
22
24
  sample?: 'composite' | 'active-layer';
23
25
  distance: ColorDistance;
26
+ /** Feather new marquee, lasso, wand and Color Range selections by this many pixels (default 0). */
27
+ feather?: number;
28
+ /** Color Range: levels over which a sampled colour's coverage fades out (0..200, default 40). */
29
+ fuzziness?: number;
30
+ /** Color Range: limit each click to this radius in pixels (Localized Color Clusters; 0 = off). */
31
+ localized?: number;
32
+ /** Color Range: select everything except the sampled colours. */
33
+ rangeInvert?: boolean;
24
34
  }
25
35
  export declare const DEFAULT_SELECTION_TOOL_OPTIONS: SelectionToolOptions;
26
36
  /**
@@ -56,3 +56,40 @@ export declare function toneRange(pixels: PixelBuffer, range: ToneRangePreset |
56
56
  * the centre, none 60° away) × chroma × (2 − max), so neutrals are never selected.
57
57
  */
58
58
  export declare function hueRange(pixels: PixelBuffer, center: number): SelectionMask;
59
+ /** Hue centres (degrees) of the Color Range colour presets. */
60
+ export declare const HUE_RANGE_PRESETS: {
61
+ readonly reds: 0;
62
+ readonly yellows: 60;
63
+ readonly greens: 120;
64
+ readonly cyans: 180;
65
+ readonly blues: 240;
66
+ readonly magentas: 300;
67
+ };
68
+ /** Select › Color Range › Select menu: colours, tones and skin tones. */
69
+ export type ColorRangePreset = keyof typeof HUE_RANGE_PRESETS | ToneRangePreset | 'skinTones';
70
+ export declare const COLOR_RANGE_PRESETS: readonly ColorRangePreset[];
71
+ export declare function isColorRangePreset(v: unknown): v is ColorRangePreset;
72
+ /**
73
+ * Select › Color Range › Skin Tones: a heuristic over hue (full at 5–40°, none by 60° and −20°),
74
+ * HSV saturation (0.12–0.8) and value (above 0.3), so it covers light to dark skin and leaves
75
+ * out neutrals and saturated oranges. It also picks up skin-like wood, sand and leather.
76
+ */
77
+ export declare function skinToneRange(pixels: PixelBuffer): SelectionMask;
78
+ /**
79
+ * A Color Range preset: `reds` … `magentas` (`hueRange`), `highlights` / `midtones` / `shadows`
80
+ * (`toneRange`, with `falloff` levels, default 20) or `skinTones` (`skinToneRange`).
81
+ */
82
+ export declare function colorRangePreset(pixels: PixelBuffer, preset: ColorRangePreset, options?: {
83
+ falloff?: number;
84
+ }): SelectionMask;
85
+ export interface GrowSimilarOptions {
86
+ /** 0..255, the largest per-channel difference from a selected colour (default 32, as the wand). */
87
+ tolerance?: number;
88
+ }
89
+ /**
90
+ * Select › Grow: adds the pixels connected (4-neighbours) to the selection whose colour is
91
+ * within `tolerance` of a selected colour. Unlike Expand, it follows colour, not distance.
92
+ */
93
+ export declare function growSelection(pixels: PixelBuffer, sel: SelectionMask, options?: GrowSimilarOptions): SelectionMask;
94
+ /** Select › Similar: adds every pixel in the image whose colour is within `tolerance` of a selected colour. */
95
+ export declare function selectSimilar(pixels: PixelBuffer, sel: SelectionMask, options?: GrowSimilarOptions): SelectionMask;
@@ -56,3 +56,40 @@ export declare function toneRange(pixels: PixelBuffer, range: ToneRangePreset |
56
56
  * the centre, none 60° away) × chroma × (2 − max), so neutrals are never selected.
57
57
  */
58
58
  export declare function hueRange(pixels: PixelBuffer, center: number): SelectionMask;
59
+ /** Hue centres (degrees) of the Color Range colour presets. */
60
+ export declare const HUE_RANGE_PRESETS: {
61
+ readonly reds: 0;
62
+ readonly yellows: 60;
63
+ readonly greens: 120;
64
+ readonly cyans: 180;
65
+ readonly blues: 240;
66
+ readonly magentas: 300;
67
+ };
68
+ /** Select › Color Range › Select menu: colours, tones and skin tones. */
69
+ export type ColorRangePreset = keyof typeof HUE_RANGE_PRESETS | ToneRangePreset | 'skinTones';
70
+ export declare const COLOR_RANGE_PRESETS: readonly ColorRangePreset[];
71
+ export declare function isColorRangePreset(v: unknown): v is ColorRangePreset;
72
+ /**
73
+ * Select › Color Range › Skin Tones: a heuristic over hue (full at 5–40°, none by 60° and −20°),
74
+ * HSV saturation (0.12–0.8) and value (above 0.3), so it covers light to dark skin and leaves
75
+ * out neutrals and saturated oranges. It also picks up skin-like wood, sand and leather.
76
+ */
77
+ export declare function skinToneRange(pixels: PixelBuffer): SelectionMask;
78
+ /**
79
+ * A Color Range preset: `reds` … `magentas` (`hueRange`), `highlights` / `midtones` / `shadows`
80
+ * (`toneRange`, with `falloff` levels, default 20) or `skinTones` (`skinToneRange`).
81
+ */
82
+ export declare function colorRangePreset(pixels: PixelBuffer, preset: ColorRangePreset, options?: {
83
+ falloff?: number;
84
+ }): SelectionMask;
85
+ export interface GrowSimilarOptions {
86
+ /** 0..255, the largest per-channel difference from a selected colour (default 32, as the wand). */
87
+ tolerance?: number;
88
+ }
89
+ /**
90
+ * Select › Grow: adds the pixels connected (4-neighbours) to the selection whose colour is
91
+ * within `tolerance` of a selected colour. Unlike Expand, it follows colour, not distance.
92
+ */
93
+ export declare function growSelection(pixels: PixelBuffer, sel: SelectionMask, options?: GrowSimilarOptions): SelectionMask;
94
+ /** Select › Similar: adds every pixel in the image whose colour is within `tolerance` of a selected colour. */
95
+ export declare function selectSimilar(pixels: PixelBuffer, sel: SelectionMask, options?: GrowSimilarOptions): SelectionMask;
@@ -1,9 +1,14 @@
1
1
  import type { AgentTool, AgentToolProvider } from '../tools/registry.cjs';
2
2
  import type { ImageEditorController } from './controller.cjs';
3
3
  /**
4
- * Agent tools for selections and masks (part of `createImageEditorToolProvider`):
5
- * `get_selection`, `select_region`, `select_by_color`, `modify_selection`, `refine_selection`
6
- * and `apply_selection_as_mask`. Coordinates are natural pixels of the source image.
4
+ * Agent tools for selections and masks (part of `createImageEditorToolProvider`): reading
5
+ * (`get_selection`), selecting (`select_all`, `deselect`, `invert_selection`, `select_rect`,
6
+ * `select_ellipse`, `select_polygon`, `select_line`, `magic_wand`, `select_color_range`,
7
+ * `grow_selection`, `select_similar`, `reselect`), loading (`load_selection_from_image`,
8
+ * `load_selection_from_mask`, `select_layer_transparency`), the view
9
+ * (`set_selection_display`), editing (`modify_selection`, `refine_selection`) and masking
10
+ * (`apply_selection_as_mask`). `select_region` and `select_by_color` are deprecated aliases.
11
+ * Coordinates are natural pixels of the source image.
7
12
  */
8
13
  export declare function createImageSelectionTools(controller: ImageEditorController): AgentTool[];
9
14
  /** Options for `createImageSegmentationToolProvider`. */
@@ -1,9 +1,14 @@
1
1
  import type { AgentTool, AgentToolProvider } from '../tools/registry.js';
2
2
  import type { ImageEditorController } from './controller.js';
3
3
  /**
4
- * Agent tools for selections and masks (part of `createImageEditorToolProvider`):
5
- * `get_selection`, `select_region`, `select_by_color`, `modify_selection`, `refine_selection`
6
- * and `apply_selection_as_mask`. Coordinates are natural pixels of the source image.
4
+ * Agent tools for selections and masks (part of `createImageEditorToolProvider`): reading
5
+ * (`get_selection`), selecting (`select_all`, `deselect`, `invert_selection`, `select_rect`,
6
+ * `select_ellipse`, `select_polygon`, `select_line`, `magic_wand`, `select_color_range`,
7
+ * `grow_selection`, `select_similar`, `reselect`), loading (`load_selection_from_image`,
8
+ * `load_selection_from_mask`, `select_layer_transparency`), the view
9
+ * (`set_selection_display`), editing (`modify_selection`, `refine_selection`) and masking
10
+ * (`apply_selection_as_mask`). `select_region` and `select_by_color` are deprecated aliases.
11
+ * Coordinates are natural pixels of the source image.
7
12
  */
8
13
  export declare function createImageSelectionTools(controller: ImageEditorController): AgentTool[];
9
14
  /** Options for `createImageSegmentationToolProvider`. */
@@ -23,8 +23,16 @@ export interface ImageEditorToolProviderOptions {
23
23
  * - `render_preview {maxSide}`: the rendered image as a PNG, longest side at most `maxSide`.
24
24
  * - `capture_view {region, rect?, ...}`: a screenshot of the whole image, the selection or a rect in
25
25
  * natural pixels (up to 8× for close inspection), optionally with the selection shaded.
26
- * - Selections and masks: `get_selection`, `select_region`, `select_by_color`, `modify_selection`,
27
- * `refine_selection`, `apply_selection_as_mask` (see `createImageSelectionTools`).
26
+ * - Selections and masks: `get_selection`, `select_all`, `deselect`, `invert_selection`,
27
+ * `select_rect`, `select_ellipse`, `select_polygon`, `select_line`, `magic_wand`,
28
+ * `select_color_range`, `grow_selection`, `select_similar`, `reselect`,
29
+ * `select_layer_transparency`, `stroke_selection`, `crop_to_selection`, `content_aware_fill`,
30
+ * `copy_pixels`, `cut_pixels`, `paste_pixels`, alpha channels (`list_channels`, `save_selection`,
31
+ * `load_selection_from_channel`, `set_channel_properties`, `duplicate_channel`,
32
+ * `delete_channel`), `set_quick_mask`,
33
+ * `load_selection_from_image`, `load_selection_from_mask`, `set_selection_display`,
34
+ * `modify_selection`, `refine_selection`, `apply_selection_as_mask` and the deprecated
35
+ * `select_region` / `select_by_color` (see `createImageSelectionTools`).
28
36
  * - Layers, geometry and history: `get_image_info`, `get_histogram`, `update_adjustment_layer`,
29
37
  * `remove_adjustment_layer`, `move_adjustment_layer`, `set_crop`, `reset_crop`, `set_rotation`,
30
38
  * `flip_image`, `undo_image`, `redo_image` (see `createImageEditTools`).
@@ -23,8 +23,16 @@ export interface ImageEditorToolProviderOptions {
23
23
  * - `render_preview {maxSide}`: the rendered image as a PNG, longest side at most `maxSide`.
24
24
  * - `capture_view {region, rect?, ...}`: a screenshot of the whole image, the selection or a rect in
25
25
  * natural pixels (up to 8× for close inspection), optionally with the selection shaded.
26
- * - Selections and masks: `get_selection`, `select_region`, `select_by_color`, `modify_selection`,
27
- * `refine_selection`, `apply_selection_as_mask` (see `createImageSelectionTools`).
26
+ * - Selections and masks: `get_selection`, `select_all`, `deselect`, `invert_selection`,
27
+ * `select_rect`, `select_ellipse`, `select_polygon`, `select_line`, `magic_wand`,
28
+ * `select_color_range`, `grow_selection`, `select_similar`, `reselect`,
29
+ * `select_layer_transparency`, `stroke_selection`, `crop_to_selection`, `content_aware_fill`,
30
+ * `copy_pixels`, `cut_pixels`, `paste_pixels`, alpha channels (`list_channels`, `save_selection`,
31
+ * `load_selection_from_channel`, `set_channel_properties`, `duplicate_channel`,
32
+ * `delete_channel`), `set_quick_mask`,
33
+ * `load_selection_from_image`, `load_selection_from_mask`, `set_selection_display`,
34
+ * `modify_selection`, `refine_selection`, `apply_selection_as_mask` and the deprecated
35
+ * `select_region` / `select_by_color` (see `createImageSelectionTools`).
28
36
  * - Layers, geometry and history: `get_image_info`, `get_histogram`, `update_adjustment_layer`,
29
37
  * `remove_adjustment_layer`, `move_adjustment_layer`, `set_crop`, `reset_crop`, `set_rotation`,
30
38
  * `flip_image`, `undo_image`, `redo_image` (see `createImageEditTools`).
@@ -2,6 +2,7 @@ import type { GlobalLight } from '../raster/effects/model.cjs';
2
2
  import type { EffectPattern } from '../raster/effects/patterns.cjs';
3
3
  import type { BlendMode } from './blend.cjs';
4
4
  import type { SelectionMask, SerializedSelection } from './selection/mask.cjs';
5
+ import type { AlphaChannel, SerializedAlphaChannel } from './selection/channels.cjs';
5
6
  import type { ImageLayer, LayerTarget } from './layers/model.cjs';
6
7
  import type { SerializedLayeredImageEditorState } from './layers/serialize.cjs';
7
8
  import type { PaintOptions } from './controller.cjs';
@@ -311,6 +312,15 @@ export interface ImageEditorState {
311
312
  selection?: SelectionMask | null;
312
313
  /** Quick Mask view: tint the unselected area instead of drawing marching ants (view state). */
313
314
  quickMask?: boolean;
315
+ /** How the canvas shows the selection (view state; `quickMask` mirrors `'quickMask'`). */
316
+ selectionDisplay?: 'ants' | 'quickMask' | 'none';
317
+ /**
318
+ * Quick Mask mode: painting and fills without a layer edit the selection itself (white
319
+ * selects, black deselects), shown as a tint. View state; the edits are undo steps.
320
+ */
321
+ quickMaskEditing?: boolean;
322
+ /** Alpha channels: saved selections kept with the document, recorded in undo history. */
323
+ channels?: AlphaChannel[];
314
324
  /**
315
325
  * The layer stack, bottom to top. Opening an image gives one pixel layer, "Background", whose
316
326
  * pixels are the source image. Layers are immutable: compare them by identity.
@@ -378,6 +388,8 @@ export interface LegacySerializedImageEditorState {
378
388
  annotations: Annotation[];
379
389
  /** The active selection, run-length encoded. Missing when nothing is selected. */
380
390
  selection?: SerializedSelection;
391
+ /** Alpha channels (saved selections), run-length encoded. */
392
+ channels?: SerializedAlphaChannel[];
381
393
  }
382
394
  /**
383
395
  * A stored editor state: the document-level schema (v1 / v2) or the layered schema (v3, see
@@ -2,6 +2,7 @@ import type { GlobalLight } from '../raster/effects/model.js';
2
2
  import type { EffectPattern } from '../raster/effects/patterns.js';
3
3
  import type { BlendMode } from './blend.js';
4
4
  import type { SelectionMask, SerializedSelection } from './selection/mask.js';
5
+ import type { AlphaChannel, SerializedAlphaChannel } from './selection/channels.js';
5
6
  import type { ImageLayer, LayerTarget } from './layers/model.js';
6
7
  import type { SerializedLayeredImageEditorState } from './layers/serialize.js';
7
8
  import type { PaintOptions } from './controller.js';
@@ -311,6 +312,15 @@ export interface ImageEditorState {
311
312
  selection?: SelectionMask | null;
312
313
  /** Quick Mask view: tint the unselected area instead of drawing marching ants (view state). */
313
314
  quickMask?: boolean;
315
+ /** How the canvas shows the selection (view state; `quickMask` mirrors `'quickMask'`). */
316
+ selectionDisplay?: 'ants' | 'quickMask' | 'none';
317
+ /**
318
+ * Quick Mask mode: painting and fills without a layer edit the selection itself (white
319
+ * selects, black deselects), shown as a tint. View state; the edits are undo steps.
320
+ */
321
+ quickMaskEditing?: boolean;
322
+ /** Alpha channels: saved selections kept with the document, recorded in undo history. */
323
+ channels?: AlphaChannel[];
314
324
  /**
315
325
  * The layer stack, bottom to top. Opening an image gives one pixel layer, "Background", whose
316
326
  * pixels are the source image. Layers are immutable: compare them by identity.
@@ -378,6 +388,8 @@ export interface LegacySerializedImageEditorState {
378
388
  annotations: Annotation[];
379
389
  /** The active selection, run-length encoded. Missing when nothing is selected. */
380
390
  selection?: SerializedSelection;
391
+ /** Alpha channels (saved selections), run-length encoded. */
392
+ channels?: SerializedAlphaChannel[];
381
393
  }
382
394
  /**
383
395
  * A stored editor state: the document-level schema (v1 / v2) or the layered schema (v3, see
@@ -7,7 +7,7 @@ var _chunkZ64NRDEHcjs = require('../chunk-Z64NRDEH.cjs');
7
7
 
8
8
 
9
9
 
10
- var _chunkUHMSMUHKcjs = require('../chunk-UHMSMUHK.cjs');
10
+ var _chunkX2KQQZBTcjs = require('../chunk-X2KQQZBT.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 } = _chunkUHMSMUHKcjs.serializeLayers.call(void 0, state.layers);
1656
+ const { layers, assets } = _chunkX2KQQZBTcjs.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 _chunkUHMSMUHKcjs.serializeLayersExternal.call(void 0, state.layers, store);
1660
+ const { layers, assets } = await _chunkX2KQQZBTcjs.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(_chunkUHMSMUHKcjs.deserializeLayers.call(void 0, r.layers, { assets, problems }), problems);
1697
+ const layers = paintLayersOnly(_chunkX2KQQZBTcjs.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 _chunkUHMSMUHKcjs.resolveExternalAssets.call(void 0, _nullishCoalesce(r.assets, () => ( {})), store, problems);
1713
+ const assets = await _chunkX2KQQZBTcjs.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 = _chunkUHMSMUHKcjs.exportPsd.call(void 0, { width, height, layers: [paperLayer, ...layers], merged, resolution: dpi });
5412
+ const result = _chunkX2KQQZBTcjs.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-Y46OMERN.js";
10
+ } from "../chunk-FW7N64XK.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.183.0",
3
+ "version": "0.186.0",
4
4
  "description": "UI-agnostic rich text and media editing core library",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -74,24 +74,9 @@ Also an optional selection and crop/rotate/flip. See `pixerate-agent-tools` for
74
74
 
75
75
  ### Free Transform
76
76
 
77
- 1. **Read the frame first**: `get_transform_frame` says what a transform would move (`content`,
78
- `moved`) and its `frame` (document bounds of the content) with its corners. Every transform
79
- works on that frame: the numeric fields are percent of it and turn about one of its anchors.
80
- 2. **Pick what moves** with `content`: `layer` (all its pixels and a linked mask), `selection`
81
- (only the selected pixels, committed back into the same layer; the hole they leave is
82
- transparent and the outline moves with them), `selection-copy` (Layer via Copy, then the copy
83
- is transformed on a new layer; the original stays) or `auto` (selection when something is
84
- selected). `channel: "mask"` moves an unlinked mask alone; `transform_selection` moves only
85
- the outline.
86
- 3. **Choose the tool by the change**: `transform_layer` with `scaleX` / `scaleY` (percent),
87
- `rotate`, `skewX` / `skewY` (degrees), `dx` / `dy` and a `reference` anchor for scale, rotate
88
- and skew; `corners` for Distort (each corner free) and Perspective (move two corners
89
- symmetrically: the top pair in by the same amount narrows the top); `matrix` when you have one;
90
- `warp_layer` for a preset bend (`style`, `bend`, `horizontal`, `vertical`) or a custom `mesh`;
91
- `flip_layer` and `rotate_layer_90` for exact flips and quarter turns.
92
- 4. **Transform once.** Every call resamples the pixels, so chained calls soften them: put scale,
93
- rotation and move into one `transform_layer` call (or one matrix) instead of several.
94
- 5. **Look**: `render_preview`, or `capture_view` at the edges; each call is one undo step.
77
+ `get_transform_frame` first, pick `content` (`layer`, `selection`, `selection-copy`, `auto`), and
78
+ put every change into one `transform_layer` call (each call resamples). Read
79
+ [references/free-transform.md](references/free-transform.md) before transforming.
95
80
 
96
81
  ### Smart objects
97
82
 
@@ -256,6 +241,7 @@ in the source frame at the document's bit depth, colour mode and profile. Read
256
241
  | `get_adjustments` | Read the current state: `values` by control id, `process` version (1 or 2), and `layers` bottom to top with id, kind, params, opacity, blend, enabled and mask. Call first, and to find layer ids. |
257
242
  | `list_controls` | Get every base control (id, label, section, min, max, step, default, `requiresProcess`), each layer kind's settable controls (plus `opacity`), the blend mode ids and the `layers.<layerId>.<controlId>` path format. Call once per session before writing. |
258
243
  | `get_selection` | Read the active selection: `empty`, `bounds` (natural pixels), `area` in pixels, `percent` of the image and a description. Check it after every select or modify step and before masking: a wand that covered 90% of the image is the wrong selection. |
244
+ | `list_channels` | Read the alpha channels (saved selections): id, name, bounds, percent, overlay and visibility, and whether Quick Mask mode is on. |
259
245
  | `select_subject` | Select › Subject (only when the host has a segmentation model): `target` describes what to select ("the dog", "the sky"); an AI model finds the matching objects and selects their outlines, combined by `mode`. `maxSegments` 1 for a single subject. Returns the selection and `found` (label, bounds per object); nothing found leaves the selection unchanged. |
260
246
  | `list_layers` | Read the layer stack bottom to top as a tree: id, type (`pixel`, `adjustment`, `group`, `shape`, `annotation`, `smart`; smart objects add their `contents`, `smartFilters` and `filterMask`; shapes a `shape` summary of component ops and kinds and paint types, and `annotation` when migrated from one), name, document `bounds` of pixel layers, visibility, opacity, `fillOpacity`, blend, `clipped`, locks, a `mask` summary, an `effects` summary (master switch, scale, effect ids and kinds), pixel-layer `filters` (the Background's `develop` filter is the base adjustments), adjustment `kind` and `params`, group `children`, and which layer is `active`. Call it to understand a document with more than the Background before changing it; adjustment layer ids work with every adjustment-layer tool. |
261
247
  | `list_layer_effects` | Read one layer's effects: `enabled` (master switch), `scale` (Scale Effects), `margin` (px they reach beyond the shape), `fromPsd`, and every effect with `id`, `kind`, `label` and all its settings. Also returns the `globalLight`, the `patterns` pattern effects can use and the effect `kinds`. Call before changing effects you did not add. |
@@ -280,7 +266,7 @@ in the source frame at the document's bit depth, colour mode and profile. Read
280
266
  | Tool | Use it to |
281
267
  | --- | --- |
282
268
  | `set_adjustments` | Set base controls and existing layer parameters in one undo step: `values` is a map of control id or `layers.<layerId>.<controlId>` path to number; `label` is echoed. Values are clamped to range; returns `applied` and `clamped` (and a note when `tint` is set on a process 1 edit). Unknown ids fail and change nothing. Cannot set curves points, booleans or colours. |
283
- | `add_adjustment_layer` | Add a layer above the other adjustment layers and below annotations (or directly above `aboveId`, or at the top of group `parentId`): `kind` (curves, levels, hueSaturation, vibrance, colorBalance, blackWhite, photoFilter, invert, posterize, threshold), optional `params` merged with neutral defaults, `opacity` 0..1 (default 1), `blend` (default normal), `name`. Returns the layer (with its new id) and its stack index. The only tool that sets curves points, `colorize`, `preserveLuminosity`, a black and white `tint` or a photo filter `color`. |
269
+ | `add_adjustment_layer` | Add a layer above the other adjustment layers and below annotations (or directly above `aboveId`, or at the top of group `parentId`): `kind` (curves, levels, hueSaturation, vibrance, colorBalance, blackWhite, photoFilter, invert, posterize, threshold), optional `params` merged with neutral defaults, `opacity` 0..1 (default 1), `blend` (default normal), `name`. With a selection the layer is masked by it (`useSelection`, default true; `false` for a global layer; `masked` in the result). Returns the layer (with its new id) and its stack index. The only tool that sets curves points, `colorize`, `preserveLuminosity`, a black and white `tint` or a photo filter `color`. |
284
270
  | `select_all` | Select › All. Returns the selection summary (`empty`, `bounds`, `area`, `percent`). |
285
271
  | `deselect` | Clear the selection so edits apply to the whole layer. Returns the empty summary. |
286
272
  | `invert_selection` | Select › Inverse (soft edges stay soft; with nothing selected, selects all). Returns the summary. |
@@ -354,7 +340,7 @@ in the source frame at the document's bit depth, colour mode and profile. Read
354
340
  | `copy_smart_object` | Duplicate a smart object above it: `linked: true` shares the contents (edits show in both), `linked: false` (default, New Smart Object via Copy) gives independent contents. Returns the copy and its `contentsId`. |
355
341
  | `edit_smart_object_contents` | Open a smart object's contents as their own document (raster contents as one pixel layer, "Layer 0") with their own undo; all tools then act inside them. Returns the size, `layerIds`, `layers` and `editing` (the open path). Always close it. |
356
342
  | `close_smart_object_contents` | Close the innermost open contents: `save` true (default) writes them back and re-renders every linked instance (one undo step), false discards. Returns `layerId`, `changed`, `layerIds` and what is still `editing`. |
357
- | `add_smart_filter` | Add a `develop` (brightness … tint) or `blur` (`radius`) smart filter with its own `blend` and `opacity`, at `index` (default top); kinds may repeat. Returns the `filter` (with id) and the stack. |
343
+ | `add_smart_filter` | Add a `develop` (brightness … tint) or `blur` (`radius`) smart filter with its own `blend` and `opacity`, at `index` (default top); kinds may repeat. With a selection the filter mask is set from it (`useSelection`, default true). Returns the `filter` (with id), the stack and `masked`. |
358
344
  | `update_smart_filter` | Change one smart filter's settings, `enabled`, `blend` or `opacity` (`filterId` from `get_smart_object`); kind `psd` filters take only the last three. Returns the filter and the stack. |
359
345
  | `remove_smart_filter` | Remove one smart filter (the filter mask goes with the last one). Returns the stack. |
360
346
  | `move_smart_filter` | Move a smart filter to `index` (0 = bottom, applied first). Returns the stack. |
@@ -398,12 +384,24 @@ in the source frame at the document's bit depth, colour mode and profile. Read
398
384
  | `mix_colors` | Read-only. Mix 1 to 16 `colors` (`{color: #rrggbb, weight, strength?}`) like paint (Kubelka-Munk over reflectance spectra). Returns `mixed` (#rrggbb), `rgbLerp` (the plain RGB average, for comparison) and the normalised `weights`. Call it before `paint_stroke` / `fill_pixels` to pick a paint colour. |
399
385
  | `clear_pixels` | Remove the selected pixels (alpha to 0; a transparency-locked layer or a mask gets the background colour). Returns the changed bounds. |
400
386
  | `import_psd` | Open a PSD / PSB (`data` base64, a `data:` URL is fine) as the document, replacing the layers, crop and rotation (one undo step). Returns the new `layers` tree with ids, `layerIds`, size and `warnings` naming the affected layers. Call `inspect_psd` first when losses matter. |
401
- | `apply_effect` | Run a provider filter effect on a layer as a smart filter (`target`, `effect`, `values`, `index`, `enabled`, `opacity`); a pixel, shape or type layer becomes a smart object in the same undo step (`converted`). Returns the filter id. |
387
+ | `apply_effect` | Run a provider filter effect on a layer as a smart filter (`target`, `effect`, `values`, `index`, `enabled`, `opacity`); a pixel, shape or type layer becomes a smart object in the same undo step (`converted`). With a selection the filter mask is set from it (`masked`): deselect first to filter the whole layer. Returns the filter id. |
402
388
  | `update_effect` | Change an effect smart filter (`values` merged, `null` resets; `enabled`, `opacity`, `index`) or redraw an effect layer (`effect: "generator"`, values only). |
403
389
  | `remove_effect` | Remove an effect smart filter, or (`generator`) keep an effect layer's pixels as a plain layer. |
404
390
  | `add_effect_layer` | Add a layer drawn by a generator, overlay or title effect at the document's size and bit depth (`values`, `time`, `animate`, `name`, `aboveId` / `belowId`, `opacity`, `blend`). An effect with its own background (`backgroundOpacity` in `describe_effect`) starts with it transparent when there are layers below. Returns the layer id. |
405
391
  | `set_effect_layer` | Change an effect layer: swap its `effect` (its defaults plus `values`), pick the still frame (`time`, ms), or make it play in an animation (`animate: { duration }`; `null` for a still frame). The longest animating layer sets the animation's length. |
406
392
  | `apply_selection_as_mask` | Mask a layer with the current selection: pass `layerId` to mask an existing layer, or `kind` (plus optional `params`) to create a new masked layer in one step. `density` 0..1 and `feather` px soften the mask non-destructively. Returns `layerId`, kind, mask info and the selection summary. |
393
+ | `stroke_selection` | Edit › Stroke: a band `width` px (≤ 250) `outside` (default), `inside` or `center` on the selection edge, with `color`, `opacity`, `blend`, on a layer's pixels or mask (`channel`). One undo step. Returns the changed `bounds`. |
394
+ | `crop_to_selection` | Set the crop box to the selection's bounds (non-destructive; `reset_crop` undoes it). Returns the `crop`. |
395
+ | `content_aware_fill` | Remove what is selected by synthesising texture from around it on a pixel layer; `output` `new-layer` (keeps the fill separate, recommended) or `current`; `sampleRadius`, `seed` (another seed, another fill). Select with a 2–4 px margin. 8-bit only. Returns `layerId` and `bounds`. |
396
+ | `copy_pixels` | Copy the selected pixels of a layer (or with `merged` the visible image) to the editor clipboard, cropped to the selection with its soft edge. No undo step. Returns `width`, `height`, `x`, `y`. |
397
+ | `cut_pixels` | Copy, then clear the selected pixels (one undo step). Returns the size and position. |
398
+ | `paste_pixels` | Paste the clipboard as a new layer: `mode` `new-layer` (centred on the selection or canvas), `in-place`, `into` (masked by the selection) or `outside` (masked by its inverse). One undo step. Returns `layerId`, `bounds`, `masked`. |
399
+ | `save_selection` | Save the selection to a new alpha channel (`name`), or into `channelId` by `mode`. Saved with the document and in PSD. Returns the `channel`. |
400
+ | `load_selection_from_channel` | Load an alpha channel as the selection (`channelId`, `invert`, `mode`). Returns the summary. |
401
+ | `set_channel_properties` | Rename a channel or change its overlay `color`, `opacity`, `indicates` (`masked` / `selected`) and `visible`. Returns the `channel`. |
402
+ | `duplicate_channel` | Copy a channel (`name`, default "<name> copy"). Returns the copy. |
403
+ | `delete_channel` | Delete a channel (undoable). Returns the remaining channels. |
404
+ | `set_quick_mask` | Quick Mask mode (`enabled`): paint, fill and retouch tools without `layerId` edit the selection (white selects, black deselects); turn it off before painting layers. Returns the mode. |
407
405
 
408
406
  ### Save and open
409
407
 
@@ -0,0 +1,22 @@
1
+ # Free Transform
2
+
3
+ How to move, scale, rotate, skew, distort and warp layers, selected pixels and masks in the Pixerate image editor. Read it before `transform_layer`, `warp_layer`, `transform_selection`, `flip_layer` or `rotate_layer_90`.
4
+
5
+ 1. **Read the frame first**: `get_transform_frame` says what a transform would move (`content`,
6
+ `moved`) and its `frame` (document bounds of the content) with its corners. Every transform
7
+ works on that frame: the numeric fields are percent of it and turn about one of its anchors.
8
+ 2. **Pick what moves** with `content`: `layer` (all its pixels and a linked mask), `selection`
9
+ (only the selected pixels, committed back into the same layer; the hole they leave is
10
+ transparent and the outline moves with them), `selection-copy` (Layer via Copy, then the copy
11
+ is transformed on a new layer; the original stays) or `auto` (selection when something is
12
+ selected). `channel: "mask"` moves an unlinked mask alone; `transform_selection` moves only
13
+ the outline.
14
+ 3. **Choose the tool by the change**: `transform_layer` with `scaleX` / `scaleY` (percent),
15
+ `rotate`, `skewX` / `skewY` (degrees), `dx` / `dy` and a `reference` anchor for scale, rotate
16
+ and skew; `corners` for Distort (each corner free) and Perspective (move two corners
17
+ symmetrically: the top pair in by the same amount narrows the top); `matrix` when you have one;
18
+ `warp_layer` for a preset bend (`style`, `bend`, `horizontal`, `vertical`) or a custom `mesh`;
19
+ `flip_layer` and `rotate_layer_90` for exact flips and quarter turns.
20
+ 4. **Transform once.** Every call resamples the pixels, so chained calls soften them: put scale,
21
+ rotation and move into one `transform_layer` call (or one matrix) instead of several.
22
+ 5. **Look**: `render_preview`, or `capture_view` at the edges; each call is one undo step.
@@ -53,19 +53,30 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
53
53
  - `grow_selection` (tolerance 10–30) picks up the shades a wand click missed;
54
54
  `select_similar` adds the same colours elsewhere in the image.
55
55
  - `contract` 1–2 px pulls the edge off a halo.
56
+ - Paint it directly: `set_quick_mask` `enabled: true`, then `paint_stroke` without
57
+ `layerId` (white adds, black removes, a soft brush feathers; `blur_stroke` softens a part
58
+ of the edge), then `set_quick_mask` `enabled: false`.
56
59
  - `refine_selection` snaps to real edges: radius 2–4 for hard edges, 8–20 for hair and fur;
57
60
  `smartRadius: true` for mixed edges.
58
61
  - `shiftEdge` −10..−30 % tightens a refined edge that picked up background.
59
62
  **Check:** `percent` changed by roughly what you intended (a few percent at most for a
60
63
  cleanup).
61
64
  5. **Output.** Choose by goal:
62
- - A local adjustment: `apply_selection_as_mask` with `kind` (one step).
65
+ - A local adjustment: `add_adjustment_layer` while the selection is active (it is masked
66
+ by it), or `apply_selection_as_mask` with `kind`. Filters (`apply_effect`,
67
+ `add_smart_filter`) are also limited to an active selection.
63
68
  - A cutout: `layer_via_copy` (the original stays), or `add_layer_mask` `from: "selection"` on
64
69
  the layer itself.
65
- - Painting or fills inside it: keep the selection active and paint.
70
+ - Painting or fills inside it: keep the selection active and paint; `stroke_selection` for
71
+ an outline that follows it.
72
+ - Removing what it covers: `content_aware_fill` `output: "new-layer"`.
73
+ - A copy elsewhere: `copy_pixels`, then `paste_pixels` (`into` masks it by a selection).
66
74
  Soften with the mask's own `feather` / `density` rather than `modify_selection` `feather`, so
67
75
  it stays adjustable.
68
- 6. **Verify.** `render_preview` at 1024 px or more, and `capture_view` on the edge at 2–8×.
76
+ 6. **Store.** `save_selection` with a clear name before you change a selection that took
77
+ work, and whenever it will be needed again (a second adjustment, a later refinement). Load
78
+ it with `load_selection_from_channel`; channels are saved with the document and in PSDs.
79
+ 7. **Verify.** `render_preview` at 1024 px or more, and `capture_view` on the edge at 2–8×.
69
80
  **Check:** no fringe of the old background colour, no stair-steps, no visible line where an
70
81
  adjustment ends. Fix by loading the mask back (`load_selection_from_mask`), refining, and
71
82
  masking again.
@@ -114,6 +125,14 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
114
125
  a smart object's filters.
115
126
  - **Clip a texture or colour to a layer:** put it directly above and set `clipped: true` with
116
127
  `set_layer_properties`; no selection is needed.
128
+ - **Remove an object:** select it with a 2–4 px margin (`modify_selection` `expand`), then
129
+ `content_aware_fill` `output: "new-layer"`; try another `seed` if the fill repeats texture,
130
+ and mask out what is wrong.
131
+ - **Outline a cutout:** `select_layer_transparency` on it, then `stroke_selection`
132
+ `location: "outside"` on a new layer below it.
133
+ - **Crop to a subject:** select it, then `crop_to_selection` (non-destructive).
134
+ - **Put a picture into a shape:** select the shape, `copy_pixels` the picture, then
135
+ `paste_pixels` `mode: "into"`.
117
136
  - **Picture inside a frame:** mask the picture with the frame's shape, then
118
137
  `set_layer_mask_properties` `linked: false` so the picture moves under a fixed mask; link it
119
138
  again afterwards.
@@ -135,6 +154,11 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
135
154
  - **Deselected by mistake:** `reselect` brings it back (or `undo_image`).
136
155
  - **Empty selection:** `modify_selection`, `refine_selection` and `apply_selection_as_mask` fail
137
156
  on it. Select first.
157
+ - **A forgotten selection limits new layers and filters:** `add_adjustment_layer`,
158
+ `apply_effect` and `add_smart_filter` mask themselves by it. `deselect` first for a global
159
+ change, or pass `useSelection: false`.
160
+ - **Quick Mask left on:** while it is on, `paint_stroke` and `fill_pixels` without `layerId`
161
+ edit the selection, not the image. Turn it off when the selection is done.
138
162
  - **Hidden selection still applies:** with `set_selection_display` `none`, paint and fills are
139
163
  still limited to it. `deselect` before a whole-layer edit.
140
164
  - **Wrong coordinates:** use natural image pixels, not preview pixels. On a cropped or rotated
@@ -152,6 +176,10 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
152
176
  - `select_layer_transparency`: a layer's own pixels
153
177
  - `grow_selection`, `select_similar`: add similar colour, connected or anywhere
154
178
  - `reselect`: undo a deselect
179
+ - `save_selection`, `load_selection_from_channel`, `list_channels`: keep selections as alpha channels
180
+ - `set_channel_properties`, `duplicate_channel`, `delete_channel`: manage channels
181
+ - `set_quick_mask`: paint the selection
182
+ - `fill_pixels`, `blur_stroke`: fill or soften the selection in Quick Mask mode
155
183
  - `magic_wand`: similar colour from a seed
156
184
  - `select_color_range`: colour samples, hue, tone and skin presets
157
185
  - `select_subject`: a subject by description (needs a host model)
@@ -167,6 +195,11 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
167
195
  - `add_layer_mask`, `set_layer_mask_properties`, `apply_layer_mask`, `remove_layer_mask`: layer masks
168
196
  - `set_smart_filter_mask`: limit smart filters
169
197
  - `layer_via_copy`: lift the selection onto a new layer
198
+ - `stroke_selection`: an outline along the selection edge
199
+ - `crop_to_selection`: crop to the selection's bounds
200
+ - `content_aware_fill`: remove what is selected
201
+ - `copy_pixels`, `cut_pixels`, `paste_pixels`: the editor clipboard
202
+ - `add_adjustment_layer`, `apply_effect`, `add_smart_filter`: masked by an active selection
170
203
  - `paint_stroke`: paint on a mask with `channel: "mask"`
171
204
  - `undo_image`: take back a step
172
205
  - `set_layer_properties`: clipping