@pixerate/editor 0.252.0 → 0.253.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 (45) hide show
  1. package/dist/brushes/index.cjs +229 -2014
  2. package/dist/brushes/index.d.cts +5 -1
  3. package/dist/brushes/index.d.ts +5 -1
  4. package/dist/brushes/index.js +236 -2021
  5. package/dist/brushes/patch.d.cts +23 -0
  6. package/dist/brushes/patch.d.ts +23 -0
  7. package/dist/brushes/ui.d.cts +105 -0
  8. package/dist/brushes/ui.d.ts +105 -0
  9. package/dist/chunk-34KLWUH4.cjs +2284 -0
  10. package/dist/chunk-QPHNVZHO.js +2284 -0
  11. package/dist/paint-editor/brush-controls.d.cts +57 -0
  12. package/dist/paint-editor/brush-controls.d.ts +57 -0
  13. package/dist/paint-editor/brush-library-tools.d.cts +15 -0
  14. package/dist/paint-editor/brush-library-tools.d.ts +15 -0
  15. package/dist/paint-editor/brush-library.d.cts +29 -0
  16. package/dist/paint-editor/brush-library.d.ts +29 -0
  17. package/dist/paint-editor/controller.d.cts +76 -17
  18. package/dist/paint-editor/controller.d.ts +76 -17
  19. package/dist/paint-editor/index.cjs +537 -852
  20. package/dist/paint-editor/index.d.cts +1 -0
  21. package/dist/paint-editor/index.d.ts +1 -0
  22. package/dist/paint-editor/index.js +327 -642
  23. package/dist/paint-editor/serialize.d.cts +7 -4
  24. package/dist/paint-editor/serialize.d.ts +7 -4
  25. package/dist/paint-editor/sticker-tools.d.cts +1 -1
  26. package/dist/paint-editor/sticker-tools.d.ts +1 -1
  27. package/dist/paint-editor/strokes.d.cts +10 -1
  28. package/dist/paint-editor/strokes.d.ts +10 -1
  29. package/dist/paint-editor/tools.d.cts +1 -1
  30. package/dist/paint-editor/tools.d.ts +1 -1
  31. package/dist/paint-editor/ui.d.cts +3 -51
  32. package/dist/paint-editor/ui.d.ts +3 -51
  33. package/dist/paint-editor/wet-tools.d.cts +1 -1
  34. package/dist/paint-editor/wet-tools.d.ts +1 -1
  35. package/package.json +1 -1
  36. package/skills/pixerate-brushes/SKILL.md +5 -3
  37. package/skills/pixerate-paint-editor/SKILL.md +26 -24
  38. package/skills/pixerate-paint-editor/references/brush-facets.md +30 -0
  39. package/skills/pixerate-paint-editor/references/gradient-paint.md +1 -1
  40. package/skills/pixerate-paint-editor/references/light-paint.md +2 -2
  41. package/skills/pixerate-paint-editor/references/oils.md +1 -1
  42. package/skills/pixerate-paint-editor/references/particle-brushes.md +8 -5
  43. package/skills/pixerate-paint-oil-impasto/SKILL.md +1 -1
  44. package/skills/pixerate-stickers/SKILL.md +1 -1
  45. package/skills/pixerate-video-editor/SKILL.md +1 -1
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The brush panel's slider tables and the controls each engine adds (`brushEngineControls`),
3
+ * as plain data: paths into the engine brush. No renderer is imported, so the brush editor
4
+ * descriptor (`@pixerate/editor/brushes` `brushEditorSections`) reuses them without bundling the
5
+ * paint engine.
6
+ */
7
+ import type { PaintBrush, PaintBrushInput } from './brushes.cjs';
8
+ /** One slider of a panel: a path into the brush (or paper) settings. */
9
+ export interface SliderControl {
10
+ path: string;
11
+ label: string;
12
+ min: number;
13
+ max: number;
14
+ step: number;
15
+ }
16
+ export declare const BRUSH_SLIDERS: readonly SliderControl[];
17
+ export interface BrushEngineControls {
18
+ /** Section heading. */
19
+ title: string;
20
+ sliders: readonly SliderControl[];
21
+ /** Where the RGB / pigment choice is stored, or `null` when the engine has none. */
22
+ mixingPath: 'behavior.mixing' | 'blender.mixing' | 'smudge.mixing' | null;
23
+ /** The dirty-brush toggle (`behavior.dirty`). */
24
+ dirty: boolean;
25
+ /** The multi-colour controls (`behavior.multicolor`; the panels show the brush gradient, `brush.paint`, instead). */
26
+ multicolor: boolean;
27
+ /** Load / Clean buttons and a swatch of what the brush carries. */
28
+ load: boolean;
29
+ /** The wet tool menu (`wet.tool`, `WET_TOOLS`) and medium menu (`wet.medium`, `WET_MEDIA`). */
30
+ wetTool?: boolean;
31
+ /** The impasto tool menu (`impasto.tool`, `IMPASTO_TOOLS`). */
32
+ impastoTool?: boolean;
33
+ /**
34
+ * The particle menus: kind and render (`particle.kind`, `particle.render`), ribbon pairs
35
+ * (`particle.ribbonPairs`), and the textured-dot stamps, order and rotation
36
+ * (`particle.dotTextures` up to `MAX_DOT_TEXTURES` of `STAMP_KINDS`, `dotTextureOrder`, `dotRotation`).
37
+ */
38
+ particleMenus?: boolean;
39
+ /** The sticker picker for the brush's stickers (`sticker.stamps`, refs from the sticker library; `toggleStickerRef`). */
40
+ stickerStamps?: boolean;
41
+ /** Light paint: the "light follows pressure" toggle (`light.pressure`); the Light slider is in `sliders`. */
42
+ light?: boolean;
43
+ }
44
+ /**
45
+ * The controls the active brush's engine adds to the brush panel (`null` for the eraser):
46
+ * blender strength and blend distance; smudge strength and length; the Mixer Brush's pick-up,
47
+ * mix and load; a dab brush's pick-up and mix ratios. Every engine but the eraser has an RGB /
48
+ * pigment choice; dab and mixer brushes also have the dirty and multi-colour controls. Pick-up
49
+ * and mix always go through `behavior`, which keeps a Mixer Brush's `mixer` block in sync.
50
+ */
51
+ export declare function brushEngineControls(brush: Pick<PaintBrush, 'engine'> & {
52
+ wet?: PaintBrush['wet'];
53
+ }): BrushEngineControls | null;
54
+ /** The multi-colour sliders for a mode: softness, and dabs per colour in `dab` mode. */
55
+ export declare function multiColorSliders(mode: string): readonly SliderControl[];
56
+ /** A brush change setting `path` (e.g. `smudge.mixing`, `behavior.dirty`) to any value, keeping the rest of its block. */
57
+ export declare function patchBrushValue(brush: PaintBrush, path: string, value: unknown): PaintBrushInput;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The brush panel's slider tables and the controls each engine adds (`brushEngineControls`),
3
+ * as plain data: paths into the engine brush. No renderer is imported, so the brush editor
4
+ * descriptor (`@pixerate/editor/brushes` `brushEditorSections`) reuses them without bundling the
5
+ * paint engine.
6
+ */
7
+ import type { PaintBrush, PaintBrushInput } from './brushes.js';
8
+ /** One slider of a panel: a path into the brush (or paper) settings. */
9
+ export interface SliderControl {
10
+ path: string;
11
+ label: string;
12
+ min: number;
13
+ max: number;
14
+ step: number;
15
+ }
16
+ export declare const BRUSH_SLIDERS: readonly SliderControl[];
17
+ export interface BrushEngineControls {
18
+ /** Section heading. */
19
+ title: string;
20
+ sliders: readonly SliderControl[];
21
+ /** Where the RGB / pigment choice is stored, or `null` when the engine has none. */
22
+ mixingPath: 'behavior.mixing' | 'blender.mixing' | 'smudge.mixing' | null;
23
+ /** The dirty-brush toggle (`behavior.dirty`). */
24
+ dirty: boolean;
25
+ /** The multi-colour controls (`behavior.multicolor`; the panels show the brush gradient, `brush.paint`, instead). */
26
+ multicolor: boolean;
27
+ /** Load / Clean buttons and a swatch of what the brush carries. */
28
+ load: boolean;
29
+ /** The wet tool menu (`wet.tool`, `WET_TOOLS`) and medium menu (`wet.medium`, `WET_MEDIA`). */
30
+ wetTool?: boolean;
31
+ /** The impasto tool menu (`impasto.tool`, `IMPASTO_TOOLS`). */
32
+ impastoTool?: boolean;
33
+ /**
34
+ * The particle menus: kind and render (`particle.kind`, `particle.render`), ribbon pairs
35
+ * (`particle.ribbonPairs`), and the textured-dot stamps, order and rotation
36
+ * (`particle.dotTextures` up to `MAX_DOT_TEXTURES` of `STAMP_KINDS`, `dotTextureOrder`, `dotRotation`).
37
+ */
38
+ particleMenus?: boolean;
39
+ /** The sticker picker for the brush's stickers (`sticker.stamps`, refs from the sticker library; `toggleStickerRef`). */
40
+ stickerStamps?: boolean;
41
+ /** Light paint: the "light follows pressure" toggle (`light.pressure`); the Light slider is in `sliders`. */
42
+ light?: boolean;
43
+ }
44
+ /**
45
+ * The controls the active brush's engine adds to the brush panel (`null` for the eraser):
46
+ * blender strength and blend distance; smudge strength and length; the Mixer Brush's pick-up,
47
+ * mix and load; a dab brush's pick-up and mix ratios. Every engine but the eraser has an RGB /
48
+ * pigment choice; dab and mixer brushes also have the dirty and multi-colour controls. Pick-up
49
+ * and mix always go through `behavior`, which keeps a Mixer Brush's `mixer` block in sync.
50
+ */
51
+ export declare function brushEngineControls(brush: Pick<PaintBrush, 'engine'> & {
52
+ wet?: PaintBrush['wet'];
53
+ }): BrushEngineControls | null;
54
+ /** The multi-colour sliders for a mode: softness, and dabs per colour in `dab` mode. */
55
+ export declare function multiColorSliders(mode: string): readonly SliderControl[];
56
+ /** A brush change setting `path` (e.g. `smudge.mixing`, `behavior.dirty`) to any value, keeping the rest of its block. */
57
+ export declare function patchBrushValue(brush: PaintBrush, path: string, value: unknown): PaintBrushInput;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The shared brush library's agent tools for a paint editor (`createPaintBrushLibraryToolProvider`),
3
+ * with `preview_brush` painted by this editor.
4
+ */
5
+ import { type BrushLibraryToolProviderOptions } from '../brushes/tools.cjs';
6
+ import type { AgentToolProvider } from '../tools/registry.cjs';
7
+ import type { PaintEditorController } from './controller.cjs';
8
+ export type PaintBrushLibraryToolProviderOptions = Omit<BrushLibraryToolProviderOptions, 'renderPreview'>;
9
+ /**
10
+ * The brush library tools for a paint editor (`@pixerate/editor/brushes`' provider on the
11
+ * editor's `brushLibrary`, `PAINT_PROFILE`), with `preview_brush` painting the test stroke on the
12
+ * editor's paper. Register it next to `createPaintEditorToolProvider`: it lists, saves, shares and
13
+ * previews the brushes `set_brush` picks.
14
+ */
15
+ export declare function createPaintBrushLibraryToolProvider(controller: PaintEditorController, options?: PaintBrushLibraryToolProviderOptions): AgentToolProvider;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The shared brush library's agent tools for a paint editor (`createPaintBrushLibraryToolProvider`),
3
+ * with `preview_brush` painted by this editor.
4
+ */
5
+ import { type BrushLibraryToolProviderOptions } from '../brushes/tools.js';
6
+ import type { AgentToolProvider } from '../tools/registry.js';
7
+ import type { PaintEditorController } from './controller.js';
8
+ export type PaintBrushLibraryToolProviderOptions = Omit<BrushLibraryToolProviderOptions, 'renderPreview'>;
9
+ /**
10
+ * The brush library tools for a paint editor (`@pixerate/editor/brushes`' provider on the
11
+ * editor's `brushLibrary`, `PAINT_PROFILE`), with `preview_brush` painting the test stroke on the
12
+ * editor's paper. Register it next to `createPaintEditorToolProvider`: it lists, saves, shares and
13
+ * previews the brushes `set_brush` picks.
14
+ */
15
+ export declare function createPaintBrushLibraryToolProvider(controller: PaintEditorController, options?: PaintBrushLibraryToolProviderOptions): AgentToolProvider;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The paint editor's side of the shared brush library (`@pixerate/editor/brushes`): the
3
+ * controller's synchronous copy of the library (each brush as stored, as facets, and as the
4
+ * engine paints it), and facet brushes for engine brushes edited by their settings.
5
+ */
6
+ import type { StorageAdapter } from '../persistence/storage.cjs';
7
+ import type { Brush } from '../brushes/types.cjs';
8
+ import { type PaintBrush } from './brushes.cjs';
9
+ /** A library brush as the paint editor keeps it: its facets and the engine form it paints with. */
10
+ export interface LibraryBrush {
11
+ spec: Brush;
12
+ brush: PaintBrush;
13
+ builtin: boolean;
14
+ }
15
+ export declare const copyJSON: <T>(v: T) => T;
16
+ export declare function libraryBrush(spec: Brush, builtin: boolean, resolved?: PaintBrush): LibraryBrush;
17
+ /** The built-in presets, paired with the engine brushes they resolve to (`BUILTIN_BRUSHES`, in the same order). */
18
+ export declare function builtinLibraryBrushes(): readonly LibraryBrush[];
19
+ /** Storage that starts with these brushes (a version 1 file) and keeps the rest in memory; none without brushes. */
20
+ export declare function seededStorage(brushes: readonly PaintBrush[]): Pick<StorageAdapter, 'get' | 'set'> | undefined;
21
+ /**
22
+ * The facet brush an engine brush edited by its settings amounts to (`migrateBrushV1`), keeping
23
+ * what the engine doesn't read from the brush it was edited from: description, tags, medium and
24
+ * variety, suggested colours, light spill and glow, and motion hints. Hints that would change how
25
+ * it paints are left out.
26
+ */
27
+ export declare function brushSpecOf(next: PaintBrush, from: Brush): Brush;
28
+ /** The engine settings `next` changes on `base`, as `normalizePaintBrush(changes, base)` takes them (lists whole). */
29
+ export declare function engineChanges(base: PaintBrush, next: PaintBrush): Omit<Partial<PaintBrush>, 'id'>;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The paint editor's side of the shared brush library (`@pixerate/editor/brushes`): the
3
+ * controller's synchronous copy of the library (each brush as stored, as facets, and as the
4
+ * engine paints it), and facet brushes for engine brushes edited by their settings.
5
+ */
6
+ import type { StorageAdapter } from '../persistence/storage.js';
7
+ import type { Brush } from '../brushes/types.js';
8
+ import { type PaintBrush } from './brushes.js';
9
+ /** A library brush as the paint editor keeps it: its facets and the engine form it paints with. */
10
+ export interface LibraryBrush {
11
+ spec: Brush;
12
+ brush: PaintBrush;
13
+ builtin: boolean;
14
+ }
15
+ export declare const copyJSON: <T>(v: T) => T;
16
+ export declare function libraryBrush(spec: Brush, builtin: boolean, resolved?: PaintBrush): LibraryBrush;
17
+ /** The built-in presets, paired with the engine brushes they resolve to (`BUILTIN_BRUSHES`, in the same order). */
18
+ export declare function builtinLibraryBrushes(): readonly LibraryBrush[];
19
+ /** Storage that starts with these brushes (a version 1 file) and keeps the rest in memory; none without brushes. */
20
+ export declare function seededStorage(brushes: readonly PaintBrush[]): Pick<StorageAdapter, 'get' | 'set'> | undefined;
21
+ /**
22
+ * The facet brush an engine brush edited by its settings amounts to (`migrateBrushV1`), keeping
23
+ * what the engine doesn't read from the brush it was edited from: description, tags, medium and
24
+ * variety, suggested colours, light spill and glow, and motion hints. Hints that would change how
25
+ * it paints are left out.
26
+ */
27
+ export declare function brushSpecOf(next: PaintBrush, from: Brush): Brush;
28
+ /** The engine settings `next` changes on `base`, as `normalizePaintBrush(changes, base)` takes them (lists whole). */
29
+ export declare function engineChanges(base: PaintBrush, next: PaintBrush): Omit<Partial<PaintBrush>, 'id'>;
@@ -18,7 +18,10 @@ import { type LayerLocks } from '../raster/layers.cjs';
18
18
  import { type LayerEffect, type LayerEffectKind, type LayerEffects } from '../raster/effects/model.cjs';
19
19
  import type { EffectPattern } from '../raster/effects/patterns.cjs';
20
20
  import { TiledSurface, type RasterRect, type Tile } from '../raster/surface.cjs';
21
- import { type PaintBrushInput, type BrushLibraryFile, type PaintBrush } from './brushes.cjs';
21
+ import { type PaintBrushInput, type PaintBrush } from './brushes.cjs';
22
+ import { type BrushLibrary, type BrushLibraryFile } from '../brushes/library.cjs';
23
+ import { type BrushPatch, type UnsettableBrushFacet } from '../brushes/patch.cjs';
24
+ import type { Brush } from '../brushes/types.cjs';
22
25
  import { type PaintCanvas, type PaintDocumentState, type PaintLayer } from './document.cjs';
23
26
  import { type ImageEncoder, type ImageFormat } from './encode.cjs';
24
27
  import type { PenPoint } from './engine.cjs';
@@ -57,7 +60,12 @@ export interface PaintEditorOptions extends Partial<PaintCanvas> {
57
60
  paper?: Partial<PaperSettings> | string;
58
61
  /** Active brush id (default `pencil-6b`). */
59
62
  brushId?: string;
60
- /** Saved user brushes to start the library with. */
63
+ /**
64
+ * The brush library: built-in presets plus saved brushes, on any storage. Pass one library to
65
+ * every editor so a brush saved in one shows in all of them (default: a library in memory).
66
+ */
67
+ brushLibrary?: BrushLibrary;
68
+ /** Saved user brushes to start the default library with (or to add to `brushLibrary`). */
61
69
  brushes?: readonly PaintBrush[];
62
70
  /** Paint colour #rrggbb (default #1f2937). */
63
71
  color?: string;
@@ -296,8 +304,17 @@ export declare class PaintEditorController {
296
304
  private undoStack;
297
305
  private redoStack;
298
306
  private readonly historyLimit;
307
+ /** The brush library this editor lists, saves into and paints from (shared with other editors when the host passes one). */
308
+ readonly brushLibrary: BrushLibrary;
299
309
  private brush;
300
- private userBrushes;
310
+ /** The active brush as facets (what `getBrushSpec` and saving use). */
311
+ private brushSpec;
312
+ /** The library's brushes as last read, each as stored (facets) and as the engine paints it. */
313
+ private brushEntries;
314
+ /** The brush id asked for at construction, while the library had not listed it yet. */
315
+ private pendingBrushId;
316
+ private brushesLoaded;
317
+ private readonly unsubscribeLibrary;
301
318
  /** The library list in the state (the same array until the user brushes change, so thumbnails are not redrawn). */
302
319
  private library;
303
320
  private color;
@@ -673,39 +690,81 @@ export declare class PaintEditorController {
673
690
  source: PaintEffectSource;
674
691
  amount: number;
675
692
  }>;
693
+ private findEntry;
676
694
  private findBrush;
677
- /** A library brush by id (`undefined` when unknown). */
678
- getBrush(id: string): PaintBrush | undefined;
695
+ private requireEntry;
679
696
  private requireBrush;
697
+ /** Reads the library again (after a change anywhere it is shared). */
698
+ private refreshBrushes;
699
+ /** Resolves once the brush library has been read (user brushes kept in storage are listed from then on). */
700
+ whenBrushesLoaded(): Promise<void>;
701
+ /** A library brush by id, as the engine paints it (`undefined` when unknown). */
702
+ getBrush(id: string): PaintBrush | undefined;
703
+ /** A library brush by id as stored (its facets), or without an id the active brush as edited. */
704
+ getBrushSpec(id?: string): Brush | undefined;
680
705
  /**
681
- * Chooses the active brush (`id`: a library brush) and / or changes its settings. Editor state,
682
- * not an undo step. Returns the active brush.
706
+ * Chooses the active brush (`id`: a library brush) and / or changes its engine settings. Editor
707
+ * state, not an undo step. Returns the active brush. `setBrushFacets` changes it by facets.
683
708
  */
684
709
  setBrush(update?: {
685
710
  id?: string;
686
711
  } & Omit<PaintBrushInput, 'id'>): PaintBrush;
687
- /** Saves the active brush (or `brush`) into the user library under `id` / `name`. */
712
+ /**
713
+ * Chooses the active brush (`id`: a library brush) and / or lays a facet patch over it (objects
714
+ * merge, lists and values replace, as the library's `update_brush` does). Editor state, not an
715
+ * undo step. `unset` removes whole facets first. Returns the active brush as the engine paints
716
+ * it, its facets, and what was clamped or dropped.
717
+ */
718
+ setBrushFacets(patch?: BrushPatch, id?: string, unset?: readonly UnsettableBrushFacet[]): {
719
+ brush: PaintBrush;
720
+ spec: Brush;
721
+ problems: string[];
722
+ };
723
+ /**
724
+ * Makes `spec` (a whole facet brush, as the brush editor edits it) the active brush, keeping the
725
+ * active brush's id. Editor state, not an undo step. Returns it as the engine paints it, its
726
+ * facets, and what was clamped or dropped.
727
+ */
728
+ setBrushSpec(spec: Brush): {
729
+ brush: PaintBrush;
730
+ spec: Brush;
731
+ problems: string[];
732
+ };
733
+ /**
734
+ * The engine settings a facet patch changes on a brush (`from`: a library brush id or a brush as
735
+ * painted; default the active brush), as `paintStroke`'s and `updateStroke`'s `brush` take them.
736
+ */
737
+ brushChanges(patch: BrushPatch, from?: string | PaintBrush): Omit<PaintBrushInput, 'id'>;
738
+ private readBrushPatch;
739
+ private useBrush;
740
+ /**
741
+ * Saves the active brush (or `brush`, engine settings on top of it) into the brush library under
742
+ * `id` / `name`, replacing a user brush with that id. Built-in ids are refused. Resolves with the
743
+ * saved brush.
744
+ */
688
745
  saveBrush(options: {
689
746
  id: string;
690
747
  name?: string;
691
748
  brush?: PaintBrushInput;
692
- }): PaintBrush;
693
- removeBrush(id: string): void;
694
- /** The user's saved brushes. */
749
+ }): Promise<PaintBrush>;
750
+ /** Removes a user brush from the brush library (built-in brushes can't be removed). */
751
+ removeBrush(id: string): Promise<void>;
752
+ /** The user's saved brushes, as the engine paints them. */
695
753
  getUserBrushes(): PaintBrush[];
696
- /** A brush library file of the given brushes (default: the user's brushes). */
697
- exportBrushes(ids?: readonly string[]): BrushLibraryFile;
754
+ /** A brush library file (version 2) of the given brushes (default: the user's brushes). */
755
+ exportBrushes(ids?: readonly string[]): Promise<BrushLibraryFile>;
698
756
  /**
699
- * Adds the brushes of a library file to the user library. A brush whose id is taken is skipped
700
- * unless `replace` (built-in ids are always skipped). Returns the ids added and the problems.
757
+ * Adds the brushes of a library file (version 1 or 2) to the brush library. A brush whose id is
758
+ * taken is skipped unless `replace` (built-in ids are always skipped). Resolves with the ids
759
+ * added or replaced, the ids skipped and the problems.
701
760
  */
702
761
  importBrushes(file: unknown, options?: {
703
762
  replace?: boolean;
704
- }): {
763
+ }): Promise<{
705
764
  added: string[];
706
765
  skipped: string[];
707
766
  problems: string[];
708
- };
767
+ }>;
709
768
  /** A thumbnail of a brush: a test stroke on the current paper. */
710
769
  brushThumbnail(id: string, options?: {
711
770
  width?: number;
@@ -18,7 +18,10 @@ import { type LayerLocks } from '../raster/layers.js';
18
18
  import { type LayerEffect, type LayerEffectKind, type LayerEffects } from '../raster/effects/model.js';
19
19
  import type { EffectPattern } from '../raster/effects/patterns.js';
20
20
  import { TiledSurface, type RasterRect, type Tile } from '../raster/surface.js';
21
- import { type PaintBrushInput, type BrushLibraryFile, type PaintBrush } from './brushes.js';
21
+ import { type PaintBrushInput, type PaintBrush } from './brushes.js';
22
+ import { type BrushLibrary, type BrushLibraryFile } from '../brushes/library.js';
23
+ import { type BrushPatch, type UnsettableBrushFacet } from '../brushes/patch.js';
24
+ import type { Brush } from '../brushes/types.js';
22
25
  import { type PaintCanvas, type PaintDocumentState, type PaintLayer } from './document.js';
23
26
  import { type ImageEncoder, type ImageFormat } from './encode.js';
24
27
  import type { PenPoint } from './engine.js';
@@ -57,7 +60,12 @@ export interface PaintEditorOptions extends Partial<PaintCanvas> {
57
60
  paper?: Partial<PaperSettings> | string;
58
61
  /** Active brush id (default `pencil-6b`). */
59
62
  brushId?: string;
60
- /** Saved user brushes to start the library with. */
63
+ /**
64
+ * The brush library: built-in presets plus saved brushes, on any storage. Pass one library to
65
+ * every editor so a brush saved in one shows in all of them (default: a library in memory).
66
+ */
67
+ brushLibrary?: BrushLibrary;
68
+ /** Saved user brushes to start the default library with (or to add to `brushLibrary`). */
61
69
  brushes?: readonly PaintBrush[];
62
70
  /** Paint colour #rrggbb (default #1f2937). */
63
71
  color?: string;
@@ -296,8 +304,17 @@ export declare class PaintEditorController {
296
304
  private undoStack;
297
305
  private redoStack;
298
306
  private readonly historyLimit;
307
+ /** The brush library this editor lists, saves into and paints from (shared with other editors when the host passes one). */
308
+ readonly brushLibrary: BrushLibrary;
299
309
  private brush;
300
- private userBrushes;
310
+ /** The active brush as facets (what `getBrushSpec` and saving use). */
311
+ private brushSpec;
312
+ /** The library's brushes as last read, each as stored (facets) and as the engine paints it. */
313
+ private brushEntries;
314
+ /** The brush id asked for at construction, while the library had not listed it yet. */
315
+ private pendingBrushId;
316
+ private brushesLoaded;
317
+ private readonly unsubscribeLibrary;
301
318
  /** The library list in the state (the same array until the user brushes change, so thumbnails are not redrawn). */
302
319
  private library;
303
320
  private color;
@@ -673,39 +690,81 @@ export declare class PaintEditorController {
673
690
  source: PaintEffectSource;
674
691
  amount: number;
675
692
  }>;
693
+ private findEntry;
676
694
  private findBrush;
677
- /** A library brush by id (`undefined` when unknown). */
678
- getBrush(id: string): PaintBrush | undefined;
695
+ private requireEntry;
679
696
  private requireBrush;
697
+ /** Reads the library again (after a change anywhere it is shared). */
698
+ private refreshBrushes;
699
+ /** Resolves once the brush library has been read (user brushes kept in storage are listed from then on). */
700
+ whenBrushesLoaded(): Promise<void>;
701
+ /** A library brush by id, as the engine paints it (`undefined` when unknown). */
702
+ getBrush(id: string): PaintBrush | undefined;
703
+ /** A library brush by id as stored (its facets), or without an id the active brush as edited. */
704
+ getBrushSpec(id?: string): Brush | undefined;
680
705
  /**
681
- * Chooses the active brush (`id`: a library brush) and / or changes its settings. Editor state,
682
- * not an undo step. Returns the active brush.
706
+ * Chooses the active brush (`id`: a library brush) and / or changes its engine settings. Editor
707
+ * state, not an undo step. Returns the active brush. `setBrushFacets` changes it by facets.
683
708
  */
684
709
  setBrush(update?: {
685
710
  id?: string;
686
711
  } & Omit<PaintBrushInput, 'id'>): PaintBrush;
687
- /** Saves the active brush (or `brush`) into the user library under `id` / `name`. */
712
+ /**
713
+ * Chooses the active brush (`id`: a library brush) and / or lays a facet patch over it (objects
714
+ * merge, lists and values replace, as the library's `update_brush` does). Editor state, not an
715
+ * undo step. `unset` removes whole facets first. Returns the active brush as the engine paints
716
+ * it, its facets, and what was clamped or dropped.
717
+ */
718
+ setBrushFacets(patch?: BrushPatch, id?: string, unset?: readonly UnsettableBrushFacet[]): {
719
+ brush: PaintBrush;
720
+ spec: Brush;
721
+ problems: string[];
722
+ };
723
+ /**
724
+ * Makes `spec` (a whole facet brush, as the brush editor edits it) the active brush, keeping the
725
+ * active brush's id. Editor state, not an undo step. Returns it as the engine paints it, its
726
+ * facets, and what was clamped or dropped.
727
+ */
728
+ setBrushSpec(spec: Brush): {
729
+ brush: PaintBrush;
730
+ spec: Brush;
731
+ problems: string[];
732
+ };
733
+ /**
734
+ * The engine settings a facet patch changes on a brush (`from`: a library brush id or a brush as
735
+ * painted; default the active brush), as `paintStroke`'s and `updateStroke`'s `brush` take them.
736
+ */
737
+ brushChanges(patch: BrushPatch, from?: string | PaintBrush): Omit<PaintBrushInput, 'id'>;
738
+ private readBrushPatch;
739
+ private useBrush;
740
+ /**
741
+ * Saves the active brush (or `brush`, engine settings on top of it) into the brush library under
742
+ * `id` / `name`, replacing a user brush with that id. Built-in ids are refused. Resolves with the
743
+ * saved brush.
744
+ */
688
745
  saveBrush(options: {
689
746
  id: string;
690
747
  name?: string;
691
748
  brush?: PaintBrushInput;
692
- }): PaintBrush;
693
- removeBrush(id: string): void;
694
- /** The user's saved brushes. */
749
+ }): Promise<PaintBrush>;
750
+ /** Removes a user brush from the brush library (built-in brushes can't be removed). */
751
+ removeBrush(id: string): Promise<void>;
752
+ /** The user's saved brushes, as the engine paints them. */
695
753
  getUserBrushes(): PaintBrush[];
696
- /** A brush library file of the given brushes (default: the user's brushes). */
697
- exportBrushes(ids?: readonly string[]): BrushLibraryFile;
754
+ /** A brush library file (version 2) of the given brushes (default: the user's brushes). */
755
+ exportBrushes(ids?: readonly string[]): Promise<BrushLibraryFile>;
698
756
  /**
699
- * Adds the brushes of a library file to the user library. A brush whose id is taken is skipped
700
- * unless `replace` (built-in ids are always skipped). Returns the ids added and the problems.
757
+ * Adds the brushes of a library file (version 1 or 2) to the brush library. A brush whose id is
758
+ * taken is skipped unless `replace` (built-in ids are always skipped). Resolves with the ids
759
+ * added or replaced, the ids skipped and the problems.
701
760
  */
702
761
  importBrushes(file: unknown, options?: {
703
762
  replace?: boolean;
704
- }): {
763
+ }): Promise<{
705
764
  added: string[];
706
765
  skipped: string[];
707
766
  problems: string[];
708
- };
767
+ }>;
709
768
  /** A thumbnail of a brush: a test stroke on the current paper. */
710
769
  brushThumbnail(id: string, options?: {
711
770
  width?: number;