@pixerate/editor 0.215.0 → 0.217.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.
@@ -176,6 +176,7 @@ export declare class ImageEditorRenderer {
176
176
  private applyPost;
177
177
  private publishHistogram;
178
178
  private quickMaskCache;
179
+ private diffCache;
179
180
  private outlineCache;
180
181
  private emptyMaskCache;
181
182
  /** An empty selection of the image's size (Quick Mask mode with nothing selected). */
@@ -190,6 +191,7 @@ export declare class ImageEditorRenderer {
190
191
  /** A channel's tint (colour, opacity, masked or selected area), drawn in natural image space. */
191
192
  private drawMaskOverlay;
192
193
  /** Rubylith over the unselected area, drawn in natural image space. */
194
+ private drawSelectionDiff;
193
195
  private drawQuickMask;
194
196
  /**
195
197
  * Marching ants: a white line under a black dashed one, one screen pixel wide. `scale` is
@@ -176,6 +176,7 @@ export declare class ImageEditorRenderer {
176
176
  private applyPost;
177
177
  private publishHistogram;
178
178
  private quickMaskCache;
179
+ private diffCache;
179
180
  private outlineCache;
180
181
  private emptyMaskCache;
181
182
  /** An empty selection of the image's size (Quick Mask mode with nothing selected). */
@@ -190,6 +191,7 @@ export declare class ImageEditorRenderer {
190
191
  /** A channel's tint (colour, opacity, masked or selected area), drawn in natural image space. */
191
192
  private drawMaskOverlay;
192
193
  /** Rubylith over the unselected area, drawn in natural image space. */
194
+ private drawSelectionDiff;
193
195
  private drawQuickMask;
194
196
  /**
195
197
  * Marching ants: a white line under a black dashed one, one screen pixel wide. `scale` is
@@ -0,0 +1,59 @@
1
+ /**
2
+ * What changed between two selections (`compare_selections`): the area added and removed, where,
3
+ * and how much they overlap (IoU), so an agent can check that a modify or refine step did what it
4
+ * meant to and a person can see it as a two-colour overlay.
5
+ */
6
+ import type { CropBox } from '../types.cjs';
7
+ import { type SelectionMask } from './mask.cjs';
8
+ /** One piece of added or removed area. */
9
+ export interface SelectionDiffRegion {
10
+ area: number;
11
+ bounds: CropBox;
12
+ }
13
+ export interface SelectionDiffSide {
14
+ /** Pixels at least half selected. */
15
+ area: number;
16
+ bounds: CropBox | null;
17
+ }
18
+ export interface SelectionDiff {
19
+ a: SelectionDiffSide;
20
+ b: SelectionDiffSide;
21
+ /** Selected in b but not in a: area, and the largest pieces (at most `maxRegions`). */
22
+ added: {
23
+ area: number;
24
+ count: number;
25
+ regions: SelectionDiffRegion[];
26
+ };
27
+ /** Selected in a but not in b. */
28
+ removed: {
29
+ area: number;
30
+ count: number;
31
+ regions: SelectionDiffRegion[];
32
+ };
33
+ /** Intersection over union, 0–1 (1 when both are empty). */
34
+ iou: number;
35
+ /** Σ |coverage b − coverage a| in pixels: also counts edges that only softened or hardened. */
36
+ coverageChange: number;
37
+ /** Every pixel's coverage is the same. */
38
+ identical: boolean;
39
+ }
40
+ export interface CompareSelectionsOptions {
41
+ /** Pieces smaller than this many pixels are counted in the area but not listed (default 4). */
42
+ minArea?: number;
43
+ /** At most this many pieces are listed per side (default 10). */
44
+ maxRegions?: number;
45
+ }
46
+ /**
47
+ * Compares selection `a` (before) with `b` (after) over a `width × height` canvas; `null` is
48
+ * nothing selected. Area, pieces and IoU are judged at half coverage. Also returns the added and
49
+ * removed masks for an overlay.
50
+ */
51
+ export declare function compareSelectionMasks(a: SelectionMask | null, b: SelectionMask | null, width: number, height: number, options?: CompareSelectionsOptions): SelectionDiff & {
52
+ addedMask: SelectionMask;
53
+ removedMask: SelectionMask;
54
+ };
55
+ /**
56
+ * The diff overlay's RGBA: added area tinted green, removed area red, the rest transparent
57
+ * (`width × height`, the masks' size).
58
+ */
59
+ export declare function selectionDiffPixels(added: SelectionMask, removed: SelectionMask): Uint8ClampedArray;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * What changed between two selections (`compare_selections`): the area added and removed, where,
3
+ * and how much they overlap (IoU), so an agent can check that a modify or refine step did what it
4
+ * meant to and a person can see it as a two-colour overlay.
5
+ */
6
+ import type { CropBox } from '../types.js';
7
+ import { type SelectionMask } from './mask.js';
8
+ /** One piece of added or removed area. */
9
+ export interface SelectionDiffRegion {
10
+ area: number;
11
+ bounds: CropBox;
12
+ }
13
+ export interface SelectionDiffSide {
14
+ /** Pixels at least half selected. */
15
+ area: number;
16
+ bounds: CropBox | null;
17
+ }
18
+ export interface SelectionDiff {
19
+ a: SelectionDiffSide;
20
+ b: SelectionDiffSide;
21
+ /** Selected in b but not in a: area, and the largest pieces (at most `maxRegions`). */
22
+ added: {
23
+ area: number;
24
+ count: number;
25
+ regions: SelectionDiffRegion[];
26
+ };
27
+ /** Selected in a but not in b. */
28
+ removed: {
29
+ area: number;
30
+ count: number;
31
+ regions: SelectionDiffRegion[];
32
+ };
33
+ /** Intersection over union, 0–1 (1 when both are empty). */
34
+ iou: number;
35
+ /** Σ |coverage b − coverage a| in pixels: also counts edges that only softened or hardened. */
36
+ coverageChange: number;
37
+ /** Every pixel's coverage is the same. */
38
+ identical: boolean;
39
+ }
40
+ export interface CompareSelectionsOptions {
41
+ /** Pieces smaller than this many pixels are counted in the area but not listed (default 4). */
42
+ minArea?: number;
43
+ /** At most this many pieces are listed per side (default 10). */
44
+ maxRegions?: number;
45
+ }
46
+ /**
47
+ * Compares selection `a` (before) with `b` (after) over a `width × height` canvas; `null` is
48
+ * nothing selected. Area, pieces and IoU are judged at half coverage. Also returns the added and
49
+ * removed masks for an overlay.
50
+ */
51
+ export declare function compareSelectionMasks(a: SelectionMask | null, b: SelectionMask | null, width: number, height: number, options?: CompareSelectionsOptions): SelectionDiff & {
52
+ addedMask: SelectionMask;
53
+ removedMask: SelectionMask;
54
+ };
55
+ /**
56
+ * The diff overlay's RGBA: added area tinted green, removed area red, the rest transparent
57
+ * (`width × height`, the masks' size).
58
+ */
59
+ export declare function selectionDiffPixels(added: SelectionMask, removed: SelectionMask): Uint8ClampedArray;
@@ -18,3 +18,5 @@ export * from './refine-edits.cjs';
18
18
  export * from './select-and-mask.cjs';
19
19
  export * from './worker.cjs';
20
20
  export * from './inspect.cjs';
21
+ export * from './uncertainty.cjs';
22
+ export * from './diff.cjs';
@@ -18,3 +18,5 @@ export * from './refine-edits.js';
18
18
  export * from './select-and-mask.js';
19
19
  export * from './worker.js';
20
20
  export * from './inspect.js';
21
+ export * from './uncertainty.js';
22
+ export * from './diff.js';
@@ -68,3 +68,12 @@ export declare function inspectMaskEdge(mask: SelectionMask, image: PixelBuffer
68
68
  x: number;
69
69
  y: number;
70
70
  }, options?: InspectMaskEdgeOptions): MaskEdgeReport;
71
+ /**
72
+ * A cached sampler of the image's edge strength at document pixels: the Sobel magnitude of
73
+ * alpha-weighted R, G and B, the largest of the three, as a fraction of a full 0–255 step.
74
+ * Reads clamp at the image's edges. `W` is the canvas width (for the cache key).
75
+ */
76
+ export declare function createGradientSampler(image: PixelBuffer & {
77
+ x: number;
78
+ y: number;
79
+ }, W: number): (x: number, y: number) => number;
@@ -68,3 +68,12 @@ export declare function inspectMaskEdge(mask: SelectionMask, image: PixelBuffer
68
68
  x: number;
69
69
  y: number;
70
70
  }, options?: InspectMaskEdgeOptions): MaskEdgeReport;
71
+ /**
72
+ * A cached sampler of the image's edge strength at document pixels: the Sobel magnitude of
73
+ * alpha-weighted R, G and B, the largest of the three, as a fraction of a full 0–255 step.
74
+ * Reads clamp at the image's edges. `W` is the canvas width (for the cache key).
75
+ */
76
+ export declare function createGradientSampler(image: PixelBuffer & {
77
+ x: number;
78
+ y: number;
79
+ }, W: number): (x: number, y: number) => number;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Where a selection is unsure of itself (a trimap's unknown band): the stretches of its edge that
3
+ * run where the image shows no edge, widened into a band. The smart selection tools report these
4
+ * as `uncertainRegions`, so an agent looks at and refines only them (`refine_selection` `regions`).
5
+ */
6
+ import type { CropBox } from '../types.cjs';
7
+ import { type PixelBuffer, type SelectionMask } from './mask.cjs';
8
+ export interface UncertainRegion {
9
+ bounds: CropBox;
10
+ /** Pixels in the unknown band. */
11
+ area: number;
12
+ /** 0–1: how strongly the image backs the edge here (low = unsure). */
13
+ confidence: number;
14
+ }
15
+ export interface SelectionUncertainty {
16
+ /** The unknown band over the canvas (255 = unsure). */
17
+ unknown: SelectionMask;
18
+ /** The least confident regions first (at most `maxRegions`). */
19
+ regions: UncertainRegion[];
20
+ /** How many regions there are in all. */
21
+ count: number;
22
+ /** 0–1 over the whole edge. */
23
+ confidence: number;
24
+ }
25
+ export interface SelectionUncertaintyOptions {
26
+ /** Half-width of the unknown band in px (default 1/200 of the shorter side, at least 2). */
27
+ band?: number;
28
+ /** Regions smaller than this many pixels are left out (default 16). */
29
+ minArea?: number;
30
+ /** At most this many regions are listed (default 20). */
31
+ maxRegions?: number;
32
+ }
33
+ /**
34
+ * The unknown band of `mask` against `image` (RGBA, straight alpha, at `image.x`, `image.y` in
35
+ * document pixels). An empty selection has no band.
36
+ */
37
+ export declare function selectionUncertainty(mask: SelectionMask, image: PixelBuffer & {
38
+ x: number;
39
+ y: number;
40
+ }, options?: SelectionUncertaintyOptions): SelectionUncertainty;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Where a selection is unsure of itself (a trimap's unknown band): the stretches of its edge that
3
+ * run where the image shows no edge, widened into a band. The smart selection tools report these
4
+ * as `uncertainRegions`, so an agent looks at and refines only them (`refine_selection` `regions`).
5
+ */
6
+ import type { CropBox } from '../types.js';
7
+ import { type PixelBuffer, type SelectionMask } from './mask.js';
8
+ export interface UncertainRegion {
9
+ bounds: CropBox;
10
+ /** Pixels in the unknown band. */
11
+ area: number;
12
+ /** 0–1: how strongly the image backs the edge here (low = unsure). */
13
+ confidence: number;
14
+ }
15
+ export interface SelectionUncertainty {
16
+ /** The unknown band over the canvas (255 = unsure). */
17
+ unknown: SelectionMask;
18
+ /** The least confident regions first (at most `maxRegions`). */
19
+ regions: UncertainRegion[];
20
+ /** How many regions there are in all. */
21
+ count: number;
22
+ /** 0–1 over the whole edge. */
23
+ confidence: number;
24
+ }
25
+ export interface SelectionUncertaintyOptions {
26
+ /** Half-width of the unknown band in px (default 1/200 of the shorter side, at least 2). */
27
+ band?: number;
28
+ /** Regions smaller than this many pixels are left out (default 16). */
29
+ minArea?: number;
30
+ /** At most this many regions are listed (default 20). */
31
+ maxRegions?: number;
32
+ }
33
+ /**
34
+ * The unknown band of `mask` against `image` (RGBA, straight alpha, at `image.x`, `image.y` in
35
+ * document pixels). An empty selection has no band.
36
+ */
37
+ export declare function selectionUncertainty(mask: SelectionMask, image: PixelBuffer & {
38
+ x: number;
39
+ y: number;
40
+ }, options?: SelectionUncertaintyOptions): SelectionUncertainty;
@@ -319,6 +319,15 @@ export interface ImageEditorState {
319
319
  * growing as the brush moves). View state: not an edit, not in history, not saved.
320
320
  */
321
321
  selectionPreview?: SelectionMask | null;
322
+ /**
323
+ * `compare_selections`' overlay: the added area tinted green, the removed red. Shown while the
324
+ * selection is still `selection` (any change hides it). View state: not in history, not saved.
325
+ */
326
+ selectionDiff?: {
327
+ added: SelectionMask;
328
+ removed: SelectionMask;
329
+ selection: SelectionMask | null;
330
+ } | null;
322
331
  /** Quick Mask view: tint the unselected area instead of drawing marching ants (view state). */
323
332
  quickMask?: boolean;
324
333
  /** How the canvas shows the selection (view state; `quickMask` mirrors `'quickMask'`). */
@@ -319,6 +319,15 @@ export interface ImageEditorState {
319
319
  * growing as the brush moves). View state: not an edit, not in history, not saved.
320
320
  */
321
321
  selectionPreview?: SelectionMask | null;
322
+ /**
323
+ * `compare_selections`' overlay: the added area tinted green, the removed red. Shown while the
324
+ * selection is still `selection` (any change hides it). View state: not in history, not saved.
325
+ */
326
+ selectionDiff?: {
327
+ added: SelectionMask;
328
+ removed: SelectionMask;
329
+ selection: SelectionMask | null;
330
+ } | null;
322
331
  /** Quick Mask view: tint the unselected area instead of drawing marching ants (view state). */
323
332
  quickMask?: boolean;
324
333
  /** How the canvas shows the selection (view state; `quickMask` mirrors `'quickMask'`). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor",
3
- "version": "0.215.0",
3
+ "version": "0.217.0",
4
4
  "description": "UI-agnostic rich text and media editing core library",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -120,8 +120,7 @@ Keep outlines as paths (`make_work_path`, `add_path`) and edit their anchors: se
120
120
 
121
121
  ### Colour management
122
122
 
123
- The working space, Assign / Convert to Profile with intents, soft proofing with a gamut warning
124
- and colour conversions for print briefs: read [references/color-management.md](references/color-management.md).
123
+ Working space, profiles, soft proofing and print conversions: read [references/color-management.md](references/color-management.md).
125
124
  ### Retouching
126
125
 
127
126
  Clone Stamp, Healing Brush, Spot Healing, Smudge, Dodge, Burn, Sponge, Blur, Sharpen and the
@@ -209,6 +208,7 @@ in the source frame at the document's bit depth, colour mode and profile. Read
209
208
  | `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. |
210
209
  | `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. |
211
210
  | `inspect_mask_edge` | Measure an edge without looking: the selection (default) or a layer's `layer-mask` / `vector-mask` (`layerId`). Returns `haloScore`, `edgeAlignment`, `jaggedness`, `softFraction`, `holes`, `islands`, `worst` regions for `capture_view` and `issues` with fixes. Inspect, fix, inspect again (see `pixerate-image-selection-masking`). |
211
+ | `compare_selections` | What changed between two selections: `a` (default the one before the last undo step) and `b` (default current), each `selection`, `history` (`steps`), `channel` or a layer's `layer-mask` / `vector-mask`. Returns `added` / `removed` (area, largest pieces with bounds), `iou` and `identical`; `show` tints the change on the canvas. Check every modify or refine step. |
212
212
  | `list_channels` | Read the alpha channels (saved selections): id, name, bounds, percent, overlay and visibility, and whether Quick Mask mode is on. |
213
213
  | `select_subject` | Select › Subject. With a host segmentation model, `target` describes what to select ("the dog", "the sky") and the model's outlines are selected (`maxSegments` 1 for one). Without one, the subject that stands out most is found classically; `target` can't choose it (`method: "saliency"`, `note`). Combined by `mode`. Returns the selection, `found` (label, bounds) and `method`; nothing found leaves the selection unchanged. |
214
214
  | `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. |
@@ -258,7 +258,7 @@ in the source frame at the document's bit depth, colour mode and profile. Read
258
258
  | `grow_selection` | Select › Grow: add connected pixels within `tolerance` (default 32) of a selected colour; follows colour, not distance. `sample`, `sampleMerged`. Fails when nothing is selected. Returns the summary. |
259
259
  | `select_similar` | Select › Similar: add every pixel in the image within `tolerance` of a selected colour, connected or not. Fails when nothing is selected. Returns the summary. |
260
260
  | `reselect` | Bring back the last selection `deselect` cleared (one undo step). Fails when there is none. Returns the summary. |
261
- | `refine_selection` | Select and Mask: snap the selection edge to image edges within `radius` px (0..64, default 8), with `smartRadius`, `smooth` 0..100, `feather` px, `contrast` 0..100 and `shiftEdge` -100..100 %, then `output` to `selection` (default), `layer-mask`, `new-layer`, `new-layer-with-mask` (on `layerId`, default active; new layers hide the source), or `new-document` / `new-document-with-mask` (host must open documents; returns `documentId`), with `decontaminate` 0..100 for new layers and documents. `edits` (`quick-select`, `brush`, `lasso`, `object` add/subtract; `refine-brush` over hair) apply first; `refineMode: "object"` uses the host model. One step; returns `refineMode`, `layerId` / `newLayerId`. Use on hair, fur, foliage or a coarse AI mask. |
261
+ | `refine_selection` | Select and Mask: snap the selection edge to image edges within `radius` px (0..64, default 8), with `smartRadius`, `smooth` 0..100, `feather` px, `contrast` 0..100 and `shiftEdge` -100..100 %, then `output` to `selection` (default), `layer-mask`, `new-layer`, `new-layer-with-mask` (on `layerId`, default active; new layers hide the source), or `new-document` / `new-document-with-mask` (host must open documents; returns `documentId`), with `decontaminate` 0..100 for new layers and documents. `edits` (`quick-select`, `brush`, `lasso`, `object` add/subtract; `refine-brush` over hair) apply first; `refineMode: "object"` uses the host model; `regions` (rects, e.g. `uncertainRegions` bounds) limits it to those. One step; returns `refineMode`, `layerId` / `newLayerId`. Use on hair, fur, foliage or a coarse AI mask. |
262
262
  | `update_adjustment_layer` | Change a layer's `enabled` (false hides it without deleting), `blend`, `opacity` (0..1) or `name` in one undo step. Returns the layer. Its parameters change with `set_adjustments`. Toggle `enabled` to compare a layer's effect before and after. |
263
263
  | `move_adjustment_layer` | Move a layer to index `to` (0 = bottom, applied first; past the top means top). Order matters: a curves layer above a hue/saturation layer works on its output. Fails when the layer is already there. Returns the ids bottom to top. |
264
264
  | `remove_adjustment_layer` | Delete a layer and its mask. Prefer hiding it with `update_adjustment_layer` when the user may want it back. Returns the remaining ids. |
@@ -43,7 +43,8 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
43
43
  2. **Build it in steps** with `mode`: start with `replace`, `add` what was missed, `subtract`
44
44
  what leaked, and `intersect` to limit it to a region (for example `select_rect` around the
45
45
  subject).
46
- **Check:** `bounds` hug the subject. A bounds as large as the image means a leak.
46
+ **Check:** `bounds` hug the subject. A bounds as large as the image means a leak. Smart tools
47
+ return `uncertainRegions` (edge with no image edge under it): look there, refine with `regions`.
47
48
  3. **Measure the edge, then look.** `inspect_mask_edge` scores it: `haloScore` ≥ 0.35 is old
48
49
  background inside the edge, `edgeAlignment` < 0.5 is off the image's edges, `jaggedness` ≥ 15
49
50
  zigzags; it counts `holes` and `islands`, `issues` names fixes, `worst` gives `capture_view` rects.
@@ -64,8 +65,8 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
64
65
  can open the same edits in the workspace. With a host model, `refineMode: "object"`
65
66
  refines from the model's outline.
66
67
  - `shiftEdge` −10..−30 % tightens a refined edge that picked up background.
67
- **Check:** `percent` changed by roughly what you intended (a few percent at most for a
68
- cleanup).
68
+ **Check:** `compare_selections` after the step: `added` / `removed` are what you meant and
69
+ `iou` stays near 1 for a cleanup (it tints the change on the canvas).
69
70
  5. **Output.** Choose by goal:
70
71
  - A local adjustment: `add_adjustment_layer` while the selection is active (it is masked
71
72
  by it), or `apply_selection_as_mask` with `kind`. Filters (`apply_effect`,
@@ -210,13 +211,12 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
210
211
 
211
212
  - `get_selection`: empty, bounds, area and percent after every step
212
213
  - `select_all`, `deselect`, `invert_selection`: whole-image selections
213
- - `select_rect`, `select_ellipse`: marquees
214
+ - `select_rect`, `select_ellipse`, `select_line`: marquees, and a single row or column
214
215
  - `select_polygon`: the lasso, along a contour; with `snapToEdges`, the Magnetic Lasso
215
216
  - `quick_select`: brush strokes that grow out to the region's edges
216
217
  - `select_object`: the object in a box or under a point
217
218
  - `select_focus_area`: what is in focus
218
219
  - `select_sky`: the sky
219
- - `select_line`: a single row or column
220
220
  - `select_layer_transparency`: a layer's own pixels
221
221
  - `grow_selection`, `select_similar`: add similar colour, connected or anywhere
222
222
  - `reselect`: undo a deselect
@@ -239,7 +239,7 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
239
239
  - `modify_selection`: expand, contract, feather, border, smooth
240
240
  - `refine_selection`: snap the edge to the image
241
241
  - `transform_selection`: move or scale the outline only
242
- - `inspect_mask_edge`: measured halo, alignment, jaggedness, softness, holes and islands of an edge
242
+ - `inspect_mask_edge`, `compare_selections`: measure an edge (halo, alignment, jaggedness, holes, islands) and what a step changed (IoU)
243
243
  - `set_edge_decontamination`, `set_edge_despill`, `set_edge_ambient`, `get_edge_color`: live edge colour on a masked cutout
244
244
  - `capture_view`, `render_preview`: look at the edge at pixel level; check the result
245
245
  - `apply_selection_as_mask`: a masked adjustment layer in one step