@pixerate/editor 0.250.0 → 0.252.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 (137) hide show
  1. package/dist/agent/index.cjs +40 -41
  2. package/dist/agent/index.js +9 -10
  3. package/dist/animation/index.cjs +6 -7
  4. package/dist/animation/index.js +5 -6
  5. package/dist/animation-editor/index.cjs +70 -70
  6. package/dist/animation-editor/index.js +12 -12
  7. package/dist/brushes/index.cjs +2087 -0
  8. package/dist/brushes/index.d.cts +17 -0
  9. package/dist/brushes/index.d.ts +17 -0
  10. package/dist/brushes/index.js +2087 -0
  11. package/dist/brushes/library.d.cts +104 -0
  12. package/dist/brushes/library.d.ts +104 -0
  13. package/dist/brushes/migrate.d.cts +17 -0
  14. package/dist/brushes/migrate.d.ts +17 -0
  15. package/dist/brushes/normalize.d.cts +15 -0
  16. package/dist/brushes/normalize.d.ts +15 -0
  17. package/dist/brushes/presets.d.cts +12 -0
  18. package/dist/brushes/presets.d.ts +12 -0
  19. package/dist/brushes/profiles.d.cts +85 -0
  20. package/dist/brushes/profiles.d.ts +85 -0
  21. package/dist/brushes/resolve.d.cts +56 -0
  22. package/dist/brushes/resolve.d.ts +56 -0
  23. package/dist/brushes/schema.d.cts +29 -0
  24. package/dist/brushes/schema.d.ts +29 -0
  25. package/dist/brushes/tools.d.cts +37 -0
  26. package/dist/brushes/tools.d.ts +37 -0
  27. package/dist/brushes/types.d.cts +323 -0
  28. package/dist/brushes/types.d.ts +323 -0
  29. package/dist/{brushstrokeRender-2FHFLSEG.js → brushstrokeRender-5PYSKZIJ.js} +8 -8
  30. package/dist/{brushstrokeRender-QGRXOKDA.cjs → brushstrokeRender-7FLLSQEC.cjs} +14 -14
  31. package/dist/canvas/index.cjs +57 -58
  32. package/dist/canvas/index.js +31 -32
  33. package/dist/chart/index.cjs +159 -160
  34. package/dist/chart/index.js +9 -10
  35. package/dist/{chunk-NOHJP5TK.js → chunk-2ZJ4DT3M.js} +1 -1
  36. package/dist/{chunk-XZNU4DUX.cjs → chunk-3NS5C7FB.cjs} +76 -76
  37. package/dist/{chunk-UTI5IITZ.cjs → chunk-4ZC5QJE2.cjs} +3 -3
  38. package/dist/{chunk-SMSGP2HQ.js → chunk-5JNA6LLY.js} +1 -1
  39. package/dist/{chunk-RD7RX52G.cjs → chunk-5UEFBM2M.cjs} +207 -18
  40. package/dist/{chunk-WA44NKPF.js → chunk-62Z5WLRA.js} +1 -1
  41. package/dist/{chunk-BVRRIW7F.js → chunk-6GRUVGKU.js} +3 -3
  42. package/dist/{chunk-QARCM66V.js → chunk-6ZZKRH7R.js} +3 -3
  43. package/dist/{chunk-BP6KSGVD.js → chunk-7272D3K2.js} +4 -6
  44. package/dist/{chunk-NTRAVM2I.js → chunk-7P6VF3FG.js} +2 -2
  45. package/dist/chunk-APX63ILU.js +173 -0
  46. package/dist/{chunk-W7L4HUT2.cjs → chunk-CJ6TBL2N.cjs} +16 -16
  47. package/dist/{chunk-7PT4RJL5.cjs → chunk-EUIDTZNQ.cjs} +111 -113
  48. package/dist/{chunk-CXXXAZ7H.js → chunk-F6R67I3A.js} +1 -1
  49. package/dist/{chunk-I7NJPHWV.cjs → chunk-FQPUF2W2.cjs} +35 -606
  50. package/dist/{chunk-D5KGAO4Z.js → chunk-GCVFEPJG.js} +2 -573
  51. package/dist/{chunk-LEQKZGQI.js → chunk-GOBOCZZI.js} +1 -1
  52. package/dist/{chunk-KDLBJ6QR.js → chunk-GYMMNCFT.js} +3 -3
  53. package/dist/{chunk-NUTMINJZ.cjs → chunk-H7ET2ISK.cjs} +52 -52
  54. package/dist/chunk-HS6QDWDF.cjs +1029 -0
  55. package/dist/{chunk-T7KQEMAE.cjs → chunk-HUAISCUX.cjs} +18 -18
  56. package/dist/{chunk-YGQVCQTG.js → chunk-ILP77UD6.js} +1 -1
  57. package/dist/{chunk-S6OT6CTS.cjs → chunk-IWEVV63O.cjs} +31 -33
  58. package/dist/{chunk-M2VKFE5L.cjs → chunk-JFHBXNR2.cjs} +88 -88
  59. package/dist/{chunk-WPUXGDH4.cjs → chunk-JPEHXIB5.cjs} +10 -10
  60. package/dist/chunk-JSDND7DF.cjs +173 -0
  61. package/dist/{chunk-36ZTV7WO.js → chunk-L6Y7TSQ4.js} +1 -1
  62. package/dist/{chunk-YW43M56C.cjs → chunk-LFMKWGFL.cjs} +1 -1
  63. package/dist/{chunk-V6DT2CDH.js → chunk-M2TPMYJE.js} +1 -1
  64. package/dist/{chunk-LJZRHVZV.cjs → chunk-M5GLQQWG.cjs} +2 -2
  65. package/dist/{chunk-JEGJP4OW.js → chunk-MHLU6GRJ.js} +196 -7
  66. package/dist/{chunk-KMPM5JM5.js → chunk-MONXNVZK.js} +4 -6
  67. package/dist/chunk-MTKY6K3Q.js +1029 -0
  68. package/dist/{chunk-MOW3JMNG.cjs → chunk-N76AMOKE.cjs} +42 -42
  69. package/dist/{chunk-75K3W4D6.cjs → chunk-NKGB64CW.cjs} +16 -16
  70. package/dist/{chunk-SGPIVHJB.js → chunk-P3K2JCJA.js} +3 -3
  71. package/dist/chunk-PXDIAYB6.cjs +594 -0
  72. package/dist/{chunk-3TWOCSCY.js → chunk-QC3OXRE4.js} +9 -9
  73. package/dist/{chunk-YM6ZBTXG.cjs → chunk-QLSGTCYT.cjs} +23 -23
  74. package/dist/{chunk-7YW7T5TJ.js → chunk-RHPSE2YR.js} +2 -2
  75. package/dist/{chunk-44BMJJVA.cjs → chunk-RUONPPMC.cjs} +8 -8
  76. package/dist/{chunk-ATK6ABN3.cjs → chunk-SABJMH6M.cjs} +145 -12
  77. package/dist/{chunk-XWOLPNBW.cjs → chunk-SOSBOS5T.cjs} +22 -22
  78. package/dist/{chunk-PPFJHTW5.cjs → chunk-TFF2GTGN.cjs} +22 -24
  79. package/dist/{chunk-X7WRL5ZZ.cjs → chunk-TLOGFIIV.cjs} +37 -37
  80. package/dist/chunk-TXFWZ5ZJ.js +594 -0
  81. package/dist/{chunk-GXAK6Q5J.js → chunk-UFEDHACQ.js} +2 -2
  82. package/dist/{chunk-GA4EU4P3.cjs → chunk-UXETV2NC.cjs} +4 -4
  83. package/dist/{chunk-N4DFDI73.js → chunk-WKMUBATU.js} +1 -1
  84. package/dist/{chunk-3R5P3DVY.cjs → chunk-WRZ6C4TU.cjs} +6 -6
  85. package/dist/{chunk-U4NUOKBT.js → chunk-WZOGDKCQ.js} +134 -1
  86. package/dist/{chunk-6ADHGQ73.js → chunk-YBAPT7NL.js} +2 -4
  87. package/dist/color/index.cjs +4 -6
  88. package/dist/color/index.js +7 -9
  89. package/dist/export/index.cjs +7 -8
  90. package/dist/export/index.js +6 -7
  91. package/dist/extensions/index.cjs +10 -11
  92. package/dist/extensions/index.js +9 -10
  93. package/dist/gradient/index.cjs +33 -34
  94. package/dist/gradient/index.js +4 -5
  95. package/dist/image-editor/index.cjs +845 -844
  96. package/dist/image-editor/index.js +39 -38
  97. package/dist/index.cjs +16 -17
  98. package/dist/index.js +10 -11
  99. package/dist/paint/index.cjs +6 -4
  100. package/dist/paint/index.js +21 -19
  101. package/dist/paint-editor/brushes.d.cts +1 -2
  102. package/dist/paint-editor/brushes.d.ts +1 -2
  103. package/dist/paint-editor/index.cjs +2193 -694
  104. package/dist/paint-editor/index.js +1575 -76
  105. package/dist/paint-editor/mark-settings.d.cts +104 -0
  106. package/dist/paint-editor/mark-settings.d.ts +104 -0
  107. package/dist/paint-editor/procedural.d.cts +2 -75
  108. package/dist/paint-editor/procedural.d.ts +2 -75
  109. package/dist/paint-editor/stickers.d.cts +1 -22
  110. package/dist/paint-editor/stickers.d.ts +1 -22
  111. package/dist/raster/index.cjs +2 -2
  112. package/dist/raster/index.js +9 -9
  113. package/dist/stickers/index.cjs +10 -10
  114. package/dist/stickers/index.js +9 -9
  115. package/dist/tokens/index.cjs +3 -4
  116. package/dist/tokens/index.js +2 -3
  117. package/dist/tools/skill-evals.d.cts +8 -1
  118. package/dist/tools/skill-evals.d.ts +8 -1
  119. package/dist/typography/index.cjs +7 -8
  120. package/dist/typography/index.js +2 -3
  121. package/dist/vector/index.cjs +974 -974
  122. package/dist/vector/index.js +12 -12
  123. package/dist/video-editor/index.cjs +6 -7
  124. package/dist/video-editor/index.js +5 -6
  125. package/package.json +11 -1
  126. package/skills/README.md +2 -0
  127. package/skills/pixerate-brushes/SKILL.md +104 -0
  128. package/skills/pixerate-brushes-design/SKILL.md +88 -0
  129. package/skills/pixerate-brushes-design/evals/canvas-chalk.json +15 -0
  130. package/skills/pixerate-brushes-design/evals/pink-highlighter.json +15 -0
  131. package/skills/pixerate-brushes-design/evals/port-to-vector.json +17 -0
  132. package/dist/chunk-4JNWA3VT.cjs +0 -302
  133. package/dist/chunk-7AHBW2CQ.cjs +0 -200
  134. package/dist/chunk-F67XMVDI.js +0 -200
  135. package/dist/chunk-GT77U7QG.cjs +0 -2637
  136. package/dist/chunk-OTEPAQJX.js +0 -2637
  137. package/dist/chunk-WKH5ZAUA.js +0 -302
@@ -0,0 +1,104 @@
1
+ /**
2
+ * The brush library: the built-in presets plus the user's brushes, kept on any `StorageAdapter`
3
+ * (`createMemoryStorage`, `createLocalStorageAdapter`, `createIndexedDbAdapter`,
4
+ * `createOpfsAdapter`) or, without one, in memory. One library shared by every editor makes a
5
+ * brush saved in one show up in all of them.
6
+ *
7
+ * The user's brushes are stored as one `pixerate-brushes` version 2 file under one key. Library
8
+ * files of version 1 (the paint editor's flat brushes) still import: each brush is migrated.
9
+ */
10
+ import type { StorageAdapter } from '../persistence/storage.cjs';
11
+ import { type BrushHostProfile } from './profiles.cjs';
12
+ import type { Brush, BrushKind, BrushMedium, BrushTool } from './types.cjs';
13
+ /** The JSON file a brush library exports to and imports from. */
14
+ export interface BrushLibraryFile {
15
+ format: 'pixerate-brushes';
16
+ version: 2;
17
+ brushes: Brush[];
18
+ }
19
+ export type BrushLibraryErrorCode = 'unknown_brush' | 'builtin' | 'exists' | 'invalid';
20
+ /** A refusal; the message says what to do instead. */
21
+ export declare class BrushLibraryError extends Error {
22
+ readonly code: BrushLibraryErrorCode;
23
+ constructor(code: BrushLibraryErrorCode, message: string);
24
+ }
25
+ export interface BrushLibraryEntry {
26
+ brush: Brush;
27
+ /** A built-in preset: it can be duplicated but not changed or removed. */
28
+ builtin: boolean;
29
+ }
30
+ export interface BrushFilter {
31
+ kind?: BrushKind;
32
+ medium?: BrushMedium;
33
+ variety?: string;
34
+ tool?: BrushTool;
35
+ /** Has this tag. */
36
+ tag?: string;
37
+ /** Words in the id, name, description, variety or tags (case-insensitive; every word must match). */
38
+ text?: string;
39
+ /** Only built-in presets or only the user's brushes. */
40
+ source?: 'builtin' | 'user';
41
+ /** Only brushes this host can render (`checkBrush` has no errors). */
42
+ profile?: BrushHostProfile;
43
+ }
44
+ export interface SaveBrushResult {
45
+ /** The brush as stored (normalised). */
46
+ brush: Brush;
47
+ /** What normalising changed. */
48
+ problems: string[];
49
+ /** It replaced a user brush of the same id. */
50
+ replaced: boolean;
51
+ }
52
+ export interface ImportBrushesResult {
53
+ added: string[];
54
+ replaced: string[];
55
+ skipped: string[];
56
+ problems: string[];
57
+ }
58
+ export interface BrushLibraryOptions {
59
+ /** Where the user's brushes are kept (default: memory only, gone on reload). */
60
+ storage?: Pick<StorageAdapter, 'get' | 'set'>;
61
+ /** Storage key prefix (default `pixerate.brushes`). */
62
+ namespace?: string;
63
+ /** The built-in presets (default `BRUSH_PRESETS`). */
64
+ presets?: readonly Brush[];
65
+ }
66
+ export interface BrushLibrary {
67
+ /** Built-in presets first (in library order), then the user's brushes (in the order saved). */
68
+ list(filter?: BrushFilter): Promise<BrushLibraryEntry[]>;
69
+ get(id: string): Promise<BrushLibraryEntry | null>;
70
+ isBuiltin(id: string): boolean;
71
+ /**
72
+ * Stores a user brush (normalised first). Refuses a built-in id and, without `overwrite`, an
73
+ * id already taken.
74
+ */
75
+ save(brush: unknown, options?: {
76
+ overwrite?: boolean;
77
+ }): Promise<SaveBrushResult>;
78
+ /** Removes a user brush; refuses a built-in one. False when there was no such brush. */
79
+ remove(id: string): Promise<boolean>;
80
+ /** Copies any brush (built-ins included) to a new user brush: `id` default `<id>-copy`, `-copy-2`…; `name` default `<name> copy`. */
81
+ duplicate(id: string, options?: {
82
+ id?: string;
83
+ name?: string;
84
+ }): Promise<Brush>;
85
+ /** A library file of the user's brushes, or of the given ids (built-ins included). */
86
+ exportFile(ids?: readonly string[]): Promise<BrushLibraryFile>;
87
+ /** Adds the brushes of a library file (version 1 or 2, or a bare array). Built-in ids are skipped; taken ids too, unless `overwrite`. */
88
+ importFile(raw: unknown, options?: {
89
+ overwrite?: boolean;
90
+ }): Promise<ImportBrushesResult>;
91
+ /** Called after every change. Returns an unsubscribe function. */
92
+ subscribe(listener: () => void): () => void;
93
+ }
94
+ /**
95
+ * Brushes read from a library file: `{ format: 'pixerate-brushes', version: 1 | 2, brushes }` or
96
+ * a bare array. Version 1 brushes are migrated (`migrateBrushV1`); every brush is normalised.
97
+ * Brushes without a valid id or name are skipped, and everything fixed or skipped is reported
98
+ * in `problems`.
99
+ */
100
+ export declare function parseBrushLibrary(raw: unknown, problems?: string[]): Brush[];
101
+ /** A brush library over `options.storage` (see the module comment). */
102
+ export declare function createBrushLibrary(options?: BrushLibraryOptions): BrushLibrary;
103
+ /** "Closest: a, b. " for an unknown id (shared prefix or substring), or "" when nothing is near. */
104
+ export declare function closest(id: string, ids: readonly string[], max?: number): string;
@@ -0,0 +1,104 @@
1
+ /**
2
+ * The brush library: the built-in presets plus the user's brushes, kept on any `StorageAdapter`
3
+ * (`createMemoryStorage`, `createLocalStorageAdapter`, `createIndexedDbAdapter`,
4
+ * `createOpfsAdapter`) or, without one, in memory. One library shared by every editor makes a
5
+ * brush saved in one show up in all of them.
6
+ *
7
+ * The user's brushes are stored as one `pixerate-brushes` version 2 file under one key. Library
8
+ * files of version 1 (the paint editor's flat brushes) still import: each brush is migrated.
9
+ */
10
+ import type { StorageAdapter } from '../persistence/storage.js';
11
+ import { type BrushHostProfile } from './profiles.js';
12
+ import type { Brush, BrushKind, BrushMedium, BrushTool } from './types.js';
13
+ /** The JSON file a brush library exports to and imports from. */
14
+ export interface BrushLibraryFile {
15
+ format: 'pixerate-brushes';
16
+ version: 2;
17
+ brushes: Brush[];
18
+ }
19
+ export type BrushLibraryErrorCode = 'unknown_brush' | 'builtin' | 'exists' | 'invalid';
20
+ /** A refusal; the message says what to do instead. */
21
+ export declare class BrushLibraryError extends Error {
22
+ readonly code: BrushLibraryErrorCode;
23
+ constructor(code: BrushLibraryErrorCode, message: string);
24
+ }
25
+ export interface BrushLibraryEntry {
26
+ brush: Brush;
27
+ /** A built-in preset: it can be duplicated but not changed or removed. */
28
+ builtin: boolean;
29
+ }
30
+ export interface BrushFilter {
31
+ kind?: BrushKind;
32
+ medium?: BrushMedium;
33
+ variety?: string;
34
+ tool?: BrushTool;
35
+ /** Has this tag. */
36
+ tag?: string;
37
+ /** Words in the id, name, description, variety or tags (case-insensitive; every word must match). */
38
+ text?: string;
39
+ /** Only built-in presets or only the user's brushes. */
40
+ source?: 'builtin' | 'user';
41
+ /** Only brushes this host can render (`checkBrush` has no errors). */
42
+ profile?: BrushHostProfile;
43
+ }
44
+ export interface SaveBrushResult {
45
+ /** The brush as stored (normalised). */
46
+ brush: Brush;
47
+ /** What normalising changed. */
48
+ problems: string[];
49
+ /** It replaced a user brush of the same id. */
50
+ replaced: boolean;
51
+ }
52
+ export interface ImportBrushesResult {
53
+ added: string[];
54
+ replaced: string[];
55
+ skipped: string[];
56
+ problems: string[];
57
+ }
58
+ export interface BrushLibraryOptions {
59
+ /** Where the user's brushes are kept (default: memory only, gone on reload). */
60
+ storage?: Pick<StorageAdapter, 'get' | 'set'>;
61
+ /** Storage key prefix (default `pixerate.brushes`). */
62
+ namespace?: string;
63
+ /** The built-in presets (default `BRUSH_PRESETS`). */
64
+ presets?: readonly Brush[];
65
+ }
66
+ export interface BrushLibrary {
67
+ /** Built-in presets first (in library order), then the user's brushes (in the order saved). */
68
+ list(filter?: BrushFilter): Promise<BrushLibraryEntry[]>;
69
+ get(id: string): Promise<BrushLibraryEntry | null>;
70
+ isBuiltin(id: string): boolean;
71
+ /**
72
+ * Stores a user brush (normalised first). Refuses a built-in id and, without `overwrite`, an
73
+ * id already taken.
74
+ */
75
+ save(brush: unknown, options?: {
76
+ overwrite?: boolean;
77
+ }): Promise<SaveBrushResult>;
78
+ /** Removes a user brush; refuses a built-in one. False when there was no such brush. */
79
+ remove(id: string): Promise<boolean>;
80
+ /** Copies any brush (built-ins included) to a new user brush: `id` default `<id>-copy`, `-copy-2`…; `name` default `<name> copy`. */
81
+ duplicate(id: string, options?: {
82
+ id?: string;
83
+ name?: string;
84
+ }): Promise<Brush>;
85
+ /** A library file of the user's brushes, or of the given ids (built-ins included). */
86
+ exportFile(ids?: readonly string[]): Promise<BrushLibraryFile>;
87
+ /** Adds the brushes of a library file (version 1 or 2, or a bare array). Built-in ids are skipped; taken ids too, unless `overwrite`. */
88
+ importFile(raw: unknown, options?: {
89
+ overwrite?: boolean;
90
+ }): Promise<ImportBrushesResult>;
91
+ /** Called after every change. Returns an unsubscribe function. */
92
+ subscribe(listener: () => void): () => void;
93
+ }
94
+ /**
95
+ * Brushes read from a library file: `{ format: 'pixerate-brushes', version: 1 | 2, brushes }` or
96
+ * a bare array. Version 1 brushes are migrated (`migrateBrushV1`); every brush is normalised.
97
+ * Brushes without a valid id or name are skipped, and everything fixed or skipped is reported
98
+ * in `problems`.
99
+ */
100
+ export declare function parseBrushLibrary(raw: unknown, problems?: string[]): Brush[];
101
+ /** A brush library over `options.storage` (see the module comment). */
102
+ export declare function createBrushLibrary(options?: BrushLibraryOptions): BrushLibrary;
103
+ /** "Closest: a, b. " for an unknown id (shared prefix or substring), or "" when nothing is near. */
104
+ export declare function closest(id: string, ids: readonly string[], max?: number): string;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Version 1 brushes (`PaintBrush`, the flat engine form) to facet brushes. Files, documents and
3
+ * recorded strokes written before the facet model still load: each v1 brush becomes the sparse
4
+ * facet brush that resolves back to it.
5
+ *
6
+ * What the engine reads survives exactly. Settings of engine blocks the brush's engine never
7
+ * reads (a dab brush's mixer settings) are dropped, and the category is derived again
8
+ * (`resolveCategory`), so a v1 brush filed under a category its settings do not match moves.
9
+ */
10
+ import { type PaintBrushInput } from '../paint-editor/brushes.cjs';
11
+ import type { Brush } from './types.cjs';
12
+ /**
13
+ * The facet brush a v1 brush (any `PaintBrush`, or the JSON of one) amounts to: its engine's
14
+ * settings, stored only where they differ from that engine's defaults.
15
+ * `resolveBrush(migrateBrushV1(b))` paints exactly as `b` does.
16
+ */
17
+ export declare function migrateBrushV1(raw: PaintBrushInput): Brush;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Version 1 brushes (`PaintBrush`, the flat engine form) to facet brushes. Files, documents and
3
+ * recorded strokes written before the facet model still load: each v1 brush becomes the sparse
4
+ * facet brush that resolves back to it.
5
+ *
6
+ * What the engine reads survives exactly. Settings of engine blocks the brush's engine never
7
+ * reads (a dab brush's mixer settings) are dropped, and the category is derived again
8
+ * (`resolveCategory`), so a v1 brush filed under a category its settings do not match moves.
9
+ */
10
+ import { type PaintBrushInput } from '../paint-editor/brushes.js';
11
+ import type { Brush } from './types.js';
12
+ /**
13
+ * The facet brush a v1 brush (any `PaintBrush`, or the JSON of one) amounts to: its engine's
14
+ * settings, stored only where they differ from that engine's defaults.
15
+ * `resolveBrush(migrateBrushV1(b))` paints exactly as `b` does.
16
+ */
17
+ export declare function migrateBrushV1(raw: PaintBrushInput): Brush;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * `normalizeBrushSpec`: a brush with every given field checked against `BRUSH_JSON_SCHEMA`. It
3
+ * stays sparse (missing fields are not filled in): numbers are clamped into range, integers
4
+ * rounded, unknown fields, bad values and facets the brush does not use are dropped, and each
5
+ * change is reported in `problems` so a caller (or an agent) sees what was fixed. Only an id,
6
+ * name or kind it cannot make sense of throws.
7
+ */
8
+ import { type Brush, type BrushTool } from './types.cjs';
9
+ /** The tools that work on the wet layer. */
10
+ export declare const WET_BRUSH_TOOLS: readonly BrushTool[];
11
+ /**
12
+ * A brush with every given field in range (see the module comment). Throws when the id, name or
13
+ * kind cannot be used; every other fix is reported in `problems`.
14
+ */
15
+ export declare function normalizeBrushSpec(raw: unknown, problems?: string[]): Brush;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * `normalizeBrushSpec`: a brush with every given field checked against `BRUSH_JSON_SCHEMA`. It
3
+ * stays sparse (missing fields are not filled in): numbers are clamped into range, integers
4
+ * rounded, unknown fields, bad values and facets the brush does not use are dropped, and each
5
+ * change is reported in `problems` so a caller (or an agent) sees what was fixed. Only an id,
6
+ * name or kind it cannot make sense of throws.
7
+ */
8
+ import { type Brush, type BrushTool } from './types.js';
9
+ /** The tools that work on the wet layer. */
10
+ export declare const WET_BRUSH_TOOLS: readonly BrushTool[];
11
+ /**
12
+ * A brush with every given field in range (see the module comment). Throws when the id, name or
13
+ * kind cannot be used; every other fix is reported in `problems`.
14
+ */
15
+ export declare function normalizeBrushSpec(raw: unknown, problems?: string[]): Brush;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The built-in brushes as facet specs. Each resolves (`resolveBrush`) to exactly the paint
3
+ * editor's built-in brush of the same id (`BUILTIN_BRUSHES`, which `tests/brushes.test.ts`
4
+ * checks), so the engine paints them as it always has. Medium and variety are new: they file
5
+ * each brush under what it is for browsing and filtering. Only what differs from the engine's
6
+ * defaults is written down.
7
+ *
8
+ * Our own settings; none come from another product.
9
+ */
10
+ import type { Brush } from './types.cjs';
11
+ /** The built-in brush library, in the paint editor's order. */
12
+ export declare const BRUSH_PRESETS: readonly Brush[];
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The built-in brushes as facet specs. Each resolves (`resolveBrush`) to exactly the paint
3
+ * editor's built-in brush of the same id (`BUILTIN_BRUSHES`, which `tests/brushes.test.ts`
4
+ * checks), so the engine paints them as it always has. Medium and variety are new: they file
5
+ * each brush under what it is for browsing and filtering. Only what differs from the engine's
6
+ * defaults is written down.
7
+ *
8
+ * Our own settings; none come from another product.
9
+ */
10
+ import type { Brush } from './types.js';
11
+ /** The built-in brush library, in the paint editor's order. */
12
+ export declare const BRUSH_PRESETS: readonly Brush[];
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Host capability profiles: what a host (the paint editor, the image editor, brushed canvas and
3
+ * vector strokes, later Swift) can render. `checkBrush` explains each refusal with a fix,
4
+ * `listPresets` filters a library down to what the host can use, and `degrade` strips the facets
5
+ * a host can't render and says what it dropped.
6
+ *
7
+ * A capability is `full` (rendered), `ignored` (the brush is accepted but the host paints without
8
+ * it: a warning) or `none` (the brush is refused: an error).
9
+ */
10
+ import { type Brush, type BrushKind, type BrushMarksKind, type BrushPaint, type BrushSurface, type BrushTipShape, type BrushTool } from './types.cjs';
11
+ export type BrushSupport = 'full' | 'ignored' | 'none';
12
+ /** What a colour is: a solid colour, a light / dark pair, a gradient or effect paint. */
13
+ export type BrushColourKind = 'solid' | 'dual' | 'gradient' | 'effect';
14
+ export interface BrushHostProfile {
15
+ /** `paint`, `image`, `canvas`, `vector`… */
16
+ id: string;
17
+ name: string;
18
+ kinds: readonly BrushKind[];
19
+ /** Tools the host has (for `kind: 'tool'`). */
20
+ tools: readonly BrushTool[];
21
+ tipShapes: readonly BrushTipShape[];
22
+ marks: readonly BrushMarksKind[];
23
+ surfaces: readonly BrushSurface[];
24
+ /** Picking paint up and mixing it in (`behaviour.exchange` pickup, mix, dirty). */
25
+ exchange: BrushSupport;
26
+ /** The Mixer Brush (`behaviour.exchange.reservoir`). */
27
+ reservoir: BrushSupport;
28
+ /** Paper grain response (`behaviour.grain`). */
29
+ grain: BrushSupport;
30
+ light: BrushSupport;
31
+ motion: BrushSupport;
32
+ /** Colour kinds the brush's default colour may be (palette entries of other kinds are warned about). */
33
+ colours: readonly BrushColourKind[];
34
+ /** Velocity dynamics (needs timed input). */
35
+ velocity: BrushSupport;
36
+ /** Tilt dynamics (needs pen tilt). */
37
+ tilt: BrushSupport;
38
+ }
39
+ export interface BrushIssue {
40
+ /** The facet path the issue is about, e.g. `tip.shape`, `light`, `behaviour.exchange.reservoir`. */
41
+ facet: string;
42
+ /** `error`: the host can't render the brush; `warning`: it renders it without this setting. */
43
+ severity: 'error' | 'warning';
44
+ reason: string;
45
+ fix: string;
46
+ }
47
+ export interface BrushCheck {
48
+ /** No errors (warnings may remain). */
49
+ ok: boolean;
50
+ issues: BrushIssue[];
51
+ }
52
+ /** The paint editor: every facet. */
53
+ export declare const PAINT_PROFILE: BrushHostProfile;
54
+ /** The image editor's brush: dry dabs on pixels, its Mixer Brush and the eraser. */
55
+ export declare const IMAGE_PROFILE: BrushHostProfile;
56
+ /**
57
+ * Brushed canvas strokes: dry dab brushes along a path. Light paint needs the paint editor's
58
+ * light channel (glow and spill), which canvas strokes don't have, so it is refused.
59
+ */
60
+ export declare const CANVAS_PROFILE: BrushHostProfile;
61
+ /** Vector brushstrokes: dry dab brushes along a path; light brushes paint as plain colour. */
62
+ export declare const VECTOR_PROFILE: BrushHostProfile;
63
+ /** The built-in profiles by id. */
64
+ export declare const BRUSH_HOST_PROFILES: Readonly<Record<string, BrushHostProfile>>;
65
+ /** What kind of colour a `BrushPaint` is. */
66
+ export declare function brushColourKind(paint: BrushPaint): BrushColourKind;
67
+ /** What a brush needs from a host, and what the host does about it. */
68
+ export declare function checkBrush(brush: Brush, profile: BrushHostProfile): BrushCheck;
69
+ /** The presets (default: the built-in ones) a host can render, in library order. */
70
+ export declare function listPresets(profile: BrushHostProfile, presets?: readonly Brush[]): Brush[];
71
+ export interface DegradedBrush {
72
+ /** The brush with what the host can't render stripped (a copy). */
73
+ brush: Brush;
74
+ /** The errors fixed by stripping a facet. */
75
+ dropped: BrushIssue[];
76
+ /** Errors that stripping cannot fix (an unsupported kind or tool): the host still refuses it. */
77
+ issues: BrushIssue[];
78
+ }
79
+ /**
80
+ * The brush the host can render: facets it refuses are removed (light, motion, a reservoir, a
81
+ * wet or thick surface, particle or object marks, bristle and stamp tips become round, an
82
+ * unsupported default colour), each reported in `dropped`. A tool the host lacks can't be
83
+ * stripped and stays in `issues`.
84
+ */
85
+ export declare function degrade(brush: Brush, profile: BrushHostProfile): DegradedBrush;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Host capability profiles: what a host (the paint editor, the image editor, brushed canvas and
3
+ * vector strokes, later Swift) can render. `checkBrush` explains each refusal with a fix,
4
+ * `listPresets` filters a library down to what the host can use, and `degrade` strips the facets
5
+ * a host can't render and says what it dropped.
6
+ *
7
+ * A capability is `full` (rendered), `ignored` (the brush is accepted but the host paints without
8
+ * it: a warning) or `none` (the brush is refused: an error).
9
+ */
10
+ import { type Brush, type BrushKind, type BrushMarksKind, type BrushPaint, type BrushSurface, type BrushTipShape, type BrushTool } from './types.js';
11
+ export type BrushSupport = 'full' | 'ignored' | 'none';
12
+ /** What a colour is: a solid colour, a light / dark pair, a gradient or effect paint. */
13
+ export type BrushColourKind = 'solid' | 'dual' | 'gradient' | 'effect';
14
+ export interface BrushHostProfile {
15
+ /** `paint`, `image`, `canvas`, `vector`… */
16
+ id: string;
17
+ name: string;
18
+ kinds: readonly BrushKind[];
19
+ /** Tools the host has (for `kind: 'tool'`). */
20
+ tools: readonly BrushTool[];
21
+ tipShapes: readonly BrushTipShape[];
22
+ marks: readonly BrushMarksKind[];
23
+ surfaces: readonly BrushSurface[];
24
+ /** Picking paint up and mixing it in (`behaviour.exchange` pickup, mix, dirty). */
25
+ exchange: BrushSupport;
26
+ /** The Mixer Brush (`behaviour.exchange.reservoir`). */
27
+ reservoir: BrushSupport;
28
+ /** Paper grain response (`behaviour.grain`). */
29
+ grain: BrushSupport;
30
+ light: BrushSupport;
31
+ motion: BrushSupport;
32
+ /** Colour kinds the brush's default colour may be (palette entries of other kinds are warned about). */
33
+ colours: readonly BrushColourKind[];
34
+ /** Velocity dynamics (needs timed input). */
35
+ velocity: BrushSupport;
36
+ /** Tilt dynamics (needs pen tilt). */
37
+ tilt: BrushSupport;
38
+ }
39
+ export interface BrushIssue {
40
+ /** The facet path the issue is about, e.g. `tip.shape`, `light`, `behaviour.exchange.reservoir`. */
41
+ facet: string;
42
+ /** `error`: the host can't render the brush; `warning`: it renders it without this setting. */
43
+ severity: 'error' | 'warning';
44
+ reason: string;
45
+ fix: string;
46
+ }
47
+ export interface BrushCheck {
48
+ /** No errors (warnings may remain). */
49
+ ok: boolean;
50
+ issues: BrushIssue[];
51
+ }
52
+ /** The paint editor: every facet. */
53
+ export declare const PAINT_PROFILE: BrushHostProfile;
54
+ /** The image editor's brush: dry dabs on pixels, its Mixer Brush and the eraser. */
55
+ export declare const IMAGE_PROFILE: BrushHostProfile;
56
+ /**
57
+ * Brushed canvas strokes: dry dab brushes along a path. Light paint needs the paint editor's
58
+ * light channel (glow and spill), which canvas strokes don't have, so it is refused.
59
+ */
60
+ export declare const CANVAS_PROFILE: BrushHostProfile;
61
+ /** Vector brushstrokes: dry dab brushes along a path; light brushes paint as plain colour. */
62
+ export declare const VECTOR_PROFILE: BrushHostProfile;
63
+ /** The built-in profiles by id. */
64
+ export declare const BRUSH_HOST_PROFILES: Readonly<Record<string, BrushHostProfile>>;
65
+ /** What kind of colour a `BrushPaint` is. */
66
+ export declare function brushColourKind(paint: BrushPaint): BrushColourKind;
67
+ /** What a brush needs from a host, and what the host does about it. */
68
+ export declare function checkBrush(brush: Brush, profile: BrushHostProfile): BrushCheck;
69
+ /** The presets (default: the built-in ones) a host can render, in library order. */
70
+ export declare function listPresets(profile: BrushHostProfile, presets?: readonly Brush[]): Brush[];
71
+ export interface DegradedBrush {
72
+ /** The brush with what the host can't render stripped (a copy). */
73
+ brush: Brush;
74
+ /** The errors fixed by stripping a facet. */
75
+ dropped: BrushIssue[];
76
+ /** Errors that stripping cannot fix (an unsupported kind or tool): the host still refuses it. */
77
+ issues: BrushIssue[];
78
+ }
79
+ /**
80
+ * The brush the host can render: facets it refuses are removed (light, motion, a reservoir, a
81
+ * wet or thick surface, particle or object marks, bristle and stamp tips become round, an
82
+ * unsupported default colour), each reported in `dropped`. A tool the host lacks can't be
83
+ * stripped and stays in `issues`.
84
+ */
85
+ export declare function degrade(brush: Brush, profile: BrushHostProfile): DegradedBrush;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * From a facet `Brush` to the flat form the paint engine reads (`ResolvedBrush`, today's
3
+ * `PaintBrush`). The engine is derived from the tip, the marks and the behaviour
4
+ * (`resolveEngine`); the category the libraries group by is derived too (`resolveCategory`).
5
+ *
6
+ * `BRUSH_FIELDS` maps each facet field to the engine field it sets, for the engines that read it.
7
+ * `migrateBrushV1` reads the same map the other way, so the two directions cannot drift apart.
8
+ */
9
+ import { type PaintBrushCategory, type PaintBrushEngine, type WetMediumKind, type WetTool } from '../paint-editor/brushes.cjs';
10
+ import type { Brush, BrushTool, ResolvedBrush } from './types.cjs';
11
+ /** One facet field and the engine field it sets. */
12
+ export interface BrushField {
13
+ /** Dot path in the facet brush. */
14
+ facet: string;
15
+ /** Dot path in the resolved brush. */
16
+ engine: string;
17
+ /** The engines that read it. */
18
+ engines: readonly PaintBrushEngine[];
19
+ /** When the two forms differ: facet value → engine value, and back. */
20
+ map?: {
21
+ toEngine(v: unknown): unknown;
22
+ toFacet(v: unknown): unknown;
23
+ };
24
+ /** Read only on these tip shapes. */
25
+ shapes?: readonly string[];
26
+ }
27
+ /** Every facet field and where it lands in the resolved brush. */
28
+ export declare const BRUSH_FIELDS: readonly BrushField[];
29
+ /** The wet tool each wet-layer tool brush is. */
30
+ export declare const WET_TOOL_OF: Readonly<Partial<Record<BrushTool, WetTool>>>;
31
+ export declare function getPath(o: unknown, path: string): unknown;
32
+ export declare function setPath(o: Record<string, unknown>, path: string, value: unknown): void;
33
+ /**
34
+ * The engine that renders a brush: tools by what they do; marks by how they are laid
35
+ * (particles, objects), the tip (bristle), the surface (thick, wet) and the paint exchange (a
36
+ * reservoir makes a Mixer Brush); everything else paints dabs.
37
+ */
38
+ export declare function resolveEngine(brush: Pick<Brush, 'kind' | 'tool' | 'tip' | 'marks' | 'behaviour'>): PaintBrushEngine;
39
+ /** The wet medium a wet brush paints: `behaviour.wet.medium`, else its variety when that is a wet medium. */
40
+ export declare function resolveWetMedium(brush: Pick<Brush, 'variety' | 'behaviour'>): WetMediumKind | undefined;
41
+ /**
42
+ * The library category a brush is grouped under: tools by what they do; light brushes; then by
43
+ * engine (particles are procedural, objects stickers, bristle, thick impasto, wet oil or
44
+ * watercolour, the Mixer Brush); dab brushes by variety, then medium.
45
+ */
46
+ export declare function resolveCategory(brush: Brush, engine?: PaintBrushEngine): PaintBrushCategory;
47
+ export interface ResolveBrushOptions {
48
+ /** Mark the result built-in (a library's presets). */
49
+ builtin?: boolean;
50
+ }
51
+ /**
52
+ * The flat engine form of a brush. Missing fields take the engine's defaults, and every value is
53
+ * clamped into range, so any brush resolves (check it with `normalizeBrushSpec` first to hear
54
+ * what was out of range).
55
+ */
56
+ export declare function resolveBrush(brush: Brush, options?: ResolveBrushOptions): ResolvedBrush;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * From a facet `Brush` to the flat form the paint engine reads (`ResolvedBrush`, today's
3
+ * `PaintBrush`). The engine is derived from the tip, the marks and the behaviour
4
+ * (`resolveEngine`); the category the libraries group by is derived too (`resolveCategory`).
5
+ *
6
+ * `BRUSH_FIELDS` maps each facet field to the engine field it sets, for the engines that read it.
7
+ * `migrateBrushV1` reads the same map the other way, so the two directions cannot drift apart.
8
+ */
9
+ import { type PaintBrushCategory, type PaintBrushEngine, type WetMediumKind, type WetTool } from '../paint-editor/brushes.js';
10
+ import type { Brush, BrushTool, ResolvedBrush } from './types.js';
11
+ /** One facet field and the engine field it sets. */
12
+ export interface BrushField {
13
+ /** Dot path in the facet brush. */
14
+ facet: string;
15
+ /** Dot path in the resolved brush. */
16
+ engine: string;
17
+ /** The engines that read it. */
18
+ engines: readonly PaintBrushEngine[];
19
+ /** When the two forms differ: facet value → engine value, and back. */
20
+ map?: {
21
+ toEngine(v: unknown): unknown;
22
+ toFacet(v: unknown): unknown;
23
+ };
24
+ /** Read only on these tip shapes. */
25
+ shapes?: readonly string[];
26
+ }
27
+ /** Every facet field and where it lands in the resolved brush. */
28
+ export declare const BRUSH_FIELDS: readonly BrushField[];
29
+ /** The wet tool each wet-layer tool brush is. */
30
+ export declare const WET_TOOL_OF: Readonly<Partial<Record<BrushTool, WetTool>>>;
31
+ export declare function getPath(o: unknown, path: string): unknown;
32
+ export declare function setPath(o: Record<string, unknown>, path: string, value: unknown): void;
33
+ /**
34
+ * The engine that renders a brush: tools by what they do; marks by how they are laid
35
+ * (particles, objects), the tip (bristle), the surface (thick, wet) and the paint exchange (a
36
+ * reservoir makes a Mixer Brush); everything else paints dabs.
37
+ */
38
+ export declare function resolveEngine(brush: Pick<Brush, 'kind' | 'tool' | 'tip' | 'marks' | 'behaviour'>): PaintBrushEngine;
39
+ /** The wet medium a wet brush paints: `behaviour.wet.medium`, else its variety when that is a wet medium. */
40
+ export declare function resolveWetMedium(brush: Pick<Brush, 'variety' | 'behaviour'>): WetMediumKind | undefined;
41
+ /**
42
+ * The library category a brush is grouped under: tools by what they do; light brushes; then by
43
+ * engine (particles are procedural, objects stickers, bristle, thick impasto, wet oil or
44
+ * watercolour, the Mixer Brush); dab brushes by variety, then medium.
45
+ */
46
+ export declare function resolveCategory(brush: Brush, engine?: PaintBrushEngine): PaintBrushCategory;
47
+ export interface ResolveBrushOptions {
48
+ /** Mark the result built-in (a library's presets). */
49
+ builtin?: boolean;
50
+ }
51
+ /**
52
+ * The flat engine form of a brush. Missing fields take the engine's defaults, and every value is
53
+ * clamped into range, so any brush resolves (check it with `normalizeBrushSpec` first to hear
54
+ * what was out of range).
55
+ */
56
+ export declare function resolveBrush(brush: Brush, options?: ResolveBrushOptions): ResolvedBrush;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * JSON Schema of a `Brush`: strict (`additionalProperties: false` at every level), every field
3
+ * described with its units and default. `normalizeBrushSpec` clamps and checks against the same
4
+ * ranges, so the schema, the normaliser and the engine agree.
5
+ */
6
+ import type { JsonSchema } from '../tools/schema.cjs';
7
+ /** Most colours in a brush's palette. */
8
+ export declare const MAX_BRUSH_PALETTE = 16;
9
+ /** Variety and tag ids: lowercase letters, digits and dashes. */
10
+ export declare const BRUSH_WORD: RegExp;
11
+ /** A sticker ref `provider:id`, or a bare editor stamp (read as `editor:<stamp>`). */
12
+ export declare const BRUSH_STAMP_PATTERN: string;
13
+ /** JSON Schema of a `Brush`. */
14
+ export declare const BRUSH_JSON_SCHEMA: JsonSchema;
15
+ /** The facet schemas, for tools that take one facet. */
16
+ export declare const BRUSH_FACET_SCHEMAS: {
17
+ readonly tip: JsonSchema;
18
+ readonly marks: {
19
+ readonly continuous: JsonSchema;
20
+ readonly particles: JsonSchema;
21
+ readonly objects: JsonSchema;
22
+ };
23
+ readonly dynamics: JsonSchema;
24
+ readonly behaviour: JsonSchema;
25
+ readonly light: JsonSchema;
26
+ readonly motion: JsonSchema;
27
+ readonly colour: JsonSchema;
28
+ readonly paint: JsonSchema;
29
+ };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * JSON Schema of a `Brush`: strict (`additionalProperties: false` at every level), every field
3
+ * described with its units and default. `normalizeBrushSpec` clamps and checks against the same
4
+ * ranges, so the schema, the normaliser and the engine agree.
5
+ */
6
+ import type { JsonSchema } from '../tools/schema.js';
7
+ /** Most colours in a brush's palette. */
8
+ export declare const MAX_BRUSH_PALETTE = 16;
9
+ /** Variety and tag ids: lowercase letters, digits and dashes. */
10
+ export declare const BRUSH_WORD: RegExp;
11
+ /** A sticker ref `provider:id`, or a bare editor stamp (read as `editor:<stamp>`). */
12
+ export declare const BRUSH_STAMP_PATTERN: string;
13
+ /** JSON Schema of a `Brush`. */
14
+ export declare const BRUSH_JSON_SCHEMA: JsonSchema;
15
+ /** The facet schemas, for tools that take one facet. */
16
+ export declare const BRUSH_FACET_SCHEMAS: {
17
+ readonly tip: JsonSchema;
18
+ readonly marks: {
19
+ readonly continuous: JsonSchema;
20
+ readonly particles: JsonSchema;
21
+ readonly objects: JsonSchema;
22
+ };
23
+ readonly dynamics: JsonSchema;
24
+ readonly behaviour: JsonSchema;
25
+ readonly light: JsonSchema;
26
+ readonly motion: JsonSchema;
27
+ readonly colour: JsonSchema;
28
+ readonly paint: JsonSchema;
29
+ };