@pixerate/editor 0.194.0 → 0.196.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.
@@ -9464,6 +9464,32 @@ var VectorEditorController = class _VectorEditorController {
9464
9464
  this.change(labels[op], () => this.setDoc(reorder(this.doc.nodes)));
9465
9465
  return { ok: true };
9466
9466
  }
9467
+ /**
9468
+ * Moves a node to `index` among its siblings (bottom to top, 0 the bottom), as dragging it in
9469
+ * a layers panel does. Clipping groups and blends keep their order: their mask and keys are
9470
+ * placed by position.
9471
+ */
9472
+ reorderNode(id, index) {
9473
+ const node = this.getNode(id);
9474
+ if (!node) return { ok: false, message: `No node "${id}".` };
9475
+ let parent = null;
9476
+ walkVectorNodes(this.doc.nodes, (n2, p) => {
9477
+ if (n2.id === id) parent = p;
9478
+ });
9479
+ const group = parent;
9480
+ if (group && (isVectorMaskGroup(group) || isVectorBlendGroup(group))) {
9481
+ return { ok: false, message: "The order inside a clipping group or blend is fixed." };
9482
+ }
9483
+ const siblings = group ? group.children : this.doc.nodes;
9484
+ const from = siblings.findIndex((n2) => n2.id === id);
9485
+ const to = Math.max(0, Math.min(siblings.length - 1, Math.round(index)));
9486
+ if (from === to) return { ok: true };
9487
+ const next = [...siblings];
9488
+ next.splice(from, 1);
9489
+ next.splice(to, 0, node);
9490
+ this.change("Move Layer", () => group ? this.replaceNode(group.id, { ...group, children: next }) : this.setDoc(next));
9491
+ return { ok: true };
9492
+ }
9467
9493
  /**
9468
9494
  * Combines the selected paths (two or more) into one with a boolean operation. `subtract`
9469
9495
  * removes the upper shapes from the lowest one. The result takes the lowest shape's style
@@ -18559,6 +18585,68 @@ function createVectorEditorDocument(controller) {
18559
18585
  }
18560
18586
  });
18561
18587
  }
18588
+
18589
+ // src/vector/layers.ts
18590
+ var VECTOR_LAYER_KIND_LABELS = {
18591
+ path: "Path",
18592
+ connector: "Connector",
18593
+ lens: "Lens",
18594
+ text: "Text",
18595
+ instance: "Symbol",
18596
+ group: "Group",
18597
+ frame: "Frame",
18598
+ mask: "Clipping Group",
18599
+ blend: "Blend",
18600
+ boolean: "Live Boolean",
18601
+ symmetry: "Symmetry"
18602
+ };
18603
+ function vectorLayerKind(node) {
18604
+ if (node.type === "text") return "text";
18605
+ if (node.type === "instance") return "instance";
18606
+ if (node.type === "path") return isVectorConnector(node) ? "connector" : isVectorLens(node) ? "lens" : "path";
18607
+ if (isVectorFrame(node)) return "frame";
18608
+ if (isVectorMaskGroup(node)) return "mask";
18609
+ if (isVectorBlendGroup(node)) return "blend";
18610
+ if (node.liveBoolean) return "boolean";
18611
+ if (isVectorSymmetryGroup(node)) return "symmetry";
18612
+ return "group";
18613
+ }
18614
+ function vectorLayerName(node) {
18615
+ const name = node.name?.trim();
18616
+ if (name) return name;
18617
+ if (node.type === "text") {
18618
+ const text = node.text.replace(/\s+/g, " ").trim();
18619
+ if (text) return text.length > 40 ? `${text.slice(0, 39)}\u2026` : text;
18620
+ }
18621
+ return VECTOR_LAYER_KIND_LABELS[vectorLayerKind(node)];
18622
+ }
18623
+ function vectorLayerChildren(node) {
18624
+ return node.type === "group" ? node.children.filter((child) => !child.blendStep) : [];
18625
+ }
18626
+ function vectorLayerRows(nodes, expanded = () => false) {
18627
+ const rows = [];
18628
+ const visit = (list, depth, parentId) => {
18629
+ for (let i = list.length - 1; i >= 0; i--) {
18630
+ const node = list[i];
18631
+ const children = vectorLayerChildren(node);
18632
+ const open = children.length > 0 && expanded(node.id);
18633
+ rows.push({
18634
+ id: node.id,
18635
+ name: vectorLayerName(node),
18636
+ kind: vectorLayerKind(node),
18637
+ depth,
18638
+ parentId,
18639
+ visible: node.visible !== false,
18640
+ locked: !!node.locked,
18641
+ childCount: children.length,
18642
+ expanded: open
18643
+ });
18644
+ if (open) visit(children, depth + 1, node.id);
18645
+ }
18646
+ };
18647
+ visit(nodes, 0, null);
18648
+ return rows;
18649
+ }
18562
18650
  export {
18563
18651
  DEFAULT_VECTOR_BLEND_DISTANCE,
18564
18652
  DEFAULT_VECTOR_BLEND_STEPS,
@@ -18625,6 +18713,7 @@ export {
18625
18713
  VECTOR_HORIZONTAL_RESIZING,
18626
18714
  VECTOR_HORIZONTAL_RESIZING_LABELS,
18627
18715
  VECTOR_INK_MAX_CORNERS,
18716
+ VECTOR_LAYER_KIND_LABELS,
18628
18717
  VECTOR_LENS_LABELS,
18629
18718
  VECTOR_LENS_SPECS,
18630
18719
  VECTOR_LENS_TYPES,
@@ -19127,6 +19216,10 @@ export {
19127
19216
  vectorInkShapeName,
19128
19217
  vectorInstanceDrawn,
19129
19218
  vectorInstanceGroup,
19219
+ vectorLayerChildren,
19220
+ vectorLayerKind,
19221
+ vectorLayerName,
19222
+ vectorLayerRows,
19130
19223
  vectorLensColorMap,
19131
19224
  vectorLensDrawn,
19132
19225
  vectorLiveBooleanSteps,
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The vector document as a layers panel shows it: one row per node, top first, groups indented
3
+ * with their children when expanded. Hosts draw the rows themselves and act on them with the
4
+ * controller (`select`, `setVisible`, `setLocked`, `rename`, `reorderNode`).
5
+ */
6
+ import type { VectorNode } from './types.cjs';
7
+ /** What a layer is, for its icon and its default name. */
8
+ export type VectorLayerKind = 'path' | 'connector' | 'lens' | 'text' | 'instance' | 'group' | 'frame' | 'mask' | 'blend' | 'boolean' | 'symmetry';
9
+ export declare const VECTOR_LAYER_KIND_LABELS: Record<VectorLayerKind, string>;
10
+ export interface VectorLayerRow {
11
+ id: string;
12
+ name: string;
13
+ kind: VectorLayerKind;
14
+ /** 0 for the document's own nodes. */
15
+ depth: number;
16
+ parentId: string | null;
17
+ visible: boolean;
18
+ locked: boolean;
19
+ /** Children the row can show (a blend's generated steps are not counted). */
20
+ childCount: number;
21
+ expanded: boolean;
22
+ }
23
+ export declare function vectorLayerKind(node: VectorNode): VectorLayerKind;
24
+ /** The node's name, else its text (for text), else its kind. */
25
+ export declare function vectorLayerName(node: VectorNode): string;
26
+ /** A group's children as the layers panel lists them: a blend's generated steps are left out. */
27
+ export declare function vectorLayerChildren(node: VectorNode): VectorNode[];
28
+ /**
29
+ * The rows for `nodes` (bottom to top, as documents keep them), top first. A group's children
30
+ * follow it, indented, when `expanded` says so.
31
+ */
32
+ export declare function vectorLayerRows(nodes: readonly VectorNode[], expanded?: (id: string) => boolean): VectorLayerRow[];
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The vector document as a layers panel shows it: one row per node, top first, groups indented
3
+ * with their children when expanded. Hosts draw the rows themselves and act on them with the
4
+ * controller (`select`, `setVisible`, `setLocked`, `rename`, `reorderNode`).
5
+ */
6
+ import type { VectorNode } from './types.js';
7
+ /** What a layer is, for its icon and its default name. */
8
+ export type VectorLayerKind = 'path' | 'connector' | 'lens' | 'text' | 'instance' | 'group' | 'frame' | 'mask' | 'blend' | 'boolean' | 'symmetry';
9
+ export declare const VECTOR_LAYER_KIND_LABELS: Record<VectorLayerKind, string>;
10
+ export interface VectorLayerRow {
11
+ id: string;
12
+ name: string;
13
+ kind: VectorLayerKind;
14
+ /** 0 for the document's own nodes. */
15
+ depth: number;
16
+ parentId: string | null;
17
+ visible: boolean;
18
+ locked: boolean;
19
+ /** Children the row can show (a blend's generated steps are not counted). */
20
+ childCount: number;
21
+ expanded: boolean;
22
+ }
23
+ export declare function vectorLayerKind(node: VectorNode): VectorLayerKind;
24
+ /** The node's name, else its text (for text), else its kind. */
25
+ export declare function vectorLayerName(node: VectorNode): string;
26
+ /** A group's children as the layers panel lists them: a blend's generated steps are left out. */
27
+ export declare function vectorLayerChildren(node: VectorNode): VectorNode[];
28
+ /**
29
+ * The rows for `nodes` (bottom to top, as documents keep them), top first. A group's children
30
+ * follow it, indented, when `expanded` says so.
31
+ */
32
+ export declare function vectorLayerRows(nodes: readonly VectorNode[], expanded?: (id: string) => boolean): VectorLayerRow[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor",
3
- "version": "0.194.0",
3
+ "version": "0.196.0",
4
4
  "description": "UI-agnostic rich text and media editing core library",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -252,7 +252,8 @@ in the source frame at the document's bit depth, colour mode and profile. Read
252
252
  | `select_rect` | Rectangular marquee: `x`, `y`, `width`, `height` (natural px), `mode` (replace, add, subtract, intersect), `feather` px, `antiAlias`. Returns the summary. |
253
253
  | `select_ellipse` | Elliptical marquee inscribed in `x`, `y`, `width`, `height`; same options. A feathered ellipse makes a vignette. Returns the summary. |
254
254
  | `select_line` | Single row or column marquee: `orientation` `row` (at y) or `column` (at x), `position` in natural px, `mode`. A 1 px line across the image. Returns the summary. |
255
- | `select_polygon` | The lasso: `points` (at least 3 `{x, y}` along the outline, closed automatically), `mode`, `feather`, `antiAlias`. Trace a subject's contour, not its box. Returns the summary. |
255
+ | `quick_select` | Quick Selection: `strokes` (brush paths of `{x, y}`), `size` (diameter, default 30), `mode`, `sampleAllLayers`, `enhanceEdge`. Each stroke grows out to its region's edges; with `replace` the first replaces and the rest add. Paint inside the area, then check and fix with `add` / `subtract` strokes. Returns the summary. |
256
+ | `select_polygon` | The lasso: `points` (at least 3 `{x, y}` along the outline, closed automatically), `mode`, `feather`, `antiAlias`; `snapToEdges: {width, contrast}` makes it the Magnetic Lasso (points snap to edges, the outline follows them). Trace a subject's contour, not its box. Returns the summary (plus `outlinePoints` when snapped). |
256
257
  | `magic_wand` | Select similar colour from `x`, `y`: `tolerance` 0..255 (default 32), `contiguous` (default true), `distance` `rgb` or `perceptual`, `sampleMerged`, `sample` (`composite` or `active-layer`), `mode`, `feather`. For flat backdrops and solid colours. Returns the summary. |
257
258
  | `select_color_range` | Color Range, soft: `samples` (`[{color, x?, y?}]`) or a `preset` (reds … magentas, highlights, midtones, shadows, skinTones); `fuzziness` (samples 0..200, default 40; tone presets' falloff, default 20), `localized` px radius around positioned samples, `invert`, `sample`, `mode`, `feather`. Returns the summary. |
258
259
  | `load_selection_from_image` | Load a mask image (`image` data URL or URL; white = selected) by `channel` (luminance, alpha, red, green, blue), with `threshold`, `invert`, `mode`. For AI or saved mattes; then `refine_selection`. Returns the summary. |
@@ -96,7 +96,9 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
96
96
  | Subject | Method |
97
97
  | --- | --- |
98
98
  | Geometric areas: a frame, a screen, a band | `select_rect` / `select_ellipse`, with `feather` for soft transitions |
99
- | A subject with a clear outline (product, person, animal) | `select_subject` if the host has a model; else `select_polygon` along the contour (20–60 points), then `refine_selection` |
99
+ | A subject with a clear outline (product, person, animal) | `select_subject` if the host has a model; else `select_polygon` with `snapToEdges` (12–30 points within `width` of the edge), then `refine_selection` |
100
+ | An area or subject that differs from its surroundings (a shirt, a sky, a car) | `quick_select` with strokes painted inside it, away from the edge (`size` a fraction of its width); `add` / `subtract` strokes where it fell short or leaked |
101
+ | A low-contrast or busy outline the edges don't follow | `select_polygon` without `snapToEdges`, along the contour (20–60 points) |
100
102
  | Flat backdrop or solid colour (studio white, green screen, sky without clouds) | `magic_wand` on the backdrop (`tolerance` 20–40; `distance: "perceptual"` for gradients), then `invert_selection` |
101
103
  | A colour spread across the image (all the reds, a dress) | `select_color_range` with `samples` (several picks) or a hue `preset`; `localized` to keep one area |
102
104
  | Tones (shadows, highlights, a luminosity mask) | `select_color_range` with `preset` `shadows` / `midtones` / `highlights` |
@@ -155,6 +157,13 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
155
157
  30–60), check `percent`, and grow with `mode: "add"`.
156
158
  - **Box instead of outline:** `select_rect` around a subject masks its background too. Trace
157
159
  the contour.
160
+ - **Quick Selection spills over a weak edge:** a stroke grows into neighbouring areas of a
161
+ similar colour. Paint smaller strokes further from the edge, then a `subtract` stroke on the
162
+ spill. Strokes near an edge also pull in the other side.
163
+ - **Magnetic outline jumps to the wrong edge:** `snapToEdges` follows the strongest edge near
164
+ each point, which can be a shadow or a texture. Put points within `width` of the edge you
165
+ want, add points where it turns or runs past another edge, raise `contrast` on a busy
166
+ background, and drop `snapToEdges` where the edge is too faint.
158
167
  - **Over-feathering hard objects:** a 10 px feather on a product looks like a glow. Keep hard
159
168
  edges at 0–2 px and refine soft ones instead.
160
169
  - **Halo:** the old background shows as a light or dark fringe. `contract` 1–2 px or
@@ -183,7 +192,8 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
183
192
  - `get_selection`: empty, bounds, area and percent after every step
184
193
  - `select_all`, `deselect`, `invert_selection`: whole-image selections
185
194
  - `select_rect`, `select_ellipse`: marquees
186
- - `select_polygon`: the lasso, along a contour
195
+ - `select_polygon`: the lasso, along a contour; with `snapToEdges`, the Magnetic Lasso
196
+ - `quick_select`: brush strokes that grow out to the region's edges
187
197
  - `select_line`: a single row or column
188
198
  - `select_layer_transparency`: a layer's own pixels
189
199
  - `grow_selection`, `select_similar`: add similar colour, connected or anywhere