@pixerate/editor 0.266.2 → 0.268.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 (149) hide show
  1. package/dist/agent/index.cjs +2 -2
  2. package/dist/agent/index.js +1 -1
  3. package/dist/animation-editor/controller.d.cts +8 -0
  4. package/dist/animation-editor/controller.d.ts +8 -0
  5. package/dist/animation-editor/effect-tools.d.cts +1 -1
  6. package/dist/animation-editor/effect-tools.d.ts +1 -1
  7. package/dist/animation-editor/index.cjs +175 -108
  8. package/dist/animation-editor/index.js +94 -27
  9. package/dist/brushes/index.cjs +201 -75
  10. package/dist/brushes/index.d.cts +4 -3
  11. package/dist/brushes/index.d.ts +4 -3
  12. package/dist/brushes/index.js +138 -12
  13. package/dist/brushes/library.d.cts +27 -1
  14. package/dist/brushes/library.d.ts +27 -1
  15. package/dist/brushes/profiles.d.cts +10 -1
  16. package/dist/brushes/profiles.d.ts +10 -1
  17. package/dist/brushes/schema.d.cts +4 -0
  18. package/dist/brushes/schema.d.ts +4 -0
  19. package/dist/brushes/sources.d.cts +114 -0
  20. package/dist/brushes/sources.d.ts +114 -0
  21. package/dist/brushes/types.d.cts +37 -5
  22. package/dist/brushes/types.d.ts +37 -5
  23. package/dist/brushes/ui.d.cts +40 -4
  24. package/dist/brushes/ui.d.ts +40 -4
  25. package/dist/{brushstrokeRender-KD453OBX.js → brushstrokeRender-A727TTNQ.js} +7 -7
  26. package/dist/{brushstrokeRender-FSUWXUZY.cjs → brushstrokeRender-WR2N5HHY.cjs} +23 -23
  27. package/dist/canvas/brushTypes.d.cts +12 -0
  28. package/dist/canvas/brushTypes.d.ts +12 -0
  29. package/dist/canvas/brushstroke.d.cts +1 -1
  30. package/dist/canvas/brushstroke.d.ts +1 -1
  31. package/dist/canvas/drawController.d.cts +19 -4
  32. package/dist/canvas/drawController.d.ts +19 -4
  33. package/dist/canvas/index.cjs +144 -105
  34. package/dist/canvas/index.js +46 -7
  35. package/dist/{chunk-TOID45I2.cjs → chunk-2WOGXEDX.cjs} +1 -1
  36. package/dist/{chunk-XORJ7UZO.cjs → chunk-3VHH6V67.cjs} +13 -12
  37. package/dist/{chunk-3C33YTNN.js → chunk-3XDSNIGF.js} +73 -6
  38. package/dist/chunk-62MFUHSI.cjs +802 -0
  39. package/dist/{chunk-R44Y3EFS.js → chunk-74ODPZLX.js} +17 -345
  40. package/dist/{chunk-GCVFEPJG.js → chunk-ACTHSQOI.js} +182 -0
  41. package/dist/chunk-ADMRNWR7.js +340 -0
  42. package/dist/{chunk-MVUQSK5J.cjs → chunk-B3G7BKUV.cjs} +111 -439
  43. package/dist/{chunk-DXYHQ2VN.js → chunk-BAZMCFVL.js} +1 -1
  44. package/dist/{chunk-VRK322ZC.cjs → chunk-BPJSI4NT.cjs} +2 -2
  45. package/dist/{chunk-RFJZGTHA.cjs → chunk-EU2ZZA6L.cjs} +400 -69
  46. package/dist/{chunk-XC6DPRYI.js → chunk-GSTSZPSR.js} +154 -949
  47. package/dist/{chunk-EZOJBP4D.js → chunk-HFY5LG5J.js} +1 -1
  48. package/dist/chunk-HVO6XCI4.js +802 -0
  49. package/dist/{chunk-YHDHFJUC.cjs → chunk-J5VQYK7T.cjs} +217 -1012
  50. package/dist/{chunk-XRXFVRGY.cjs → chunk-JBB3GDBB.cjs} +2483 -208
  51. package/dist/{chunk-ACFSFUQH.js → chunk-JL6YH5WD.js} +4 -0
  52. package/dist/{chunk-CIJBSUM5.js → chunk-KZI3YWKW.js} +353 -22
  53. package/dist/{chunk-4ODRXQRW.js → chunk-LETF75CA.js} +1 -1
  54. package/dist/{chunk-FQPUF2W2.cjs → chunk-NIBFKXMD.cjs} +217 -35
  55. package/dist/{chunk-AQZQWXEU.cjs → chunk-NPWWSCWT.cjs} +35 -35
  56. package/dist/{chunk-DOZ6F2K2.js → chunk-NSETJI74.js} +3 -2
  57. package/dist/{chunk-PE6YJMQT.cjs → chunk-PFHJS4HE.cjs} +20 -19
  58. package/dist/{chunk-7KNW3WXZ.cjs → chunk-QEVLRLUN.cjs} +63 -0
  59. package/dist/{chunk-LK4SXL3V.js → chunk-QNY7BDFP.js} +1 -1
  60. package/dist/{chunk-OTN6VB5S.cjs → chunk-RDFHWAHL.cjs} +5 -1
  61. package/dist/{chunk-PXDIAYB6.cjs → chunk-STLTOISW.cjs} +34 -214
  62. package/dist/{chunk-T2LDTEXK.js → chunk-UVTO3D4E.js} +2333 -58
  63. package/dist/{chunk-6JL2D5SG.js → chunk-VE6UKYQ2.js} +3 -2
  64. package/dist/{chunk-TXFWZ5ZJ.js → chunk-W6JDUXP3.js} +4 -184
  65. package/dist/{chunk-3SBLVGOM.cjs → chunk-YOVXQHA3.cjs} +23 -23
  66. package/dist/{chunk-WLUVZKVD.cjs → chunk-YXP6X3SY.cjs} +103 -36
  67. package/dist/chunk-YZWM6ZIP.cjs +340 -0
  68. package/dist/{chunk-472INMWH.js → chunk-ZA7L5YJB.js} +63 -0
  69. package/dist/commands/builtins.d.cts +12 -1
  70. package/dist/commands/builtins.d.ts +12 -1
  71. package/dist/commands/index.cjs +2 -2
  72. package/dist/commands/index.js +1 -1
  73. package/dist/image-editor/controller.d.cts +1 -1
  74. package/dist/image-editor/controller.d.ts +1 -1
  75. package/dist/image-editor/index.cjs +868 -864
  76. package/dist/image-editor/index.js +123 -119
  77. package/dist/paint/index.cjs +8 -6
  78. package/dist/paint/index.js +56 -54
  79. package/dist/paint-editor/brush-library-tools.d.cts +12 -2
  80. package/dist/paint-editor/brush-library-tools.d.ts +12 -2
  81. package/dist/paint-editor/brush-model.d.cts +29 -1
  82. package/dist/paint-editor/brush-model.d.ts +29 -1
  83. package/dist/paint-editor/brush-source-art.d.cts +32 -0
  84. package/dist/paint-editor/brush-source-art.d.ts +32 -0
  85. package/dist/paint-editor/controller.d.cts +11 -4
  86. package/dist/paint-editor/controller.d.ts +11 -4
  87. package/dist/paint-editor/dab-stroke.d.cts +2 -0
  88. package/dist/paint-editor/dab-stroke.d.ts +2 -0
  89. package/dist/paint-editor/engine.d.cts +11 -2
  90. package/dist/paint-editor/engine.d.ts +11 -2
  91. package/dist/paint-editor/index.cjs +967 -2335
  92. package/dist/paint-editor/index.d.cts +3 -1
  93. package/dist/paint-editor/index.d.ts +3 -1
  94. package/dist/paint-editor/index.js +394 -1762
  95. package/dist/paint-editor/path-samples.d.cts +2 -0
  96. package/dist/paint-editor/path-samples.d.ts +2 -0
  97. package/dist/paint-editor/presence.d.cts +20 -1
  98. package/dist/paint-editor/presence.d.ts +20 -1
  99. package/dist/paint-editor/stamp-tip.d.cts +8 -5
  100. package/dist/paint-editor/stamp-tip.d.ts +8 -5
  101. package/dist/paint-editor/thumbnails.d.cts +6 -0
  102. package/dist/paint-editor/thumbnails.d.ts +6 -0
  103. package/dist/paint-editor/tip-grain.d.cts +34 -0
  104. package/dist/paint-editor/tip-grain.d.ts +34 -0
  105. package/dist/raster/index.cjs +2 -2
  106. package/dist/raster/index.js +9 -9
  107. package/dist/stickers/helpers.d.cts +2 -0
  108. package/dist/stickers/helpers.d.ts +2 -0
  109. package/dist/stickers/index.cjs +41 -35
  110. package/dist/stickers/index.js +22 -16
  111. package/dist/stickers/types.d.cts +4 -2
  112. package/dist/stickers/types.d.ts +4 -2
  113. package/dist/vector/backdrop.d.cts +13 -0
  114. package/dist/vector/backdrop.d.ts +13 -0
  115. package/dist/vector/brushstroke.d.cts +54 -7
  116. package/dist/vector/brushstroke.d.ts +54 -7
  117. package/dist/vector/brushstrokeMedia.d.cts +96 -0
  118. package/dist/vector/brushstrokeMedia.d.ts +96 -0
  119. package/dist/vector/controller.d.cts +17 -3
  120. package/dist/vector/controller.d.ts +17 -3
  121. package/dist/vector/index.cjs +1850 -1390
  122. package/dist/vector/index.d.cts +2 -0
  123. package/dist/vector/index.d.ts +2 -0
  124. package/dist/vector/index.js +520 -60
  125. package/dist/vector/render.d.cts +6 -0
  126. package/dist/vector/render.d.ts +6 -0
  127. package/dist/vector/types.d.cts +14 -0
  128. package/dist/vector/types.d.ts +14 -0
  129. package/package.json +1 -1
  130. package/skills/pixerate-animation-editor/SKILL.md +1 -0
  131. package/skills/pixerate-brushes/SKILL.md +37 -10
  132. package/skills/pixerate-brushes-design/SKILL.md +15 -5
  133. package/skills/pixerate-brushes-design/evals/port-to-vector.json +8 -8
  134. package/skills/pixerate-canvas/SKILL.md +11 -1
  135. package/skills/pixerate-image-editor/SKILL.md +19 -64
  136. package/skills/pixerate-image-editor/references/color-management.md +2 -1
  137. package/skills/pixerate-image-editor/references/free-transform.md +12 -0
  138. package/skills/pixerate-image-editor/references/layer-effects.md +10 -0
  139. package/skills/pixerate-image-editor/references/painting.md +17 -0
  140. package/skills/pixerate-image-editor/references/photoshop-files.md +1 -1
  141. package/skills/pixerate-image-editor/references/shapes-and-type.md +14 -0
  142. package/skills/pixerate-image-editor/references/smart-objects.md +10 -0
  143. package/skills/pixerate-paint-editor/SKILL.md +14 -4
  144. package/skills/pixerate-paint-editor/references/brush-facets.md +3 -1
  145. package/skills/pixerate-paint-editor/references/wet-media.md +9 -0
  146. package/skills/pixerate-paint-sketch-to-finish/SKILL.md +1 -0
  147. package/skills/pixerate-vector/SKILL.md +16 -6
  148. package/skills/pixerate-vector/references/best-practices.md +1 -0
  149. package/skills/pixerate-vector/references/commands.md +19 -0
@@ -3,7 +3,7 @@ import {
3
3
  editorStickers,
4
4
  stampOutline,
5
5
  stampPixels
6
- } from "../chunk-DOZ6F2K2.js";
6
+ } from "../chunk-NSETJI74.js";
7
7
  import {
8
8
  createInsertStickerTool,
9
9
  createStickerToolProvider,
@@ -11,7 +11,7 @@ import {
11
11
  describeStickerRef,
12
12
  insertSticker,
13
13
  stickerForAgent
14
- } from "../chunk-6JL2D5SG.js";
14
+ } from "../chunk-VE6UKYQ2.js";
15
15
  import {
16
16
  DEFAULT_STICKER_PAGE,
17
17
  EDITOR_STICKERS_ID,
@@ -25,6 +25,7 @@ import {
25
25
  editorStampOf,
26
26
  normalizeStickerRef,
27
27
  parseStickerRef,
28
+ providerCollections,
28
29
  stampCoverage,
29
30
  stickerCollections,
30
31
  stickerIdFor,
@@ -32,7 +33,7 @@ import {
32
33
  stickerRefOf,
33
34
  stickerVariantFor,
34
35
  stickerVariantsOf
35
- } from "../chunk-ACFSFUQH.js";
36
+ } from "../chunk-JL6YH5WD.js";
36
37
  import {
37
38
  StickerError
38
39
  } from "../chunk-SZTI6EER.js";
@@ -121,7 +122,9 @@ function createFakeStickerProvider(options = {}) {
121
122
  if (!s) return void 0;
122
123
  return { ...summaryOf(s), silhouette: s.shape === "badge" ? "M60 6A54 54 0 1 1 59.9 6Z" : "M10 10H110V110H10Z", colours: s.shape === "badge" ? [s.colours[0]] : [...s.colours] };
123
124
  },
125
+ collections: () => stickerCollections(summaries, COLLECTIONS),
124
126
  listCollections: () => stickerCollections(summaries, COLLECTIONS),
127
+ // the older name, kept for callers that use it
125
128
  categories: () => [...new Set(CATALOGUE.map((s) => s.category))].map((c) => ({ id: c, name: c, count: CATALOGUE.filter((s) => s.category === c).length })),
126
129
  async load(id, o = {}) {
127
130
  if (options.loadDelayMs) await new Promise((r) => setTimeout(r, options.loadDelayMs));
@@ -157,7 +160,7 @@ function combineStickerProviders(providers, options = {}) {
157
160
  const collectionOwner = (c) => {
158
161
  const i = c.indexOf(":");
159
162
  const p = i > 0 ? byId.get(c.slice(0, i)) : void 0;
160
- if (!p) throw new StickerError("invalid_argument", `No collection "${c}"; collections here are "provider:collection" (listCollections lists them).`);
163
+ if (!p) throw new StickerError("invalid_argument", `No collection "${c}"; collections here are "provider:collection" (collections() lists them).`);
161
164
  return { p, collection: p.namespaced ? c : c.slice(i + 1) };
162
165
  };
163
166
  const page = async (query, o = {}) => {
@@ -196,6 +199,11 @@ function combineStickerProviders(providers, options = {}) {
196
199
  if (!next && items.length >= limit && i < providers.length) next = `${i}|`;
197
200
  return { items, total, ...next ? { next } : {} };
198
201
  };
202
+ const collections = async () => {
203
+ const out = [];
204
+ for (const p of providers) for (const c of await providerCollections(p) ?? []) out.push({ ...c, id: collectionOf(p, c.id) });
205
+ return out;
206
+ };
199
207
  return {
200
208
  id: options.id ?? "stickers",
201
209
  name: options.name ?? providers.map((p) => p.name).join(" + "),
@@ -232,11 +240,8 @@ function combineStickerProviders(providers, options = {}) {
232
240
  }
233
241
  return [...counts.values()];
234
242
  },
235
- async listCollections() {
236
- const out = [];
237
- for (const p of providers) for (const c of await p.listCollections?.() ?? []) out.push({ ...c, id: collectionOf(p, c.id) });
238
- return out;
239
- }
243
+ collections,
244
+ listCollections: collections
240
245
  };
241
246
  }
242
247
 
@@ -263,7 +268,7 @@ function checkSummary(s, provider, issues) {
263
268
  else if (!v.includes("light")) at("variants must include light (every sticker has its light art)");
264
269
  }
265
270
  if (s.collection !== void 0 && (typeof s.collection !== "string" || !s.collection)) at("collection must be a non-empty string when present");
266
- if (s.collection !== void 0 && !provider.listCollections) at(`is in collection "${s.collection}" but the provider has no listCollections()`);
271
+ if (s.collection !== void 0 && !provider.collections && !provider.listCollections) at(`is in collection "${s.collection}" but the provider has no collections()`);
267
272
  }
268
273
  async function checkStickerProvider(provider, options = {}) {
269
274
  const issues = [];
@@ -322,24 +327,24 @@ async function checkStickerProvider(provider, options = {}) {
322
327
  if (d?.colours?.some((c) => !HEX.test(c))) top(`describe("${first.id}").colours must be #rrggbb`);
323
328
  }
324
329
  if (await provider.describe("__no_such_sticker__") !== void 0) top("describe of an unknown id returned a sticker");
325
- if (provider.listCollections) {
330
+ if (provider.collections || provider.listCollections) {
326
331
  try {
327
- const collections = await provider.listCollections();
332
+ const collections = await providerCollections(provider) ?? [];
328
333
  const known = /* @__PURE__ */ new Set();
329
334
  for (const c of collections) {
330
335
  if (typeof c?.id !== "string" || !c.id || typeof c.name !== "string" || !c.name || !(Number.isInteger(c.count) && c.count >= 0)) {
331
- top(`listCollections() returned ${JSON.stringify(c)}; each needs id, name and a count`);
336
+ top(`collections() returned ${JSON.stringify(c)}; each needs id, name and a count`);
332
337
  continue;
333
338
  }
334
- if (known.has(c.id)) top(`listCollections() lists "${c.id}" twice`);
339
+ if (known.has(c.id)) top(`collections() lists "${c.id}" twice`);
335
340
  known.add(c.id);
336
341
  const page = await provider.list({ collection: c.id, limit: 100 });
337
342
  if (page.total !== c.count) top(`collection "${c.id}" says count ${c.count} but list({ collection }) has ${page.total}`);
338
343
  if (page.items.some((x) => x.collection !== c.id)) top(`list({ collection: "${c.id}" }) returned stickers of other collections`);
339
344
  }
340
- for (const x of all) if (x.collection !== void 0 && !known.has(x.collection)) issues.push({ stickerId: x.id, message: `collection "${x.collection}" is not in listCollections()` });
345
+ for (const x of all) if (x.collection !== void 0 && !known.has(x.collection)) issues.push({ stickerId: x.id, message: `collection "${x.collection}" is not in collections()` });
341
346
  } catch (e) {
342
- top(`listCollections() rejected: ${e.message}`);
347
+ top(`collections() rejected: ${e.message}`);
343
348
  }
344
349
  }
345
350
  let rejected = false;
@@ -413,6 +418,7 @@ export {
413
418
  insertSticker,
414
419
  normalizeStickerRef,
415
420
  parseStickerRef,
421
+ providerCollections,
416
422
  stampCoverage,
417
423
  stampOutline,
418
424
  stampPixels,
@@ -61,7 +61,7 @@ export interface StickerSummary {
61
61
  * picker or brush can pick the dark one for a dark background without calling `describe` first.
62
62
  */
63
63
  variants?: StickerVariant[];
64
- /** The collection it belongs to (`listCollections`), when the provider groups its stickers. */
64
+ /** The collection it belongs to (`collections()`), when the provider groups its stickers. */
65
65
  collection?: string;
66
66
  }
67
67
  /** Everything about one sticker. */
@@ -93,7 +93,7 @@ export interface StickerPage {
93
93
  }
94
94
  export interface StickerSearchOptions {
95
95
  category?: string;
96
- /** Only stickers of this collection (`listCollections`). */
96
+ /** Only stickers of this collection (`collections()`). */
97
97
  collection?: string;
98
98
  /** Every tag must match. */
99
99
  tags?: string[];
@@ -144,6 +144,8 @@ export interface StickerProvider {
144
144
  count: number;
145
145
  }>>;
146
146
  /** Its collections with counts, when it groups its stickers. Ids stay unique across collections, so `load(id)` needs none. */
147
+ collections?(): MaybePromise<StickerCollection[]>;
148
+ /** The older name for `collections()`, still read when a provider has no `collections()`. */
147
149
  listCollections?(): MaybePromise<StickerCollection[]>;
148
150
  }
149
151
  /** `provider:id`, the name of a sticker across providers. A bare editor stamp (`"star"`) means `editor:star`. */
@@ -61,7 +61,7 @@ export interface StickerSummary {
61
61
  * picker or brush can pick the dark one for a dark background without calling `describe` first.
62
62
  */
63
63
  variants?: StickerVariant[];
64
- /** The collection it belongs to (`listCollections`), when the provider groups its stickers. */
64
+ /** The collection it belongs to (`collections()`), when the provider groups its stickers. */
65
65
  collection?: string;
66
66
  }
67
67
  /** Everything about one sticker. */
@@ -93,7 +93,7 @@ export interface StickerPage {
93
93
  }
94
94
  export interface StickerSearchOptions {
95
95
  category?: string;
96
- /** Only stickers of this collection (`listCollections`). */
96
+ /** Only stickers of this collection (`collections()`). */
97
97
  collection?: string;
98
98
  /** Every tag must match. */
99
99
  tags?: string[];
@@ -144,6 +144,8 @@ export interface StickerProvider {
144
144
  count: number;
145
145
  }>>;
146
146
  /** Its collections with counts, when it groups its stickers. Ids stay unique across collections, so `load(id)` needs none. */
147
+ collections?(): MaybePromise<StickerCollection[]>;
148
+ /** The older name for `collections()`, still read when a provider has no `collections()`. */
147
149
  listCollections?(): MaybePromise<StickerCollection[]>;
148
150
  }
149
151
  /** `provider:id`, the name of a sticker across providers. A bare editor stamp (`"star"`) means `editor:star`. */
@@ -0,0 +1,13 @@
1
+ import type { RasterRect } from '../raster/surface.cjs';
2
+ import { type VectorPaintDef } from './gradient.cjs';
3
+ import type { VectorRenderNode } from './render.cjs';
4
+ import type { VectorMatrix } from './types.cjs';
5
+ type M = VectorMatrix;
6
+ /** An SVG `transform` attribute (`matrix`, `translate`, `scale`, `rotate`, in order) as a matrix. */
7
+ export declare function parseVectorTransform(value: string | undefined): M;
8
+ /**
9
+ * The render data's `nodes` (document space) drawn over `rect` (pixels) through `toPixels`, the
10
+ * map from document space to those pixels: straight-alpha RGBA, `rect.width × rect.height`.
11
+ */
12
+ export declare function rasterizeVectorBackdrop(nodes: readonly VectorRenderNode[], defs: readonly VectorPaintDef[], toPixels: VectorMatrix, rect: RasterRect): Uint8ClampedArray;
13
+ export {};
@@ -0,0 +1,13 @@
1
+ import type { RasterRect } from '../raster/surface.js';
2
+ import { type VectorPaintDef } from './gradient.js';
3
+ import type { VectorRenderNode } from './render.js';
4
+ import type { VectorMatrix } from './types.js';
5
+ type M = VectorMatrix;
6
+ /** An SVG `transform` attribute (`matrix`, `translate`, `scale`, `rotate`, in order) as a matrix. */
7
+ export declare function parseVectorTransform(value: string | undefined): M;
8
+ /**
9
+ * The render data's `nodes` (document space) drawn over `rect` (pixels) through `toPixels`, the
10
+ * map from document space to those pixels: straight-alpha RGBA, `rect.width × rect.height`.
11
+ */
12
+ export declare function rasterizeVectorBackdrop(nodes: readonly VectorRenderNode[], defs: readonly VectorPaintDef[], toPixels: VectorMatrix, rect: RasterRect): Uint8ClampedArray;
13
+ export {};
@@ -1,11 +1,14 @@
1
1
  import { type PaintBrush } from '../paint-editor/brushes.cjs';
2
2
  import type { Brush } from '../brushes/types.cjs';
3
- import type { VectorBrushstroke, VectorBrushstrokePaper, VectorPathNode, VectorStrokeLayer } from './types.cjs';
3
+ import { type VectorBrushstrokeBackdrop } from './brushstrokeMedia.cjs';
4
+ import type { VectorBrushstroke, VectorBrushstrokePaint, VectorBrushstrokePaper, VectorDocument, VectorPathNode, VectorStrokeLayer } from './types.cjs';
4
5
  /** Image pixels per document unit brushstrokes are rendered at. */
5
6
  export declare const VECTOR_BRUSHSTROKE_SCALE = 2;
7
+ /** Whether a brush's engine paints vector brushstrokes: dry dab brushes, wet paint, the Mixer Brush, smudge and blender. */
8
+ export declare function isVectorBrushstrokeEngine(brush: PaintBrush): boolean;
6
9
  /**
7
10
  * The brushes a brushstroke can use, as the engine paints them: the presets `VECTOR_PROFILE`
8
- * admits (dry dab-engine brushes), then the saved brushes given (facet brushes, e.g. a brush
11
+ * admits (dry, wet paint, mixer, smudge and blender brushes), then the saved brushes given (facet brushes, e.g. a brush
9
12
  * library's) it admits.
10
13
  */
11
14
  export declare function vectorBrushstrokeBrushes(user?: readonly Brush[]): PaintBrush[];
@@ -30,6 +33,15 @@ export declare function vectorBrushstrokeDefaultColour(id: string, user?: readon
30
33
  * kept in `spec`.
31
34
  */
32
35
  export declare function sanitizeVectorBrushstroke(value: unknown, user?: readonly Brush[]): VectorBrushstroke | null;
36
+ /**
37
+ * What is wrong with a brushstroke's gradient `paint` (from an agent or a caller), or null when it
38
+ * is valid. Paint is `{ type: 'color', color }` or `{ type: 'gradient', stops, space, map, softness }`
39
+ * (the paint engine's `PaintSource`, lengths in document units). `velocity` and `time` mappings
40
+ * are refused: a vector path has no timing.
41
+ */
42
+ export declare function vectorBrushstrokePaintError(paint: unknown): string | null;
43
+ /** Reads brushstroke paint from untrusted data (a stored layer); null when it is not valid paint. */
44
+ export declare function readVectorBrushstrokePaint(value: unknown): VectorBrushstrokePaint | null;
33
45
  /** The largest brushstroke seed. */
34
46
  export declare const VECTOR_BRUSHSTROKE_MAX_SEED = 2147483647;
35
47
  /** Reads paper settings from untrusted data; null when there are none (the default paper). */
@@ -59,10 +71,45 @@ export interface VectorBrushstrokeImage {
59
71
  width: number;
60
72
  height: number;
61
73
  }
74
+ /** Where and how a brushstroke renders. */
75
+ export interface VectorBrushstrokeContext {
76
+ /**
77
+ * The document the path is in. A Mixer Brush, smudge or blender acts on what lies beneath the
78
+ * stroke there; without it, only on the path's own fills and lower strokes.
79
+ */
80
+ document?: VectorDocument | null;
81
+ /**
82
+ * `async` (default): a wet brushstroke shows the paint as laid until its simulation has settled
83
+ * (`onVectorBrushstrokesSettled` says when). `now`: settle it first (exports, agents).
84
+ */
85
+ settle?: 'async' | 'now';
86
+ }
87
+ /**
88
+ * What lies beneath stroke layer `index` of `node`: the objects it depends on (compared by
89
+ * identity: any change beneath gives a new one), and the backdrop renderer. The scene renderer
90
+ * registers it (`setVectorBackdropSource`), as it is the one that can draw the scene.
91
+ */
92
+ export type VectorBackdropSource = (document: VectorDocument | null, node: VectorPathNode, index: number, settle: 'async' | 'now') => {
93
+ deps: readonly unknown[];
94
+ backdrop: VectorBrushstrokeBackdrop;
95
+ };
96
+ /** Registers what draws the backdrop of mixer, smudge and blender brushstrokes (done by `render.ts`). */
97
+ export declare function setVectorBackdropSource(source: VectorBackdropSource | null): void;
98
+ /**
99
+ * Calls `listener` whenever a wet brushstroke has settled, so its final image replaces the one
100
+ * shown while it settled. Re-render the scene then (the controller does: it notifies its
101
+ * subscribers). Returns the unsubscribe function.
102
+ */
103
+ export declare function onVectorBrushstrokesSettled(listener: () => void): () => void;
104
+ /** How many wet brushstrokes are still settling. */
105
+ export declare function vectorBrushstrokesSettling(): number;
106
+ /** Settles every wet brushstroke still settling, now (slow for many long strokes). Returns how many there were. */
107
+ export declare function settleVectorBrushstrokes(): number;
62
108
  /**
63
- * The images of a brushstroke layer on a path, one per subpath, in the path's own space (cached
64
- * per path and layer object, and by the shared renderer's cache, whose key includes the seed and
65
- * paper). A layer with a seed renders with the brush's character, the grain laid in the path's
66
- * own space (paper point = path point).
109
+ * The images of a brushstroke layer on a path, one per subpath, in the path's own space. Cached
110
+ * per path and layer object, by the shared renderer's cache (whose key includes the seed and
111
+ * paper), and for mixer, smudge and blender strokes by what lies beneath them. A layer with a
112
+ * seed renders with the brush's character, the grain laid in the path's own space (paper point =
113
+ * path point). See `VectorBrushstrokeContext` for wet strokes and backdrops.
67
114
  */
68
- export declare function vectorBrushstrokeImages(node: VectorPathNode, layer: VectorStrokeLayer): VectorBrushstrokeImage[];
115
+ export declare function vectorBrushstrokeImages(node: VectorPathNode, layer: VectorStrokeLayer, context?: VectorBrushstrokeContext): VectorBrushstrokeImage[];
@@ -1,11 +1,14 @@
1
1
  import { type PaintBrush } from '../paint-editor/brushes.js';
2
2
  import type { Brush } from '../brushes/types.js';
3
- import type { VectorBrushstroke, VectorBrushstrokePaper, VectorPathNode, VectorStrokeLayer } from './types.js';
3
+ import { type VectorBrushstrokeBackdrop } from './brushstrokeMedia.js';
4
+ import type { VectorBrushstroke, VectorBrushstrokePaint, VectorBrushstrokePaper, VectorDocument, VectorPathNode, VectorStrokeLayer } from './types.js';
4
5
  /** Image pixels per document unit brushstrokes are rendered at. */
5
6
  export declare const VECTOR_BRUSHSTROKE_SCALE = 2;
7
+ /** Whether a brush's engine paints vector brushstrokes: dry dab brushes, wet paint, the Mixer Brush, smudge and blender. */
8
+ export declare function isVectorBrushstrokeEngine(brush: PaintBrush): boolean;
6
9
  /**
7
10
  * The brushes a brushstroke can use, as the engine paints them: the presets `VECTOR_PROFILE`
8
- * admits (dry dab-engine brushes), then the saved brushes given (facet brushes, e.g. a brush
11
+ * admits (dry, wet paint, mixer, smudge and blender brushes), then the saved brushes given (facet brushes, e.g. a brush
9
12
  * library's) it admits.
10
13
  */
11
14
  export declare function vectorBrushstrokeBrushes(user?: readonly Brush[]): PaintBrush[];
@@ -30,6 +33,15 @@ export declare function vectorBrushstrokeDefaultColour(id: string, user?: readon
30
33
  * kept in `spec`.
31
34
  */
32
35
  export declare function sanitizeVectorBrushstroke(value: unknown, user?: readonly Brush[]): VectorBrushstroke | null;
36
+ /**
37
+ * What is wrong with a brushstroke's gradient `paint` (from an agent or a caller), or null when it
38
+ * is valid. Paint is `{ type: 'color', color }` or `{ type: 'gradient', stops, space, map, softness }`
39
+ * (the paint engine's `PaintSource`, lengths in document units). `velocity` and `time` mappings
40
+ * are refused: a vector path has no timing.
41
+ */
42
+ export declare function vectorBrushstrokePaintError(paint: unknown): string | null;
43
+ /** Reads brushstroke paint from untrusted data (a stored layer); null when it is not valid paint. */
44
+ export declare function readVectorBrushstrokePaint(value: unknown): VectorBrushstrokePaint | null;
33
45
  /** The largest brushstroke seed. */
34
46
  export declare const VECTOR_BRUSHSTROKE_MAX_SEED = 2147483647;
35
47
  /** Reads paper settings from untrusted data; null when there are none (the default paper). */
@@ -59,10 +71,45 @@ export interface VectorBrushstrokeImage {
59
71
  width: number;
60
72
  height: number;
61
73
  }
74
+ /** Where and how a brushstroke renders. */
75
+ export interface VectorBrushstrokeContext {
76
+ /**
77
+ * The document the path is in. A Mixer Brush, smudge or blender acts on what lies beneath the
78
+ * stroke there; without it, only on the path's own fills and lower strokes.
79
+ */
80
+ document?: VectorDocument | null;
81
+ /**
82
+ * `async` (default): a wet brushstroke shows the paint as laid until its simulation has settled
83
+ * (`onVectorBrushstrokesSettled` says when). `now`: settle it first (exports, agents).
84
+ */
85
+ settle?: 'async' | 'now';
86
+ }
87
+ /**
88
+ * What lies beneath stroke layer `index` of `node`: the objects it depends on (compared by
89
+ * identity: any change beneath gives a new one), and the backdrop renderer. The scene renderer
90
+ * registers it (`setVectorBackdropSource`), as it is the one that can draw the scene.
91
+ */
92
+ export type VectorBackdropSource = (document: VectorDocument | null, node: VectorPathNode, index: number, settle: 'async' | 'now') => {
93
+ deps: readonly unknown[];
94
+ backdrop: VectorBrushstrokeBackdrop;
95
+ };
96
+ /** Registers what draws the backdrop of mixer, smudge and blender brushstrokes (done by `render.ts`). */
97
+ export declare function setVectorBackdropSource(source: VectorBackdropSource | null): void;
98
+ /**
99
+ * Calls `listener` whenever a wet brushstroke has settled, so its final image replaces the one
100
+ * shown while it settled. Re-render the scene then (the controller does: it notifies its
101
+ * subscribers). Returns the unsubscribe function.
102
+ */
103
+ export declare function onVectorBrushstrokesSettled(listener: () => void): () => void;
104
+ /** How many wet brushstrokes are still settling. */
105
+ export declare function vectorBrushstrokesSettling(): number;
106
+ /** Settles every wet brushstroke still settling, now (slow for many long strokes). Returns how many there were. */
107
+ export declare function settleVectorBrushstrokes(): number;
62
108
  /**
63
- * The images of a brushstroke layer on a path, one per subpath, in the path's own space (cached
64
- * per path and layer object, and by the shared renderer's cache, whose key includes the seed and
65
- * paper). A layer with a seed renders with the brush's character, the grain laid in the path's
66
- * own space (paper point = path point).
109
+ * The images of a brushstroke layer on a path, one per subpath, in the path's own space. Cached
110
+ * per path and layer object, by the shared renderer's cache (whose key includes the seed and
111
+ * paper), and for mixer, smudge and blender strokes by what lies beneath them. A layer with a
112
+ * seed renders with the brush's character, the grain laid in the path's own space (paper point =
113
+ * path point). See `VectorBrushstrokeContext` for wet strokes and backdrops.
67
114
  */
68
- export declare function vectorBrushstrokeImages(node: VectorPathNode, layer: VectorStrokeLayer): VectorBrushstrokeImage[];
115
+ export declare function vectorBrushstrokeImages(node: VectorPathNode, layer: VectorStrokeLayer, context?: VectorBrushstrokeContext): VectorBrushstrokeImage[];
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Brushstrokes on vector paths with media that need more than an empty image (#519): wet paint, and
3
+ * the Mixer Brush, smudge and blender, which act on the paint under them. The path stays the data
4
+ * and the pixels a cache, as for dry brushstrokes (`renderBrushstroke`).
5
+ *
6
+ * - **Wet** (`engine: 'wet'`, wet paint brushes: watercolour, gouache, wet oils): the stroke gets its
7
+ * own small `WetWorld` over its bounds, the paint editor's `WetStrokeDriver` lays it, the
8
+ * simulation runs `VECTOR_WET_STEPS` steps (time is counted in steps, never wall time) and the
9
+ * paint is dried where it is. The bytes depend only on the path, brush, colour and seed. The
10
+ * simulation costs about 9 ms per 256 px tile per step on the TypeScript kernel, so
11
+ * `WetBrushstrokeJob` can run it in slices: hosts show the bake of the paint as laid (step 0) at
12
+ * once, and swap in the settled image when it is done.
13
+ * - **Backdrop media** (`mixer`, `smudge`, `blender`): the stroke runs the paint editor's dab-by-dab
14
+ * engine (`PaintDabStroke`) over a local copy of what lies beneath it (the backdrop, rendered by
15
+ * the caller), and keeps only the pixels it changed. Drawn over the content it was made from,
16
+ * that is the stroke's result. A smudge can drag paint over bare paper, but it can't take paint
17
+ * away from the content underneath (the image is drawn over it).
18
+ *
19
+ * Coordinates: like `renderBrushstroke`, inputs are in path units, rendered at `scale` image pixels
20
+ * per unit, and the result's `bounds` place the image back in path units.
21
+ */
22
+ import { type BrushstrokeImage, type BrushstrokeInput } from '../paint/brushstroke.cjs';
23
+ import { type PaintSource } from '../paint/gradient-paint.cjs';
24
+ import type { PaintBrush } from '../paint-editor/brushes.cjs';
25
+ import { type RasterRect } from '../raster/surface.cjs';
26
+ /** Simulation steps a wet brushstroke runs before it is dried (enough to bleed, bloom and granulate). */
27
+ export declare const VECTOR_WET_STEPS = 120;
28
+ /** Engines a vector brushstroke paints through this module. */
29
+ export type VectorMediaEngine = 'wet' | 'mixer' | 'smudge' | 'blender';
30
+ /** Whether a brush paints through this module (and which way), or null for a dry dab brush. */
31
+ export declare function vectorMediaEngine(brush: Pick<PaintBrush, 'engine' | 'wet'>): VectorMediaEngine | null;
32
+ /** Whether a media engine acts on the paint beneath the stroke (and needs a backdrop). */
33
+ export declare const usesVectorBackdrop: (engine: VectorMediaEngine | null) => boolean;
34
+ export interface VectorMediaOptions {
35
+ /** Image pixels per path unit (default 1). */
36
+ scale?: number;
37
+ /** RGBA 0..255 of the paint (the alpha scales the result). */
38
+ color: readonly [number, number, number, number];
39
+ /** Gradient paint sampled dab by dab (lengths in path units), or null for `color`. */
40
+ paint?: PaintSource | null;
41
+ /** Jitter seed (default 0). */
42
+ seed?: number;
43
+ closed?: boolean;
44
+ /** The largest image to render, in pixels (default 4096²); a bigger stroke renders at a lower scale. */
45
+ maxPixels?: number;
46
+ }
47
+ /**
48
+ * One wet brushstroke's simulation: the paint is laid when the job is made; `run` advances it
49
+ * (within a time budget, for hosts that slice the work), `image` bakes it as it is now and
50
+ * `settled` once all `VECTOR_WET_STEPS` steps have run. Call `destroy` when done with it.
51
+ */
52
+ export declare class WetBrushstrokeJob {
53
+ readonly steps: number;
54
+ private readonly world;
55
+ private readonly placed;
56
+ private readonly dabs;
57
+ private done;
58
+ private final;
59
+ constructor(input: BrushstrokeInput, brush: PaintBrush, options: VectorMediaOptions, steps?: number);
60
+ /** Steps run so far. */
61
+ get progress(): number;
62
+ get finished(): boolean;
63
+ /** Runs steps until `budgetMs` has passed (at least one) or all have run. Returns whether it is finished. */
64
+ run(budgetMs?: number): boolean;
65
+ /** The paint dried where it is now (step 0: as laid). */
66
+ image(): BrushstrokeImage;
67
+ /** The settled image: runs the remaining steps now. */
68
+ settled(): BrushstrokeImage;
69
+ destroy(): void;
70
+ private bake;
71
+ }
72
+ /** A wet brushstroke rendered and settled at once (`VECTOR_WET_STEPS` steps; slow for long strokes). */
73
+ export declare function renderWetBrushstroke(input: BrushstrokeInput, brush: PaintBrush, options: VectorMediaOptions): BrushstrokeImage;
74
+ /**
75
+ * What lies beneath a stroke: straight-alpha RGBA of `rect` (image pixels at the stroke's scale,
76
+ * path units × scale), row by row. Called once per render.
77
+ */
78
+ export type VectorBrushstrokeBackdrop = (rect: RasterRect, scale: number) => Uint8ClampedArray;
79
+ /** The image pixels a backdrop brushstroke reads, in path units × the scale it renders at (for callers that render backdrops). */
80
+ export declare function vectorBackdropRect(input: BrushstrokeInput, brush: PaintBrush, options: VectorMediaOptions): {
81
+ rect: RasterRect;
82
+ scale: number;
83
+ } | null;
84
+ /**
85
+ * A Mixer Brush, smudge or blender stroke over `backdrop`: the pixels it changed (the rest are
86
+ * transparent). Without a backdrop it acts on bare paper: a Mixer Brush still paints from its
87
+ * reservoir; a smudge or blender changes nothing.
88
+ */
89
+ export declare function renderBackdropBrushstroke(input: BrushstrokeInput, brush: PaintBrush, options: VectorMediaOptions & {
90
+ backdrop?: VectorBrushstrokeBackdrop | null;
91
+ /** A cache for the result, given the backdrop's bytes (the result depends only on them and the stroke). */
92
+ cache?: (backdrop: Uint8ClampedArray) => {
93
+ get(): BrushstrokeImage | undefined;
94
+ set(image: BrushstrokeImage): void;
95
+ };
96
+ }): BrushstrokeImage;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Brushstrokes on vector paths with media that need more than an empty image (#519): wet paint, and
3
+ * the Mixer Brush, smudge and blender, which act on the paint under them. The path stays the data
4
+ * and the pixels a cache, as for dry brushstrokes (`renderBrushstroke`).
5
+ *
6
+ * - **Wet** (`engine: 'wet'`, wet paint brushes: watercolour, gouache, wet oils): the stroke gets its
7
+ * own small `WetWorld` over its bounds, the paint editor's `WetStrokeDriver` lays it, the
8
+ * simulation runs `VECTOR_WET_STEPS` steps (time is counted in steps, never wall time) and the
9
+ * paint is dried where it is. The bytes depend only on the path, brush, colour and seed. The
10
+ * simulation costs about 9 ms per 256 px tile per step on the TypeScript kernel, so
11
+ * `WetBrushstrokeJob` can run it in slices: hosts show the bake of the paint as laid (step 0) at
12
+ * once, and swap in the settled image when it is done.
13
+ * - **Backdrop media** (`mixer`, `smudge`, `blender`): the stroke runs the paint editor's dab-by-dab
14
+ * engine (`PaintDabStroke`) over a local copy of what lies beneath it (the backdrop, rendered by
15
+ * the caller), and keeps only the pixels it changed. Drawn over the content it was made from,
16
+ * that is the stroke's result. A smudge can drag paint over bare paper, but it can't take paint
17
+ * away from the content underneath (the image is drawn over it).
18
+ *
19
+ * Coordinates: like `renderBrushstroke`, inputs are in path units, rendered at `scale` image pixels
20
+ * per unit, and the result's `bounds` place the image back in path units.
21
+ */
22
+ import { type BrushstrokeImage, type BrushstrokeInput } from '../paint/brushstroke.js';
23
+ import { type PaintSource } from '../paint/gradient-paint.js';
24
+ import type { PaintBrush } from '../paint-editor/brushes.js';
25
+ import { type RasterRect } from '../raster/surface.js';
26
+ /** Simulation steps a wet brushstroke runs before it is dried (enough to bleed, bloom and granulate). */
27
+ export declare const VECTOR_WET_STEPS = 120;
28
+ /** Engines a vector brushstroke paints through this module. */
29
+ export type VectorMediaEngine = 'wet' | 'mixer' | 'smudge' | 'blender';
30
+ /** Whether a brush paints through this module (and which way), or null for a dry dab brush. */
31
+ export declare function vectorMediaEngine(brush: Pick<PaintBrush, 'engine' | 'wet'>): VectorMediaEngine | null;
32
+ /** Whether a media engine acts on the paint beneath the stroke (and needs a backdrop). */
33
+ export declare const usesVectorBackdrop: (engine: VectorMediaEngine | null) => boolean;
34
+ export interface VectorMediaOptions {
35
+ /** Image pixels per path unit (default 1). */
36
+ scale?: number;
37
+ /** RGBA 0..255 of the paint (the alpha scales the result). */
38
+ color: readonly [number, number, number, number];
39
+ /** Gradient paint sampled dab by dab (lengths in path units), or null for `color`. */
40
+ paint?: PaintSource | null;
41
+ /** Jitter seed (default 0). */
42
+ seed?: number;
43
+ closed?: boolean;
44
+ /** The largest image to render, in pixels (default 4096²); a bigger stroke renders at a lower scale. */
45
+ maxPixels?: number;
46
+ }
47
+ /**
48
+ * One wet brushstroke's simulation: the paint is laid when the job is made; `run` advances it
49
+ * (within a time budget, for hosts that slice the work), `image` bakes it as it is now and
50
+ * `settled` once all `VECTOR_WET_STEPS` steps have run. Call `destroy` when done with it.
51
+ */
52
+ export declare class WetBrushstrokeJob {
53
+ readonly steps: number;
54
+ private readonly world;
55
+ private readonly placed;
56
+ private readonly dabs;
57
+ private done;
58
+ private final;
59
+ constructor(input: BrushstrokeInput, brush: PaintBrush, options: VectorMediaOptions, steps?: number);
60
+ /** Steps run so far. */
61
+ get progress(): number;
62
+ get finished(): boolean;
63
+ /** Runs steps until `budgetMs` has passed (at least one) or all have run. Returns whether it is finished. */
64
+ run(budgetMs?: number): boolean;
65
+ /** The paint dried where it is now (step 0: as laid). */
66
+ image(): BrushstrokeImage;
67
+ /** The settled image: runs the remaining steps now. */
68
+ settled(): BrushstrokeImage;
69
+ destroy(): void;
70
+ private bake;
71
+ }
72
+ /** A wet brushstroke rendered and settled at once (`VECTOR_WET_STEPS` steps; slow for long strokes). */
73
+ export declare function renderWetBrushstroke(input: BrushstrokeInput, brush: PaintBrush, options: VectorMediaOptions): BrushstrokeImage;
74
+ /**
75
+ * What lies beneath a stroke: straight-alpha RGBA of `rect` (image pixels at the stroke's scale,
76
+ * path units × scale), row by row. Called once per render.
77
+ */
78
+ export type VectorBrushstrokeBackdrop = (rect: RasterRect, scale: number) => Uint8ClampedArray;
79
+ /** The image pixels a backdrop brushstroke reads, in path units × the scale it renders at (for callers that render backdrops). */
80
+ export declare function vectorBackdropRect(input: BrushstrokeInput, brush: PaintBrush, options: VectorMediaOptions): {
81
+ rect: RasterRect;
82
+ scale: number;
83
+ } | null;
84
+ /**
85
+ * A Mixer Brush, smudge or blender stroke over `backdrop`: the pixels it changed (the rest are
86
+ * transparent). Without a backdrop it acts on bare paper: a Mixer Brush still paints from its
87
+ * reservoir; a smudge or blender changes nothing.
88
+ */
89
+ export declare function renderBackdropBrushstroke(input: BrushstrokeInput, brush: PaintBrush, options: VectorMediaOptions & {
90
+ backdrop?: VectorBrushstrokeBackdrop | null;
91
+ /** A cache for the result, given the backdrop's bytes (the result depends only on them and the stroke). */
92
+ cache?: (backdrop: Uint8ClampedArray) => {
93
+ get(): BrushstrokeImage | undefined;
94
+ set(image: BrushstrokeImage): void;
95
+ };
96
+ }): BrushstrokeImage;
@@ -29,7 +29,7 @@ import { type VectorSnapGuides, type VectorTrackingPoint } from './snap.cjs';
29
29
  import { type VectorBlendMode, type VectorFilterType, type VectorWarpMesh } from './types.cjs';
30
30
  import { EffectsSlot } from '../effects/helpers.cjs';
31
31
  import type { EffectProvider, EffectValue } from '../effects/types.cjs';
32
- import type { Vec2, VectorEffectType, VectorAlign, VectorAnchor, VectorAnchorRef, VectorAnchorType, VectorArrange, VectorAverageAxis, VectorBlendOptions, VectorBooleanOperation, VectorBounds, VectorCornerKind, VectorDefaults, VectorDistribute, VectorDocument, VectorGroupNode, VectorProviderEffect, VectorHandleRef, VectorMask, VectorMaskType, VectorModifiers, VectorNode, VectorPathfinderOperation, VectorPathNode, VectorSegmentRef, VectorStyle, VectorSubpath, VectorTool, VectorTransformHandle, VectorViewport, VectorWidthPoint, VectorCharacterStyle, VectorParagraphStyle, VectorTextAlign, VectorTextFrame, VectorTextNode, VectorMatrix, VectorSymbol, VectorPaint, VectorConstraint, VectorConstraintKind, VectorParameter, VectorBrushstroke, VectorBrushstrokePaper } from './types.cjs';
32
+ import type { Vec2, VectorEffectType, VectorAlign, VectorAnchor, VectorAnchorRef, VectorAnchorType, VectorArrange, VectorAverageAxis, VectorBlendOptions, VectorBooleanOperation, VectorBounds, VectorCornerKind, VectorDefaults, VectorDistribute, VectorDocument, VectorGroupNode, VectorProviderEffect, VectorHandleRef, VectorMask, VectorMaskType, VectorModifiers, VectorNode, VectorPathfinderOperation, VectorPathNode, VectorSegmentRef, VectorStyle, VectorSubpath, VectorTool, VectorTransformHandle, VectorViewport, VectorWidthPoint, VectorCharacterStyle, VectorParagraphStyle, VectorTextAlign, VectorTextFrame, VectorTextNode, VectorMatrix, VectorSymbol, VectorPaint, VectorConstraint, VectorConstraintKind, VectorParameter, VectorBrushstroke, VectorBrushstrokePaint, VectorBrushstrokePaper } from './types.cjs';
33
33
  export interface VectorSnapOptions {
34
34
  /** Grid size in document units (0 = off, default 0). Points and boxes that snap to no object snap to it. */
35
35
  grid: number;
@@ -113,6 +113,8 @@ export interface VectorEditorOptions {
113
113
  brushPreview?: BrushPreviewRenderer;
114
114
  /** The app's colour scheme, which picks the side of a brush's light / dark default colour (default light). */
115
115
  colorScheme?: 'light' | 'dark';
116
+ /** Start with the colour locked: new and rebrushed brushstrokes keep their colour (see `setColorLocked`). */
117
+ colorLocked?: boolean;
116
118
  /** History to record into, e.g. one shared with the rest of the app (default: a new one). */
117
119
  history?: HistoryManager;
118
120
  /** Starting tool (default `select`). */
@@ -408,6 +410,10 @@ export interface VectorEditorState {
408
410
  * a side skews along it (Alt: about the centre).
409
411
  */
410
412
  freeDistort: boolean;
413
+ /** The colour scheme that picks the side of a brush's light / dark default colour. */
414
+ colorScheme: 'light' | 'dark';
415
+ /** "Keep my colour": brushstrokes don't take a brush's default colour (`setColorLocked`). */
416
+ colorLocked: boolean;
411
417
  /** The text being edited on the canvas, or null. Typing goes to `textInput`, keys to `keyDown`. */
412
418
  textEdit: VectorTextEditState | null;
413
419
  /** Font and alignment new text gets. */
@@ -799,6 +805,7 @@ export declare class VectorEditorController {
799
805
  private revision;
800
806
  private state;
801
807
  private colorScheme;
808
+ private colorLocked;
802
809
  constructor(options?: VectorEditorOptions);
803
810
  /** Listens to the history (once; again after `destroy`). */
804
811
  private connect;
@@ -1133,7 +1140,7 @@ export declare class VectorEditorController {
1133
1140
  * Adds a brushstroke to the selected (or given) paths: a stroke layer that paints the path with
1134
1141
  * brush `brushId` (a preset, or a saved brush of the brush library: `listBrushstrokeBrushes`), `size` document
1135
1142
  * units across (default the brush's own), an optional `pressure` profile along the length, in
1136
- * `color` (default the brush's own default colour, unless `keepColour`; else the top stroke's
1143
+ * `color` (default the brush's own default colour, unless `keepColour` or the colour is locked; else the top stroke's
1137
1144
  * paint, else black) at `opacity`. At the top of the stroke
1138
1145
  * stack or at `index`. A `seed` gives it the brush's character (jitter and the grain of `paper`,
1139
1146
  * `null` for none); without one it renders plain. One undo step.
@@ -1150,7 +1157,7 @@ export declare class VectorEditorController {
1150
1157
  * given) paths: another brush, size, pressure profile (null removes it), colour or opacity, the
1151
1158
  * character `seed` (null removes it: plain dabs) or the `paper` (replaced whole: `null` for no
1152
1159
  * grain, `{}` for the default paper). Another brush without a `color` takes that brush's default
1153
- * colour, if it has one, unless `keepColour`. Fails, changing nothing, when that layer is not a
1160
+ * colour, if it has one, unless `keepColour` or the colour is locked. Fails, changing nothing, when that layer is not a
1154
1161
  * brushstroke. With `merge`, consecutive edits fold into one undo step.
1155
1162
  */
1156
1163
  setBrushstroke(index: number, patch: {
@@ -1162,12 +1169,19 @@ export declare class VectorEditorController {
1162
1169
  opacity?: number;
1163
1170
  seed?: number | null;
1164
1171
  paper?: VectorBrushstrokePaper | null;
1172
+ paint?: VectorBrushstrokePaint | null;
1165
1173
  }, options?: {
1166
1174
  ids?: readonly string[];
1167
1175
  merge?: boolean | string;
1168
1176
  }): VectorActionResult;
1169
1177
  /** Sets the colour scheme that picks the side of a brush's light / dark default colour. Existing strokes keep their colour. */
1170
1178
  setColorScheme(scheme: 'light' | 'dark'): void;
1179
+ /**
1180
+ * Locks the brushstroke colour ("Keep my colour"): `addBrushstroke` and `setBrushstroke` with
1181
+ * another brush keep the colour instead of taking the brush's default; an explicit `color` still
1182
+ * applies. Existing strokes are unchanged. Editor state, not an undo step.
1183
+ */
1184
+ setColorLocked(locked: boolean): void;
1171
1185
  /** Removes layer `index` of the fill or stroke stack of the selected (or given) paths. */
1172
1186
  removePaintLayer(kind: 'fill' | 'stroke', index: number, options?: {
1173
1187
  ids?: readonly string[];