@pixerate/editor 0.262.1 → 0.264.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 (89) hide show
  1. package/dist/animation-editor/index.cjs +60 -60
  2. package/dist/animation-editor/index.js +4 -4
  3. package/dist/brushes/index.cjs +158 -71
  4. package/dist/brushes/index.d.cts +3 -3
  5. package/dist/brushes/index.d.ts +3 -3
  6. package/dist/brushes/index.js +113 -26
  7. package/dist/brushes/library.d.cts +2 -0
  8. package/dist/brushes/library.d.ts +2 -0
  9. package/dist/brushes/profiles.d.cts +21 -2
  10. package/dist/brushes/profiles.d.ts +21 -2
  11. package/dist/brushes/ui.d.cts +57 -4
  12. package/dist/brushes/ui.d.ts +57 -4
  13. package/dist/{brushstrokeRender-25Q5BEY6.js → brushstrokeRender-3MOI7PH4.js} +3 -3
  14. package/dist/{brushstrokeRender-N6UPZSQS.cjs → brushstrokeRender-SMMPIJF2.cjs} +13 -13
  15. package/dist/canvas/index.cjs +15 -14
  16. package/dist/canvas/index.js +3 -2
  17. package/dist/canvas/stickers.d.cts +5 -1
  18. package/dist/canvas/stickers.d.ts +5 -1
  19. package/dist/{chunk-X2EXYY4J.js → chunk-4OJC4MCD.js} +9 -8
  20. package/dist/{chunk-J5UQHWVL.js → chunk-6JL2D5SG.js} +27 -15
  21. package/dist/{chunk-APX63ILU.js → chunk-ACFSFUQH.js} +31 -0
  22. package/dist/{chunk-TZOX7KWV.cjs → chunk-BY5EBP26.cjs} +35 -14
  23. package/dist/{chunk-CQVTGBQI.js → chunk-CECCP2V7.js} +1 -1
  24. package/dist/{chunk-F7CPSRLN.js → chunk-DOZ6F2K2.js} +12 -4
  25. package/dist/{chunk-5IW65VQ6.js → chunk-EE7SAYBF.js} +24 -3
  26. package/dist/{chunk-PYTJBVW5.cjs → chunk-F3KKR6DD.cjs} +37 -36
  27. package/dist/{chunk-YLUIFXSL.cjs → chunk-FQC5TDB6.cjs} +35 -35
  28. package/dist/{chunk-UVZ7RQOE.cjs → chunk-G5ADO47G.cjs} +1 -1
  29. package/dist/{chunk-D4TE5JNW.js → chunk-HAUDEVMC.js} +1 -1
  30. package/dist/{chunk-EHALWBBD.js → chunk-JEML4FSA.js} +1 -1
  31. package/dist/{chunk-43NT242D.cjs → chunk-KXNZNOEF.cjs} +86 -86
  32. package/dist/{chunk-JSDND7DF.cjs → chunk-OTN6VB5S.cjs} +32 -1
  33. package/dist/{chunk-AVTPZO4Z.cjs → chunk-PE6YJMQT.cjs} +37 -25
  34. package/dist/{chunk-QAR2N7HZ.js → chunk-ROBSFJ6F.js} +1 -1
  35. package/dist/{chunk-7AUQ2UJ3.cjs → chunk-USJX5SW4.cjs} +10 -10
  36. package/dist/{chunk-3TQW5MPW.cjs → chunk-V7MJ4HIN.cjs} +23 -23
  37. package/dist/{chunk-6NZL24W4.js → chunk-WWI6FLH2.js} +1 -1
  38. package/dist/{chunk-RVGP2XLF.cjs → chunk-XORJ7UZO.cjs} +21 -13
  39. package/dist/image-editor/controller.d.cts +3 -0
  40. package/dist/image-editor/controller.d.ts +3 -0
  41. package/dist/image-editor/index.cjs +792 -788
  42. package/dist/image-editor/index.js +10 -6
  43. package/dist/image-editor/stickers.d.cts +7 -2
  44. package/dist/image-editor/stickers.d.ts +7 -2
  45. package/dist/image-editor/types.d.cts +7 -0
  46. package/dist/image-editor/types.d.ts +7 -0
  47. package/dist/paint-editor/brush-library-tools.d.cts +8 -1
  48. package/dist/paint-editor/brush-library-tools.d.ts +8 -1
  49. package/dist/paint-editor/controller.d.cts +7 -2
  50. package/dist/paint-editor/controller.d.ts +7 -2
  51. package/dist/paint-editor/index.cjs +581 -524
  52. package/dist/paint-editor/index.d.cts +1 -1
  53. package/dist/paint-editor/index.d.ts +1 -1
  54. package/dist/paint-editor/index.js +105 -48
  55. package/dist/paint-editor/sticker-art.d.cts +20 -10
  56. package/dist/paint-editor/sticker-art.d.ts +20 -10
  57. package/dist/paint-editor/stickers.d.cts +8 -1
  58. package/dist/paint-editor/stickers.d.ts +8 -1
  59. package/dist/paint-editor/stroke.d.cts +2 -0
  60. package/dist/paint-editor/stroke.d.ts +2 -0
  61. package/dist/stickers/browser.d.cts +10 -3
  62. package/dist/stickers/browser.d.ts +10 -3
  63. package/dist/stickers/combine.d.cts +2 -0
  64. package/dist/stickers/combine.d.ts +2 -0
  65. package/dist/stickers/conformance.d.cts +1 -1
  66. package/dist/stickers/conformance.d.ts +1 -1
  67. package/dist/stickers/errors.d.cts +1 -1
  68. package/dist/stickers/errors.d.ts +1 -1
  69. package/dist/stickers/fake.d.cts +4 -2
  70. package/dist/stickers/fake.d.ts +4 -2
  71. package/dist/stickers/helpers.d.cts +16 -2
  72. package/dist/stickers/helpers.d.ts +16 -2
  73. package/dist/stickers/index.cjs +116 -29
  74. package/dist/stickers/index.js +106 -19
  75. package/dist/stickers/tools.d.cts +9 -2
  76. package/dist/stickers/tools.d.ts +9 -2
  77. package/dist/stickers/types.d.cts +32 -5
  78. package/dist/stickers/types.d.ts +32 -5
  79. package/dist/vector/controller.d.cts +13 -0
  80. package/dist/vector/controller.d.ts +13 -0
  81. package/dist/vector/index.cjs +1218 -1207
  82. package/dist/vector/index.js +19 -8
  83. package/dist/vector/stickers.d.cts +7 -2
  84. package/dist/vector/stickers.d.ts +7 -2
  85. package/package.json +1 -1
  86. package/skills/pixerate-brushes/SKILL.md +11 -3
  87. package/skills/pixerate-brushes-design/SKILL.md +1 -0
  88. package/skills/pixerate-stickers/SKILL.md +20 -9
  89. package/skills/pixerate-vector/SKILL.md +1 -1
@@ -3,7 +3,7 @@ import {
3
3
  editorStickers,
4
4
  stampOutline,
5
5
  stampPixels
6
- } from "../chunk-F7CPSRLN.js";
6
+ } from "../chunk-DOZ6F2K2.js";
7
7
  import {
8
8
  createInsertStickerTool,
9
9
  createStickerToolProvider,
@@ -11,23 +11,28 @@ import {
11
11
  describeStickerRef,
12
12
  insertSticker,
13
13
  stickerForAgent
14
- } from "../chunk-J5UQHWVL.js";
14
+ } from "../chunk-6JL2D5SG.js";
15
15
  import {
16
16
  DEFAULT_STICKER_PAGE,
17
17
  EDITOR_STICKERS_ID,
18
18
  MAX_STICKER_PAGE,
19
19
  STAMP_KINDS,
20
+ STICKER_VARIANTS,
20
21
  StickersSlot,
21
22
  checkStickerSearchOptions,
23
+ checkStickerVariant,
22
24
  closestStickerRefs,
23
25
  editorStampOf,
24
26
  normalizeStickerRef,
25
27
  parseStickerRef,
26
28
  stampCoverage,
29
+ stickerCollections,
27
30
  stickerIdFor,
28
31
  stickerPage,
29
- stickerRefOf
30
- } from "../chunk-APX63ILU.js";
32
+ stickerRefOf,
33
+ stickerVariantFor,
34
+ stickerVariantsOf
35
+ } from "../chunk-ACFSFUQH.js";
31
36
  import {
32
37
  StickerError
33
38
  } from "../chunk-SZTI6EER.js";
@@ -39,11 +44,17 @@ import "../chunk-QGHEDC7B.js";
39
44
 
40
45
  // src/stickers/fake.ts
41
46
  var CATALOGUE = [
42
- ...["sun", "moon", "cloud", "rain", "snow", "wind"].map((w, i) => ({ id: `weather-${w}`, name: w[0].toUpperCase() + w.slice(1), category: "weather", tags: ["weather", w], shape: "badge", colours: [["#f59e0b", "#64748b", "#94a3b8", "#2563eb", "#e0f2fe", "#14b8a6"][i], "#ffffff"], commercial: true })),
43
- ...["apple", "pear", "cherry", "lemon", "grape"].map((f, i) => ({ id: `fruit-${f}`, name: f[0].toUpperCase() + f.slice(1), category: "food", tags: ["fruit", f], shape: "tile", colours: [["#dc2626", "#84cc16", "#be123c", "#facc15", "#7c3aed"][i], "#166534"], commercial: true })),
44
- ...["new", "sale", "hot", "free"].map((b, i) => ({ id: `badge-${b}`, name: `${b[0].toUpperCase() + b.slice(1)} badge`, category: "badges", tags: ["badge", "label", b], shape: "badge", colours: [["#16a34a", "#dc2626", "#ea580c", "#0891b2"][i], "#ffffff"], commercial: true })),
47
+ ...["sun", "moon", "cloud", "rain", "snow", "wind"].map((w, i) => ({ id: `weather-${w}`, name: w[0].toUpperCase() + w.slice(1), category: "weather", tags: ["weather", w], shape: "badge", colours: [["#f59e0b", "#64748b", "#94a3b8", "#2563eb", "#e0f2fe", "#14b8a6"][i], "#ffffff"], commercial: true, collection: "outdoors", aiGenerated: true })),
48
+ ...["apple", "pear", "cherry", "lemon", "grape"].map((f, i) => ({ id: `fruit-${f}`, name: f[0].toUpperCase() + f.slice(1), category: "food", tags: ["fruit", f], shape: "tile", colours: [["#dc2626", "#84cc16", "#be123c", "#facc15", "#7c3aed"][i], "#166534"], commercial: true, collection: "kitchen" })),
49
+ ...["new", "sale", "hot", "free"].map((b, i) => ({ id: `badge-${b}`, name: `${b[0].toUpperCase() + b.slice(1)} badge`, category: "badges", tags: ["badge", "label", b], shape: "badge", colours: [["#16a34a", "#dc2626", "#ea580c", "#0891b2"][i], "#ffffff"], commercial: true, collection: "shop" })),
45
50
  { id: "art-sketch", name: "Sketch", category: "art", tags: ["drawing", "non-commercial"], shape: "tile", colours: ["#1f2937", "#fde68a"], commercial: false }
46
51
  ];
52
+ var COLLECTIONS = {
53
+ outdoors: { name: "Outdoors", description: "Weather, AI-generated." },
54
+ kitchen: { name: "Kitchen", description: "Fruit tiles with dark variants." },
55
+ shop: { name: "Shop", description: "Tintable sale badges." }
56
+ };
57
+ var DARK_TOP = "#f8fafc";
47
58
  var W = 120;
48
59
  var H = 120;
49
60
  function summaryOf(s) {
@@ -57,16 +68,22 @@ function summaryOf(s) {
57
68
  height: H,
58
69
  formats: ["svg", "png"],
59
70
  licence: s.commercial ? { spdx: "CC0-1.0", name: "CC0 1.0", commercial: true } : { spdx: "CC-BY-NC-4.0", name: "CC BY-NC 4.0", attribution: "Fake Artist", commercial: false },
60
- tintable: s.shape === "badge"
71
+ tintable: s.shape === "badge",
72
+ ...s.shape === "tile" ? { variants: ["light", "dark"] } : {},
73
+ ...s.collection ? { collection: s.collection } : {},
74
+ ...s.aiGenerated ? { aiGenerated: true } : {}
61
75
  };
62
76
  }
63
- function svgOf(s, fill) {
77
+ var tileColours = (s, variant) => variant === "dark" ? [s.colours[0], DARK_TOP] : s.colours;
78
+ function svgOf(s, fill, variant) {
64
79
  if (s.shape === "badge") {
65
80
  return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}"><path fill="${fill}" fill-rule="evenodd" d="M60 6A54 54 0 1 1 59.9 6ZM45 45H75V75H45Z"/></svg>`;
66
81
  }
67
- return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}"><rect x="10" y="10" width="100" height="100" fill="${s.colours[0]}"/><rect x="10" y="10" width="100" height="50" fill="${s.colours[1]}"/></svg>`;
82
+ const [bottom, top] = tileColours(s, variant);
83
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}"><rect x="10" y="10" width="100" height="100" fill="${bottom}"/><rect x="10" y="10" width="100" height="50" fill="${top}"/></svg>`;
68
84
  }
69
- function pixelsOf(s, size, fill) {
85
+ function pixelsOf(s, size, fill, variant) {
86
+ const [bottom, top] = tileColours(s, variant);
70
87
  const out = new Uint8ClampedArray(size * size * 4);
71
88
  const hex = (c) => [parseInt(c.slice(1, 3), 16), parseInt(c.slice(3, 5), 16), parseInt(c.slice(5, 7), 16)];
72
89
  for (let y = 0; y < size; y++) {
@@ -78,7 +95,7 @@ function pixelsOf(s, size, fill) {
78
95
  const inDisc = Math.hypot(u - 60, v - 60) <= 54;
79
96
  const inNotch = u >= 45 && u <= 75 && v >= 45 && v <= 75;
80
97
  if (inDisc && !inNotch) rgb = hex(fill);
81
- } else if (u >= 10 && u <= 110 && v >= 10 && v <= 110) rgb = hex(v < 60 ? s.colours[1] : s.colours[0]);
98
+ } else if (u >= 10 && u <= 110 && v >= 10 && v <= 110) rgb = hex(v < 60 ? top : bottom);
82
99
  if (!rgb) continue;
83
100
  const i = (y * size + x) * 4;
84
101
  out[i] = rgb[0];
@@ -104,19 +121,21 @@ function createFakeStickerProvider(options = {}) {
104
121
  if (!s) return void 0;
105
122
  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] };
106
123
  },
124
+ listCollections: () => stickerCollections(summaries, COLLECTIONS),
107
125
  categories: () => [...new Set(CATALOGUE.map((s) => s.category))].map((c) => ({ id: c, name: c, count: CATALOGUE.filter((s) => s.category === c).length })),
108
126
  async load(id, o = {}) {
109
127
  if (options.loadDelayMs) await new Promise((r) => setTimeout(r, options.loadDelayMs));
110
128
  const s = find(id);
111
129
  if (!s) throw new StickerError("unknown_sticker", `No fake sticker "${id}".`);
112
130
  if (options.failIds?.includes(id)) throw new StickerError("load_failed", `The fake sticker "${id}" failed to load (failIds).`);
131
+ const variant = checkStickerVariant(summaryOf(s), o.variant);
113
132
  const fill = s.shape === "badge" && o.colour ? o.colour.toLowerCase() : s.colours[0];
114
133
  if (o.format === "png") {
115
134
  const size = o.size ?? 128;
116
135
  if (!(Number.isInteger(size) && size >= 8 && size <= 512)) throw new StickerError("invalid_argument", `size must be 8..512 px, got ${size}.`);
117
- return { kind: "image", url: pngDataUrl(size, size, pixelsOf(s, size, fill)), width: size, height: size };
136
+ return { kind: "image", url: pngDataUrl(size, size, pixelsOf(s, size, fill, variant)), width: size, height: size };
118
137
  }
119
- return { kind: "svg", svg: svgOf(s, fill) };
138
+ return { kind: "svg", svg: svgOf(s, fill, variant) };
120
139
  }
121
140
  };
122
141
  }
@@ -133,10 +152,18 @@ function combineStickerProviders(providers, options = {}) {
133
152
  if (!p) throw new StickerError("unknown_provider", `No sticker provider "${provider}"; the providers are ${ids.join(", ") || "none"}.`);
134
153
  return { p, id };
135
154
  };
136
- const prefix = (p, s) => ({ ...s, id: stickerRefOf(p, s.id) });
155
+ const collectionOf = (p, c) => p.namespaced ? c : `${p.id}:${c}`;
156
+ const prefix = (p, s) => ({ ...s, id: stickerRefOf(p, s.id), ...s.collection ? { collection: collectionOf(p, s.collection) } : {} });
157
+ const collectionOwner = (c) => {
158
+ const i = c.indexOf(":");
159
+ 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).`);
161
+ return { p, collection: p.namespaced ? c : c.slice(i + 1) };
162
+ };
137
163
  const page = async (query, o = {}) => {
138
- const { after, limit = DEFAULT_STICKER_PAGE, ...filters } = o;
164
+ const { after, limit = DEFAULT_STICKER_PAGE, collection, ...filters } = o;
139
165
  checkStickerSearchOptions({ limit });
166
+ const only = collection !== void 0 ? collectionOwner(collection) : null;
140
167
  let i = 0;
141
168
  let inner;
142
169
  if (after !== void 0) {
@@ -145,7 +172,11 @@ function combineStickerProviders(providers, options = {}) {
145
172
  i = Number(m[1]);
146
173
  inner = m[2] || void 0;
147
174
  }
148
- const get = (p, opts) => query === null ? p.list(opts) : p.search(query, opts);
175
+ const get = async (p, opts) => {
176
+ if (only && only.p !== p) return { items: [], total: 0 };
177
+ const withCollection = only ? { ...opts, collection: only.collection } : opts;
178
+ return query === null ? p.list(withCollection) : p.search(query, withCollection);
179
+ };
149
180
  let total = 0;
150
181
  for (const p of providers) total += (await get(p, { ...filters, limit: 1 })).total;
151
182
  const items = [];
@@ -185,7 +216,7 @@ function combineStickerProviders(providers, options = {}) {
185
216
  return void 0;
186
217
  }
187
218
  const d = await o.p.describe(o.id);
188
- return d ? { ...d, id: stickerRefOf(o.p, d.id) } : void 0;
219
+ return d ? prefix(o.p, d) : void 0;
189
220
  },
190
221
  async load(ref, o) {
191
222
  const { p, id } = owner(ref);
@@ -200,6 +231,11 @@ function combineStickerProviders(providers, options = {}) {
200
231
  }
201
232
  }
202
233
  return [...counts.values()];
234
+ },
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;
203
239
  }
204
240
  };
205
241
  }
@@ -220,6 +256,14 @@ function checkSummary(s, provider, issues) {
220
256
  if (typeof s.licence?.commercial !== "boolean") at("licence.commercial is not a boolean");
221
257
  if (typeof s.tintable !== "boolean") at("tintable is not a boolean");
222
258
  if (s.thumbnail !== void 0 && !/^(data:|https:)/.test(s.thumbnail)) at("thumbnail must be a data: or https: URL");
259
+ if (s.aiGenerated !== void 0 && typeof s.aiGenerated !== "boolean") at("aiGenerated must be a boolean when present");
260
+ if (s.variants !== void 0) {
261
+ const v = s.variants;
262
+ if (!Array.isArray(v) || v.some((x) => x !== "light" && x !== "dark") || new Set(v).size !== v.length) at("variants must list light and / or dark, each once");
263
+ else if (!v.includes("light")) at("variants must include light (every sticker has its light art)");
264
+ }
265
+ 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()`);
223
267
  }
224
268
  async function checkStickerProvider(provider, options = {}) {
225
269
  const issues = [];
@@ -278,6 +322,26 @@ async function checkStickerProvider(provider, options = {}) {
278
322
  if (d?.colours?.some((c) => !HEX.test(c))) top(`describe("${first.id}").colours must be #rrggbb`);
279
323
  }
280
324
  if (await provider.describe("__no_such_sticker__") !== void 0) top("describe of an unknown id returned a sticker");
325
+ if (provider.listCollections) {
326
+ try {
327
+ const collections = await provider.listCollections();
328
+ const known = /* @__PURE__ */ new Set();
329
+ for (const c of collections) {
330
+ 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`);
332
+ continue;
333
+ }
334
+ if (known.has(c.id)) top(`listCollections() lists "${c.id}" twice`);
335
+ known.add(c.id);
336
+ const page = await provider.list({ collection: c.id, limit: 100 });
337
+ if (page.total !== c.count) top(`collection "${c.id}" says count ${c.count} but list({ collection }) has ${page.total}`);
338
+ if (page.items.some((x) => x.collection !== c.id)) top(`list({ collection: "${c.id}" }) returned stickers of other collections`);
339
+ }
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()` });
341
+ } catch (e) {
342
+ top(`listCollections() rejected: ${e.message}`);
343
+ }
344
+ }
281
345
  let rejected = false;
282
346
  try {
283
347
  await provider.load("__no_such_sticker__");
@@ -296,6 +360,24 @@ async function checkStickerProvider(provider, options = {}) {
296
360
  }
297
361
  }
298
362
  }
363
+ const dark = all.find((s) => s.variants?.includes("dark"));
364
+ if (dark) {
365
+ try {
366
+ await provider.load(dark.id, { format: dark.formats[0], variant: "dark", size: 32 });
367
+ } catch (e) {
368
+ issues.push({ stickerId: dark.id, message: `load({ variant: "dark" }) rejected for a sticker that lists it: ${e.message}` });
369
+ }
370
+ }
371
+ const lightOnly = all.find((s) => !s.variants?.includes("dark"));
372
+ if (lightOnly) {
373
+ let refused = false;
374
+ try {
375
+ await provider.load(lightOnly.id, { format: lightOnly.formats[0], variant: "dark", size: 32 });
376
+ } catch {
377
+ refused = true;
378
+ }
379
+ if (!refused) issues.push({ stickerId: lightOnly.id, message: 'load({ variant: "dark" }) did not reject for a sticker without a dark variant (checkStickerVariant does it)' });
380
+ }
299
381
  const tintable = all.find((s) => s.tintable);
300
382
  if (tintable) {
301
383
  try {
@@ -312,11 +394,13 @@ export {
312
394
  EDITOR_STICKERS_ID,
313
395
  MAX_STICKER_PAGE,
314
396
  STAMP_KINDS,
397
+ STICKER_VARIANTS,
315
398
  StickerBrowser,
316
399
  StickerError,
317
400
  StickersSlot,
318
401
  checkStickerProvider,
319
402
  checkStickerSearchOptions,
403
+ checkStickerVariant,
320
404
  closestStickerRefs,
321
405
  combineStickerProviders,
322
406
  createFakeStickerProvider,
@@ -332,8 +416,11 @@ export {
332
416
  stampCoverage,
333
417
  stampOutline,
334
418
  stampPixels,
419
+ stickerCollections,
335
420
  stickerForAgent,
336
421
  stickerIdFor,
337
422
  stickerPage,
338
- stickerRefOf
423
+ stickerRefOf,
424
+ stickerVariantFor,
425
+ stickerVariantsOf
339
426
  };
@@ -4,7 +4,7 @@
4
4
  * stickers. Refusals name the valid choices.
5
5
  */
6
6
  import type { AgentTool, AgentToolProvider } from '../tools/registry.cjs';
7
- import type { MaybePromise, StickerAsset, StickerDescription, StickerFormat, StickerProvider, StickerRef, StickerSummary } from './types.cjs';
7
+ import { type MaybePromise, type StickerAsset, type StickerDescription, type StickerFormat, type StickerProvider, type StickerRef, type StickerSummary, type StickerVariant } from './types.cjs';
8
8
  /** Where and how big an inserted sticker goes, with its art. */
9
9
  export interface StickerPlacement {
10
10
  ref: StickerRef;
@@ -18,6 +18,8 @@ export interface StickerPlacement {
18
18
  height: number;
19
19
  /** #rrggbb it was tinted to, when tintable and asked for. */
20
20
  colour?: string;
21
+ /** The variant its art is (`dark` for a dark background, when the sticker has it). */
22
+ variant: StickerVariant;
21
23
  }
22
24
  /**
23
25
  * An editor that can take an inserted sticker (`insert_sticker`): the vector editor (editable
@@ -33,6 +35,8 @@ export interface StickerInsertHost {
33
35
  x: number;
34
36
  y: number;
35
37
  };
38
+ /** The background is dark (a dark canvas or theme): stickers with a dark variant use it unless the call names one. */
39
+ dark?(): boolean;
36
40
  /** Adds the sticker as one undo step; returns what it made (its id at least) for later tools. */
37
41
  insert(placement: StickerPlacement): MaybePromise<{
38
42
  id: string;
@@ -57,7 +61,8 @@ export declare function createStickerTools(options: StickerToolsOptions): AgentT
57
61
  * Puts one sticker into an editor through its host (the panels and `insert_sticker` both use it):
58
62
  * checks the ref, loads the art in the host's format, tinted when asked and tintable, and places
59
63
  * it centred at (`x`, `y`) (default the host's centre), `width` wide (default its natural width).
60
- * Returns what the host made, the ref, the size and the licence.
64
+ * The art is the `variant` asked for, else the dark one when the host's background is dark and
65
+ * the sticker has it. Returns what the host made, the ref, the size, the variant and the licence.
61
66
  */
62
67
  export declare function insertSticker(provider: StickerProvider, host: StickerInsertHost, options: {
63
68
  ref: StickerRef;
@@ -65,11 +70,13 @@ export declare function insertSticker(provider: StickerProvider, host: StickerIn
65
70
  y?: number;
66
71
  width?: number;
67
72
  color?: string;
73
+ variant?: StickerVariant;
68
74
  }): Promise<{
69
75
  id: string;
70
76
  ref: StickerRef;
71
77
  width: number;
72
78
  height: number;
79
+ variant: StickerVariant;
73
80
  licence: StickerDescription['licence'];
74
81
  } & Record<string, unknown>>;
75
82
  /** `insert_sticker`: one sticker by ref into an editor, at a place and size, as one undo step. */
@@ -4,7 +4,7 @@
4
4
  * stickers. Refusals name the valid choices.
5
5
  */
6
6
  import type { AgentTool, AgentToolProvider } from '../tools/registry.js';
7
- import type { MaybePromise, StickerAsset, StickerDescription, StickerFormat, StickerProvider, StickerRef, StickerSummary } from './types.js';
7
+ import { type MaybePromise, type StickerAsset, type StickerDescription, type StickerFormat, type StickerProvider, type StickerRef, type StickerSummary, type StickerVariant } from './types.js';
8
8
  /** Where and how big an inserted sticker goes, with its art. */
9
9
  export interface StickerPlacement {
10
10
  ref: StickerRef;
@@ -18,6 +18,8 @@ export interface StickerPlacement {
18
18
  height: number;
19
19
  /** #rrggbb it was tinted to, when tintable and asked for. */
20
20
  colour?: string;
21
+ /** The variant its art is (`dark` for a dark background, when the sticker has it). */
22
+ variant: StickerVariant;
21
23
  }
22
24
  /**
23
25
  * An editor that can take an inserted sticker (`insert_sticker`): the vector editor (editable
@@ -33,6 +35,8 @@ export interface StickerInsertHost {
33
35
  x: number;
34
36
  y: number;
35
37
  };
38
+ /** The background is dark (a dark canvas or theme): stickers with a dark variant use it unless the call names one. */
39
+ dark?(): boolean;
36
40
  /** Adds the sticker as one undo step; returns what it made (its id at least) for later tools. */
37
41
  insert(placement: StickerPlacement): MaybePromise<{
38
42
  id: string;
@@ -57,7 +61,8 @@ export declare function createStickerTools(options: StickerToolsOptions): AgentT
57
61
  * Puts one sticker into an editor through its host (the panels and `insert_sticker` both use it):
58
62
  * checks the ref, loads the art in the host's format, tinted when asked and tintable, and places
59
63
  * it centred at (`x`, `y`) (default the host's centre), `width` wide (default its natural width).
60
- * Returns what the host made, the ref, the size and the licence.
64
+ * The art is the `variant` asked for, else the dark one when the host's background is dark and
65
+ * the sticker has it. Returns what the host made, the ref, the size, the variant and the licence.
61
66
  */
62
67
  export declare function insertSticker(provider: StickerProvider, host: StickerInsertHost, options: {
63
68
  ref: StickerRef;
@@ -65,11 +70,13 @@ export declare function insertSticker(provider: StickerProvider, host: StickerIn
65
70
  y?: number;
66
71
  width?: number;
67
72
  color?: string;
73
+ variant?: StickerVariant;
68
74
  }): Promise<{
69
75
  id: string;
70
76
  ref: StickerRef;
71
77
  width: number;
72
78
  height: number;
79
+ variant: StickerVariant;
73
80
  licence: StickerDescription['licence'];
74
81
  } & Record<string, unknown>>;
75
82
  /** `insert_sticker`: one sticker by ref into an editor, at a place and size, as one undo step. */
@@ -20,6 +20,22 @@ export interface StickerLicence {
20
20
  commercial: boolean;
21
21
  }
22
22
  export type StickerFormat = 'svg' | 'png';
23
+ /**
24
+ * A version of a sticker drawn for a light or a dark background: the same subject, pose and size.
25
+ * `light` is the default art every sticker has.
26
+ */
27
+ export type StickerVariant = 'light' | 'dark';
28
+ /** The variants there are, in order. */
29
+ export declare const STICKER_VARIANTS: readonly StickerVariant[];
30
+ /** A themed set of stickers within one provider (one per brand, say). */
31
+ export interface StickerCollection {
32
+ /** Unique within the provider; what `StickerSummary.collection` and `StickerSearchOptions.collection` name. */
33
+ id: string;
34
+ name: string;
35
+ description?: string;
36
+ /** How many stickers it holds. */
37
+ count: number;
38
+ }
23
39
  /** A sticker in a list or search result. */
24
40
  export interface StickerSummary {
25
41
  /** The provider's id (no `:` needed; `combineStickerProviders` prefixes its own). */
@@ -38,6 +54,15 @@ export interface StickerSummary {
38
54
  licence: StickerLicence;
39
55
  /** Flat, single-colour art: `load(id, { colour })` recolours it (the paint editor's spray colours it). */
40
56
  tintable: boolean;
57
+ /** The art was made by an AI model (absent: not said), so pickers and agents can label it. */
58
+ aiGenerated?: boolean;
59
+ /**
60
+ * The variants `load(id, { variant })` returns, always with `light` (absent: only `light`), so a
61
+ * picker or brush can pick the dark one for a dark background without calling `describe` first.
62
+ */
63
+ variants?: StickerVariant[];
64
+ /** The collection it belongs to (`listCollections`), when the provider groups its stickers. */
65
+ collection?: string;
41
66
  }
42
67
  /** Everything about one sticker. */
43
68
  export interface StickerDescription extends StickerSummary {
@@ -45,10 +70,6 @@ export interface StickerDescription extends StickerSummary {
45
70
  silhouette?: string;
46
71
  /** Dominant colours #rrggbb, for matching and recolouring. */
47
72
  colours?: string[];
48
- variants?: {
49
- id: string;
50
- name: string;
51
- }[];
52
73
  }
53
74
  /** A sticker's art. */
54
75
  export type StickerAsset = {
@@ -72,6 +93,8 @@ export interface StickerPage {
72
93
  }
73
94
  export interface StickerSearchOptions {
74
95
  category?: string;
96
+ /** Only stickers of this collection (`listCollections`). */
97
+ collection?: string;
75
98
  /** Every tag must match. */
76
99
  tags?: string[];
77
100
  /** Page size (providers cap it; 1..100, default 24). */
@@ -88,6 +111,8 @@ export interface StickerLoadOptions {
88
111
  size?: number;
89
112
  /** #rrggbb: recolour a tintable sticker. */
90
113
  colour?: string;
114
+ /** The art for a light (default) or dark background; one the sticker doesn't list is refused, naming the ones it has. */
115
+ variant?: StickerVariant;
91
116
  }
92
117
  export interface StickerProviderCapabilities {
93
118
  supportsSearch: boolean;
@@ -110,7 +135,7 @@ export interface StickerProvider {
110
135
  search(query: string, options?: StickerSearchOptions): MaybePromise<StickerPage>;
111
136
  /** A sticker by id, or `undefined`. */
112
137
  describe(id: string): MaybePromise<StickerDescription | undefined>;
113
- /** The art; rejects with a `StickerError` (`unknown_sticker`, `unsupported_format`) when it can't. */
138
+ /** The art; rejects with a `StickerError` (`unknown_sticker`, `unsupported_format`, `unsupported_variant`) when it can't. */
114
139
  load(id: string, options?: StickerLoadOptions): MaybePromise<StickerAsset>;
115
140
  /** Categories with counts, when the provider knows them. */
116
141
  categories?(): MaybePromise<Array<{
@@ -118,6 +143,8 @@ export interface StickerProvider {
118
143
  name: string;
119
144
  count: number;
120
145
  }>>;
146
+ /** Its collections with counts, when it groups its stickers. Ids stay unique across collections, so `load(id)` needs none. */
147
+ listCollections?(): MaybePromise<StickerCollection[]>;
121
148
  }
122
149
  /** `provider:id`, the name of a sticker across providers. A bare editor stamp (`"star"`) means `editor:star`. */
123
150
  export type StickerRef = string;
@@ -20,6 +20,22 @@ export interface StickerLicence {
20
20
  commercial: boolean;
21
21
  }
22
22
  export type StickerFormat = 'svg' | 'png';
23
+ /**
24
+ * A version of a sticker drawn for a light or a dark background: the same subject, pose and size.
25
+ * `light` is the default art every sticker has.
26
+ */
27
+ export type StickerVariant = 'light' | 'dark';
28
+ /** The variants there are, in order. */
29
+ export declare const STICKER_VARIANTS: readonly StickerVariant[];
30
+ /** A themed set of stickers within one provider (one per brand, say). */
31
+ export interface StickerCollection {
32
+ /** Unique within the provider; what `StickerSummary.collection` and `StickerSearchOptions.collection` name. */
33
+ id: string;
34
+ name: string;
35
+ description?: string;
36
+ /** How many stickers it holds. */
37
+ count: number;
38
+ }
23
39
  /** A sticker in a list or search result. */
24
40
  export interface StickerSummary {
25
41
  /** The provider's id (no `:` needed; `combineStickerProviders` prefixes its own). */
@@ -38,6 +54,15 @@ export interface StickerSummary {
38
54
  licence: StickerLicence;
39
55
  /** Flat, single-colour art: `load(id, { colour })` recolours it (the paint editor's spray colours it). */
40
56
  tintable: boolean;
57
+ /** The art was made by an AI model (absent: not said), so pickers and agents can label it. */
58
+ aiGenerated?: boolean;
59
+ /**
60
+ * The variants `load(id, { variant })` returns, always with `light` (absent: only `light`), so a
61
+ * picker or brush can pick the dark one for a dark background without calling `describe` first.
62
+ */
63
+ variants?: StickerVariant[];
64
+ /** The collection it belongs to (`listCollections`), when the provider groups its stickers. */
65
+ collection?: string;
41
66
  }
42
67
  /** Everything about one sticker. */
43
68
  export interface StickerDescription extends StickerSummary {
@@ -45,10 +70,6 @@ export interface StickerDescription extends StickerSummary {
45
70
  silhouette?: string;
46
71
  /** Dominant colours #rrggbb, for matching and recolouring. */
47
72
  colours?: string[];
48
- variants?: {
49
- id: string;
50
- name: string;
51
- }[];
52
73
  }
53
74
  /** A sticker's art. */
54
75
  export type StickerAsset = {
@@ -72,6 +93,8 @@ export interface StickerPage {
72
93
  }
73
94
  export interface StickerSearchOptions {
74
95
  category?: string;
96
+ /** Only stickers of this collection (`listCollections`). */
97
+ collection?: string;
75
98
  /** Every tag must match. */
76
99
  tags?: string[];
77
100
  /** Page size (providers cap it; 1..100, default 24). */
@@ -88,6 +111,8 @@ export interface StickerLoadOptions {
88
111
  size?: number;
89
112
  /** #rrggbb: recolour a tintable sticker. */
90
113
  colour?: string;
114
+ /** The art for a light (default) or dark background; one the sticker doesn't list is refused, naming the ones it has. */
115
+ variant?: StickerVariant;
91
116
  }
92
117
  export interface StickerProviderCapabilities {
93
118
  supportsSearch: boolean;
@@ -110,7 +135,7 @@ export interface StickerProvider {
110
135
  search(query: string, options?: StickerSearchOptions): MaybePromise<StickerPage>;
111
136
  /** A sticker by id, or `undefined`. */
112
137
  describe(id: string): MaybePromise<StickerDescription | undefined>;
113
- /** The art; rejects with a `StickerError` (`unknown_sticker`, `unsupported_format`) when it can't. */
138
+ /** The art; rejects with a `StickerError` (`unknown_sticker`, `unsupported_format`, `unsupported_variant`) when it can't. */
114
139
  load(id: string, options?: StickerLoadOptions): MaybePromise<StickerAsset>;
115
140
  /** Categories with counts, when the provider knows them. */
116
141
  categories?(): MaybePromise<Array<{
@@ -118,6 +143,8 @@ export interface StickerProvider {
118
143
  name: string;
119
144
  count: number;
120
145
  }>>;
146
+ /** Its collections with counts, when it groups its stickers. Ids stay unique across collections, so `load(id)` needs none. */
147
+ listCollections?(): MaybePromise<StickerCollection[]>;
121
148
  }
122
149
  /** `provider:id`, the name of a sticker across providers. A bare editor stamp (`"star"`) means `editor:star`. */
123
150
  export type StickerRef = string;
@@ -1,4 +1,5 @@
1
1
  import { type BrushLibrary } from '../brushes/library.cjs';
2
+ import type { BrushPreviewRenderer } from '../brushes/tools.cjs';
2
3
  import type { PaintBrush } from '../paint-editor/brushes.cjs';
3
4
  import { type VectorStylizeOptions, type VectorStylizeStyle } from './stylize.cjs';
4
5
  import { type VectorDistortBrushMode } from './distortBrush.cjs';
@@ -104,6 +105,12 @@ export interface VectorEditorOptions {
104
105
  * profile decides which). Share one with the paint editor so a brush saved there paints here.
105
106
  */
106
107
  brushLibrary?: Pick<BrushLibrary, 'list' | 'subscribe'>;
108
+ /**
109
+ * Draws a brush's preview stroke for the brush editor ("Edit brush…"), e.g.
110
+ * `renderCanvasBrushPreview` from `@pixerate/editor/canvas` (the renderer brushstrokes paint
111
+ * with; not bundled here). Without it the brush editor has no preview.
112
+ */
113
+ brushPreview?: BrushPreviewRenderer;
107
114
  /** The app's colour scheme, which picks the side of a brush's light / dark default colour (default light). */
108
115
  colorScheme?: 'light' | 'dark';
109
116
  /** History to record into, e.g. one shared with the rest of the app (default: a new one). */
@@ -802,6 +809,12 @@ export declare class VectorEditorController {
802
809
  */
803
810
  /** The saved brushes of the brush library as last read (empty without one). */
804
811
  private brushes;
812
+ /** The brush library brushstrokes use (`brushLibrary` option), or `null`. A full `BrushLibrary` lets the UI edit brushes (`isBrushLibrary`). */
813
+ readonly brushLibrary: Pick<BrushLibrary, 'list' | 'subscribe'> | null;
814
+ /** The `brushPreview` option: draws the brush editor's preview stroke, or `null`. */
815
+ readonly brushPreview: BrushPreviewRenderer | null;
816
+ /** Reads the brush library again now (it is also read after every change to it); resolves once read. */
817
+ refreshBrushes(): Promise<void>;
805
818
  /** The brushes a brushstroke can use: the presets, then the brush library's saved brushes the vector profile admits. */
806
819
  listBrushstrokeBrushes(): PaintBrush[];
807
820
  /** Resolves once the brush library has been read (its saved brushes can be used from then on). */
@@ -1,4 +1,5 @@
1
1
  import { type BrushLibrary } from '../brushes/library.js';
2
+ import type { BrushPreviewRenderer } from '../brushes/tools.js';
2
3
  import type { PaintBrush } from '../paint-editor/brushes.js';
3
4
  import { type VectorStylizeOptions, type VectorStylizeStyle } from './stylize.js';
4
5
  import { type VectorDistortBrushMode } from './distortBrush.js';
@@ -104,6 +105,12 @@ export interface VectorEditorOptions {
104
105
  * profile decides which). Share one with the paint editor so a brush saved there paints here.
105
106
  */
106
107
  brushLibrary?: Pick<BrushLibrary, 'list' | 'subscribe'>;
108
+ /**
109
+ * Draws a brush's preview stroke for the brush editor ("Edit brush…"), e.g.
110
+ * `renderCanvasBrushPreview` from `@pixerate/editor/canvas` (the renderer brushstrokes paint
111
+ * with; not bundled here). Without it the brush editor has no preview.
112
+ */
113
+ brushPreview?: BrushPreviewRenderer;
107
114
  /** The app's colour scheme, which picks the side of a brush's light / dark default colour (default light). */
108
115
  colorScheme?: 'light' | 'dark';
109
116
  /** History to record into, e.g. one shared with the rest of the app (default: a new one). */
@@ -802,6 +809,12 @@ export declare class VectorEditorController {
802
809
  */
803
810
  /** The saved brushes of the brush library as last read (empty without one). */
804
811
  private brushes;
812
+ /** The brush library brushstrokes use (`brushLibrary` option), or `null`. A full `BrushLibrary` lets the UI edit brushes (`isBrushLibrary`). */
813
+ readonly brushLibrary: Pick<BrushLibrary, 'list' | 'subscribe'> | null;
814
+ /** The `brushPreview` option: draws the brush editor's preview stroke, or `null`. */
815
+ readonly brushPreview: BrushPreviewRenderer | null;
816
+ /** Reads the brush library again now (it is also read after every change to it); resolves once read. */
817
+ refreshBrushes(): Promise<void>;
805
818
  /** The brushes a brushstroke can use: the presets, then the brush library's saved brushes the vector profile admits. */
806
819
  listBrushstrokeBrushes(): PaintBrush[];
807
820
  /** Resolves once the brush library has been read (its saved brushes can be used from then on). */