@pixerate/editor 0.103.0 → 0.105.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.
Files changed (34) hide show
  1. package/THIRD_PARTY_NOTICES.md +2 -0
  2. package/dist/{chunk-NQLQ2JOI.js → chunk-4X5MIVCE.js} +1155 -295
  3. package/dist/chunk-4X5MIVCE.js.map +1 -0
  4. package/dist/{chunk-VSMM7B3P.cjs → chunk-ZID226ZG.cjs} +1169 -309
  5. package/dist/chunk-ZID226ZG.cjs.map +1 -0
  6. package/dist/{compositor-CTMU-B1t.d.cts → compositor-Bc3PNP3C.d.cts} +1 -1
  7. package/dist/{compositor-B1ZrKR89.d.ts → compositor-BsYYllvR.d.ts} +1 -1
  8. package/dist/{export-X9rY79p0.d.cts → export-_m3Es4ru.d.cts} +19 -4
  9. package/dist/{export-BLpMyCYg.d.ts → export-iMrop3Jm.d.ts} +19 -4
  10. package/dist/image-editor/index.cjs +523 -539
  11. package/dist/image-editor/index.cjs.map +1 -1
  12. package/dist/image-editor/index.d.cts +16 -8
  13. package/dist/image-editor/index.d.ts +16 -8
  14. package/dist/image-editor/index.js +37 -53
  15. package/dist/image-editor/index.js.map +1 -1
  16. package/dist/{mixer-DGcH6HLu.d.cts → mixer-C1M1Nua_.d.cts} +1 -1
  17. package/dist/{mixer-DG_9Yu2W.d.ts → mixer-Cu5a9Bzd.d.ts} +1 -1
  18. package/dist/paint/index.d.cts +2 -2
  19. package/dist/paint/index.d.ts +2 -2
  20. package/dist/paint-editor/index.cjs +1531 -128
  21. package/dist/paint-editor/index.cjs.map +1 -1
  22. package/dist/paint-editor/index.d.cts +461 -14
  23. package/dist/paint-editor/index.d.ts +461 -14
  24. package/dist/paint-editor/index.js +1479 -76
  25. package/dist/paint-editor/index.js.map +1 -1
  26. package/dist/raster/index.d.cts +4 -4
  27. package/dist/raster/index.d.ts +4 -4
  28. package/dist/{surface-AfHCH7BR.d.cts → surface-BBJN9wxg.d.cts} +1 -1
  29. package/dist/{surface-AfHCH7BR.d.ts → surface-BBJN9wxg.d.ts} +1 -1
  30. package/package.json +1 -1
  31. package/skills/pixerate-image-editor/SKILL.md +14 -5
  32. package/skills/pixerate-paint-editor/SKILL.md +49 -8
  33. package/dist/chunk-NQLQ2JOI.js.map +0 -1
  34. package/dist/chunk-VSMM7B3P.cjs.map +0 -1
@@ -1,9 +1,9 @@
1
- import { R as RasterLayer, a as RasterGroupLayer, b as RasterPixelLayer, C as CompositorLayer, c as RasterCompositor, d as RasterSources, L as LayerLocks } from '../compositor-B1ZrKR89.js';
2
- import { B as BrushSettings, M as MixerSettings, C as CoverageMap, D as Dab, S as StrokePoint, a as MixerLoad } from '../mixer-DG_9Yu2W.js';
3
- export { b as MAX_BRUSH_SIZE } from '../mixer-DG_9Yu2W.js';
4
- import { R as RasterRect, a as TiledSurface, B as BlendMode } from '../surface-AfHCH7BR.js';
5
- export { b as Tile } from '../surface-AfHCH7BR.js';
6
- import { I as ImageEditorAssetStore, S as SerializedLayer, P as PsdExportResult } from '../export-BLpMyCYg.js';
1
+ import { R as RasterLayer, a as RasterGroupLayer, b as RasterPixelLayer, C as CompositorLayer, c as RasterCompositor, d as RasterSources, L as LayerLocks } from '../compositor-BsYYllvR.js';
2
+ import { B as BrushSettings, M as MixerSettings, C as CoverageMap, D as Dab, S as StrokePoint, a as MixerLoad } from '../mixer-Cu5a9Bzd.js';
3
+ export { b as MAX_BRUSH_SIZE } from '../mixer-Cu5a9Bzd.js';
4
+ import { R as RasterRect, a as TiledSurface, B as BlendMode } from '../surface-BBJN9wxg.js';
5
+ export { b as BLEND_MODES, c as Tile, i as isBlendMode } from '../surface-BBJN9wxg.js';
6
+ import { I as ImageEditorAssetStore, S as SerializedLayer, P as PsdExportResult } from '../export-iMrop3Jm.js';
7
7
  import { a as AgentToolProvider } from '../registry-D-4fit9C.js';
8
8
  import '../types-CwMPoRMU.js';
9
9
  import '../warp-CWHZzE3x.js';
@@ -203,12 +203,14 @@ interface PaintBrush {
203
203
  }
204
204
  declare function normalizeDynamics(raw?: Partial<BrushDynamics>, base?: BrushDynamics): BrushDynamics;
205
205
  declare const BRUSH_ID: RegExp;
206
- /** A brush with every field in range; missing fields come from `base` (default: a plain round dab brush). */
207
- declare function normalizePaintBrush(raw?: Partial<Omit<PaintBrush, 'tip' | 'dynamics' | 'mixer'>> & {
206
+ /** Brush settings to change or read: any subset of a brush, its tip, dynamics and mixer. */
207
+ type PaintBrushInput = Partial<Omit<PaintBrush, 'tip' | 'dynamics' | 'mixer'>> & {
208
208
  tip?: Partial<BrushSettings>;
209
209
  dynamics?: Partial<BrushDynamics>;
210
210
  mixer?: Partial<MixerSettings>;
211
- }, base?: PaintBrush): PaintBrush;
211
+ };
212
+ /** A brush with every field in range; missing fields come from `base` (default: a plain round dab brush). */
213
+ declare function normalizePaintBrush(raw?: PaintBrushInput, base?: PaintBrush): PaintBrush;
212
214
  /** The built-in brush library. */
213
215
  declare const BUILTIN_BRUSHES: readonly PaintBrush[];
214
216
  declare const DEFAULT_BRUSH_ID = "pencil-6b";
@@ -517,6 +519,8 @@ interface StrokeSessionInit {
517
519
  /** The stroke's extra copies as affine maps (recorded with the stroke); `copies` holds all of them as engine copies. */
518
520
  affineCopies: readonly Affine[];
519
521
  copies: readonly StrokeCopy[];
522
+ /** Applied to each raw sample before it is painted and recorded (perspective snapping). */
523
+ inputFilter?: (p: PenPoint) => PenPoint;
520
524
  /** Mixer engine: the brush load at the start of the stroke. */
521
525
  mixerLoad: MixerLoad;
522
526
  }
@@ -657,6 +661,9 @@ interface SerializeOptions {
657
661
  strokes?: boolean;
658
662
  /** Later-phase fields to write as they were read. */
659
663
  extensions?: PaintDocumentExtensions;
664
+ /** Painting guides (symmetry, perspective, tracing and reference images as PNG data URLs). */
665
+ guides?: unknown;
666
+ swatches?: unknown;
660
667
  }
661
668
  /** The document as JSON with inline PNG data URLs. */
662
669
  declare function serializePaintDocument(state: PaintDocumentState, options?: SerializeOptions): SerializedPaintDocument;
@@ -665,6 +672,9 @@ declare function serializePaintDocumentExternal(state: PaintDocumentState, store
665
672
  interface LoadedPaintDocument {
666
673
  state: PaintDocumentState;
667
674
  extensions: PaintDocumentExtensions;
675
+ /** The saved guides and swatches, as stored (the controller validates them). */
676
+ guides?: unknown;
677
+ swatches?: unknown;
668
678
  /** What could not be read (the rest still loads). */
669
679
  problems: string[];
670
680
  }
@@ -815,6 +825,17 @@ type ImageEncoder = (image: {
815
825
  }, format: 'image/jpeg' | 'image/webp', quality: number) => Promise<Uint8Array>;
816
826
  /** An encoder through `OffscreenCanvas`, or `null` where there is none (Node, tests). */
817
827
  declare function defaultImageEncoder(): ImageEncoder | null;
828
+ /**
829
+ * Decodes an image file (PNG, JPEG, WebP, … whatever the browser reads) into RGBA pixels through
830
+ * `createImageBitmap` and an `OffscreenCanvas`. Browser and Worker only.
831
+ */
832
+ declare function decodeImageBlob(blob: Blob): Promise<{
833
+ width: number;
834
+ height: number;
835
+ data: Uint8ClampedArray;
836
+ }>;
837
+ /** Saves bytes as a file through a temporary link. Browser only. */
838
+ declare function downloadBytes(bytes: Uint8Array | string, fileName: string, mimeType: string): void;
818
839
 
819
840
  /**
820
841
  * Brush thumbnails: a test stroke (an S-curve with pressure swelling and fading, at a steady
@@ -837,6 +858,213 @@ declare function renderBrushThumbnail(brush: PaintBrush, options: {
837
858
  color: string;
838
859
  }): BrushThumbnail;
839
860
 
861
+ /**
862
+ * Painting guides: mirror and kaleidoscope symmetry (every stroke paints its reflected or rotated
863
+ * copies as one stroke), perspective guides with one to three vanishing points that strokes snap
864
+ * to, a tracing image under the paint (optionally the source of the stroke colour), and floating
865
+ * reference images to pick colours from. Geometry only: no pixels, no DOM.
866
+ */
867
+
868
+ type SymmetryMode = 'off' | 'vertical' | 'horizontal' | 'dual' | 'diagonal' | 'kaleidoscope';
869
+ declare const SYMMETRY_MODES: readonly SymmetryMode[];
870
+ interface SymmetryGuide {
871
+ mode: SymmetryMode;
872
+ /** Centre of the axes in document pixels (default the canvas centre). */
873
+ center: {
874
+ x: number;
875
+ y: number;
876
+ };
877
+ /** Turns the axes, degrees clockwise (default 0). */
878
+ rotation: number;
879
+ /** Kaleidoscope: number of segments, 2..32 (default 6). */
880
+ segments: number;
881
+ /** Kaleidoscope: reflect every other segment as well as rotating (default true), like a real kaleidoscope. */
882
+ mirror: boolean;
883
+ }
884
+ interface Point2 {
885
+ x: number;
886
+ y: number;
887
+ }
888
+ interface PerspectiveGuide {
889
+ enabled: boolean;
890
+ /** One to three vanishing points in document pixels (they may lie outside the canvas). */
891
+ points: Point2[];
892
+ /** Strokes snap to the guide lines (default true when enabled). */
893
+ snap: boolean;
894
+ }
895
+ /** A view-only image under the paint, placed over the canvas. */
896
+ interface TracingImage {
897
+ /** PNG or JPEG data URL. */
898
+ src: string;
899
+ /** Natural size of the image in pixels. */
900
+ width: number;
901
+ height: number;
902
+ /** Where it is drawn, in document pixels (default: fitted to the canvas). */
903
+ rect: {
904
+ x: number;
905
+ y: number;
906
+ width: number;
907
+ height: number;
908
+ };
909
+ /** 0..1 (default 0.35). */
910
+ opacity: number;
911
+ visible: boolean;
912
+ /** Strokes without an explicit colour take the tracing image's colour where they start. */
913
+ sampleColors: boolean;
914
+ }
915
+ /** A floating image to look at and pick colours from (shown beside the canvas, never painted). */
916
+ interface ReferenceImage {
917
+ id: string;
918
+ name: string;
919
+ src: string;
920
+ width: number;
921
+ height: number;
922
+ /** Where the panel floats over the viewport, in screen pixels, and its displayed width. */
923
+ x: number;
924
+ y: number;
925
+ displayWidth: number;
926
+ visible: boolean;
927
+ }
928
+ interface PaintGuides {
929
+ symmetry: SymmetryGuide;
930
+ perspective: PerspectiveGuide;
931
+ tracing: TracingImage | null;
932
+ references: ReferenceImage[];
933
+ }
934
+ declare function defaultGuides(canvas: {
935
+ width: number;
936
+ height: number;
937
+ }): PaintGuides;
938
+ declare const MAX_COORDINATE = 1000000;
939
+ declare function normalizeSymmetry(raw: Partial<SymmetryGuide> | Record<string, unknown>, base: SymmetryGuide): SymmetryGuide;
940
+ /** The axis directions of a mirror mode (radians), as photocraft's `directions`. */
941
+ declare function symmetryAxisAngles(g: SymmetryGuide): number[];
942
+ /**
943
+ * The extra copies a stroke paints under the symmetry guide, as affine maps (the stroke itself is
944
+ * not included). Vertical, horizontal and diagonal: one reflection. Dual: two reflections and the
945
+ * point reflection. Kaleidoscope: rotations by 360°/n, and with `mirror` the reflections between them.
946
+ */
947
+ declare function symmetryCopies(g: SymmetryGuide): Affine[];
948
+ /** The axes clipped to the canvas, for drawing (photocraft's `segments`). */
949
+ declare function symmetryAxisSegments(g: SymmetryGuide, size: {
950
+ width: number;
951
+ height: number;
952
+ }): Array<[Point2, Point2]>;
953
+ declare function normalizePerspective(raw: Partial<PerspectiveGuide> | Record<string, unknown>, base: PerspectiveGuide): PerspectiveGuide;
954
+ /** The line families a perspective guide snaps to at `p`: unit directions. */
955
+ declare function perspectiveDirections(g: PerspectiveGuide, p: Point2): Point2[];
956
+ /** How far the pen must move before a stroke chooses its perspective line (document pixels). */
957
+ declare const SNAP_DECIDE_DISTANCE = 6;
958
+ /**
959
+ * Snaps one stroke to a perspective guide: once the pen has moved `SNAP_DECIDE_DISTANCE` from
960
+ * where it started, the stroke picks the guide line through its start that best matches its
961
+ * direction, and every sample after that is projected onto the line.
962
+ */
963
+ declare class PerspectiveSnapper {
964
+ private readonly guide;
965
+ private start;
966
+ private dir;
967
+ constructor(guide: PerspectiveGuide);
968
+ /** The line the stroke follows, once chosen. */
969
+ get line(): {
970
+ from: Point2;
971
+ direction: Point2;
972
+ } | null;
973
+ snap<P extends Point2>(p: P): P;
974
+ }
975
+ /** Guide lines to draw for a perspective guide: rays from each vanishing point through the canvas. */
976
+ declare function perspectiveGuideLines(g: PerspectiveGuide, size: {
977
+ width: number;
978
+ height: number;
979
+ }, count?: number): Array<[Point2, Point2]>;
980
+
981
+ /**
982
+ * Colour for painters: hex / RGB / HSV / HSL conversions, a colour wheel that is either the
983
+ * painter's red-yellow-blue wheel (where red's complement is green) or the RGB wheel (where it is
984
+ * cyan), harmonies on it, and swatches.
985
+ */
986
+ type Rgb255 = [number, number, number];
987
+ declare function parseHexColor(value: unknown, what?: string): Rgb255;
988
+ declare function rgbToHexColor(r: number, g: number, b: number): string;
989
+ /** RGB 0..255 → HSV with h in degrees 0..360, s and v 0..1. */
990
+ declare function rgbToHsv(r: number, g: number, b: number): {
991
+ h: number;
992
+ s: number;
993
+ v: number;
994
+ };
995
+ declare function hsvToRgb(h: number, s: number, v: number): Rgb255;
996
+ /** RGB 0..255 → HSL with h in degrees, s and l 0..1. */
997
+ declare function rgbToHsl(r: number, g: number, b: number): {
998
+ h: number;
999
+ s: number;
1000
+ l: number;
1001
+ };
1002
+ declare function hslToRgb(h: number, s: number, l: number): Rgb255;
1003
+ /** Which wheel hues live on: `ryb` (painter's: red ↔ green complements) or `rgb` (red ↔ cyan). */
1004
+ type ColorWheel = 'ryb' | 'rgb';
1005
+ /** A wheel angle (degrees) → an RGB hue. */
1006
+ declare function wheelToHue(angle: number, wheel: ColorWheel): number;
1007
+ /** An RGB hue → its wheel angle (degrees). */
1008
+ declare function hueToWheel(hue: number, wheel: ColorWheel): number;
1009
+ type HarmonyKind = 'complementary' | 'split-complementary' | 'analogous' | 'triadic' | 'tetradic' | 'square' | 'monochromatic';
1010
+ declare const HARMONY_KINDS: readonly HarmonyKind[];
1011
+ interface HarmonyColor {
1012
+ color: string;
1013
+ /** Position on the wheel, degrees. */
1014
+ angle: number;
1015
+ }
1016
+ /**
1017
+ * The colours of a harmony around `base`, the base first. Hue-based harmonies keep the base's
1018
+ * saturation and value and turn it around the wheel; `analogous` spreads by `spread` degrees
1019
+ * (default 30); `monochromatic` keeps the hue and steps the lightness.
1020
+ */
1021
+ declare function colorHarmony(base: string, kind: HarmonyKind, options?: {
1022
+ wheel?: ColorWheel;
1023
+ spread?: number;
1024
+ }): HarmonyColor[];
1025
+ interface Swatch {
1026
+ color: string;
1027
+ name?: string;
1028
+ }
1029
+ /** A starting palette of our own generic paint colours. */
1030
+ declare const DEFAULT_SWATCHES: readonly Swatch[];
1031
+
1032
+ /**
1033
+ * The mixing pad: a small canvas beside the painting to put paint on, smear it with a palette
1034
+ * knife and pick the mixed colour from. Paint is laid with the Mixer Brush in pigment mode and the
1035
+ * knife is a pigment smudge, so blue and yellow pushed together make green. It is scratch space:
1036
+ * not part of the painting and not in its undo history.
1037
+ */
1038
+
1039
+ type MixingPadTool = 'paint' | 'knife';
1040
+ declare const MIXING_PAD_WIDTH = 360;
1041
+ declare const MIXING_PAD_HEIGHT = 220;
1042
+ declare class MixingPad {
1043
+ readonly width = 360;
1044
+ readonly height = 220;
1045
+ private surface;
1046
+ /** Increments on every change (for redrawing). */
1047
+ version: number;
1048
+ /** Paints (`paint`, with `color`) or smears (`knife`) through `points` in pad pixels. */
1049
+ stroke(points: readonly PenPoint[], options: {
1050
+ tool: MixingPadTool;
1051
+ color: string;
1052
+ size?: number;
1053
+ }): {
1054
+ bounds: {
1055
+ x: number;
1056
+ y: number;
1057
+ width: number;
1058
+ height: number;
1059
+ } | null;
1060
+ };
1061
+ /** The average colour within `radius` pixels of (x, y), or `null` where the pad is bare. */
1062
+ pick(x: number, y: number, radius?: number): string | null;
1063
+ clear(): void;
1064
+ /** The pad over white, `width × height` RGBA. */
1065
+ pixels(): Uint8ClampedArray;
1066
+ }
1067
+
840
1068
  /**
841
1069
  * `PaintEditorController`: the natural-media paint editor's state, framework-free.
842
1070
  *
@@ -887,6 +1115,12 @@ interface PaintEditorState {
887
1115
  canRedo: boolean;
888
1116
  undoLabel: string | null;
889
1117
  redoLabel: string | null;
1118
+ /** Mirror / kaleidoscope, perspective, tracing image and reference images (saved with the document, not undo steps). */
1119
+ guides: PaintGuides;
1120
+ /** The colour swatches (saved with the document, not undo steps). */
1121
+ swatches: readonly Swatch[];
1122
+ /** Changes whenever the mixing pad does. */
1123
+ mixingPadVersion: number;
890
1124
  /** A stroke is in progress. */
891
1125
  stroking: boolean;
892
1126
  /** Increments on every change. */
@@ -916,11 +1150,18 @@ interface PaintStrokeInit {
916
1150
  /** Default the active brush. */
917
1151
  brushId?: string;
918
1152
  /** Changes to the brush for this stroke only. */
919
- brush?: Parameters<typeof normalizePaintBrush>[0];
1153
+ brush?: PaintBrushInput;
920
1154
  /** Jitter seed (default: a new one per stroke). */
921
1155
  seed?: number;
922
- /** Extra copies of the stroke as affine maps (mirror / kaleidoscope). */
1156
+ /** Extra copies of the stroke as affine maps (default: the symmetry guide's). */
923
1157
  copies?: readonly Affine[];
1158
+ /** Snap the samples to the perspective guide (default true for `beginStroke`, false for `paintStroke`). */
1159
+ snapToPerspective?: boolean;
1160
+ /** Where the stroke starts, for taking its colour from the tracing image before the first sample (`beginStroke`). */
1161
+ start?: {
1162
+ x: number;
1163
+ y: number;
1164
+ };
924
1165
  }
925
1166
  interface LayerUpdate {
926
1167
  name?: string;
@@ -988,6 +1229,13 @@ declare class PaintEditorController {
988
1229
  private dirty;
989
1230
  private seedCounter;
990
1231
  private strokeCounter;
1232
+ private guides;
1233
+ private swatches;
1234
+ private readonly pad;
1235
+ private tracingVersion;
1236
+ private tracingCache;
1237
+ private readonly imagePixels;
1238
+ private nextReferenceId;
991
1239
  private readonly viewCompositor;
992
1240
  private readonly exportCompositor;
993
1241
  constructor(options?: PaintEditorOptions);
@@ -1072,12 +1320,12 @@ declare class PaintEditorController {
1072
1320
  */
1073
1321
  setBrush(update?: {
1074
1322
  id?: string;
1075
- } & Omit<Parameters<typeof normalizePaintBrush>[0], 'id'>): PaintBrush;
1323
+ } & Omit<PaintBrushInput, 'id'>): PaintBrush;
1076
1324
  /** Saves the active brush (or `brush`) into the user library under `id` / `name`. */
1077
1325
  saveBrush(options: {
1078
1326
  id: string;
1079
1327
  name?: string;
1080
- brush?: Parameters<typeof normalizePaintBrush>[0];
1328
+ brush?: PaintBrushInput;
1081
1329
  }): PaintBrush;
1082
1330
  removeBrush(id: string): void;
1083
1331
  /** The user's saved brushes. */
@@ -1154,6 +1402,8 @@ declare class PaintEditorController {
1154
1402
  private currentLayers;
1155
1403
  /** View-only layers between the paper and the paint (the painting guides add the tracing image). */
1156
1404
  protected underlayLayers(): ViewLayer[];
1405
+ /** The tracing image resampled onto the canvas (cached until it or the canvas changes). */
1406
+ private tracingNode;
1157
1407
  private renderLayers;
1158
1408
  /**
1159
1409
  * Composites the viewport stack (paper, view-only underlays, layers with the live stroke) tile by
@@ -1210,10 +1460,79 @@ declare class PaintEditorController {
1210
1460
  width: number;
1211
1461
  height: number;
1212
1462
  };
1463
+ private setGuides;
1464
+ /** Mirror and kaleidoscope painting: every new stroke paints its copies too (one stroke, one undo step). */
1465
+ setSymmetry(update: Partial<SymmetryGuide>): SymmetryGuide;
1466
+ /** Perspective guides with one to three vanishing points that pointer strokes snap to. */
1467
+ setPerspective(update: Partial<PerspectiveGuide>): PerspectiveGuide;
1468
+ /** An image from a PNG (base64 or data URL) or from RGBA pixels, stored as a PNG data URL. */
1469
+ private importImage;
1470
+ private decodeImage;
1471
+ /**
1472
+ * Puts an image under the paint to trace over: view only, never in exports. Placed to fit the
1473
+ * canvas unless `rect` is given; `sampleColors` makes strokes take their colour from it.
1474
+ */
1475
+ setTracingImage(options: {
1476
+ png?: string;
1477
+ pixels?: PixelImage;
1478
+ rect?: TracingImage['rect'];
1479
+ opacity?: number;
1480
+ sampleColors?: boolean;
1481
+ }): TracingImage;
1482
+ /** Changes the tracing image's opacity, visibility, placement or colour sampling. */
1483
+ updateTracing(update: Partial<Pick<TracingImage, 'opacity' | 'visible' | 'sampleColors' | 'rect'>>, base?: TracingImage | null): TracingImage;
1484
+ clearTracingImage(): void;
1485
+ /** The tracing image's colour at a document pixel, or `null` outside it or where it is clear. */
1486
+ sampleTracingColor(x: number, y: number): string | null;
1487
+ /** Adds a floating reference image to look at and pick colours from. */
1488
+ addReferenceImage(options: {
1489
+ png?: string;
1490
+ pixels?: PixelImage;
1491
+ name?: string;
1492
+ x?: number;
1493
+ y?: number;
1494
+ displayWidth?: number;
1495
+ }): ReferenceImage;
1496
+ private requireReference;
1497
+ /** Moves, resizes, renames, shows or hides a reference image. */
1498
+ updateReferenceImage(id: string, update: Partial<Pick<ReferenceImage, 'name' | 'x' | 'y' | 'displayWidth' | 'visible'>>): ReferenceImage;
1499
+ removeReferenceImage(id: string): void;
1500
+ /** A reference image's pixels (decoded once). */
1501
+ referencePixels(id: string): PixelImage;
1502
+ /** The colour of a reference image at its pixel (x, y). */
1503
+ pickReferenceColor(id: string, x: number, y: number): string;
1504
+ private readGuides;
1505
+ /** Colour harmonies around a colour (default the paint colour) on the painter's RYB wheel or the RGB wheel. */
1506
+ harmony(kind: HarmonyKind, options?: {
1507
+ color?: string;
1508
+ wheel?: ColorWheel;
1509
+ spread?: number;
1510
+ }): HarmonyColor[];
1511
+ addSwatch(color: string, name?: string): Swatch[];
1512
+ removeSwatch(index: number): Swatch[];
1513
+ setSwatches(swatches: readonly Swatch[]): Swatch[];
1514
+ get mixingPad(): MixingPad;
1515
+ /** Paints (`paint`) or smears with the palette knife (`knife`) on the mixing pad, in pad pixels. */
1516
+ mixingPadStroke(points: readonly PenPoint[], options?: {
1517
+ tool?: MixingPadTool;
1518
+ color?: string;
1519
+ size?: number;
1520
+ }): {
1521
+ bounds: RasterRect | null;
1522
+ };
1523
+ /** Picks the mixed colour on the pad (averaged over `radius` px); with `setColor` it becomes the paint colour. */
1524
+ mixingPadPick(x: number, y: number, options?: {
1525
+ radius?: number;
1526
+ setColor?: boolean;
1527
+ }): string | null;
1528
+ clearMixingPad(): void;
1213
1529
  /** A layer's tile surface (live while stroking), for thumbnails. */
1214
1530
  layerSurface(id: string): TiledSurface | null;
1215
1531
  }
1216
1532
 
1533
+ /** Largest tracing or reference image side, in pixels. */
1534
+ declare const MAX_IMAGE_SIDE = 8192;
1535
+
1217
1536
  /**
1218
1537
  * Agent tools for the paint editor (`createPaintEditorToolProvider`). Strict schemas, every
1219
1538
  * argument described, all four annotations. Each write is one undo step (or editor state, said so)
@@ -1230,4 +1549,132 @@ interface PaintEditorToolProviderOptions {
1230
1549
  /** Agent tools for one paint editor. */
1231
1550
  declare function createPaintEditorToolProvider(controller: PaintEditorController, options?: PaintEditorToolProviderOptions): AgentToolProvider;
1232
1551
 
1233
- export { type Affine, BRUSH_ID, BRUSH_ROTATIONS, BUILTIN_BRUSHES, type BrushDynamics, type BrushLibraryFile, type BrushRotation, type BrushThumbnail, DEFAULT_BRUSH_ID, DEFAULT_DPI, DEFAULT_DYNAMICS, DEFAULT_PAPER, DEFAULT_VIEW, DryStroke, type DryStrokeOptions, type FillLayerOptions, GRAIN_PATTERNS, GRAIN_TILE, type GrainPattern, type GrainTuning, type Hand, IDENTITY_AFFINE, IDENTITY_COPY, type ImageEncoder, type ImageFormat, type ImageNode, type LayerUpdate, type Linear2, type LoadedPaintDocument, MAX_CANVAS_SIDE, MAX_ZOOM, MIN_CANVAS_SIDE, MIN_ZOOM, PAINT_BRUSH_CATEGORIES, PAINT_BRUSH_ENGINES, PAINT_FORMAT, PAINT_SCHEMA_VERSION, PAPER_FAMILIES, PAPER_LIBRARY, PAPER_NODE_ID, type PaintAssetStore, type PaintBrush, type PaintBrushCategory, type PaintBrushEngine, type PaintCanvas, PaintDocumentError, type PaintDocumentExtensions, type PaintDocumentState, PaintEditorController, PaintEditorError, type PaintEditorOptions, type PaintEditorState, type PaintEditorToolProviderOptions, type PaintGroupLayer, type PaintLayer, type PaintLayerInfo, type PaintPixelLayer, type PaintStrokeInit, type PaintStrokeResult, PaintStrokeSession, type PaintView, type PaperFamily, PaperGrain, type PaperNode, type PaperSettings, type PaperTiling, type PenDab, PenDabWalker, type PenPoint, PenSmoother, type PixelImage, type PointerSample, type RecordedStroke, type RenderOptions, type ReplayOptions, type SerializeOptions, type SerializedPaintDocument, type StrokeCopy, type StrokeSessionHost, type StrokeSessionInit, type StrokeVariation, TRACING_NODE_ID, TileableNoise, type ViewLayer, affineCopy, applyAffine, brushToJSON, clonePaper, composeAffine, createPaintCompositor, createPaintDocument, createPaintEditorToolProvider, createPaintLayer, defaultImageEncoder, deserializePaintDocument, deserializePaintDocumentAsync, documentExtensions, documentToScreen, downsampleRgba, fitZoom, grainCatch, grainHeights, hash2, hexToRgb8, invertAffine, isPalmTouch, mapPenPoint, mix32, mixerLoadForStroke, mixerTip, newPaintLayerId, normalizeBrushStroke, normalizeCanvas, normalizeDynamics, normalizePaintBrush, normalizePaper, normalizeRotation, normalizeView, paperGrain, paperKey, paperNode, paperPreset, parseBrushLibrary, penOrientation, pointerToPenPoint, rand01, randSigned, renderBrushThumbnail, renderPaper, samplePressure, screenToDocument, seedFromString, serializePaintDocument, serializePaintDocumentExternal, strokeBounds, thumbnailStroke, tiltToOrientation, toStrokePoint, variationAffine, varyStroke, viewCssTransform, viewMatrix, viewPenToDocument };
1552
+ /**
1553
+ * Framework-free UI logic shared by the React and Svelte paint editors, so both behave the same:
1554
+ *
1555
+ * - `PaintGesture` turns pointer events (structurally typed, no DOM import) into strokes (with
1556
+ * coalesced samples and a predicted tail), colour picks, pans, view rotation and zoom, with palm
1557
+ * rejection while a pen is down.
1558
+ * - `PaintCanvasPresenter` keeps a document-sized 2D canvas up to date from the compositor,
1559
+ * uploading only the tiles that changed.
1560
+ * - Guide overlays as line segments, and the slider tables of the brush and paper panels.
1561
+ */
1562
+
1563
+ /** The Pointer Event fields the gesture reads. */
1564
+ interface PaintPointerEvent extends PointerSample {
1565
+ pointerId: number;
1566
+ clientX: number;
1567
+ clientY: number;
1568
+ button?: number;
1569
+ buttons?: number;
1570
+ shiftKey?: boolean;
1571
+ altKey?: boolean;
1572
+ ctrlKey?: boolean;
1573
+ metaKey?: boolean;
1574
+ getCoalescedEvents?(): PaintPointerEvent[];
1575
+ getPredictedEvents?(): PaintPointerEvent[];
1576
+ }
1577
+ type PaintTool = 'brush' | 'eyedropper' | 'pan' | 'rotate';
1578
+ declare const PAINT_TOOLS: ReadonlyArray<{
1579
+ id: PaintTool;
1580
+ label: string;
1581
+ key: string;
1582
+ }>;
1583
+ /** Where the viewport element is on screen and how big it is (CSS pixels). */
1584
+ interface ViewportBox {
1585
+ left: number;
1586
+ top: number;
1587
+ width: number;
1588
+ height: number;
1589
+ }
1590
+ declare class PaintGesture {
1591
+ private readonly controller;
1592
+ private readonly box;
1593
+ tool: PaintTool;
1594
+ /** The last refusal (a hidden or locked layer), for the editor to show. */
1595
+ error: string | null;
1596
+ /** The predicted tail of the current stroke in document pixels (draw it as a thin preview). */
1597
+ predicted: PenPoint[];
1598
+ private mode;
1599
+ private penAt;
1600
+ private penDown;
1601
+ constructor(controller: PaintEditorController, box: () => ViewportBox);
1602
+ private doc;
1603
+ private pen;
1604
+ /** Pointer down; returns true when the gesture took the pointer (capture it). */
1605
+ down(e: PaintPointerEvent): boolean;
1606
+ private pick;
1607
+ move(e: PaintPointerEvent): void;
1608
+ up(e: PaintPointerEvent): void;
1609
+ cancel(): void;
1610
+ get active(): boolean;
1611
+ /** Wheel: zoom about the pointer (Ctrl / pinch or plain wheel), Shift+wheel rotates the view. */
1612
+ wheel(e: {
1613
+ clientX: number;
1614
+ clientY: number;
1615
+ deltaY: number;
1616
+ shiftKey?: boolean;
1617
+ }): void;
1618
+ }
1619
+ /** Keyboard shortcuts shared by both editors; returns true when it handled the key. */
1620
+ declare function handlePaintShortcut(controller: PaintEditorController, gesture: PaintGesture, e: {
1621
+ key: string;
1622
+ ctrlKey?: boolean;
1623
+ metaKey?: boolean;
1624
+ shiftKey?: boolean;
1625
+ target?: unknown;
1626
+ }): boolean;
1627
+ /** The 2D context methods the presenter uses (a `CanvasRenderingContext2D`). */
1628
+ interface Paint2DContext {
1629
+ createImageData(width: number, height: number): {
1630
+ data: Uint8ClampedArray;
1631
+ };
1632
+ putImageData(image: never, dx: number, dy: number, sx?: number, sy?: number, sw?: number, sh?: number): void;
1633
+ }
1634
+ /** Keeps a document-sized canvas in step with the controller, tile by tile. */
1635
+ declare class PaintCanvasPresenter {
1636
+ private readonly controller;
1637
+ private readonly uploaded;
1638
+ private scratch;
1639
+ private size;
1640
+ constructor(controller: PaintEditorController);
1641
+ /** Draws what changed into `ctx` (a canvas the size of the document); returns the tiles drawn. */
1642
+ render(ctx: Paint2DContext): number;
1643
+ }
1644
+ /** Lines to draw over the canvas for the guides, in document pixels, with a style key. */
1645
+ declare function guideOverlay(controller: PaintEditorController): Array<{
1646
+ from: Point2;
1647
+ to: Point2;
1648
+ kind: 'symmetry' | 'perspective' | 'snap';
1649
+ }>;
1650
+ /** One slider of a panel: a path into the brush (or paper) settings. */
1651
+ interface SliderControl {
1652
+ path: string;
1653
+ label: string;
1654
+ min: number;
1655
+ max: number;
1656
+ step: number;
1657
+ }
1658
+ declare const BRUSH_SLIDERS: readonly SliderControl[];
1659
+ declare const PAPER_SLIDERS: readonly SliderControl[];
1660
+ /** Reads `a.b` from an object. */
1661
+ declare function readPath(obj: unknown, path: string): number;
1662
+ /** `{a: {b: value}}` for `a.b`, keeping the rest of `obj`'s `a`. */
1663
+ declare function patchPath(obj: Record<string, unknown>, path: string, value: number): Record<string, unknown>;
1664
+ /** The point on a colour wheel of `radius` (centre 0, 0) for a wheel angle in degrees, red at the top. */
1665
+ declare function wheelPoint(angle: number, radius: number): Point2;
1666
+ /** The wheel angle (degrees, red at the top, clockwise) of a point relative to the wheel centre. */
1667
+ declare function wheelAngle(x: number, y: number): number;
1668
+ /**
1669
+ * A hue ring for a colour wheel, `size × size` RGBA: hues at full saturation and value between
1670
+ * `inner` and 1 of the radius, red at the top, laid out on the RYB or RGB wheel.
1671
+ */
1672
+ declare function renderColorWheel(size: number, wheel: ColorWheel, inner?: number): {
1673
+ width: number;
1674
+ height: number;
1675
+ data: Uint8ClampedArray;
1676
+ };
1677
+ /** The colour for a click on the wheel ring at (dx, dy) from its centre, keeping `color`'s saturation and value. */
1678
+ declare function wheelPick(dx: number, dy: number, wheel: ColorWheel, color: string): string;
1679
+
1680
+ export { type Affine, BRUSH_ID, BRUSH_ROTATIONS, BRUSH_SLIDERS, BUILTIN_BRUSHES, BlendMode, type BrushDynamics, type BrushLibraryFile, type BrushRotation, type BrushThumbnail, type ColorWheel, DEFAULT_BRUSH_ID, DEFAULT_DPI, DEFAULT_DYNAMICS, DEFAULT_PAPER, DEFAULT_SWATCHES, DEFAULT_VIEW, DryStroke, type DryStrokeOptions, type FillLayerOptions, GRAIN_PATTERNS, GRAIN_TILE, type GrainPattern, type GrainTuning, HARMONY_KINDS, type Hand, type HarmonyColor, type HarmonyKind, IDENTITY_AFFINE, IDENTITY_COPY, type ImageEncoder, type ImageFormat, type ImageNode, type LayerUpdate, type Linear2, type LoadedPaintDocument, MAX_CANVAS_SIDE, MAX_COORDINATE, MAX_IMAGE_SIDE, MAX_ZOOM, MIN_CANVAS_SIDE, MIN_ZOOM, MIXING_PAD_HEIGHT, MIXING_PAD_WIDTH, MixingPad, type MixingPadTool, PAINT_BRUSH_CATEGORIES, PAINT_BRUSH_ENGINES, PAINT_FORMAT, PAINT_SCHEMA_VERSION, PAINT_TOOLS, PAPER_FAMILIES, PAPER_LIBRARY, PAPER_NODE_ID, PAPER_SLIDERS, type Paint2DContext, type PaintAssetStore, type PaintBrush, type PaintBrushCategory, type PaintBrushEngine, type PaintBrushInput, type PaintCanvas, PaintCanvasPresenter, PaintDocumentError, type PaintDocumentExtensions, type PaintDocumentState, PaintEditorController, PaintEditorError, type PaintEditorOptions, type PaintEditorState, type PaintEditorToolProviderOptions, PaintGesture, type PaintGroupLayer, type PaintGuides, type PaintLayer, type PaintLayerInfo, type PaintPixelLayer, type PaintPointerEvent, type PaintStrokeInit, type PaintStrokeResult, PaintStrokeSession, type PaintTool, type PaintView, type PaperFamily, PaperGrain, type PaperNode, type PaperSettings, type PaperTiling, type PenDab, PenDabWalker, type PenPoint, PenSmoother, type PerspectiveGuide, PerspectiveSnapper, type PixelImage, type Point2, type PointerSample, type RecordedStroke, type ReferenceImage, type RenderOptions, type ReplayOptions, type Rgb255, SNAP_DECIDE_DISTANCE, SYMMETRY_MODES, type SerializeOptions, type SerializedPaintDocument, type SliderControl, type StrokeCopy, type StrokeSessionHost, type StrokeSessionInit, type StrokeVariation, type Swatch, type SymmetryGuide, type SymmetryMode, TRACING_NODE_ID, TileableNoise, type TracingImage, type ViewLayer, type ViewportBox, affineCopy, applyAffine, brushToJSON, clonePaper, colorHarmony, composeAffine, createPaintCompositor, createPaintDocument, createPaintEditorToolProvider, createPaintLayer, decodeImageBlob, defaultGuides, defaultImageEncoder, deserializePaintDocument, deserializePaintDocumentAsync, documentExtensions, documentToScreen, downloadBytes, downsampleRgba, fitZoom, grainCatch, grainHeights, guideOverlay, handlePaintShortcut, hash2, hexToRgb8, hslToRgb, hsvToRgb, hueToWheel, invertAffine, isPalmTouch, mapPenPoint, mix32, mixerLoadForStroke, mixerTip, newPaintLayerId, normalizeBrushStroke, normalizeCanvas, normalizeDynamics, normalizePaintBrush, normalizePaper, normalizePerspective, normalizeRotation, normalizeSymmetry, normalizeView, paperGrain, paperKey, paperNode, paperPreset, parseBrushLibrary, parseHexColor, patchPath, penOrientation, perspectiveDirections, perspectiveGuideLines, pointerToPenPoint, rand01, randSigned, readPath, renderBrushThumbnail, renderColorWheel, renderPaper, rgbToHexColor, rgbToHsl, rgbToHsv, samplePressure, screenToDocument, seedFromString, serializePaintDocument, serializePaintDocumentExternal, strokeBounds, symmetryAxisAngles, symmetryAxisSegments, symmetryCopies, thumbnailStroke, tiltToOrientation, toStrokePoint, variationAffine, varyStroke, viewCssTransform, viewMatrix, viewPenToDocument, wheelAngle, wheelPick, wheelPoint, wheelToHue };