@pixerate/editor 0.178.0 → 0.180.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.
@@ -68,6 +68,8 @@ export interface RenderOptions {
68
68
  outline: ImagePoint[];
69
69
  handles: ImagePoint[];
70
70
  reference: ImagePoint;
71
+ /** The rotate handle, on a stem from the top edge's midpoint (`outline[0]` to `outline[1]`). */
72
+ rotate?: ImagePoint;
71
73
  /** Warp mode: the mesh's grid lines as cubics, its anchors, and handles with their anchors. */
72
74
  warp?: {
73
75
  curves: ImagePoint[][];
@@ -174,7 +176,10 @@ export declare class ImageEditorRenderer {
174
176
  private outlineCache;
175
177
  /** Rubylith over the unselected area, drawn in natural image space. */
176
178
  private drawQuickMask;
177
- /** Marching ants: a white line under a black dashed one, one screen pixel wide. */
179
+ /**
180
+ * Marching ants: a white line under a black dashed one, one screen pixel wide. `scale` is
181
+ * screen pixels per natural pixel (the projection scale times the host's CSS scale).
182
+ */
178
183
  private drawAnts;
179
184
  private drawDraftPath;
180
185
  private getSourceId;
@@ -68,6 +68,8 @@ export interface RenderOptions {
68
68
  outline: ImagePoint[];
69
69
  handles: ImagePoint[];
70
70
  reference: ImagePoint;
71
+ /** The rotate handle, on a stem from the top edge's midpoint (`outline[0]` to `outline[1]`). */
72
+ rotate?: ImagePoint;
71
73
  /** Warp mode: the mesh's grid lines as cubics, its anchors, and handles with their anchors. */
72
74
  warp?: {
73
75
  curves: ImagePoint[][];
@@ -174,7 +176,10 @@ export declare class ImageEditorRenderer {
174
176
  private outlineCache;
175
177
  /** Rubylith over the unselected area, drawn in natural image space. */
176
178
  private drawQuickMask;
177
- /** Marching ants: a white line under a black dashed one, one screen pixel wide. */
179
+ /**
180
+ * Marching ants: a white line under a black dashed one, one screen pixel wide. `scale` is
181
+ * screen pixels per natural pixel (the projection scale times the host's CSS scale).
182
+ */
178
183
  private drawAnts;
179
184
  private drawDraftPath;
180
185
  private getSourceId;
@@ -7,3 +7,4 @@ export * from './layer-mask.cjs';
7
7
  export * from './overlay.cjs';
8
8
  export * from './tool-options.cjs';
9
9
  export * from './gesture.cjs';
10
+ export * from './segmentation.cjs';
@@ -7,3 +7,4 @@ export * from './layer-mask.js';
7
7
  export * from './overlay.js';
8
8
  export * from './tool-options.js';
9
9
  export * from './gesture.js';
10
+ export * from './segmentation.js';
@@ -16,3 +16,11 @@ export interface QuickMaskStyle {
16
16
  * selection shows through clear, as Photoshop's rubylith overlay.
17
17
  */
18
18
  export declare function quickMaskPixels(mask: SelectionMask, style?: QuickMaskStyle): Uint8ClampedArray;
19
+ /** How often marching ants step (ms); each step moves the dashes one screen pixel. */
20
+ export declare const MARCHING_ANTS_STEP_MS = 100;
21
+ /**
22
+ * Marches the ants: calls `tick` with the next `RenderOptions.antsOffset` every `stepMs` until the
23
+ * returned stop function is called. Hosts run it while a selection is shown and render on each
24
+ * tick. With reduced motion requested the ants stand still and `tick` is never called.
25
+ */
26
+ export declare function startMarchingAnts(tick: (offset: number) => void, stepMs?: number): () => void;
@@ -16,3 +16,11 @@ export interface QuickMaskStyle {
16
16
  * selection shows through clear, as Photoshop's rubylith overlay.
17
17
  */
18
18
  export declare function quickMaskPixels(mask: SelectionMask, style?: QuickMaskStyle): Uint8ClampedArray;
19
+ /** How often marching ants step (ms); each step moves the dashes one screen pixel. */
20
+ export declare const MARCHING_ANTS_STEP_MS = 100;
21
+ /**
22
+ * Marches the ants: calls `tick` with the next `RenderOptions.antsOffset` every `stepMs` until the
23
+ * returned stop function is called. Hosts run it while a selection is shown and render on each
24
+ * tick. With reduced motion requested the ants stand still and `tick` is never called.
25
+ */
26
+ export declare function startMarchingAnts(tick: (offset: number) => void, stepMs?: number): () => void;
@@ -0,0 +1,49 @@
1
+ import type { CropBox, ImagePoint } from '../types.cjs';
2
+ import { type PixelBuffer, type SelectionMask } from './mask.cjs';
3
+ /** What a segmentation provider is asked for (see `SegmentationProvider`). */
4
+ export interface SegmentationRequest {
5
+ /** The flattened image at its natural size, as a PNG data URL. */
6
+ image: string;
7
+ /** The image's natural size in pixels. */
8
+ width: number;
9
+ height: number;
10
+ /** What to find, in words ("the dog", "the sky", "every separate object"). */
11
+ target: string;
12
+ /** Return at most this many objects (the provider's default when absent). */
13
+ maxSegments?: number;
14
+ signal?: AbortSignal;
15
+ }
16
+ /**
17
+ * One object a provider found, in natural image pixels: an outline `polygon` (or several, for
18
+ * an object in pieces), or a `mask` covering the whole image (white = the object, any size;
19
+ * it is resampled), or both, which are combined.
20
+ */
21
+ export interface SegmentationResult {
22
+ label?: string;
23
+ polygon?: ImagePoint[] | ImagePoint[][];
24
+ mask?: string | PixelBuffer;
25
+ /** The object's bounding box, when the provider knows it. */
26
+ box?: CropBox;
27
+ /** The provider's confidence, 0..1. */
28
+ score?: number;
29
+ }
30
+ /**
31
+ * Finds objects in an image by a description and returns their shapes: an AI segmentation
32
+ * model (SAM, Gemini, a background remover…) behind `ImageEditorController.selectSubject`.
33
+ */
34
+ export interface SegmentationProvider {
35
+ /** Shown in the UI and in tool results (e.g. "Gemini"). */
36
+ readonly name?: string;
37
+ segment(request: SegmentationRequest): Promise<SegmentationResult[]>;
38
+ }
39
+ /** A segmentation result with its `mask` already decoded to pixels (strings are decoded by the controller). */
40
+ export type DecodedSegmentationResult = Omit<SegmentationResult, 'mask'> & {
41
+ mask?: PixelBuffer;
42
+ };
43
+ /**
44
+ * One segmentation result as a selection over a `width × height` image, or null when it has no
45
+ * usable shape. Polygons are anti-aliased; several polygons and a mask are added together.
46
+ */
47
+ export declare function segmentToSelection(width: number, height: number, result: DecodedSegmentationResult): SelectionMask | null;
48
+ /** Every usable result added into one selection (null when none has a shape). */
49
+ export declare function segmentsToSelection(width: number, height: number, results: readonly DecodedSegmentationResult[]): SelectionMask | null;
@@ -0,0 +1,49 @@
1
+ import type { CropBox, ImagePoint } from '../types.js';
2
+ import { type PixelBuffer, type SelectionMask } from './mask.js';
3
+ /** What a segmentation provider is asked for (see `SegmentationProvider`). */
4
+ export interface SegmentationRequest {
5
+ /** The flattened image at its natural size, as a PNG data URL. */
6
+ image: string;
7
+ /** The image's natural size in pixels. */
8
+ width: number;
9
+ height: number;
10
+ /** What to find, in words ("the dog", "the sky", "every separate object"). */
11
+ target: string;
12
+ /** Return at most this many objects (the provider's default when absent). */
13
+ maxSegments?: number;
14
+ signal?: AbortSignal;
15
+ }
16
+ /**
17
+ * One object a provider found, in natural image pixels: an outline `polygon` (or several, for
18
+ * an object in pieces), or a `mask` covering the whole image (white = the object, any size;
19
+ * it is resampled), or both, which are combined.
20
+ */
21
+ export interface SegmentationResult {
22
+ label?: string;
23
+ polygon?: ImagePoint[] | ImagePoint[][];
24
+ mask?: string | PixelBuffer;
25
+ /** The object's bounding box, when the provider knows it. */
26
+ box?: CropBox;
27
+ /** The provider's confidence, 0..1. */
28
+ score?: number;
29
+ }
30
+ /**
31
+ * Finds objects in an image by a description and returns their shapes: an AI segmentation
32
+ * model (SAM, Gemini, a background remover…) behind `ImageEditorController.selectSubject`.
33
+ */
34
+ export interface SegmentationProvider {
35
+ /** Shown in the UI and in tool results (e.g. "Gemini"). */
36
+ readonly name?: string;
37
+ segment(request: SegmentationRequest): Promise<SegmentationResult[]>;
38
+ }
39
+ /** A segmentation result with its `mask` already decoded to pixels (strings are decoded by the controller). */
40
+ export type DecodedSegmentationResult = Omit<SegmentationResult, 'mask'> & {
41
+ mask?: PixelBuffer;
42
+ };
43
+ /**
44
+ * One segmentation result as a selection over a `width × height` image, or null when it has no
45
+ * usable shape. Polygons are anti-aliased; several polygons and a mask are added together.
46
+ */
47
+ export declare function segmentToSelection(width: number, height: number, result: DecodedSegmentationResult): SelectionMask | null;
48
+ /** Every usable result added into one selection (null when none has a shape). */
49
+ export declare function segmentsToSelection(width: number, height: number, results: readonly DecodedSegmentationResult[]): SelectionMask | null;
@@ -1,4 +1,4 @@
1
- import type { AgentTool } from '../tools/registry.cjs';
1
+ import type { AgentTool, AgentToolProvider } from '../tools/registry.cjs';
2
2
  import type { ImageEditorController } from './controller.cjs';
3
3
  /**
4
4
  * Agent tools for selections and masks (part of `createImageEditorToolProvider`):
@@ -6,3 +6,20 @@ import type { ImageEditorController } from './controller.cjs';
6
6
  * and `apply_selection_as_mask`. Coordinates are natural pixels of the source image.
7
7
  */
8
8
  export declare function createImageSelectionTools(controller: ImageEditorController): AgentTool[];
9
+ /** Options for `createImageSegmentationToolProvider`. */
10
+ export interface ImageSegmentationToolProviderOptions {
11
+ /** Provider id (default `image-segmentation`). */
12
+ id?: string;
13
+ /** Tool-name prefix (`<namespace>__<name>`). */
14
+ namespace?: string;
15
+ /** Agent playback surface (default the image editor's). */
16
+ playbackSurface?: string;
17
+ }
18
+ /** The `select_subject` tool: Select › Subject through the controller's segmentation provider. */
19
+ export declare function createImageSubjectSelectionTool(controller: ImageEditorController): AgentTool;
20
+ /**
21
+ * Agent tools that need the controller's segmentation provider (`select_subject`). The list is
22
+ * empty while no provider is set, so a host can register this once and the tool appears when a
23
+ * model is available.
24
+ */
25
+ export declare function createImageSegmentationToolProvider(controller: ImageEditorController, options?: ImageSegmentationToolProviderOptions): AgentToolProvider;
@@ -1,4 +1,4 @@
1
- import type { AgentTool } from '../tools/registry.js';
1
+ import type { AgentTool, AgentToolProvider } from '../tools/registry.js';
2
2
  import type { ImageEditorController } from './controller.js';
3
3
  /**
4
4
  * Agent tools for selections and masks (part of `createImageEditorToolProvider`):
@@ -6,3 +6,20 @@ import type { ImageEditorController } from './controller.js';
6
6
  * and `apply_selection_as_mask`. Coordinates are natural pixels of the source image.
7
7
  */
8
8
  export declare function createImageSelectionTools(controller: ImageEditorController): AgentTool[];
9
+ /** Options for `createImageSegmentationToolProvider`. */
10
+ export interface ImageSegmentationToolProviderOptions {
11
+ /** Provider id (default `image-segmentation`). */
12
+ id?: string;
13
+ /** Tool-name prefix (`<namespace>__<name>`). */
14
+ namespace?: string;
15
+ /** Agent playback surface (default the image editor's). */
16
+ playbackSurface?: string;
17
+ }
18
+ /** The `select_subject` tool: Select › Subject through the controller's segmentation provider. */
19
+ export declare function createImageSubjectSelectionTool(controller: ImageEditorController): AgentTool;
20
+ /**
21
+ * Agent tools that need the controller's segmentation provider (`select_subject`). The list is
22
+ * empty while no provider is set, so a host can register this once and the tool appears when a
23
+ * model is available.
24
+ */
25
+ export declare function createImageSegmentationToolProvider(controller: ImageEditorController, options?: ImageSegmentationToolProviderOptions): AgentToolProvider;
@@ -395,6 +395,7 @@ export interface ExportOptions {
395
395
  layerIds?: string[];
396
396
  }
397
397
  import type { EffectProvider } from '../effects/types.cjs';
398
+ import type { SegmentationProvider } from './selection/segmentation.cjs';
398
399
  export interface ImageEditorOptions {
399
400
  /**
400
401
  * What lays out and draws type layers. Default: the browser's fonts through Canvas 2D
@@ -421,6 +422,12 @@ export interface ImageEditorOptions {
421
422
  * with `setEffectsProvider`.
422
423
  */
423
424
  effects?: EffectProvider | null;
425
+ /**
426
+ * A segmentation provider (an AI model that finds objects by description) for Select ›
427
+ * Subject (`selectSubject`, the `select_subject` tool). Optional; change it later with
428
+ * `setSegmentationProvider`.
429
+ */
430
+ segmentation?: SegmentationProvider | null;
424
431
  }
425
432
  export interface InpaintOptions {
426
433
  prompt: string;
@@ -395,6 +395,7 @@ export interface ExportOptions {
395
395
  layerIds?: string[];
396
396
  }
397
397
  import type { EffectProvider } from '../effects/types.js';
398
+ import type { SegmentationProvider } from './selection/segmentation.js';
398
399
  export interface ImageEditorOptions {
399
400
  /**
400
401
  * What lays out and draws type layers. Default: the browser's fonts through Canvas 2D
@@ -421,6 +422,12 @@ export interface ImageEditorOptions {
421
422
  * with `setEffectsProvider`.
422
423
  */
423
424
  effects?: EffectProvider | null;
425
+ /**
426
+ * A segmentation provider (an AI model that finds objects by description) for Select ›
427
+ * Subject (`selectSubject`, the `select_subject` tool). Optional; change it later with
428
+ * `setSegmentationProvider`.
429
+ */
430
+ segmentation?: SegmentationProvider | null;
424
431
  }
425
432
  export interface InpaintOptions {
426
433
  prompt: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor",
3
- "version": "0.178.0",
3
+ "version": "0.180.0",
4
4
  "description": "UI-agnostic rich text and media editing core library",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -47,7 +47,7 @@ batching.
47
47
  3. **Write in few steps.** Put every base change of one correction into one `set_adjustments`
48
48
  call (one undo step) with a `label`. Create layers with their `params` in the same
49
49
  `add_adjustment_layer` call rather than adding a neutral layer and then setting it.
50
- 4. **Local edits:** select (`select_region`, `select_by_color`), refine (`modify_selection`,
50
+ 4. **Local edits:** select (`select_region`, `select_by_color`, `select_subject`), refine (`modify_selection`,
51
51
  `refine_selection`), check with `get_selection` (area, percent, bounds), then
52
52
  `apply_selection_as_mask` with a `kind` to create the masked layer in one step.
53
53
  5. **Verify.** `render_preview` after each group of corrections, not after each slider, then
@@ -62,7 +62,8 @@ batching.
62
62
  `set_adjustments` base controls edit the active pixel layer's `develop` filter.
63
63
  2. **New content goes on its own layer**: `add_pixel_layer` (empty, a colour fill or a PNG at
64
64
  `x`, `y`), `layer_via_copy` to lift a selection onto a layer, `duplicate_layer` before a
65
- risky change. Name layers by intent.
65
+ risky change. Name layers by intent. Split out a subject by its outline, not its box
66
+ (`select_subject`, or a `select_region` polygon, then `refine_selection`), then `layer_via_copy`.
66
67
  3. **Shape layers non-destructively**: masks (`add_layer_mask` from the selection, or
67
68
  reveal-all / hide-all, softened with `density` / `feather`) instead of deleting pixels;
68
69
  `clipped: true` (via `set_layer_properties`) to limit a layer or adjustment to the layer below;
@@ -265,6 +266,7 @@ in the source frame at the document's bit depth, colour mode and profile. Read
265
266
  | `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. |
266
267
  | `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. |
267
268
  | `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. |
269
+ | `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. |
268
270
  | `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. |
269
271
  | `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. |
270
272
  | `get_image_info` | Read the geometry: source `image` size, `crop` box (source pixels, or null), `rotation` degrees, `flipH` / `flipV`, the `output` size after crop and rotation, `bitDepth` (8, 16 or 32), the layer count and `canUndo` / `canRedo`. Call before cropping or selecting so coordinates are right. |
@@ -289,7 +291,7 @@ in the source frame at the document's bit depth, colour mode and profile. Read
289
291
  | --- | --- |
290
292
  | `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. |
291
293
  | `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`. |
292
- | `select_region` | Select by `shape`: `rect` or `ellipse` (with `x`, `y`, `width`, `height` in natural pixels), `all`, `none` or `invert`. `mode` replace (default), add, subtract or intersect; `feather` px (default 0). Returns the selection summary. |
294
+ | `select_region` | Select by `shape`: `rect` or `ellipse` (with `x`, `y`, `width`, `height` in natural pixels), `polygon` (the lasso: `points`, at least 3 `{x, y}` along the outline, closed automatically), `all`, `none` or `invert`. `mode` replace (default), add, subtract or intersect; `feather` px (default 0). Returns the selection summary. |
293
295
  | `select_by_color` | Magic wand from a seed (`x`, `y`, `tolerance` 0..255 default 32, `contiguous` default true) or Color Range for every similar pixel (`color` "#rrggbb", `fuzziness` 0..200 default 40). `sampleMerged: true` samples the adjusted image instead of the source; `sample: "active-layer"` samples only the active pixel layer (pick it with `set_active_layer`) instead of the whole image. `mode` as above. Use it for skies, backdrops and flat-coloured objects. |
294
296
  | `modify_selection` | Grow, shrink, feather, border or smooth the selection (`op`) by `radius` px (0..500). Use shrink then feather to keep an adjustment from bleeding over an edge; smooth to remove wand speckle. Fails when nothing is selected. |
295
297
  | `refine_selection` | Refine Edge: 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 %. Use on hair, fur, foliage or after loading a coarse AI mask. |