@pixerate/editor 0.112.0 → 0.113.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.
@@ -483,4 +483,221 @@ declare class SmudgeTip {
483
483
  dab(medium: TileMedium, d: SmudgeDab, rect: RasterRect, cov: ArrayLike<number>): void;
484
484
  }
485
485
 
486
- export { tipShape as $, blendRadius as A, type BrushSettings as B, CoverageMap as C, type Dab as D, EMPTY_MIXER_LOAD as E, blenderDab as F, cleanMixer as G, coverageTileCoord as H, createBlenderScratch as I, dabCoverage as J, dabRect as K, exchangeRates as L, type MixerSettings as M, loadMixer as N, mixerRates as O, type PaintTileTarget as P, normalizeBlender as Q, type RgbTriple as R, type SmudgeSettings as S, TileMedium as T, normalizeBrush as U, normalizeMixer as V, normalizeSmudge as W, rasterizeDab as X, smoothPoints as Y, tipFalloff as Z, tipReach as _, type MixingModel as a, walkDabs as a0, type BlenderSettings as b, type StrokePoint as c, type MixerLoad as d, MAX_BRUSH_SIZE as e, StrokeCoverage as f, type MixerTarget as g, MixerStroke as h, type StrokeApply as i, type BlenderScratch as j, DEFAULT_BLENDER as k, DEFAULT_BRUSH as l, DEFAULT_MIXER as m, DEFAULT_SMUDGE as n, DabWalker as o, type ExchangeRates as p, MIXER_DRAIN as q, MIXER_PICKUP_RATE as r, MIXING_MODELS as s, PaintExchange as t, type PaintMedium as u, type PaintSample as v, type SmudgeDab as w, SmudgeTip as x, type TileMediumOptions as y, applyStrokeToTile as z };
486
+ /**
487
+ * The retouching tools on the shared brush engine: Clone Stamp, Healing Brush, Spot Healing
488
+ * Brush, Smudge, Dodge, Burn and Sponge. Two stroke shapes cover them all:
489
+ *
490
+ * - **Accumulate, then composite** (Clone Stamp, Dodge, Burn; the Healing tools' result): the
491
+ * stroke's dabs build up one `CoverageMap` (flow builds towards the dab's opacity ceiling), and
492
+ * per-pixel paint taken from the pre-stroke state (a cloned source, a toned copy of the pixel,
493
+ * a healed patch) is composited through it with `applyRetouchToTile`. Pixels painted earlier in
494
+ * the stroke are never sampled again, and overlapping dabs never exceed the stroke's opacity.
495
+ * - **Sequential dabs** (Smudge, Sponge): `RetouchDabStroke` runs each dab in order over live
496
+ * tiles, so dab n sees what dab n − 1 did.
497
+ *
498
+ * The Healing Brush clones while it is drawn and, when the stroke ends, replaces the clone with a
499
+ * seamless clone (`healPixels`, Poisson blending): the source's texture with the colour and light
500
+ * of the pixels around the stroke. The Spot Healing Brush finds its own source (`spotHealPixels`).
501
+ *
502
+ * Tone curves (`dodgeBurnCurve`, `dodgeBurn`, `sponge`) are photocraft's, made to behave like
503
+ * Photoshop's tools: the chosen range moves most, black and white stay put for midtones, and
504
+ * Protect Tones works on luma so hue and saturation are kept. Data in, pixels out; no DOM.
505
+ */
506
+
507
+ /** The tonal range Dodge and Burn work on most. */
508
+ type ToneRange = 'shadows' | 'midtones' | 'highlights';
509
+ declare const TONE_RANGES: readonly ToneRange[];
510
+ /** Rec. 601 luma of straight RGB (0..1). */
511
+ declare function luma(r: number, g: number, b: number): number;
512
+ /** The dodge (or burn) transfer of one value `v` (0..1) at exposure `e` (0..1). */
513
+ declare function dodgeBurnCurve(v: number, e: number, range: ToneRange, burn: boolean): number;
514
+ /**
515
+ * Dodge (or burn) straight RGB `c` (0..1, written in place) at exposure `e`. With `protectTones`
516
+ * the curve moves luma and the colour is scaled to it (hue and saturation kept); otherwise each
517
+ * channel goes through the curve.
518
+ */
519
+ declare function dodgeBurn(c: number[], e: number, range: ToneRange, burn: boolean, protectTones: boolean): number[];
520
+ /** HSV saturation (chroma / max) of straight RGB. */
521
+ declare function saturationOf(r: number, g: number, b: number): number;
522
+ /**
523
+ * Sponge: moves straight RGB `c` (written in place) away from (`saturate`) or towards its luma
524
+ * grey by `amount` (0..1). `vibrance` damps it where it would clip: saturating touches already
525
+ * saturated colours less, desaturating touches near-greys less. Gamut-compressed, never clipped.
526
+ */
527
+ declare function sponge(c: number[], amount: number, saturate: boolean, vibrance: boolean): number[];
528
+ /** Per-dab strength so that one pass over a point totals about `e`, whatever the spacing. */
529
+ declare function perDabStrength(e: number, spacing: number): number;
530
+ /** Which pixels the Clone Stamp and the Healing tools sample. */
531
+ type RetouchSample = 'current' | 'current-below' | 'all';
532
+ declare const RETOUCH_SAMPLES: readonly RetouchSample[];
533
+ /** How the Spot Healing Brush finds its source. */
534
+ type SpotHealType = 'proximity-match' | 'smooth';
535
+ declare const SPOT_HEAL_TYPES: readonly SpotHealType[];
536
+ /** The retouching tools' options. */
537
+ interface RetouchSettings {
538
+ /** Clone / heal: keep the source offset from stroke to stroke (default true); off, every stroke starts at the source point. */
539
+ aligned: boolean;
540
+ /** Clone / heal / spot heal: what is sampled (default `current`: the target layer). */
541
+ sample: RetouchSample;
542
+ /** Spot heal: `proximity-match` (default: the best matching patch nearby) or `smooth` (a smooth fill from the edges). */
543
+ spotType: SpotHealType;
544
+ /** Dodge / burn: the tonal range (default `midtones`). */
545
+ range: ToneRange;
546
+ /** Dodge / burn: exposure 0..1 (default 0.5). */
547
+ exposure: number;
548
+ /** Dodge / burn: tone luma only, keeping hue and saturation (default true). */
549
+ protectTones: boolean;
550
+ /** Sponge: `desaturate` (default) or `saturate`. */
551
+ spongeMode: 'desaturate' | 'saturate';
552
+ /** Sponge: damp the change where colours would clip (default true). */
553
+ vibrance: boolean;
554
+ /** Smudge: how strongly the finger carries and lays paint, 0..1 (default 0.5). */
555
+ strength: number;
556
+ }
557
+ declare const DEFAULT_RETOUCH: Readonly<RetouchSettings>;
558
+ /** Retouch settings with every field valid (missing or invalid fields from `base`). */
559
+ declare function normalizeRetouch(raw?: Partial<RetouchSettings>, base?: RetouchSettings): RetouchSettings;
560
+ /** Pixels a retouching stroke samples, by document pixel. */
561
+ interface PixelSource {
562
+ /** Straight RGBA 0..255 of document pixel (x, y) into `out`; false (and `out` zeroed) where there is nothing. */
563
+ sample(x: number, y: number, out: number[]): boolean;
564
+ }
565
+ /**
566
+ * A source over raster tiles (layer pixels, a mask, or composite tiles): `readTile` gives tile
567
+ * (tx, ty) in source space or `null` when it is empty; source pixel = document pixel − `offset`.
568
+ * One-channel tiles read as grey (alpha 255). Tiles are cached, so make one per stroke.
569
+ */
570
+ declare class TilePixelSource implements PixelSource {
571
+ private readonly readTile;
572
+ readonly channels: 1 | 4;
573
+ readonly offset: {
574
+ x: number;
575
+ y: number;
576
+ };
577
+ private readonly cache;
578
+ private lastKey;
579
+ private last;
580
+ constructor(readTile: (tx: number, ty: number) => Uint8ClampedArray | null, channels: 1 | 4, offset?: {
581
+ x: number;
582
+ y: number;
583
+ });
584
+ sample(x: number, y: number, out: number[]): boolean;
585
+ }
586
+ /** A source over a region of float RGBA pixels (0..255), e.g. a healed patch. */
587
+ declare class RegionPixelSource implements PixelSource {
588
+ readonly rect: RasterRect;
589
+ readonly data: Float32Array;
590
+ constructor(rect: RasterRect, data: Float32Array);
591
+ sample(x: number, y: number, out: number[]): boolean;
592
+ }
593
+ /** `rect` of a source, shifted by (dx, dy), as float RGBA 0..255 (`rect.width × rect.height × 4`). */
594
+ declare function readSourceRegion(source: PixelSource, rect: RasterRect, dx?: number, dy?: number): Float32Array;
595
+ /** What a coverage-based retouching stroke lays through its coverage. */
596
+ type RetouchPaint =
597
+ /** Source pixels at document pixel + (dx, dy) (Clone Stamp, a healed patch). */
598
+ {
599
+ kind: 'source';
600
+ source: PixelSource;
601
+ dx: number;
602
+ dy: number;
603
+ }
604
+ /** The target pixel itself, toned (Dodge, Burn): `tone` edits straight RGB 0..1 in place. */
605
+ | {
606
+ kind: 'tone';
607
+ tone: (rgb: number[]) => void;
608
+ }
609
+ /** A flat colour (the Spot Healing Brush's trail while it is drawn), RGBA 0..255. */
610
+ | {
611
+ kind: 'color';
612
+ color: readonly [number, number, number, number];
613
+ };
614
+ /** How a coverage-based retouching stroke composites. */
615
+ interface RetouchApply {
616
+ /** Stroke opacity 0..1. */
617
+ opacity: number;
618
+ /** Blend mode for source paint (default normal). */
619
+ blend?: BlendMode;
620
+ lockTransparency: boolean;
621
+ /** Selection coverage (document space, canvas-sized), or `null`. */
622
+ selection: {
623
+ width: number;
624
+ height: number;
625
+ data: ArrayLike<number>;
626
+ } | null;
627
+ /** Layer offset (target pixels are in layer space). */
628
+ offset: {
629
+ x: number;
630
+ y: number;
631
+ };
632
+ }
633
+ /**
634
+ * Composites `paint` through the stroke's coverage over `tileData` (target tile (tx, ty) as it was
635
+ * before the stroke, layer space; RGBA, or one byte per pixel for masks) and returns the new tile.
636
+ * With `into` (the previous result) and `region` (document space), only `region` is recomputed.
637
+ * Tone paint keeps alpha; source and colour paint composite with `blend` (source-over by
638
+ * default), keeping alpha on a locked layer.
639
+ */
640
+ declare function applyRetouchToTile(coverage: CoverageMap, tileData: Uint8ClampedArray, channels: 1 | 4, tx: number, ty: number, paint: RetouchPaint, s: RetouchApply, options?: {
641
+ into?: Uint8ClampedArray;
642
+ region?: RasterRect;
643
+ }): Uint8ClampedArray;
644
+ /** The tone function of a Dodge or Burn stroke. */
645
+ declare function toneOf(tool: 'dodge' | 'burn', settings: Pick<RetouchSettings, 'exposure' | 'range' | 'protectTones'>): (rgb: number[]) => void;
646
+ /**
647
+ * Heals `src` into `dst` over `mask` (all `w × h`, RGBA floats 0..255): a seamless clone solved
648
+ * only over the mask's bounds plus a one-pixel fixed ring; elsewhere the result is `dst`.
649
+ */
650
+ declare function healPixels(w: number, h: number, src: Float32Array, dst: Float32Array, mask: ArrayLike<boolean | number>): Float32Array;
651
+ /**
652
+ * Spot healing: fills `hole` (`w × h`) of `img` (RGBA floats 0..255) with no given source, then
653
+ * heals the fill into its surroundings. `proximity-match` copies the best matching nearby patch
654
+ * (falling back to `smooth` when none fits); `smooth` interpolates the edges inwards. `size` is
655
+ * the brush diameter in pixels. Returns the healed pixels over the whole grid.
656
+ */
657
+ declare function spotHealPixels(w: number, h: number, img: Float32Array, hole: ArrayLike<boolean | number>, size: number, type: SpotHealType,
658
+ /** Told the patch offset it copied (`null`: a smooth fill). */
659
+ onOffset?: (offset: [number, number] | null) => void): Float32Array;
660
+ /** The window a Spot Healing stroke over `bounds` looks for texture in: the bounds grown by their size, on the canvas. */
661
+ declare function spotHealWindow(bounds: RasterRect, canvas: {
662
+ width: number;
663
+ height: number;
664
+ }): RasterRect;
665
+ /**
666
+ * One dab of a sequential retouching stroke: `cov` is the tip's pure coverage over `rect`
667
+ * (document space, 0..1); `d.alpha` is the flow and `d.opacity` the pressure opacity.
668
+ */
669
+ type RetouchDabEffect = (medium: TileMedium, d: Dab, rect: RasterRect, cov: Float32Array) => void;
670
+ /**
671
+ * A stroke whose dabs edit live tiles in order (Smudge, Sponge). Feed points with `push` (any
672
+ * chunks give the same dabs), end with `finish`; each returns the document rectangles it touched.
673
+ */
674
+ declare class RetouchDabStroke {
675
+ readonly brush: BrushSettings;
676
+ private readonly effect;
677
+ dabCount: number;
678
+ bounds: RasterRect | null;
679
+ private readonly walker;
680
+ private readonly medium;
681
+ private smoothPos;
682
+ private buf;
683
+ constructor(brush: BrushSettings, target: PaintTileTarget, effect: RetouchDabEffect);
684
+ push(points: readonly StrokePoint[]): RasterRect[];
685
+ finish(last?: StrokePoint): RasterRect[];
686
+ private smooth;
687
+ private paint;
688
+ }
689
+ /** The Smudge's per-dab effect: a `SmudgeTip` whose strength carries and lays the paint. */
690
+ declare function smudgeEffect(brush: BrushSettings, strength: number, mixing?: SmudgeSettings['mixing']): RetouchDabEffect;
691
+ /** The Sponge's per-dab effect: each dab moves saturation by about half its flow, compounding over passes. */
692
+ declare function spongeEffect(brush: BrushSettings, saturate: boolean, vibrance: boolean): RetouchDabEffect;
693
+ /**
694
+ * An RGBA view of one-channel tiles (masks), for the sequential-dab tools: reads expand grey to
695
+ * opaque RGB, writes land in RGBA copies, and `flush` writes their red channel back through
696
+ * `target.writeTile`. Alpha is locked, so a mask's values are all a dab changes.
697
+ */
698
+ declare function rgbaViewOfGrey(target: PaintTileTarget): {
699
+ target: PaintTileTarget;
700
+ flush(): void;
701
+ };
702
+
703
+ export { cleanMixer as $, type PixelSource as A, type BrushSettings as B, CoverageMap as C, type Dab as D, EMPTY_MIXER_LOAD as E, RETOUCH_SAMPLES as F, RegionPixelSource as G, type RetouchDabEffect as H, RetouchDabStroke as I, type RetouchSample as J, SPOT_HEAL_TYPES as K, type SmudgeDab as L, type MixerSettings as M, SmudgeTip as N, type SpotHealType as O, type PaintTileTarget as P, TileMedium as Q, type RgbTriple as R, type SmudgeSettings as S, TONE_RANGES as T, type TileMediumOptions as U, TilePixelSource as V, type ToneRange as W, applyRetouchToTile as X, applyStrokeToTile as Y, blendRadius as Z, blenderDab as _, type MixingModel as a, coverageTileCoord as a0, createBlenderScratch as a1, dabCoverage as a2, dabRect as a3, dodgeBurn as a4, dodgeBurnCurve as a5, exchangeRates as a6, healPixels as a7, loadMixer as a8, luma as a9, mixerRates as aa, normalizeBlender as ab, normalizeBrush as ac, normalizeMixer as ad, normalizeRetouch as ae, normalizeSmudge as af, perDabStrength as ag, rasterizeDab as ah, readSourceRegion as ai, rgbaViewOfGrey as aj, saturationOf as ak, smoothPoints as al, smudgeEffect as am, sponge as an, spongeEffect as ao, spotHealPixels as ap, spotHealWindow as aq, tipFalloff as ar, tipReach as as, tipShape as at, toneOf as au, walkDabs as av, type BlenderSettings as b, type StrokePoint as c, type MixerLoad as d, MAX_BRUSH_SIZE as e, type RetouchSettings as f, StrokeCoverage as g, type MixerTarget as h, MixerStroke as i, type RetouchApply as j, type RetouchPaint as k, type StrokeApply as l, type BlenderScratch as m, DEFAULT_BLENDER as n, DEFAULT_BRUSH as o, DEFAULT_MIXER as p, DEFAULT_RETOUCH as q, DEFAULT_SMUDGE as r, DabWalker as s, type ExchangeRates as t, MIXER_DRAIN as u, MIXER_PICKUP_RATE as v, MIXING_MODELS as w, PaintExchange as x, type PaintMedium as y, type PaintSample as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor",
3
- "version": "0.112.0",
3
+ "version": "0.113.0",
4
4
  "description": "UI-agnostic rich text and media editing core library",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: pixerate-image-editor
3
- description: How to colour-correct, composite and retouch a photo in the Pixerate image editor through its agent tools - layers (pixel, adjustment, group, shape, type, annotation and smart object layers with masks, clipping, blend modes, opacity and fill; merge, flatten, filters), smart objects (non-destructive transforms, smart filters, linked contents), vector shape layers (live shapes, arrows, paths, path operations, fills and strokes), live type layers, live layer effects, base adjustments, adjustment layers, selections and masks, Free Transform, crop, rotate, flip, Mixer Brush, undo, PSD, histogram and previews. Use when asked to edit, grade, correct, crop, straighten or mask an image, add a shadow, glow or outline, transform or warp a layer, draw a shape or arrow, add or edit text, make a smart object, or open or save a PSD, or when list_layers, set_adjustments, add_layer_effect, transform_layer, add_shape_layer, add_text_layer, convert_to_smart_object, paint_stroke, import_psd or render_preview are available.
3
+ description: How to colour-correct, composite and retouch a photo in the Pixerate image editor through its agent tools - layers (pixel, adjustment, group, shape, type, annotation and smart object layers; masks, clipping, blend modes, merge, filters), smart objects and smart filters, shape and type layers, layer effects, adjustments, selections and masks, Free Transform, crop, rotate, flip, painting and the Mixer Brush, retouching (Clone Stamp, Healing Brush, Spot Healing, Smudge, Dodge, Burn, Sponge), undo, PSD, histogram and previews. Use when asked to edit, grade, correct, crop, straighten or mask an image, remove a blemish or spot, clone, dodge or burn, add a shadow, glow or outline, transform or warp a layer, draw a shape or arrow, add or edit text, make a smart object, or open or save a PSD, or when list_layers, set_adjustments, add_layer_effect, transform_layer, add_shape_layer, add_text_layer, paint_stroke, clone_stamp, spot_heal, import_psd or render_preview are available.
4
4
  ---
5
5
 
6
6
  # Image editor
@@ -183,6 +183,13 @@ drawing or editing either.
183
183
  false` and `cleanEachStroke: false`, start with `load_mixer_brush`, read the brush with
184
184
  `get_mixer_brush` when unsure, and reset it with `clean_mixer_brush`.
185
185
 
186
+ ### Retouching
187
+
188
+ Clone Stamp, Healing Brush, Spot Healing, Smudge, Dodge, Burn and Sponge: retouch on an empty
189
+ layer with `sample: "current-below"`, pick `spot_heal` for small spots, `heal_brush` for larger
190
+ flaws and `clone_stamp` to copy exactly, and keep strokes short and soft. Read
191
+ [references/retouching.md](references/retouching.md) before retouching.
192
+
186
193
  ### Photoshop files
187
194
 
188
195
  1. **Inspect before opening.** `inspect_psd` lists every layer with the type it would become and
@@ -198,9 +205,14 @@ drawing or editing either.
198
205
  with `list_layer_effects` and change as usual, with the file's patterns (listed in
199
206
  `list_layer_effects.patterns`) and its global light. An effects block the editor cannot read
200
207
  is kept and written back by `export_psd`, but not drawn; the warnings say so.
201
- 3. **`import_psd` replaces the document** (one undo step). CMYK, Lab, grayscale, indexed, 16-bit
202
- and 32-bit files are converted to 8-bit RGB, and ICC profiles are ignored; the warnings say
203
- so. Afterwards work with `list_layers` as usual: there is no Background source image, every
208
+ 3. **`import_psd` replaces the document** (one undo step). 16- and 32-bit files keep their
209
+ depth (`get_image_info.bitDepth`); CMYK, Lab, grayscale and indexed files are converted to RGB,
210
+ and ICC profiles are ignored; the warnings say so. `export_psd` writes at the document's depth.
211
+ Change it with `set_bit_depth`: 16-bit for smooth gradients and strong grades, 32-bit for
212
+ linear-light HDR (layers blend in linear light there). Adjustment layers, blend modes, merges
213
+ and the exported merged image keep the depth; layer effects, layer filters, painting and
214
+ transforms still work in 8 bits on what they change, so grade a deep file with adjustment
215
+ layers rather than repainting it when the depth matters. Afterwards work with `list_layers` as usual: there is no Background source image, every
204
216
  pixel comes from the layers.
205
217
  4. **Smart objects come in live**: embedded contents (a PSB opens as a nested document you can
206
218
  edit with `edit_smart_object_contents`, a PNG as raster contents), the placement and warp, the
@@ -297,11 +309,11 @@ drawing or editing either.
297
309
  | `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. |
298
310
  | `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. |
299
311
  | `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. |
300
- | `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, the layer count and `canUndo` / `canRedo`. Call before cropping or selecting so coordinates are right. |
312
+ | `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. |
301
313
  | `get_histogram` | Judge exposure numerically: per channel and luminance `mean`, `p1` and `p99` (0..255), and `clipping` percent at pure black and white per channel. Reads the last render, so call `render_preview` first. `bins` (16, 32, 64 or 256) adds the counts. Cheaper than another image for checking a tonal change. |
302
314
  | `render_preview` | See the edited image (crop, adjustments, layers, annotations) as a PNG, or with `layerId` one layer alone. `maxSide` 16..2048 px, default 512. Images cost tokens: render after a group of edits, use 512 for overall look and 1024+ to judge clipping, edges and masks. Fails in environments without rendering. |
303
315
  | `inspect_psd` | Read a PSD / PSB (`data` base64) without opening it: size, `depth`, `colorMode`, `resolution`, every layer bottom first with `psdKind`, the `type` it would become (`smart`, `shape` and `type` for Photoshop smart objects, shapes and text) and `status` (`mapped`, `approximated`, `dropped`) with `notes`, and the import `warnings`. Call before `import_psd`. |
304
- | `export_psd` | Save the document as an 8-bit RGB PSD (PSB above 30000 px) with layers, groups, adjustment layers and masks. Returns `data` (base64), `size` in bytes, `psb` and `warnings` (crop or rotation not applied, filters and depth masks baked, annotations rasterized or left out, Pigment blends written as Normal). Shape and type layers are written as real Photoshop shape and type layers. Changes nothing. |
316
+ | `export_psd` | Save the document as an RGB PSD at its bit depth (PSB above 30000 px) with layers, groups, adjustment layers and masks. Returns `data` (base64), `size` in bytes, `psb` and `warnings` (crop or rotation not applied, filters and depth masks baked, annotations rasterized or left out, Pigment blends written as Normal). Shape and type layers are written as real Photoshop shape and type layers. Changes nothing. |
305
317
  | `get_transform_frame` | Before a transform: what it would move (`content`, `moved`: pixels, mask, selection), its `frame` (document bounds of the content), `corners` (top-left, top-right, bottom-right, bottom-left) and `center`. `meshColumns` / `meshRows` also return an identity warp `mesh` over the frame to edit for `warp_layer`. Changes nothing. |
306
318
  | `get_smart_object` | Read a smart object: `contents` (`id` shared by linked instances, `name`, `kind` raster or document, `width` / `height`, `decoded`, `fileType`, `linkedPath`, `instances`), `placement` (`corners`, `warp` in contents px, `interpolation`, and when `affine` the `scaleX` / `scaleY` in percent of the contents, `rotate`, `skewX`, `center`, `bounds`) and `smartFilters` (`enabled`, `filters` bottom to top with ids, settings, `blend`, `opacity`; kind `psd` is a Photoshop filter kept but not drawn; the filter `mask`). Call before changing a smart object. |
307
319
  | `list_fonts` | Read the font families type layers can use here (generic families always work) and which shaper draws them. Call before choosing a font. |
@@ -326,6 +338,7 @@ drawing or editing either.
326
338
  | `set_crop` | Crop to `x`, `y`, `width`, `height` in source pixels of the un-rotated image (see `get_image_info`). Non-destructive; refused if the box leaves the image. For an aspect ratio, compute the largest box of that ratio inside the image and centre it on the subject. Returns the geometry. |
327
339
  | `reset_crop` | Remove the crop and show the whole image again. |
328
340
  | `set_rotation` | Set the absolute rotation in degrees clockwise (-360..360). Quarter turns keep every pixel; small angles straighten a horizon but add transparent corners to crop away. Returns the geometry. |
341
+ | `set_bit_depth` | Image › Mode: `depth` 8, 16 or 32 (float, linear light). Up costs nothing; down rounds the layers' extra precision away (32 → 16 clips colour above 1). One undo step. Returns `bitDepth` and `changedLayerIds`. |
329
342
  | `flip_image` | Mirror the image `horizontal` (left-right) or `vertical`. Avoid flipping images with text, logos or recognisable places. |
330
343
  | `undo_image` | Undo the last edit (one tool call is one step). Returns `canUndo` / `canRedo`. |
331
344
  | `redo_image` | Redo the edit you just undid. |
@@ -390,6 +403,16 @@ drawing or editing either.
390
403
  | `get_mixer_brush` | Read-only. The Mixer Brush settings and what it carries (`reservoir` #rrggbb or null, `amount` left 0..1, `pickup` #rrggbb or null). Call it before a mixer stroke with `loadEachStroke: false`. |
391
404
  | `load_mixer_brush` | Fill the Mixer Brush reservoir with `color` (default the foreground), amount 1; keeps the pickup. View state, not an undo step. Use before strokes with `loadEachStroke: false`. Returns the load. |
392
405
  | `clean_mixer_brush` | Empty the Mixer Brush pickup well (`emptyReservoir: true` also drops the loaded paint, so it only smears). View state, not an undo step. Returns the load. |
406
+ | `clone_stamp` | Copy pixels from the clone source along `points`: each point paints what is at point + offset (source − first point, whole px). `source` `{x, y}` sets the source for this stroke (else the current one, see `set_clone_source`); `aligned` keeps the offset for later strokes; `sample` current \| current-below \| all; `brush` `{size, hardness, spacing, opacity, flow, smoothing, pressureSize, pressureOpacity}`; `layerId`, `channel` (a mask clones from itself). One undo step. Returns the layer, channel, `bounds`, dabs and `clone` `{offset, source, aligned}`. Call `set_clone_source` or pass `source` first. |
407
+ | `heal_brush` | As `clone_stamp`, then blends the cloned texture into the colour and light around the stroke (Poisson seamless cloning, when the stroke ends). Use for blemishes, wrinkles, dust and seams where the surroundings have the right tone. Returns as `clone_stamp`. |
408
+ | `spot_heal` | Remove what `points` cover with no source: `type` proximity-match (default: copies the best matching nearby patch, then heals it in) or smooth (fills from the edges). `sample`, `brush`, `layerId`, `channel`. One undo step. Returns the layer, channel, `bounds`, dabs and `spotHeal` `{offset}` (null for a smooth fill). |
409
+ | `smudge` | Drag colour along `points` like a finger in wet paint; `strength` 0..1 (default 0.5) is how far. Works on masks too. One undo step. Returns the layer, channel, `bounds` and dabs. |
410
+ | `dodge` | Lighten what `points` cover, most in `range` shadows \| midtones \| highlights, at `exposure` 0..1 (overlaps in one stroke never exceed it); `protectTones` keeps hue and saturation. Alpha kept. One undo step. Returns the layer, channel, `bounds` and dabs. |
411
+ | `burn` | As `dodge`, darkening. |
412
+ | `sponge` | Desaturate (`mode` desaturate, default) or saturate what `points` cover; `vibrance` damps clipping; passes compound, `brush.flow` sets each pass's strength. Pixels only. One undo step. Returns the layer, channel, `bounds` and dabs. |
413
+ | `get_retouch_options` | Read-only. The retouching defaults (`aligned`, `sample`, `spotType`, `range`, `exposure`, `protectTones`, `spongeMode`, `vibrance`, `strength`) and the `cloneSource` `{x, y, offset}` or null. Call before a clone or heal stroke without `source`. |
414
+ | `set_retouch_options` | Set those defaults (view state, not an undo step); each stroke tool can override them per call. Changing `aligned` clears the kept offset. Returns the options. |
415
+ | `set_clone_source` | Set the clone source `{x, y}` in document px (the Alt-click), or remove it with `clear: true`. View state. The next `clone_stamp` / `heal_brush` fixes the offset from its first point. Returns the source. |
393
416
  | `fill_pixels` | Fill the selection (or the canvas without one / with `useSelection: false`) on pixels or a mask with `color`, `opacity`, `blend`. Returns the changed bounds. |
394
417
  | `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. |
395
418
  | `clear_pixels` | Remove the selected pixels (alpha to 0; a transparency-locked layer or a mask gets the background colour). Returns the changed bounds. |
@@ -413,6 +436,7 @@ drawing or editing either.
413
436
  `locks` only when the user wants that layer changed.
414
437
  - `merge_down`, `merge_visible` and `flatten_image` merge annotation layers only in a browser
415
438
  (they need a canvas); elsewhere they fail and say so.
439
+ - Retouching pitfalls (empty layers, sources, what ignores opacity): see references/retouching.md.
416
440
  - `paint_stroke`, `fill_pixels` and `clear_pixels` need a visible pixel layer (or a layer with a
417
441
  raster mask and `channel: "mask"`); adjustment, group, shape, type, annotation and smart object layers can
418
442
  only be painted through their masks (and a smart object through `channel: "filter-mask"`).
@@ -0,0 +1,36 @@
1
+ # Retouching
2
+
3
+ 1. **Retouch on a new empty layer** above the photo with `sample: "current-below"` (or `"all"`):
4
+ `clone_stamp`, `heal_brush` and `spot_heal` then paint onto that layer and the photo stays
5
+ untouched, so a bad stroke is easy to mask or erase. Smudge, Dodge, Burn and Sponge change the
6
+ pixels they touch, so run them on a duplicate (`duplicate_layer`) instead.
7
+ 2. **Pick the tool by the job.** A small spot or blemish on skin, sky or a wall: `spot_heal` with
8
+ a brush a little larger than the spot (`proximity-match` copies a nearby patch; `smooth` fills
9
+ from the edges and suits flat areas). A larger flaw, or one next to an edge: `heal_brush` from a
10
+ clean source of the same texture; it takes the colour and light around the stroke, so the source
11
+ may be lighter or darker. Copy exactly (a repeat, a straight line, an edge): `clone_stamp`.
12
+ 3. **Sources and offsets.** `set_clone_source` (or `source` on the stroke) is the Alt-click. The
13
+ first stroke fixes the offset = source − its first point; with `aligned` (default) later strokes
14
+ keep it, so the source moves with the brush; with `aligned: false` every stroke restarts at the
15
+ source point. The result's `clone.offset` and `clone.source` say what was used. Choose a source
16
+ that is clean for the whole length of the stroke: the stroke samples what was there before it,
17
+ never what it just painted.
18
+ 4. **Use soft, short strokes** (`brush.hardness` 0..0.5, a size just over the flaw), and check each
19
+ with `capture_view` on the area (scale 2..4). Several small strokes heal better than one long one.
20
+ 5. **Dodge and burn gently**: `exposure` 0.1..0.3, `range: "midtones"` for skin and form,
21
+ `"highlights"` / `"shadows"` to lift or deepen only those tones. A stroke never goes past its
22
+ exposure where its dabs overlap; another stroke builds on it. `protectTones` (default) keeps hue
23
+ and saturation. `sponge` with `mode: "desaturate"` takes colour out (a colour cast on teeth or
24
+ eyes), `saturate` adds it; passes compound, so use a low `brush.flow`.
25
+ 6. **Smudge** drags colour along the stroke like a finger in wet paint (`strength` how far); for
26
+ blending paint colours use the Mixer Brush instead.
27
+
28
+ ## Pitfalls
29
+
30
+ - The retouching tools need a source to be useful: `clone_stamp` / `heal_brush` refuse without a
31
+ clone source, and with `sample: "current"` an empty layer has nothing to copy (the stroke then
32
+ changes nothing). Sample `current-below` or `all` to retouch onto an empty layer.
33
+ - `heal_brush` and `spot_heal` show a plain clone or a dark trail while a person draws and blend
34
+ when the stroke ends; through the tools the result is already blended.
35
+ - `dodge`, `burn`, `sponge` and `smudge` ignore `brush.opacity` (exposure, flow and strength set
36
+ their strength) and `brush.blend`. The Sponge refuses masks, which have no colour.
@@ -1,13 +1,14 @@
1
1
  ---
2
2
  name: pixerate-vector
3
- description: How to draw and edit vector artwork in the Pixerate vector editor through its vector.* commands - adding shapes and SVG paths, fills, strokes, stacked fills and strokes with blend modes, drop shadows, glows and blurs, clipping masks and opacity masks, gradients (linear, radial, conic, Oklab interpolation), stroke alignment, arrowheads, variable-width and tapered strokes, live corner radii, boolean operations and Pathfinder (unite, subtract, divide, trim), Shape Builder, paint bucket, live path effects (trim paths, repeater, zig zag, offset), envelope warps and free distort, blends and morphing, anchor editing (join, smooth, simplify, scissors, knife, eraser), align, distribute, group, stacking order, and SVG import and export. Use when creating an icon, logo, illustration, glyph, badge or any Bezier path artwork, or editing an SVG in a Pixerate vector editor.
3
+ description: How to draw and edit vector artwork in the Pixerate vector editor through its vector.* commands - adding shapes and SVG paths, text (point, area, on a path, styles, Create Outlines), symbols and instances, CAD constraints (parallel, tangent, dimensions, auto-constrain), fills, strokes, stacked fills and strokes with blend modes, drop shadows, glows and blurs, clipping masks and opacity masks, gradients (linear, radial, conic, Oklab interpolation), stroke alignment, arrowheads, variable-width and tapered strokes, live corner radii, boolean operations and Pathfinder (unite, subtract, divide, trim), Shape Builder, paint bucket, live path effects (trim paths, repeater, zig zag, offset), envelope warps and free distort, blends and morphing, anchor editing (join, smooth, simplify, scissors, knife, eraser), align, distribute, group, stacking order, and SVG import and export. Use when creating an icon, logo, illustration, glyph, badge or any Bezier path artwork, or editing an SVG in a Pixerate vector editor.
4
4
  ---
5
5
 
6
6
  # Pixerate vector editor
7
7
 
8
8
  The vector document is an artboard (`width` x `height` in document units, y down) holding a tree
9
9
  of **nodes**: `path` nodes (one or more subpaths of Bezier anchors, with fill, stroke and stroke
10
- settings) and `group` nodes. Groups can also be live booleans, live blends, mask groups, or
10
+ settings), `text` nodes (a string set as point text, area text or along a path, painted with
11
+ the same fills and strokes), `instance` nodes (a placed copy of a symbol) and `group` nodes. Groups can also be live booleans, live blends, mask groups, or
11
12
  carry a live effect stack. Every command is a `vector.*` command run with `run_command` / `run_commands` (see
12
13
  `pixerate-agent-tools` for batching with `"$0.id"` references, `atomic: true` and error codes).
13
14
 
@@ -28,6 +29,15 @@ carry a live effect stack. Every command is a `vector.*` command run with `run_c
28
29
  inside the mask's outline (`clip`), or where it is bright (`luminance`) or opaque (`alpha`).
29
30
  `invert` swaps shown and hidden. `vector.list` gives a mask group a `mask`
30
31
  `{ type, invert, maskId, contents, enabled }`; a hidden mask (`enabled: false`) masks nothing.
32
+ - **Text** has a base `font` (family, weight, italic, size, tracking, leading, baseline shift,
33
+ underline, strike-through), optional runs that override fields over ranges (`start` / `end`
34
+ in UTF-16 code units, plus a range `fill`), a `paragraph` style and a `frame`: `point` (x, y
35
+ of the first baseline), `area` (a box it wraps in) or `path` (a line it follows).
36
+ - **Symbols** are reusable art kept with the document (`vector.listSymbols`), drawn by `instance`
37
+ nodes through a transform that, unlike other nodes', may scale and skew. Editing a symbol
38
+ (`vector.editSymbol` … `vector.exitSymbolEdit`) changes every instance.
39
+ - **Constraints** (CAD-style) keep geometric relations and dimensions between anchors, segments
40
+ and round paths; they are solved after every edit, so later moves keep them.
31
41
  - Coordinates are document units. A rotated node stores its geometry in its own space: the
32
42
  `bounds` that `vector.list` reports are document-space, its `d` is the node's own.
33
43
 
@@ -54,6 +64,28 @@ Parameter details (gradient fields, effect parameters, width profiles, envelopes
54
64
 
55
65
  ## Best practices
56
66
 
67
+ - **Constraints for precise, editable geometry.** For technical drawings, diagrams, floor plans or
68
+ any shape that must keep exact relations, draw it roughly, run `vector.autoConstrain`, then add
69
+ the dimensions that matter (`vector.addConstraint` distance / angle / radius) and name them so
70
+ related sizes follow one another (`vector.setConstraintValue` with expressions like `w / 2`).
71
+ Check `vector.listConstraints` `dof`: aim for `fully` on parts that must not drift; `over`
72
+ means some constraints repeat others. Prefer few, meaningful constraints over many.
73
+
74
+ - **Symbols for repeats.** When the same art appears several times (icons in a set, trees in a
75
+ scene, bullets, a logo mark), make it once and place instances (`vector.makeSymbol`, then
76
+ `vector.placeSymbol` with `x` / `y` or a `transform`), so one edit in isolation changes them all.
77
+ Give each a clear name. Use `vector.setInstanceOptions` `fill` / `stroke` for colour variants and
78
+ `hidden` to drop a part, rather than detaching; detach only when one copy must diverge for good.
79
+
80
+ - **Text.** Use point text for labels and headlines, area text (`width`, `height`) for anything
81
+ that should wrap, text on a path for badges and seals. Check `overflow` in the summary every
82
+ text write returns: hidden lines or characters mean the box or path is too small. Keep one or
83
+ two families per design (`vector.listFonts` lists what the shaper has; a missing family draws
84
+ in a fallback and appears in `missingFonts`). Style ranges with `vector.setTextStyle` rather
85
+ than splitting text into separate nodes, so it still edits as one paragraph. Create Outlines
86
+ only for final artwork or logos that must not depend on fonts; keep a copy as text if it may
87
+ change.
88
+
57
89
  - **Clean paths.** Use the fewest anchors that describe the shape: anchors at extrema (top,
58
90
  bottom, left, right of a curve) and at true corners, handles horizontal or vertical at extrema,
59
91
  each handle about a third of its segment. Run `vector.removeRedundantPoints` after
@@ -61,7 +93,8 @@ Parameter details (gradient fields, effect parameters, width profiles, envelopes
61
93
  keeps the outline by refitting neighbours.
62
94
  - **Live corners over drawn arcs.** Give rectangles, polygons and stars a `cornerRadius`
63
95
  (`vector.addShape`) or set it with `vector.setCorners`; the anchors stay sharp and editable and
64
- radii shrink to fit short sides. Keep one radius scale across a design.
96
+ radii shrink to fit short sides. Keep one radius scale across a design. For app icons and UI
97
+ cards use `smoothing` (about 0.6) on `vector.setCorners` for continuous, squircle corners.
65
98
  - **Non-destructive first.** Live booleans (`vector.boolean` with `live: true`), path effects
66
99
  and envelopes keep the operands editable. A destructive boolean or Pathfinder replaces its
67
100
  inputs; do that last, and only when asked for flat output.
@@ -120,7 +153,7 @@ Parameter details (gradient fields, effect parameters, width profiles, envelopes
120
153
  | --- | --- |
121
154
  | `vector.addShape` | Add a `kind` `rect`, `ellipse`, `polygon` (`sides`, at least 3), `star` (`points`, `innerRatio` 0 to 1) or `line` at `x`, `y`, `width`, `height` (all required; for a line, width and height are the end's offset). `cornerRadius` and `cornerKind` give live corners; any style field and `name` apply. Uses the editor's default style for anything not given. Returns `{ id }`. |
122
155
  | `vector.addPath` | Add a path from SVG path data `d` (absolute or relative M, L, H, V, C, S, Q, T, A, Z; arcs and quadratics become cubics) in document units, with `name` and style fields; unset style comes from the editor defaults. Fails when `d` has no points. Returns `{ id }`. |
123
- | `vector.importSvg` | Add the shapes of SVG markup `svg` on top of the document, or replace the document with `replace: true`. Reads paths, basic shapes, groups, transforms, linear and radial gradients, and `clip-path` / `mask` references (as mask groups); skips text, images, foreign filters and `use`. |
156
+ | `vector.importSvg` | Add the shapes of SVG markup `svg` on top of the document, or replace the document with `replace: true`. Reads paths, basic shapes, groups, transforms, linear and radial gradients, `clip-path` / `mask` references (as mask groups) `<text>` (as point text: content, font, anchor and fill) and `<use>` (as instances of symbols made from what they refer to); skips images and foreign filters. |
124
157
  | `vector.delete` | Delete nodes (`ids`). |
125
158
 
126
159
  ### Style
@@ -179,6 +212,50 @@ arrays, to build or replace a stack in one call.
179
212
  | `vector.fillRegion` | Paint bucket: fill the area visible outlines enclose around `point` `[x, y]` with a new path in the current fill (islands stay holes). `gap` closes gaps up to that size between loose ends. Returns `{ id }`. |
180
213
  | `vector.outlineStroke` | Turn strokes into filled shapes of exactly the painted area (alignment, caps, joins, dashes, arrowheads, width profiles included), above a copy keeping the fill. Returns the new ids. |
181
214
 
215
+ ### Text
216
+
217
+ | Command | Use it to |
218
+ | --- | --- |
219
+ | `vector.listFonts` | See which families the text shaper can draw and whether it gives glyph outlines (`outlines`); read it before choosing fonts or calling `vector.createOutlines`. Returns `{ shaper, outlines, families }`. |
220
+ | `vector.addText` | Add point text (`x`, `y`: the first baseline's start), area text (`x`, `y`, `width`, `height`: wrapped in the box) or text along `path` (SVG path data, with `start`, `end`, `flip`, `orientation`, `baseline`). `font`, `paragraph`, `name`, `fill`, `stroke`, `strokeWidth` optional; black, no stroke by default. Returns the text summary (`id`, lines, bounds, overflow). |
221
+ | `vector.getText` | Read a text node: text, base font, runs as `{ start, end, style }`, paragraph, frame, fill, stroke, laid-out lines, document bounds, `overflow`, `outlines`, shaper and missing fonts. Call before editing ranges. |
222
+ | `vector.setText` | Replace `[start, end)` of the text with `text` (default the whole text; `start` = `end` inserts, `""` deletes). Inserted characters take the style before them. Returns the summary. |
223
+ | `vector.setTextStyle` | Set character fields (`fontFamily`, `fontWeight`, `italic`, `fontSize`, `tracking`, `leading`, `baselineShift`, `underline`, `strikethrough`; null clears) and `fill` over `[start, end)`; the whole text (default) changes the base font and the node's fill. Returns the summary with runs. |
224
+ | `vector.setParagraphStyle` | Set `align` (left, center, right, justify), `autoLeading`, indents and spacing for the node's paragraphs. Returns the summary. |
225
+ | `vector.setTextFrame` | Switch between point, area and path text (`kind`), resize an area (`width`, `height`), move the origin (`x`, `y`) or change path options. Returns the summary. |
226
+ | `vector.typeOnPath` | Turn path node `pathId` into text along it (it takes the path's place and drops its paint) with `text`, `font`, `paragraph` (`align` places it along the path) and path options. Returns the summary with the new id. |
227
+ | `vector.createOutlines` | Turn text (`ids`, or text inside groups; default the selection) into paths of its glyph outlines, keeping paint, effects, filters, placement and id. Needs `outlines: true` from `vector.listFonts`. Returns `{ ids }`. |
228
+
229
+ Fill, stroke, stacked layers, filters, blend modes, transforms, masks and blends work on text as
230
+ on paths (`vector.setStyle`, `vector.addFill`, `vector.addFilter`, `vector.transform`, …). Path
231
+ effects, envelopes and booleans act on its glyph outlines, so they need an outline shaper; with
232
+ the browser's shaper they are kept but not drawn until outlines are available.
233
+
234
+ ### Symbols and instances
235
+
236
+ | Command | Use it to |
237
+ | --- | --- |
238
+ | `vector.listSymbols` | Read the symbols: id, name, the art's node ids (for `hidden`), bounds around the registration point, instance ids, and which symbol is being edited. |
239
+ | `vector.makeSymbol` | Turn objects sharing a parent (`ids`, default the selection) into a symbol, registration point at their centre, with an instance in their place. Returns `{ symbolId, instanceId, symbol }`. |
240
+ | `vector.placeSymbol` | Place an instance of `symbolId` with its registration point at `x`, `y` (default the artboard centre) or through `transform` (scale, rotate and skew allowed). Returns `{ id }`. |
241
+ | `vector.setInstanceOptions` | Change instances: `scaleStrokes` (default true), `fill` / `stroke` overrides painting all of the art (null clears), `hidden` art node ids. Returns the instances as listed. |
242
+ | `vector.replaceSymbol` | Make instances (`ids`) draw `symbolId` instead, keeping placement and colour overrides. Returns `{ ids }`. |
243
+ | `vector.detachInstance` | Break instances' link: they become plain groups of their art as drawn. Returns `{ ids }`. |
244
+ | `vector.editSymbol` | Enter isolation mode on a symbol (its id or an instance's): every command then edits the art in symbol space (registration point at 0, 0). Returns `{ symbolId, nodes }`. |
245
+ | `vector.exitSymbolEdit` | Leave isolation mode; the edited symbol's instances update. Returns `{ symbolId }`. |
246
+ | `vector.renameSymbol` | Rename `symbolId` to `name`. Returns the symbol summary. |
247
+ | `vector.deleteSymbol` | Delete `symbolId`; fails while instances use it unless `detach: true` turns them into groups. |
248
+
249
+ ### Constraints
250
+
251
+ | Command | Use it to |
252
+ | --- | --- |
253
+ | `vector.listConstraints` | Read every constraint (id, kind, refs with anchor ids, value, name, evaluated value, satisfied), the parameters, the degrees of freedom left (`dof`, `status`, `redundant`, per path) and conflicts from the last solve. |
254
+ | `vector.addConstraint` | Add a constraint (`kind`, `refs` by `{ nodeId, subpath, index }` or anchor id, with `segment`, `handle` or `circle`; `value` / `name` / `axis` for dimensions) and solve. Fails, changing nothing, on conflicts or redundancy, naming the others. Returns `{ id, constraint }`. |
255
+ | `vector.removeConstraint` | Remove constraints by `ids`; geometry stays. |
256
+ | `vector.setConstraintValue` | Set a dimension's value (by id or name) or a named parameter (`null` removes a parameter); values are expressions. Returns the summary. |
257
+ | `vector.autoConstrain` | Add what a sketch nearly satisfies (tolerances `distanceTolerance`, `angleTolerance`), skipping redundant or conflicting candidates. Returns `{ ids }`. |
258
+
182
259
  ### Masks and clipping
183
260
 
184
261
  | Command | Use it to |
@@ -233,7 +310,7 @@ contents whole while you work, without losing the mask.
233
310
  | Command | Use it to |
234
311
  | --- | --- |
235
312
  | `vector.setEditing` | Set editing behaviour: `keepShapeOnDelete`, `curvatureMoves`, `pencilSmoothing` (0 to 1), `recognizeShapes`, `eraserSize`, `pencilPressure` (pen, all, off). Returns the settings. |
236
- | `vector.setTool` | Switch the interactive tool (select, direct, pen, pencil, rect, ellipse, polygon, star, line, scissors, knife, eraser, width, shapeBuilder, paintBucket, blend, gradient, envelope). Only for handing the canvas to the user in a given mode; commands do not need a tool. |
313
+ | `vector.setTool` | Switch the interactive tool (select, direct, pen, pencil, rect, ellipse, polygon, star, line, scissors, knife, eraser, width, shapeBuilder, paintBucket, blend, gradient, envelope, type, typePath). Only for handing the canvas to the user in a given mode; commands do not need a tool. |
237
314
 
238
315
  ## Pitfalls
239
316
 
@@ -267,5 +344,25 @@ contents whole while you work, without losing the mask.
267
344
  - Filters and blend modes are drawn, not geometry: bounds, snapping, booleans and Outline Stroke
268
345
  ignore them. A node's blend mode and filter isolate its layers, so layer blend modes inside a
269
346
  node with a filter mix only with the node's own layers. Background blur is not available.
347
+ - Text positions are code units: an emoji or other character outside the Basic Multilingual
348
+ Plane counts 2. Take ranges from `vector.getText` rather than counting characters yourself.
349
+ - Scaling text scales its font size (by the square root of the area change) and an area box per
350
+ axis; it is never stretched. Skew is not kept on text.
351
+ - Without an outline shaper (`vector.listFonts` `outlines: false`), text exports as SVG `<text>`
352
+ positioned character by character; other viewers need the same fonts installed. Create Outlines
353
+ (with an outline shaper) when the SVG must look the same everywhere.
354
+ - An instance's `transform` may scale; its art's strokes scale with it unless `scaleStrokes: false`.
355
+ Transform commands act on the instance, not the art: edit the art with `vector.editSymbol`.
356
+ - While a symbol is being edited (`vector.listSymbols` `editing`), `vector.list` lists its art, not
357
+ the document. Always finish with `vector.exitSymbolEdit`. A symbol cannot contain an instance
358
+ of itself (directly or through another symbol).
359
+ - Constraint refs to anchors give them stable ids; later anchor edits keep them, but rebuilding a
360
+ path (booleans, Outline Stroke, Expand) drops them, and constraints on missing anchors are
361
+ removed. `segment` means the straight line between two anchors, even when the segment is curved;
362
+ `tangent` / `smooth` on one anchor act on its handles.
363
+ - After any edit the solver moves the rest of the geometry to keep the constraints; the anchors
364
+ you just moved move least. A fixed or fully constrained part will not move: remove or change a
365
+ constraint first.
366
+ - `vector.typeOnPath` consumes the path. Duplicate it first if the path should stay visible.
270
367
  - A command that fails changes nothing; read its message (for example "Nothing is selected; pass
271
368
  ids.") and retry with the missing input.
@@ -1 +0,0 @@
1
- "use strict";//# sourceMappingURL=chunk-FTBZPI77.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/chunk-FTBZPI77.cjs"],"names":[],"mappings":"AAAA","file":"/home/runner/work/editor/editor/packages/core/dist/chunk-FTBZPI77.cjs"}
@@ -1 +0,0 @@
1
- //# sourceMappingURL=chunk-VKK5VS2T.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}