@pixerate/editor 0.212.0 → 0.214.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.
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Framework-free logic behind the path tools of the React and Svelte image editors (Photoshop's
3
+ * Path Selection, Direct Selection and Pen): hit-testing anchors, handles and segments, dragging
4
+ * them (one undo step per drag, the target updated live while dragging), converting, inserting and
5
+ * deleting anchors, drawing new subpaths with the Pen, and the overlay to draw. They edit what
6
+ * `controller.pathEditTarget()` names: the active path, the active shape layer or the active
7
+ * layer's vector mask. The components only translate DOM events and draw `overlay()`.
8
+ */
9
+ import type { ImageEditorController } from './controller.cjs';
10
+ import type { CropBox, ImagePoint } from './types.cjs';
11
+ /** The path tools: whole components (`path-select`), anchors and handles (`direct-select`), and the Pen (`path-pen`). */
12
+ export type PathEditTool = 'path-select' | 'direct-select' | 'path-pen';
13
+ export declare const PATH_EDIT_TOOLS: readonly PathEditTool[];
14
+ export declare function isPathEditTool(tool: string): tool is PathEditTool;
15
+ /** One anchor of a target: component, subpath and anchor index. */
16
+ export interface PathAnchorRef {
17
+ component: number;
18
+ subpath: number;
19
+ anchor: number;
20
+ }
21
+ /** What `RenderOptions.pathEdit` draws, in document pixels. */
22
+ export interface PathEditOverlay {
23
+ /** The target's segments: 2 points for a line, 4 for a cubic. */
24
+ curves: ImagePoint[][];
25
+ /** Anchors shown; selected ones are drawn filled. */
26
+ anchors: Array<{
27
+ point: ImagePoint;
28
+ selected: boolean;
29
+ }>;
30
+ /** Direction handles of the selected anchors, with the anchor each belongs to. */
31
+ handles: Array<{
32
+ point: ImagePoint;
33
+ anchor: ImagePoint;
34
+ }>;
35
+ /** A marquee being dragged to select anchors. */
36
+ marquee: CropBox | null;
37
+ /** The Pen's next segment, from the last anchor to the pointer (2 or 4 points). */
38
+ preview: ImagePoint[] | null;
39
+ }
40
+ export interface PathEditModifiers {
41
+ shift?: boolean;
42
+ alt?: boolean;
43
+ }
44
+ /**
45
+ * The path tools' pointer and key handling. Call `setView` with the screen pixels per document
46
+ * pixel so hits are a fixed size on screen; `down` / `move` / `up` take document pixels.
47
+ */
48
+ export declare class PathEditGesture {
49
+ private readonly controller;
50
+ /** Selected anchors, as `component:subpath:anchor` keys. */
51
+ private selected;
52
+ private target;
53
+ private drag;
54
+ /** The Pen's open subpath being drawn. */
55
+ private drawing;
56
+ private pointer;
57
+ private scale;
58
+ error: string | null;
59
+ constructor(controller: ImageEditorController);
60
+ /** Screen pixels per document pixel (the viewport projection's scale). */
61
+ setView(scale: number): void;
62
+ get active(): boolean;
63
+ /** The anchors selected now (on the current target). */
64
+ get selection(): PathAnchorRef[];
65
+ /** True while the Pen has an open subpath it adds to. */
66
+ get isDrawing(): boolean;
67
+ private get tolerance();
68
+ /** The target's components, or null (and the selection dropped) when the target changed or is gone. */
69
+ private components;
70
+ private anchorAt;
71
+ /** A handle of a selected anchor near `p`. */
72
+ private handleAt;
73
+ /** The segment near `p`: its first anchor (as `anchor`) and the parameter on it. */
74
+ private segmentAt;
75
+ /** The component under `p`: on its outline, or inside its fill. */
76
+ private componentAt;
77
+ private selectComponent;
78
+ private commit;
79
+ down(p: ImagePoint, tool: PathEditTool, mods?: PathEditModifiers): void;
80
+ /** Alt-click: a corner anchor becomes smooth, a smooth one a corner (one undo step). */
81
+ private convert;
82
+ private penDown;
83
+ move(p: ImagePoint, mods?: PathEditModifiers): void;
84
+ /** The pointer moving with no button down (the Pen's preview of its next segment). */
85
+ hover(p: ImagePoint): void;
86
+ up(): void;
87
+ /** Ends the Pen's subpath (left open). */
88
+ finish(): void;
89
+ cancel(): void;
90
+ /**
91
+ * Keys: Delete / Backspace remove the selected anchors (one undo step), Enter and Escape end the
92
+ * Pen's subpath, Escape also deselects. Returns true when the key was handled.
93
+ */
94
+ key(k: string): boolean;
95
+ /** What to draw (`RenderOptions.pathEdit`) for `tool`, or null when there is no target. */
96
+ overlay(tool: PathEditTool): PathEditOverlay | null;
97
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Framework-free logic behind the path tools of the React and Svelte image editors (Photoshop's
3
+ * Path Selection, Direct Selection and Pen): hit-testing anchors, handles and segments, dragging
4
+ * them (one undo step per drag, the target updated live while dragging), converting, inserting and
5
+ * deleting anchors, drawing new subpaths with the Pen, and the overlay to draw. They edit what
6
+ * `controller.pathEditTarget()` names: the active path, the active shape layer or the active
7
+ * layer's vector mask. The components only translate DOM events and draw `overlay()`.
8
+ */
9
+ import type { ImageEditorController } from './controller.js';
10
+ import type { CropBox, ImagePoint } from './types.js';
11
+ /** The path tools: whole components (`path-select`), anchors and handles (`direct-select`), and the Pen (`path-pen`). */
12
+ export type PathEditTool = 'path-select' | 'direct-select' | 'path-pen';
13
+ export declare const PATH_EDIT_TOOLS: readonly PathEditTool[];
14
+ export declare function isPathEditTool(tool: string): tool is PathEditTool;
15
+ /** One anchor of a target: component, subpath and anchor index. */
16
+ export interface PathAnchorRef {
17
+ component: number;
18
+ subpath: number;
19
+ anchor: number;
20
+ }
21
+ /** What `RenderOptions.pathEdit` draws, in document pixels. */
22
+ export interface PathEditOverlay {
23
+ /** The target's segments: 2 points for a line, 4 for a cubic. */
24
+ curves: ImagePoint[][];
25
+ /** Anchors shown; selected ones are drawn filled. */
26
+ anchors: Array<{
27
+ point: ImagePoint;
28
+ selected: boolean;
29
+ }>;
30
+ /** Direction handles of the selected anchors, with the anchor each belongs to. */
31
+ handles: Array<{
32
+ point: ImagePoint;
33
+ anchor: ImagePoint;
34
+ }>;
35
+ /** A marquee being dragged to select anchors. */
36
+ marquee: CropBox | null;
37
+ /** The Pen's next segment, from the last anchor to the pointer (2 or 4 points). */
38
+ preview: ImagePoint[] | null;
39
+ }
40
+ export interface PathEditModifiers {
41
+ shift?: boolean;
42
+ alt?: boolean;
43
+ }
44
+ /**
45
+ * The path tools' pointer and key handling. Call `setView` with the screen pixels per document
46
+ * pixel so hits are a fixed size on screen; `down` / `move` / `up` take document pixels.
47
+ */
48
+ export declare class PathEditGesture {
49
+ private readonly controller;
50
+ /** Selected anchors, as `component:subpath:anchor` keys. */
51
+ private selected;
52
+ private target;
53
+ private drag;
54
+ /** The Pen's open subpath being drawn. */
55
+ private drawing;
56
+ private pointer;
57
+ private scale;
58
+ error: string | null;
59
+ constructor(controller: ImageEditorController);
60
+ /** Screen pixels per document pixel (the viewport projection's scale). */
61
+ setView(scale: number): void;
62
+ get active(): boolean;
63
+ /** The anchors selected now (on the current target). */
64
+ get selection(): PathAnchorRef[];
65
+ /** True while the Pen has an open subpath it adds to. */
66
+ get isDrawing(): boolean;
67
+ private get tolerance();
68
+ /** The target's components, or null (and the selection dropped) when the target changed or is gone. */
69
+ private components;
70
+ private anchorAt;
71
+ /** A handle of a selected anchor near `p`. */
72
+ private handleAt;
73
+ /** The segment near `p`: its first anchor (as `anchor`) and the parameter on it. */
74
+ private segmentAt;
75
+ /** The component under `p`: on its outline, or inside its fill. */
76
+ private componentAt;
77
+ private selectComponent;
78
+ private commit;
79
+ down(p: ImagePoint, tool: PathEditTool, mods?: PathEditModifiers): void;
80
+ /** Alt-click: a corner anchor becomes smooth, a smooth one a corner (one undo step). */
81
+ private convert;
82
+ private penDown;
83
+ move(p: ImagePoint, mods?: PathEditModifiers): void;
84
+ /** The pointer moving with no button down (the Pen's preview of its next segment). */
85
+ hover(p: ImagePoint): void;
86
+ up(): void;
87
+ /** Ends the Pen's subpath (left open). */
88
+ finish(): void;
89
+ cancel(): void;
90
+ /**
91
+ * Keys: Delete / Backspace remove the selected anchors (one undo step), Enter and Escape end the
92
+ * Pen's subpath, Escape also deselects. Returns true when the key was handled.
93
+ */
94
+ key(k: string): boolean;
95
+ /** What to draw (`RenderOptions.pathEdit`) for `tool`, or null when there is no target. */
96
+ overlay(tool: PathEditTool): PathEditOverlay | null;
97
+ }
@@ -39,3 +39,17 @@ export declare function serializePaths(paths: readonly DocumentPath[]): Serializ
39
39
  * Path; ids are made unique.
40
40
  */
41
41
  export declare function readPaths(stored: unknown, problems?: string[]): DocumentPath[];
42
+ /**
43
+ * What the path tools edit: a document path, a shape layer's components or a layer's vector mask
44
+ * (see `ImageEditorController.pathEditTarget`).
45
+ */
46
+ export type PathEditTarget = {
47
+ kind: 'path';
48
+ id: string;
49
+ } | {
50
+ kind: 'shape';
51
+ layerId: string;
52
+ } | {
53
+ kind: 'vector-mask';
54
+ layerId: string;
55
+ };
@@ -39,3 +39,17 @@ export declare function serializePaths(paths: readonly DocumentPath[]): Serializ
39
39
  * Path; ids are made unique.
40
40
  */
41
41
  export declare function readPaths(stored: unknown, problems?: string[]): DocumentPath[];
42
+ /**
43
+ * What the path tools edit: a document path, a shape layer's components or a layer's vector mask
44
+ * (see `ImageEditorController.pathEditTarget`).
45
+ */
46
+ export type PathEditTarget = {
47
+ kind: 'path';
48
+ id: string;
49
+ } | {
50
+ kind: 'shape';
51
+ layerId: string;
52
+ } | {
53
+ kind: 'vector-mask';
54
+ layerId: string;
55
+ };
@@ -1,3 +1,4 @@
1
+ import type { PathEditOverlay } from './path-edit.cjs';
1
2
  import type { Annotation, CropBox, ImagePoint, ExportOptions, ImageEditorFit, ImageEditorState } from './types.cjs';
2
3
  import type { PixelTransform } from './color-management.cjs';
3
4
  import { type ImageHistogram } from './histogram.cjs';
@@ -80,6 +81,8 @@ export interface RenderOptions {
80
81
  }>;
81
82
  };
82
83
  } | null;
84
+ /** The path tools' target outline, anchors, handles, marquee and Pen preview in natural pixels (`PathEditGesture.overlay()`). */
85
+ pathEdit?: PathEditOverlay | null;
83
86
  }
84
87
  /**
85
88
  * Image Cache to avoid refetching HTMLImageElement objects repeatedly during re-renders.
@@ -1,3 +1,4 @@
1
+ import type { PathEditOverlay } from './path-edit.js';
1
2
  import type { Annotation, CropBox, ImagePoint, ExportOptions, ImageEditorFit, ImageEditorState } from './types.js';
2
3
  import type { PixelTransform } from './color-management.js';
3
4
  import { type ImageHistogram } from './histogram.js';
@@ -80,6 +81,8 @@ export interface RenderOptions {
80
81
  }>;
81
82
  };
82
83
  } | null;
84
+ /** The path tools' target outline, anchors, handles, marquee and Pen preview in natural pixels (`PathEditGesture.overlay()`). */
85
+ pathEdit?: PathEditOverlay | null;
83
86
  }
84
87
  /**
85
88
  * Image Cache to avoid refetching HTMLImageElement objects repeatedly during re-renders.
@@ -17,3 +17,4 @@ export * from './segment/index.cjs';
17
17
  export * from './refine-edits.cjs';
18
18
  export * from './select-and-mask.cjs';
19
19
  export * from './worker.cjs';
20
+ export * from './inspect.cjs';
@@ -17,3 +17,4 @@ export * from './segment/index.js';
17
17
  export * from './refine-edits.js';
18
18
  export * from './select-and-mask.js';
19
19
  export * from './worker.js';
20
+ export * from './inspect.js';
@@ -0,0 +1,70 @@
1
+ /**
2
+ * A measured report on a mask's edge (`inspect_mask_edge`), so an agent can check a selection or
3
+ * a mask without looking at it: halo (old background left just inside the edge), how much of the
4
+ * edge sits on the image's own edges, how jagged the outline is, how soft it is, holes and stray
5
+ * islands, and the worst places to look at. Read-only; works in the mask's bounds.
6
+ */
7
+ import type { CropBox } from '../types.cjs';
8
+ import type { PixelBuffer, SelectionMask } from './mask.cjs';
9
+ export interface InspectMaskEdgeOptions {
10
+ /** Holes and islands smaller than this many pixels are left out (default 16). */
11
+ minArea?: number;
12
+ }
13
+ /** A hole or island: its area in pixels and bounds. */
14
+ export interface MaskRegion {
15
+ area: number;
16
+ bounds: CropBox;
17
+ }
18
+ export interface MaskEdgeReport {
19
+ /** Pixels at least half covered. */
20
+ area: number;
21
+ bounds: CropBox;
22
+ /** Pixels along the edge (inside, at least half covered, beside one less than half). */
23
+ edgeLength: number;
24
+ /**
25
+ * 0–1: how close the colour just inside the edge is to the colour just outside it, relative to
26
+ * the colour further in (0: the edge ring looks like the subject; 1: it looks like the
27
+ * background). Measured only where the inside and outside differ.
28
+ */
29
+ haloScore: number;
30
+ /** 0–1: the share of the edge within 2 px of a strong image edge. */
31
+ edgeAlignment: number;
32
+ /** Turning of the outline beyond what closing it needs, in radians per 100 px of outline (0: smooth). */
33
+ jaggedness: number;
34
+ /** 0–1: the share of edge pixels that are partly covered (0 < α < 1). */
35
+ softFraction: number;
36
+ /** Unselected areas enclosed by the selection, largest first (at most 10 listed). */
37
+ holes: {
38
+ count: number;
39
+ totalArea: number;
40
+ largest: MaskRegion[];
41
+ };
42
+ /** Selected pieces apart from the largest one, largest first (at most 10 listed). */
43
+ islands: {
44
+ count: number;
45
+ totalArea: number;
46
+ largest: MaskRegion[];
47
+ };
48
+ /** Up to 5 places to look at first (for `capture_view`), worst first. */
49
+ worst: Array<{
50
+ bounds: CropBox;
51
+ reason: string;
52
+ }>;
53
+ /** Plain-language findings with a suggested fix, empty when the edge looks clean. */
54
+ issues: string[];
55
+ }
56
+ /** Report thresholds (also in the masking skill). */
57
+ export declare const MASK_EDGE_THRESHOLDS: {
58
+ readonly halo: 0.35;
59
+ readonly alignment: 0.5;
60
+ readonly jaggedness: 15;
61
+ readonly soft: 0.9;
62
+ };
63
+ /**
64
+ * Inspects `mask`'s edge against `image` (RGBA, straight alpha, covering document pixels from
65
+ * `image.x`, `image.y`; outside it counts as transparent). Throws for an empty mask.
66
+ */
67
+ export declare function inspectMaskEdge(mask: SelectionMask, image: PixelBuffer & {
68
+ x: number;
69
+ y: number;
70
+ }, options?: InspectMaskEdgeOptions): MaskEdgeReport;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * A measured report on a mask's edge (`inspect_mask_edge`), so an agent can check a selection or
3
+ * a mask without looking at it: halo (old background left just inside the edge), how much of the
4
+ * edge sits on the image's own edges, how jagged the outline is, how soft it is, holes and stray
5
+ * islands, and the worst places to look at. Read-only; works in the mask's bounds.
6
+ */
7
+ import type { CropBox } from '../types.js';
8
+ import type { PixelBuffer, SelectionMask } from './mask.js';
9
+ export interface InspectMaskEdgeOptions {
10
+ /** Holes and islands smaller than this many pixels are left out (default 16). */
11
+ minArea?: number;
12
+ }
13
+ /** A hole or island: its area in pixels and bounds. */
14
+ export interface MaskRegion {
15
+ area: number;
16
+ bounds: CropBox;
17
+ }
18
+ export interface MaskEdgeReport {
19
+ /** Pixels at least half covered. */
20
+ area: number;
21
+ bounds: CropBox;
22
+ /** Pixels along the edge (inside, at least half covered, beside one less than half). */
23
+ edgeLength: number;
24
+ /**
25
+ * 0–1: how close the colour just inside the edge is to the colour just outside it, relative to
26
+ * the colour further in (0: the edge ring looks like the subject; 1: it looks like the
27
+ * background). Measured only where the inside and outside differ.
28
+ */
29
+ haloScore: number;
30
+ /** 0–1: the share of the edge within 2 px of a strong image edge. */
31
+ edgeAlignment: number;
32
+ /** Turning of the outline beyond what closing it needs, in radians per 100 px of outline (0: smooth). */
33
+ jaggedness: number;
34
+ /** 0–1: the share of edge pixels that are partly covered (0 < α < 1). */
35
+ softFraction: number;
36
+ /** Unselected areas enclosed by the selection, largest first (at most 10 listed). */
37
+ holes: {
38
+ count: number;
39
+ totalArea: number;
40
+ largest: MaskRegion[];
41
+ };
42
+ /** Selected pieces apart from the largest one, largest first (at most 10 listed). */
43
+ islands: {
44
+ count: number;
45
+ totalArea: number;
46
+ largest: MaskRegion[];
47
+ };
48
+ /** Up to 5 places to look at first (for `capture_view`), worst first. */
49
+ worst: Array<{
50
+ bounds: CropBox;
51
+ reason: string;
52
+ }>;
53
+ /** Plain-language findings with a suggested fix, empty when the edge looks clean. */
54
+ issues: string[];
55
+ }
56
+ /** Report thresholds (also in the masking skill). */
57
+ export declare const MASK_EDGE_THRESHOLDS: {
58
+ readonly halo: 0.35;
59
+ readonly alignment: 0.5;
60
+ readonly jaggedness: 15;
61
+ readonly soft: 0.9;
62
+ };
63
+ /**
64
+ * Inspects `mask`'s edge against `image` (RGBA, straight alpha, covering document pixels from
65
+ * `image.x`, `image.y`; outside it counts as transparent). Throws for an empty mask.
66
+ */
67
+ export declare function inspectMaskEdge(mask: SelectionMask, image: PixelBuffer & {
68
+ x: number;
69
+ y: number;
70
+ }, options?: InspectMaskEdgeOptions): MaskEdgeReport;
@@ -25,8 +25,8 @@ export declare function imageToolForShortcut(key: string, shift?: boolean): Imag
25
25
  /** How a canvas handles pointer gestures for a tool (the Svelte and React canvases' `mode`). */
26
26
  export type ImageToolCanvasMode = 'transform' | 'crop' | 'select' | 'paint' | 'annotate';
27
27
  /**
28
- * The canvas mode a tool works in. Move picks and transforms layers; shapes, type and the
29
- * annotations draw in `annotate`.
28
+ * The canvas mode a tool works in. Move picks and transforms layers; shapes, type, the path tools
29
+ * and the annotations draw in `annotate`.
30
30
  */
31
31
  export declare function imageToolCanvasMode(tool: ImageEditorTool): ImageToolCanvasMode;
32
32
  /** The `layerPanels` panel holding a tool's options, or null when it has none. */
@@ -25,8 +25,8 @@ export declare function imageToolForShortcut(key: string, shift?: boolean): Imag
25
25
  /** How a canvas handles pointer gestures for a tool (the Svelte and React canvases' `mode`). */
26
26
  export type ImageToolCanvasMode = 'transform' | 'crop' | 'select' | 'paint' | 'annotate';
27
27
  /**
28
- * The canvas mode a tool works in. Move picks and transforms layers; shapes, type and the
29
- * annotations draw in `annotate`.
28
+ * The canvas mode a tool works in. Move picks and transforms layers; shapes, type, the path tools
29
+ * and the annotations draw in `annotate`.
30
30
  */
31
31
  export declare function imageToolCanvasMode(tool: ImageEditorTool): ImageToolCanvasMode;
32
32
  /** The `layerPanels` panel holding a tool's options, or null when it has none. */
@@ -280,7 +280,9 @@ export type ImageEditorTool = 'select' | 'crop' | 'pen' | 'rect' | 'circle' | 'a
280
280
  /** Live shape layers (see `ShapeDrawGesture`). */
281
281
  | 'shape'
282
282
  /** Live type layers, edited on the canvas (see `TypeToolGesture`). */
283
- | 'type';
283
+ | 'type'
284
+ /** The path tools (see `PathEditGesture`): Path Selection, Direct Selection and the Pen for paths. */
285
+ | 'path-select' | 'direct-select' | 'path-pen';
284
286
  export type ImageEditorFit = 'contain' | 'margin';
285
287
  import type { DocumentColorMode, DocumentColorProfile, ProofSetup } from './color-management.cjs';
286
288
  import type { BitDepth } from '../raster/deep.cjs';
@@ -280,7 +280,9 @@ export type ImageEditorTool = 'select' | 'crop' | 'pen' | 'rect' | 'circle' | 'a
280
280
  /** Live shape layers (see `ShapeDrawGesture`). */
281
281
  | 'shape'
282
282
  /** Live type layers, edited on the canvas (see `TypeToolGesture`). */
283
- | 'type';
283
+ | 'type'
284
+ /** The path tools (see `PathEditGesture`): Path Selection, Direct Selection and the Pen for paths. */
285
+ | 'path-select' | 'direct-select' | 'path-pen';
284
286
  export type ImageEditorFit = 'contain' | 'margin';
285
287
  import type { DocumentColorMode, DocumentColorProfile, ProofSetup } from './color-management.js';
286
288
  import type { BitDepth } from '../raster/deep.js';