@pixerate/editor 0.276.0 → 0.278.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 (65) hide show
  1. package/dist/animation-editor/index.cjs +59 -59
  2. package/dist/animation-editor/index.js +3 -3
  3. package/dist/brushes/index.cjs +69 -67
  4. package/dist/brushes/index.js +6 -4
  5. package/dist/brushes/patch.d.cts +4 -4
  6. package/dist/brushes/patch.d.ts +4 -4
  7. package/dist/brushes/profiles.d.cts +2 -0
  8. package/dist/brushes/profiles.d.ts +2 -0
  9. package/dist/brushes/schema.d.cts +1 -0
  10. package/dist/brushes/schema.d.ts +1 -0
  11. package/dist/brushes/types.d.cts +10 -0
  12. package/dist/brushes/types.d.ts +10 -0
  13. package/dist/{brushstrokeRender-I6MYFCOU.cjs → brushstrokeRender-7HOMKZNO.cjs} +15 -15
  14. package/dist/{brushstrokeRender-FK3ASUUJ.js → brushstrokeRender-EIK3625T.js} +3 -3
  15. package/dist/canvas/index.cjs +13 -13
  16. package/dist/canvas/index.js +1 -1
  17. package/dist/{chunk-U26LUXCJ.cjs → chunk-4NZTK7LM.cjs} +35 -35
  18. package/dist/{chunk-CVBTJVRN.js → chunk-DWPFGWW5.js} +1 -1
  19. package/dist/{chunk-XMSH66VU.js → chunk-FNIWHSLI.js} +1 -1
  20. package/dist/{chunk-QNNZA3V5.cjs → chunk-H3EME3S5.cjs} +2 -2
  21. package/dist/{chunk-4EM5NGCL.js → chunk-HHX7ST6Z.js} +1 -1
  22. package/dist/{chunk-Q33V5PEF.cjs → chunk-HRUK5TFW.cjs} +73 -12
  23. package/dist/{chunk-5HR5CSLC.cjs → chunk-JVFGK3DK.cjs} +69 -58
  24. package/dist/{chunk-JNMNM3SL.js → chunk-LZE2QZCT.js} +1 -1
  25. package/dist/{chunk-OS6OG7DN.js → chunk-N4MCWQZ7.js} +1 -1
  26. package/dist/{chunk-IJ6NSWKM.js → chunk-NJLPW4MM.js} +17 -6
  27. package/dist/{chunk-OY24XRYF.cjs → chunk-PEQWARCP.cjs} +12 -12
  28. package/dist/{chunk-XVPQNX7O.js → chunk-PPDUCZZV.js} +66 -5
  29. package/dist/{chunk-X7XBEX3O.cjs → chunk-QBZEC2JJ.cjs} +86 -86
  30. package/dist/{chunk-GHYJAMMS.js → chunk-ROURHJYT.js} +1 -1
  31. package/dist/{chunk-ZIE6EJGR.cjs → chunk-VNF7A2JA.cjs} +23 -23
  32. package/dist/{chunk-ISCAJBPQ.cjs → chunk-WFSYZO5Z.cjs} +1 -1
  33. package/dist/image-editor/index.cjs +788 -788
  34. package/dist/image-editor/index.js +4 -4
  35. package/dist/paint-editor/brush-model.d.cts +15 -1
  36. package/dist/paint-editor/brush-model.d.ts +15 -1
  37. package/dist/paint-editor/controller.d.cts +90 -9
  38. package/dist/paint-editor/controller.d.ts +90 -9
  39. package/dist/paint-editor/document.d.cts +8 -1
  40. package/dist/paint-editor/document.d.ts +8 -1
  41. package/dist/paint-editor/ferro-tools.d.cts +10 -0
  42. package/dist/paint-editor/ferro-tools.d.ts +10 -0
  43. package/dist/paint-editor/ferro.d.cts +134 -0
  44. package/dist/paint-editor/ferro.d.ts +134 -0
  45. package/dist/paint-editor/index.cjs +2138 -872
  46. package/dist/paint-editor/index.d.cts +2 -0
  47. package/dist/paint-editor/index.d.ts +2 -0
  48. package/dist/paint-editor/index.js +1446 -180
  49. package/dist/paint-editor/serialize.d.cts +3 -1
  50. package/dist/paint-editor/serialize.d.ts +3 -1
  51. package/dist/paint-editor/stroke.d.cts +17 -0
  52. package/dist/paint-editor/stroke.d.ts +17 -0
  53. package/dist/paint-editor/strokes.d.cts +11 -1
  54. package/dist/paint-editor/strokes.d.ts +11 -1
  55. package/dist/paint-editor/thick.d.cts +15 -2
  56. package/dist/paint-editor/thick.d.ts +15 -2
  57. package/dist/paint-editor/ui.d.cts +2 -0
  58. package/dist/paint-editor/ui.d.ts +2 -0
  59. package/dist/vector/index.cjs +998 -998
  60. package/dist/vector/index.js +4 -4
  61. package/package.json +1 -1
  62. package/skills/pixerate-brushes/SKILL.md +1 -0
  63. package/skills/pixerate-paint-editor/SKILL.md +12 -30
  64. package/skills/pixerate-paint-editor/references/brush-facets.md +1 -0
  65. package/skills/pixerate-paint-editor/references/ferrofluid.md +36 -0
@@ -59,7 +59,9 @@ export interface SerializeOptions {
59
59
  }
60
60
  /** The document as JSON with inline PNG data URLs. */
61
61
  export declare function serializePaintDocument(state: PaintDocumentState, options?: SerializeOptions): SerializedPaintDocument;
62
- /** The document as JSON with each PNG uploaded to `store` once (by content). */
62
+ /** How an extension field refers to a PNG in `assets`: `asset:<asset id>`. */
63
+ export declare const EXTENSION_ASSET_REF = "asset:";
64
+ /** The document as JSON with each PNG (layers and extension fields alike) uploaded to `store` once (by content). */
63
65
  export declare function serializePaintDocumentExternal(state: PaintDocumentState, store: PaintAssetStore, options?: SerializeOptions): Promise<SerializedPaintDocument>;
64
66
  export interface LoadedPaintDocument {
65
67
  state: PaintDocumentState;
@@ -59,7 +59,9 @@ export interface SerializeOptions {
59
59
  }
60
60
  /** The document as JSON with inline PNG data URLs. */
61
61
  export declare function serializePaintDocument(state: PaintDocumentState, options?: SerializeOptions): SerializedPaintDocument;
62
- /** The document as JSON with each PNG uploaded to `store` once (by content). */
62
+ /** How an extension field refers to a PNG in `assets`: `asset:<asset id>`. */
63
+ export declare const EXTENSION_ASSET_REF = "asset:";
64
+ /** The document as JSON with each PNG (layers and extension fields alike) uploaded to `store` once (by content). */
63
65
  export declare function serializePaintDocumentExternal(state: PaintDocumentState, store: PaintAssetStore, options?: SerializeOptions): Promise<SerializedPaintDocument>;
64
66
  export interface LoadedPaintDocument {
65
67
  state: PaintDocumentState;
@@ -22,6 +22,7 @@ import { type PaperGrain } from './paper.cjs';
22
22
  import { type Sticker } from './stickers.cjs';
23
23
  import type { StickerArtLookup } from './sticker-art.cjs';
24
24
  import type { StickerRef, StickerVariant } from '../stickers/types.cjs';
25
+ import type { DualColor } from '../color/dual.cjs';
25
26
  export interface StrokeSessionHost {
26
27
  /** Shows the layer's live surface (no history); `rects` are where it changed. */
27
28
  update(layerId: string, surface: TiledSurface, rects: RasterRect[]): void;
@@ -48,6 +49,8 @@ export interface StrokeSessionInit {
48
49
  seed: number;
49
50
  /** The paint colour as #rrggbb (recorded with the stroke). */
50
51
  colorHex: string;
52
+ /** The light / dark pair `colorHex` is one side of, when the colour came from one (recorded with the stroke). */
53
+ dualColor?: DualColor;
51
54
  /** The stroke's extra copies as affine maps (recorded with the stroke); `copies` holds all of them as engine copies. */
52
55
  affineCopies: readonly Affine[];
53
56
  copies: readonly StrokeCopy[];
@@ -114,6 +117,13 @@ export interface StrokeSessionInit {
114
117
  thermo?: {
115
118
  surface: TiledSurface;
116
119
  } | null;
120
+ /**
121
+ * Ferrofluid paint: the layer's ferrofluid channel before the stroke. With it, a twin of the
122
+ * stroke paints the ferrofluid channel as grey (see `ferro.ts`); `ferroChannel()` returns the result.
123
+ */
124
+ ferro?: {
125
+ surface: TiledSurface;
126
+ } | null;
117
127
  }
118
128
  export interface PaintStrokeResult {
119
129
  layerId: string;
@@ -148,6 +158,9 @@ export declare class PaintStrokeSession {
148
158
  /** Light paint: the stroke's twin over the light channel, and what it left there. */
149
159
  private readonly lightTwin;
150
160
  private lightResult;
161
+ /** Ferrofluid paint: the stroke's twin over the ferrofluid channel, and what it left there. */
162
+ private readonly ferroTwin;
163
+ private ferroResult;
151
164
  /** UV-fluorescent ink: the stroke's twin over the fluorescence channel, and what it left there. */
152
165
  private readonly fluorTwin;
153
166
  private fluorResult;
@@ -186,6 +199,10 @@ export declare class PaintStrokeSession {
186
199
  fluorChannel(): TiledSurface | null;
187
200
  /** Thermochromic ink: the layer's thermochromic channel after the stroke, or `null` when unchanged. */
188
201
  thermoChannel(): TiledSurface | null;
202
+ /** The stroke's twin over the ferrofluid channel: the same marks in grey at the brush's ferro amount (see `ferro.ts`). */
203
+ private makeFerroTwin;
204
+ /** Ferrofluid paint: the layer's ferrofluid channel after the stroke, or `null` when the stroke has no twin or left it unchanged. */
205
+ ferroChannel(): TiledSurface | null;
189
206
  private tileTarget;
190
207
  get open(): boolean;
191
208
  /** Adds raw pen samples (document pixels). */
@@ -22,6 +22,7 @@ import { type PaperGrain } from './paper.js';
22
22
  import { type Sticker } from './stickers.js';
23
23
  import type { StickerArtLookup } from './sticker-art.js';
24
24
  import type { StickerRef, StickerVariant } from '../stickers/types.js';
25
+ import type { DualColor } from '../color/dual.js';
25
26
  export interface StrokeSessionHost {
26
27
  /** Shows the layer's live surface (no history); `rects` are where it changed. */
27
28
  update(layerId: string, surface: TiledSurface, rects: RasterRect[]): void;
@@ -48,6 +49,8 @@ export interface StrokeSessionInit {
48
49
  seed: number;
49
50
  /** The paint colour as #rrggbb (recorded with the stroke). */
50
51
  colorHex: string;
52
+ /** The light / dark pair `colorHex` is one side of, when the colour came from one (recorded with the stroke). */
53
+ dualColor?: DualColor;
51
54
  /** The stroke's extra copies as affine maps (recorded with the stroke); `copies` holds all of them as engine copies. */
52
55
  affineCopies: readonly Affine[];
53
56
  copies: readonly StrokeCopy[];
@@ -114,6 +117,13 @@ export interface StrokeSessionInit {
114
117
  thermo?: {
115
118
  surface: TiledSurface;
116
119
  } | null;
120
+ /**
121
+ * Ferrofluid paint: the layer's ferrofluid channel before the stroke. With it, a twin of the
122
+ * stroke paints the ferrofluid channel as grey (see `ferro.ts`); `ferroChannel()` returns the result.
123
+ */
124
+ ferro?: {
125
+ surface: TiledSurface;
126
+ } | null;
117
127
  }
118
128
  export interface PaintStrokeResult {
119
129
  layerId: string;
@@ -148,6 +158,9 @@ export declare class PaintStrokeSession {
148
158
  /** Light paint: the stroke's twin over the light channel, and what it left there. */
149
159
  private readonly lightTwin;
150
160
  private lightResult;
161
+ /** Ferrofluid paint: the stroke's twin over the ferrofluid channel, and what it left there. */
162
+ private readonly ferroTwin;
163
+ private ferroResult;
151
164
  /** UV-fluorescent ink: the stroke's twin over the fluorescence channel, and what it left there. */
152
165
  private readonly fluorTwin;
153
166
  private fluorResult;
@@ -186,6 +199,10 @@ export declare class PaintStrokeSession {
186
199
  fluorChannel(): TiledSurface | null;
187
200
  /** Thermochromic ink: the layer's thermochromic channel after the stroke, or `null` when unchanged. */
188
201
  thermoChannel(): TiledSurface | null;
202
+ /** The stroke's twin over the ferrofluid channel: the same marks in grey at the brush's ferro amount (see `ferro.ts`). */
203
+ private makeFerroTwin;
204
+ /** Ferrofluid paint: the layer's ferrofluid channel after the stroke, or `null` when the stroke has no twin or left it unchanged. */
205
+ ferroChannel(): TiledSurface | null;
189
206
  private tileTarget;
190
207
  get open(): boolean;
191
208
  /** Adds raw pen samples (document pixels). */
@@ -14,6 +14,7 @@ import type { MixerLoad } from '../paint/exchange.cjs';
14
14
  import { type Stencil } from './stencils.cjs';
15
15
  import { type PaintGradientSource } from '../paint/gradient-paint.cjs';
16
16
  import { type PaintEffectPaint } from './effect-paint.cjs';
17
+ import { type DualColor } from '../color/dual.cjs';
17
18
  /** An affine map [a, b, c, d, e, f]: (x, y) → (a·x + b·y + e, c·x + d·y + f). */
18
19
  export type Affine = readonly [number, number, number, number, number, number];
19
20
  export declare const IDENTITY_AFFINE: Affine;
@@ -21,8 +22,17 @@ export interface RecordedStroke {
21
22
  id: string;
22
23
  layerId: string;
23
24
  brush: PaintBrush;
24
- /** #rrggbb */
25
+ /**
26
+ * #rrggbb: the colour the document draws the stroke with. A dual-colour stroke on a stroke layer
27
+ * keeps its light side here, whoever painted it; on a pixel layer, the side it was painted in.
28
+ */
25
29
  color: string;
30
+ /**
31
+ * The light / dark pair the colour came from (both #rrggbb), when it came from one. A stroke
32
+ * layer shows it in the viewer's side (`PaintEditorController.setColorScheme`) without changing
33
+ * the document; a pixel layer's pixels stay as painted.
34
+ */
35
+ dualColor?: DualColor;
26
36
  /** Raw pen samples in document pixels, before smoothing. */
27
37
  points: PenPoint[];
28
38
  seed: number;
@@ -14,6 +14,7 @@ import type { MixerLoad } from '../paint/exchange.js';
14
14
  import { type Stencil } from './stencils.js';
15
15
  import { type PaintGradientSource } from '../paint/gradient-paint.js';
16
16
  import { type PaintEffectPaint } from './effect-paint.js';
17
+ import { type DualColor } from '../color/dual.js';
17
18
  /** An affine map [a, b, c, d, e, f]: (x, y) → (a·x + b·y + e, c·x + d·y + f). */
18
19
  export type Affine = readonly [number, number, number, number, number, number];
19
20
  export declare const IDENTITY_AFFINE: Affine;
@@ -21,8 +22,17 @@ export interface RecordedStroke {
21
22
  id: string;
22
23
  layerId: string;
23
24
  brush: PaintBrush;
24
- /** #rrggbb */
25
+ /**
26
+ * #rrggbb: the colour the document draws the stroke with. A dual-colour stroke on a stroke layer
27
+ * keeps its light side here, whoever painted it; on a pixel layer, the side it was painted in.
28
+ */
25
29
  color: string;
30
+ /**
31
+ * The light / dark pair the colour came from (both #rrggbb), when it came from one. A stroke
32
+ * layer shows it in the viewer's side (`PaintEditorController.setColorScheme`) without changing
33
+ * the document; a pixel layer's pixels stay as painted.
34
+ */
35
+ dualColor?: DualColor;
26
36
  /** Raw pen samples in document pixels, before smoothing. */
27
37
  points: PenPoint[];
28
38
  seed: number;
@@ -20,6 +20,7 @@ import { TiledSurface, type RasterRect } from '../raster/surface.cjs';
20
20
  import type { IridescenceSettings } from './brush-model.cjs';
21
21
  import type { PaintBrush } from './brushes.cjs';
22
22
  import { type PenPoint, type StrokeCopy } from './engine.cjs';
23
+ import { type FerroLayerInput } from './ferro.cjs';
23
24
  /** Where the light comes from. */
24
25
  export interface PaintLighting {
25
26
  /** Degrees clockwise from the top of the canvas (315: top left, the default). */
@@ -66,8 +67,20 @@ export declare function lightTile(color: Uint8ClampedArray, height: Uint8Clamped
66
67
  /** Cache of lit tiles for one layer, by the identity of their inputs. */
67
68
  export declare class LitCache {
68
69
  private readonly tiles;
69
- /** The layer's pixels lit (tiles without relief or film nearby are the colour tiles themselves). */
70
- lit(color: TiledSurface, ch: ThickChannels, light: PaintLighting, viewTilt?: PaintViewTilt): TiledSurface;
70
+ /**
71
+ * The layer's pixels lit (tiles without relief, film or ferrofluid nearby are the colour tiles
72
+ * themselves). Without ferrofluid this is the thick-paint / thin-film pass; with it, the liquid
73
+ * ferrofluid adds its carrier colour, glossy metallic material and (near magnets) Rosensweig
74
+ * spike heights before the same lighting.
75
+ */
76
+ lit(color: TiledSurface, ch: ThickChannels, light: PaintLighting, viewTilt?: PaintViewTilt, ferro?: FerroLayerInput | null): TiledSurface;
77
+ /**
78
+ * The ferrofluid lighting pass: every tile with ferrofluid or thick relief nearby is lit, with the
79
+ * liquid's carrier colour and gloss / metal material blended in and its spikes added to the
80
+ * height field. Thick relief is lit even when `light.relief` is 0 here (the spikes need it), and
81
+ * a thin film on the same layer still colours the lit tiles.
82
+ */
83
+ private litFerro;
71
84
  }
72
85
  /** Live thin-film iridescence buffer for any stroke engine (deposits or erases into 4-channel `ThickChannels.film`). */
73
86
  export declare class FilmBuffer {
@@ -20,6 +20,7 @@ import { TiledSurface, type RasterRect } from '../raster/surface.js';
20
20
  import type { IridescenceSettings } from './brush-model.js';
21
21
  import type { PaintBrush } from './brushes.js';
22
22
  import { type PenPoint, type StrokeCopy } from './engine.js';
23
+ import { type FerroLayerInput } from './ferro.js';
23
24
  /** Where the light comes from. */
24
25
  export interface PaintLighting {
25
26
  /** Degrees clockwise from the top of the canvas (315: top left, the default). */
@@ -66,8 +67,20 @@ export declare function lightTile(color: Uint8ClampedArray, height: Uint8Clamped
66
67
  /** Cache of lit tiles for one layer, by the identity of their inputs. */
67
68
  export declare class LitCache {
68
69
  private readonly tiles;
69
- /** The layer's pixels lit (tiles without relief or film nearby are the colour tiles themselves). */
70
- lit(color: TiledSurface, ch: ThickChannels, light: PaintLighting, viewTilt?: PaintViewTilt): TiledSurface;
70
+ /**
71
+ * The layer's pixels lit (tiles without relief, film or ferrofluid nearby are the colour tiles
72
+ * themselves). Without ferrofluid this is the thick-paint / thin-film pass; with it, the liquid
73
+ * ferrofluid adds its carrier colour, glossy metallic material and (near magnets) Rosensweig
74
+ * spike heights before the same lighting.
75
+ */
76
+ lit(color: TiledSurface, ch: ThickChannels, light: PaintLighting, viewTilt?: PaintViewTilt, ferro?: FerroLayerInput | null): TiledSurface;
77
+ /**
78
+ * The ferrofluid lighting pass: every tile with ferrofluid or thick relief nearby is lit, with the
79
+ * liquid's carrier colour and gloss / metal material blended in and its spikes added to the
80
+ * height field. Thick relief is lit even when `light.relief` is 0 here (the spikes need it), and
81
+ * a thin film on the same layer still colours the lit tiles.
82
+ */
83
+ private litFerro;
71
84
  }
72
85
  /** Live thin-film iridescence buffer for any stroke engine (deposits or erases into 4-channel `ThickChannels.film`). */
73
86
  export declare class FilmBuffer {
@@ -302,6 +302,8 @@ export declare const STROKE_SLIDERS: readonly SliderControl[];
302
302
  export declare const STICKER_SLIDERS: readonly SliderControl[];
303
303
  /** The evolution panel's mutation sliders (`0..1`). */
304
304
  export declare const EVOLUTION_SLIDERS: readonly SliderControl[];
305
+ /** The ferrofluid panel's sliders for a magnet (`strength` and `radius`). */
306
+ export declare const MAGNET_SLIDERS: readonly SliderControl[];
305
307
  /** Stencils a toolbar can lay with one click, centred on the canvas. */
306
308
  export declare function quickStencil(kind: 'ruler' | 'circle' | 'ellipse-mask' | 'edge-mask', canvas: {
307
309
  width: number;
@@ -302,6 +302,8 @@ export declare const STROKE_SLIDERS: readonly SliderControl[];
302
302
  export declare const STICKER_SLIDERS: readonly SliderControl[];
303
303
  /** The evolution panel's mutation sliders (`0..1`). */
304
304
  export declare const EVOLUTION_SLIDERS: readonly SliderControl[];
305
+ /** The ferrofluid panel's sliders for a magnet (`strength` and `radius`). */
306
+ export declare const MAGNET_SLIDERS: readonly SliderControl[];
305
307
  /** Stencils a toolbar can lay with one click, centred on the canvas. */
306
308
  export declare function quickStencil(kind: 'ruler' | 'circle' | 'ellipse-mask' | 'edge-mask', canvas: {
307
309
  width: number;