@pixerate/editor 0.262.0 → 0.263.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 (69) 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 +52 -52
  4. package/dist/brushes/index.js +4 -4
  5. package/dist/{brushstrokeRender-N6UPZSQS.cjs → brushstrokeRender-2GBME4XD.cjs} +13 -13
  6. package/dist/{brushstrokeRender-25Q5BEY6.js → brushstrokeRender-S5DABANG.js} +3 -3
  7. package/dist/canvas/index.cjs +15 -14
  8. package/dist/canvas/index.js +3 -2
  9. package/dist/canvas/stickers.d.cts +5 -1
  10. package/dist/canvas/stickers.d.ts +5 -1
  11. package/dist/{chunk-6NZL24W4.js → chunk-34HIZXFX.js} +1 -1
  12. package/dist/{chunk-UVZ7RQOE.cjs → chunk-4E6BGHWN.cjs} +1 -1
  13. package/dist/{chunk-J5UQHWVL.js → chunk-6JL2D5SG.js} +27 -15
  14. package/dist/{chunk-QAR2N7HZ.js → chunk-6XX7E7O2.js} +1 -1
  15. package/dist/{chunk-3TQW5MPW.cjs → chunk-73QFSPDG.cjs} +23 -23
  16. package/dist/{chunk-APX63ILU.js → chunk-ACFSFUQH.js} +31 -0
  17. package/dist/{chunk-X2EXYY4J.js → chunk-CNLO6E2K.js} +2 -2
  18. package/dist/{chunk-F7CPSRLN.js → chunk-DOZ6F2K2.js} +12 -4
  19. package/dist/{chunk-CQVTGBQI.js → chunk-JGI4PZWO.js} +1 -1
  20. package/dist/{chunk-EHALWBBD.js → chunk-LG6MEEV7.js} +1 -1
  21. package/dist/{chunk-7AUQ2UJ3.cjs → chunk-LWHUU6A6.cjs} +10 -10
  22. package/dist/{chunk-JSDND7DF.cjs → chunk-OTN6VB5S.cjs} +32 -1
  23. package/dist/{chunk-5IW65VQ6.js → chunk-P5343XZO.js} +1 -1
  24. package/dist/{chunk-AVTPZO4Z.cjs → chunk-PE6YJMQT.cjs} +37 -25
  25. package/dist/{chunk-D4TE5JNW.js → chunk-QW7LKWKE.js} +1 -1
  26. package/dist/{chunk-YLUIFXSL.cjs → chunk-TIHW7CUY.cjs} +35 -35
  27. package/dist/{chunk-PYTJBVW5.cjs → chunk-TILEUVRO.cjs} +31 -31
  28. package/dist/{chunk-RVGP2XLF.cjs → chunk-XORJ7UZO.cjs} +21 -13
  29. package/dist/{chunk-43NT242D.cjs → chunk-XQW6R7VW.cjs} +86 -86
  30. package/dist/{chunk-TZOX7KWV.cjs → chunk-ZBACQHW3.cjs} +8 -8
  31. package/dist/image-editor/index.cjs +789 -788
  32. package/dist/image-editor/index.js +7 -6
  33. package/dist/image-editor/stickers.d.cts +7 -2
  34. package/dist/image-editor/stickers.d.ts +7 -2
  35. package/dist/paint-editor/controller.d.cts +7 -2
  36. package/dist/paint-editor/controller.d.ts +7 -2
  37. package/dist/paint-editor/index.cjs +576 -523
  38. package/dist/paint-editor/index.js +99 -46
  39. package/dist/paint-editor/sticker-art.d.cts +20 -10
  40. package/dist/paint-editor/sticker-art.d.ts +20 -10
  41. package/dist/paint-editor/stickers.d.cts +8 -1
  42. package/dist/paint-editor/stickers.d.ts +8 -1
  43. package/dist/paint-editor/stroke.d.cts +2 -0
  44. package/dist/paint-editor/stroke.d.ts +2 -0
  45. package/dist/stickers/browser.d.cts +10 -3
  46. package/dist/stickers/browser.d.ts +10 -3
  47. package/dist/stickers/combine.d.cts +2 -0
  48. package/dist/stickers/combine.d.ts +2 -0
  49. package/dist/stickers/conformance.d.cts +1 -1
  50. package/dist/stickers/conformance.d.ts +1 -1
  51. package/dist/stickers/errors.d.cts +1 -1
  52. package/dist/stickers/errors.d.ts +1 -1
  53. package/dist/stickers/fake.d.cts +4 -2
  54. package/dist/stickers/fake.d.ts +4 -2
  55. package/dist/stickers/helpers.d.cts +16 -2
  56. package/dist/stickers/helpers.d.ts +16 -2
  57. package/dist/stickers/index.cjs +116 -29
  58. package/dist/stickers/index.js +106 -19
  59. package/dist/stickers/tools.d.cts +9 -2
  60. package/dist/stickers/tools.d.ts +9 -2
  61. package/dist/stickers/types.d.cts +32 -5
  62. package/dist/stickers/types.d.ts +32 -5
  63. package/dist/vector/index.cjs +972 -971
  64. package/dist/vector/index.js +7 -6
  65. package/dist/vector/stickers.d.cts +7 -2
  66. package/dist/vector/stickers.d.ts +7 -2
  67. package/package.json +1 -1
  68. package/skills/pixerate-paint-editor/SKILL.md +1 -1
  69. package/skills/pixerate-stickers/SKILL.md +20 -9
@@ -3,6 +3,8 @@ import type { StickerProvider } from './types.cjs';
3
3
  * One provider over several, in order. Its ids are refs (`provider:id`, `namespaced`), so the
4
4
  * same id from two libraries stays two stickers. Pages run through the providers in turn; the
5
5
  * cursor is `<provider index>|<that provider's cursor>`. Capabilities are what any of them offers.
6
+ * Collections are namespaced the same way (`provider:collection`), so a picker can tell a
7
+ * provider's collections apart from another provider's.
6
8
  */
7
9
  export declare function combineStickerProviders(providers: readonly StickerProvider[], options?: {
8
10
  id?: string;
@@ -3,6 +3,8 @@ import type { StickerProvider } from './types.js';
3
3
  * One provider over several, in order. Its ids are refs (`provider:id`, `namespaced`), so the
4
4
  * same id from two libraries stays two stickers. Pages run through the providers in turn; the
5
5
  * cursor is `<provider index>|<that provider's cursor>`. Capabilities are what any of them offers.
6
+ * Collections are namespaced the same way (`provider:collection`), so a picker can tell a
7
+ * provider's collections apart from another provider's.
6
8
  */
7
9
  export declare function combineStickerProviders(providers: readonly StickerProvider[], options?: {
8
10
  id?: string;
@@ -13,6 +13,6 @@ export interface StickerConformanceOptions {
13
13
  /**
14
14
  * Checks a sticker provider against the contract: capabilities, every listed sticker (walking the
15
15
  * pages), search and list agreeing, paging limits, describe and load for known and unknown ids,
16
- * and tinting. Returns the problems found (empty when it conforms).
16
+ * collections, light / dark variants and tinting. Returns the problems found (empty when it conforms).
17
17
  */
18
18
  export declare function checkStickerProvider(provider: StickerProvider, options?: StickerConformanceOptions): Promise<StickerConformanceIssue[]>;
@@ -13,6 +13,6 @@ export interface StickerConformanceOptions {
13
13
  /**
14
14
  * Checks a sticker provider against the contract: capabilities, every listed sticker (walking the
15
15
  * pages), search and list agreeing, paging limits, describe and load for known and unknown ids,
16
- * and tinting. Returns the problems found (empty when it conforms).
16
+ * collections, light / dark variants and tinting. Returns the problems found (empty when it conforms).
17
17
  */
18
18
  export declare function checkStickerProvider(provider: StickerProvider, options?: StickerConformanceOptions): Promise<StickerConformanceIssue[]>;
@@ -1,4 +1,4 @@
1
- export type StickerErrorCode = 'unknown_sticker' | 'unknown_provider' | 'unsupported_format' | 'no_provider' | 'load_failed' | 'invalid_argument';
1
+ export type StickerErrorCode = 'unknown_sticker' | 'unknown_provider' | 'unsupported_format' | 'unsupported_variant' | 'no_provider' | 'load_failed' | 'invalid_argument';
2
2
  /** A refusal from a sticker provider or the sticker tools. The message says what to do instead. */
3
3
  export declare class StickerError extends Error {
4
4
  readonly code: StickerErrorCode;
@@ -1,4 +1,4 @@
1
- export type StickerErrorCode = 'unknown_sticker' | 'unknown_provider' | 'unsupported_format' | 'no_provider' | 'load_failed' | 'invalid_argument';
1
+ export type StickerErrorCode = 'unknown_sticker' | 'unknown_provider' | 'unsupported_format' | 'unsupported_variant' | 'no_provider' | 'load_failed' | 'invalid_argument';
2
2
  /** A refusal from a sticker provider or the sticker tools. The message says what to do instead. */
3
3
  export declare class StickerError extends Error {
4
4
  readonly code: StickerErrorCode;
@@ -1,7 +1,9 @@
1
1
  /**
2
2
  * `createFakeStickerProvider()`: a small, deterministic sticker library for tests and demos (id
3
- * `fake`). Flat badges are tintable; two-colour tiles are not; one sticker is for non-commercial
4
- * use only. Art is a generated SVG and a PNG drawn to the same design, so it works in Node.
3
+ * `fake`). Flat badges are tintable; two-colour tiles are not, and have a dark variant (a pale top
4
+ * half); one sticker is for non-commercial use only; the weather set is AI-generated. Stickers are
5
+ * in collections (`outdoors`, `kitchen`, `shop`) but the sketch, which is in none. Art is a
6
+ * generated SVG and a PNG drawn to the same design, so it works in Node.
5
7
  */
6
8
  import type { StickerProvider } from './types.cjs';
7
9
  export interface FakeStickerProviderOptions {
@@ -1,7 +1,9 @@
1
1
  /**
2
2
  * `createFakeStickerProvider()`: a small, deterministic sticker library for tests and demos (id
3
- * `fake`). Flat badges are tintable; two-colour tiles are not; one sticker is for non-commercial
4
- * use only. Art is a generated SVG and a PNG drawn to the same design, so it works in Node.
3
+ * `fake`). Flat badges are tintable; two-colour tiles are not, and have a dark variant (a pale top
4
+ * half); one sticker is for non-commercial use only; the weather set is AI-generated. Stickers are
5
+ * in collections (`outdoors`, `kitchen`, `shop`) but the sketch, which is in none. Art is a
6
+ * generated SVG and a PNG drawn to the same design, so it works in Node.
5
7
  */
6
8
  import type { StickerProvider } from './types.js';
7
9
  export interface FakeStickerProviderOptions {
@@ -3,7 +3,7 @@
3
3
  * catalogue, `provider:id` refs, and the slot an editor keeps its provider in.
4
4
  */
5
5
  import { type StampKind } from './stamps.cjs';
6
- import type { StickerPage, StickerProvider, StickerRef, StickerSearchOptions, StickerSummary } from './types.cjs';
6
+ import { type StickerCollection, type StickerPage, type StickerProvider, type StickerRef, type StickerSearchOptions, type StickerSummary, type StickerVariant } from './types.cjs';
7
7
  /** Default and largest page size. */
8
8
  export declare const DEFAULT_STICKER_PAGE = 24;
9
9
  export declare const MAX_STICKER_PAGE = 100;
@@ -12,11 +12,25 @@ export declare const EDITOR_STICKERS_ID = "editor";
12
12
  /** Checks search options, throwing a `StickerError` that names the bad one. */
13
13
  export declare function checkStickerSearchOptions(options?: StickerSearchOptions): void;
14
14
  /**
15
- * One page of `items` filtered by `options` (category, every tag, commercial licence) and, when
15
+ * One page of `items` filtered by `options` (category, collection, every tag, commercial licence) and, when
16
16
  * `query` has words, by every word appearing in the name, tags, category or description. Cursors
17
17
  * are offsets as strings.
18
18
  */
19
19
  export declare function stickerPage(items: readonly StickerSummary[], options?: StickerSearchOptions, query?: string): StickerPage;
20
+ /** The variants a sticker has (`light` always, first). */
21
+ export declare function stickerVariantsOf(s: Pick<StickerSummary, 'variants'>): StickerVariant[];
22
+ /**
23
+ * Checks `load`'s variant against the sticker's, throwing an `unsupported_variant` `StickerError`
24
+ * that names the ones it has. Providers call it at the top of `load`; returns the variant to draw.
25
+ */
26
+ export declare function checkStickerVariant(s: Pick<StickerSummary, 'id' | 'variants'>, variant: StickerVariant | undefined): StickerVariant;
27
+ /** The variant to show on a dark or light background: `dark` when the background is dark and the sticker has it, else `light`. */
28
+ export declare function stickerVariantFor(s: Pick<StickerSummary, 'variants'>, dark: boolean): StickerVariant;
29
+ /** The collections of an in-memory catalogue with their counts, in first-seen order; `names` gives names and descriptions by id. */
30
+ export declare function stickerCollections(items: readonly StickerSummary[], names?: Record<string, {
31
+ name: string;
32
+ description?: string;
33
+ }>): StickerCollection[];
20
34
  /** A ref split into provider and id. A bare editor stamp (`"star"`) is `editor:star`; any other bare id is refused. */
21
35
  export declare function parseStickerRef(ref: StickerRef): {
22
36
  provider: string;
@@ -3,7 +3,7 @@
3
3
  * catalogue, `provider:id` refs, and the slot an editor keeps its provider in.
4
4
  */
5
5
  import { type StampKind } from './stamps.js';
6
- import type { StickerPage, StickerProvider, StickerRef, StickerSearchOptions, StickerSummary } from './types.js';
6
+ import { type StickerCollection, type StickerPage, type StickerProvider, type StickerRef, type StickerSearchOptions, type StickerSummary, type StickerVariant } from './types.js';
7
7
  /** Default and largest page size. */
8
8
  export declare const DEFAULT_STICKER_PAGE = 24;
9
9
  export declare const MAX_STICKER_PAGE = 100;
@@ -12,11 +12,25 @@ export declare const EDITOR_STICKERS_ID = "editor";
12
12
  /** Checks search options, throwing a `StickerError` that names the bad one. */
13
13
  export declare function checkStickerSearchOptions(options?: StickerSearchOptions): void;
14
14
  /**
15
- * One page of `items` filtered by `options` (category, every tag, commercial licence) and, when
15
+ * One page of `items` filtered by `options` (category, collection, every tag, commercial licence) and, when
16
16
  * `query` has words, by every word appearing in the name, tags, category or description. Cursors
17
17
  * are offsets as strings.
18
18
  */
19
19
  export declare function stickerPage(items: readonly StickerSummary[], options?: StickerSearchOptions, query?: string): StickerPage;
20
+ /** The variants a sticker has (`light` always, first). */
21
+ export declare function stickerVariantsOf(s: Pick<StickerSummary, 'variants'>): StickerVariant[];
22
+ /**
23
+ * Checks `load`'s variant against the sticker's, throwing an `unsupported_variant` `StickerError`
24
+ * that names the ones it has. Providers call it at the top of `load`; returns the variant to draw.
25
+ */
26
+ export declare function checkStickerVariant(s: Pick<StickerSummary, 'id' | 'variants'>, variant: StickerVariant | undefined): StickerVariant;
27
+ /** The variant to show on a dark or light background: `dark` when the background is dark and the sticker has it, else `light`. */
28
+ export declare function stickerVariantFor(s: Pick<StickerSummary, 'variants'>, dark: boolean): StickerVariant;
29
+ /** The collections of an in-memory catalogue with their counts, in first-seen order; `names` gives names and descriptions by id. */
30
+ export declare function stickerCollections(items: readonly StickerSummary[], names?: Record<string, {
31
+ name: string;
32
+ description?: string;
33
+ }>): StickerCollection[];
20
34
  /** A ref split into provider and id. A bare editor stamp (`"star"`) is `editor:star`; any other bare id is refused. */
21
35
  export declare function parseStickerRef(ref: StickerRef): {
22
36
  provider: string;
@@ -3,7 +3,7 @@
3
3
 
4
4
 
5
5
 
6
- var _chunkRVGP2XLFcjs = require('../chunk-RVGP2XLF.cjs');
6
+ var _chunkXORJ7UZOcjs = require('../chunk-XORJ7UZO.cjs');
7
7
 
8
8
 
9
9
 
@@ -11,7 +11,7 @@ var _chunkRVGP2XLFcjs = require('../chunk-RVGP2XLF.cjs');
11
11
 
12
12
 
13
13
 
14
- var _chunkAVTPZO4Zcjs = require('../chunk-AVTPZO4Z.cjs');
14
+ var _chunkPE6YJMQTcjs = require('../chunk-PE6YJMQT.cjs');
15
15
 
16
16
 
17
17
 
@@ -27,7 +27,12 @@ var _chunkAVTPZO4Zcjs = require('../chunk-AVTPZO4Z.cjs');
27
27
 
28
28
 
29
29
 
30
- var _chunkJSDND7DFcjs = require('../chunk-JSDND7DF.cjs');
30
+
31
+
32
+
33
+
34
+
35
+ var _chunkOTN6VB5Scjs = require('../chunk-OTN6VB5S.cjs');
31
36
 
32
37
 
33
38
  var _chunkU67EN7GGcjs = require('../chunk-U67EN7GG.cjs');
@@ -39,11 +44,17 @@ require('../chunk-XFM3OCHU.cjs');
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];
@@ -97,26 +114,28 @@ function createFakeStickerProvider(options = {}) {
97
114
  name: "Fake stickers",
98
115
  version: "1",
99
116
  capabilities: { supportsSearch: true, supportsSvg: true, supportsRaster: true, maxSize: 512 },
100
- list: (o) => _chunkJSDND7DFcjs.stickerPage.call(void 0, summaries, o),
101
- search: (q, o) => _chunkJSDND7DFcjs.stickerPage.call(void 0, summaries, o, q),
117
+ list: (o) => _chunkOTN6VB5Scjs.stickerPage.call(void 0, summaries, o),
118
+ search: (q, o) => _chunkOTN6VB5Scjs.stickerPage.call(void 0, summaries, o, q),
102
119
  describe(id) {
103
120
  const s = find(id);
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: () => _chunkOTN6VB5Scjs.stickerCollections.call(void 0, 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 (0, _chunkU67EN7GGcjs.StickerError)("unknown_sticker", `No fake sticker "${id}".`);
112
130
  if (_optionalChain([options, 'access', _ => _.failIds, 'optionalAccess', _2 => _2.includes, 'call', _3 => _3(id)])) throw new (0, _chunkU67EN7GGcjs.StickerError)("load_failed", `The fake sticker "${id}" failed to load (failIds).`);
131
+ const variant = _chunkOTN6VB5Scjs.checkStickerVariant.call(void 0, 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 = _nullishCoalesce(o.size, () => ( 128));
116
135
  if (!(Number.isInteger(size) && size >= 8 && size <= 512)) throw new (0, _chunkU67EN7GGcjs.StickerError)("invalid_argument", `size must be 8..512 px, got ${size}.`);
117
- return { kind: "image", url: _chunkW5M3J7PVcjs.pngDataUrl.call(void 0, size, size, pixelsOf(s, size, fill)), width: size, height: size };
136
+ return { kind: "image", url: _chunkW5M3J7PVcjs.pngDataUrl.call(void 0, 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
  }
@@ -128,15 +147,23 @@ function combineStickerProviders(providers, options = {}) {
128
147
  if (dup) throw new (0, _chunkU67EN7GGcjs.StickerError)("invalid_argument", `Two sticker providers have the id "${dup}"; give each a unique id.`);
129
148
  const byId = new Map(providers.map((p) => [p.id, p]));
130
149
  const owner = (ref) => {
131
- const { provider, id } = _chunkJSDND7DFcjs.parseStickerRef.call(void 0, ref);
150
+ const { provider, id } = _chunkOTN6VB5Scjs.parseStickerRef.call(void 0, ref);
132
151
  const p = byId.get(provider);
133
152
  if (!p) throw new (0, _chunkU67EN7GGcjs.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: _chunkJSDND7DFcjs.stickerRefOf.call(void 0, p, s.id) });
155
+ const collectionOf = (p, c) => p.namespaced ? c : `${p.id}:${c}`;
156
+ const prefix = (p, s) => ({ ...s, id: _chunkOTN6VB5Scjs.stickerRefOf.call(void 0, 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 (0, _chunkU67EN7GGcjs.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 = _chunkJSDND7DFcjs.DEFAULT_STICKER_PAGE, ...filters } = o;
139
- _chunkJSDND7DFcjs.checkStickerSearchOptions.call(void 0, { limit });
164
+ const { after, limit = _chunkOTN6VB5Scjs.DEFAULT_STICKER_PAGE, collection, ...filters } = o;
165
+ _chunkOTN6VB5Scjs.checkStickerSearchOptions.call(void 0, { 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: _chunkJSDND7DFcjs.stickerRefOf.call(void 0, 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 _asyncNullishCoalesce(await _optionalChain([p, 'access', _8 => _8.listCollections, 'optionalCall', _9 => _9()]), async () => ( []))) out.push({ ...c, id: collectionOf(p, c.id) });
238
+ return out;
203
239
  }
204
240
  };
205
241
  }
@@ -214,12 +250,20 @@ function checkSummary(s, provider, issues) {
214
250
  if (typeof s.category !== "string" || !s.category) at("category is missing");
215
251
  if (!(s.width > 0 && s.height > 0)) at(`width and height must be positive, got ${s.width} \xD7 ${s.height}`);
216
252
  if (!Array.isArray(s.formats) || !s.formats.length || s.formats.some((f) => f !== "svg" && f !== "png")) at("formats must list svg and / or png");
217
- if (_optionalChain([s, 'access', _8 => _8.formats, 'optionalAccess', _9 => _9.includes, 'call', _10 => _10("svg")]) && !provider.capabilities.supportsSvg) at("lists svg but capabilities.supportsSvg is false");
218
- if (_optionalChain([s, 'access', _11 => _11.formats, 'optionalAccess', _12 => _12.includes, 'call', _13 => _13("png")]) && !provider.capabilities.supportsRaster) at("lists png but capabilities.supportsRaster is false");
253
+ if (_optionalChain([s, 'access', _10 => _10.formats, 'optionalAccess', _11 => _11.includes, 'call', _12 => _12("svg")]) && !provider.capabilities.supportsSvg) at("lists svg but capabilities.supportsSvg is false");
254
+ if (_optionalChain([s, 'access', _13 => _13.formats, 'optionalAccess', _14 => _14.includes, 'call', _15 => _15("png")]) && !provider.capabilities.supportsRaster) at("lists png but capabilities.supportsRaster is false");
219
255
  if (!s.licence || typeof s.licence.name !== "string" || !s.licence.name) at("licence.name is missing (every sticker says how it may be used)");
220
- if (typeof _optionalChain([s, 'access', _14 => _14.licence, 'optionalAccess', _15 => _15.commercial]) !== "boolean") at("licence.commercial is not a boolean");
256
+ if (typeof _optionalChain([s, 'access', _16 => _16.licence, 'optionalAccess', _17 => _17.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 = [];
@@ -228,8 +272,8 @@ async function checkStickerProvider(provider, options = {}) {
228
272
  else if (provider.id.includes(":")) top('provider id contains ":"');
229
273
  if (typeof provider.name !== "string" || !provider.name) top("provider has no name");
230
274
  const caps = provider.capabilities;
231
- for (const k of ["supportsSearch", "supportsSvg", "supportsRaster"]) if (typeof _optionalChain([caps, 'optionalAccess', _16 => _16[k]]) !== "boolean") top(`capabilities.${k} is not a boolean`);
232
- if (_optionalChain([caps, 'optionalAccess', _17 => _17.maxSize]) !== void 0 && !(caps.maxSize > 0)) top("capabilities.maxSize must be positive");
275
+ for (const k of ["supportsSearch", "supportsSvg", "supportsRaster"]) if (typeof _optionalChain([caps, 'optionalAccess', _18 => _18[k]]) !== "boolean") top(`capabilities.${k} is not a boolean`);
276
+ if (_optionalChain([caps, 'optionalAccess', _19 => _19.maxSize]) !== void 0 && !(caps.maxSize > 0)) top("capabilities.maxSize must be positive");
233
277
  const all = [];
234
278
  const ids = /* @__PURE__ */ new Set();
235
279
  let after;
@@ -242,7 +286,7 @@ async function checkStickerProvider(provider, options = {}) {
242
286
  top(`list() rejected: ${e.message}`);
243
287
  break;
244
288
  }
245
- if (!Array.isArray(_optionalChain([page, 'optionalAccess', _18 => _18.items])) || typeof page.total !== "number") {
289
+ if (!Array.isArray(_optionalChain([page, 'optionalAccess', _20 => _20.items])) || typeof page.total !== "number") {
246
290
  top("list() did not return { items, total }");
247
291
  break;
248
292
  }
@@ -275,9 +319,29 @@ async function checkStickerProvider(provider, options = {}) {
275
319
  const d = await provider.describe(first.id);
276
320
  if (!d) top(`describe("${first.id}") returned nothing for a listed sticker`);
277
321
  else if (d.id !== first.id) top(`describe("${first.id}") returned id "${d.id}"`);
278
- if (_optionalChain([d, 'optionalAccess', _19 => _19.colours, 'optionalAccess', _20 => _20.some, 'call', _21 => _21((c) => !HEX.test(c))])) top(`describe("${first.id}").colours must be #rrggbb`);
322
+ if (_optionalChain([d, 'optionalAccess', _21 => _21.colours, 'optionalAccess', _22 => _22.some, 'call', _23 => _23((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 _optionalChain([c, 'optionalAccess', _24 => _24.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) => _optionalChain([s, 'access', _25 => _25.variants, 'optionalAccess', _26 => _26.includes, 'call', _27 => _27("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) => !_optionalChain([s, 'access', _28 => _28.variants, 'optionalAccess', _29 => _29.includes, 'call', _30 => _30("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 (e4) {
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 {
@@ -336,4 +418,9 @@ async function checkStickerProvider(provider, options = {}) {
336
418
 
337
419
 
338
420
 
339
- exports.DEFAULT_STICKER_PAGE = _chunkJSDND7DFcjs.DEFAULT_STICKER_PAGE; exports.EDITOR_STICKERS_ID = _chunkJSDND7DFcjs.EDITOR_STICKERS_ID; exports.MAX_STICKER_PAGE = _chunkJSDND7DFcjs.MAX_STICKER_PAGE; exports.STAMP_KINDS = _chunkJSDND7DFcjs.STAMP_KINDS; exports.StickerBrowser = _chunkRVGP2XLFcjs.StickerBrowser; exports.StickerError = _chunkU67EN7GGcjs.StickerError; exports.StickersSlot = _chunkJSDND7DFcjs.StickersSlot; exports.checkStickerProvider = checkStickerProvider; exports.checkStickerSearchOptions = _chunkJSDND7DFcjs.checkStickerSearchOptions; exports.closestStickerRefs = _chunkJSDND7DFcjs.closestStickerRefs; exports.combineStickerProviders = combineStickerProviders; exports.createFakeStickerProvider = createFakeStickerProvider; exports.createInsertStickerTool = _chunkAVTPZO4Zcjs.createInsertStickerTool; exports.createStickerToolProvider = _chunkAVTPZO4Zcjs.createStickerToolProvider; exports.createStickerTools = _chunkAVTPZO4Zcjs.createStickerTools; exports.describeStickerRef = _chunkAVTPZO4Zcjs.describeStickerRef; exports.editorStampOf = _chunkJSDND7DFcjs.editorStampOf; exports.editorStickers = _chunkRVGP2XLFcjs.editorStickers; exports.insertSticker = _chunkAVTPZO4Zcjs.insertSticker; exports.normalizeStickerRef = _chunkJSDND7DFcjs.normalizeStickerRef; exports.parseStickerRef = _chunkJSDND7DFcjs.parseStickerRef; exports.stampCoverage = _chunkJSDND7DFcjs.stampCoverage; exports.stampOutline = _chunkRVGP2XLFcjs.stampOutline; exports.stampPixels = _chunkRVGP2XLFcjs.stampPixels; exports.stickerForAgent = _chunkAVTPZO4Zcjs.stickerForAgent; exports.stickerIdFor = _chunkJSDND7DFcjs.stickerIdFor; exports.stickerPage = _chunkJSDND7DFcjs.stickerPage; exports.stickerRefOf = _chunkJSDND7DFcjs.stickerRefOf;
421
+
422
+
423
+
424
+
425
+
426
+ exports.DEFAULT_STICKER_PAGE = _chunkOTN6VB5Scjs.DEFAULT_STICKER_PAGE; exports.EDITOR_STICKERS_ID = _chunkOTN6VB5Scjs.EDITOR_STICKERS_ID; exports.MAX_STICKER_PAGE = _chunkOTN6VB5Scjs.MAX_STICKER_PAGE; exports.STAMP_KINDS = _chunkOTN6VB5Scjs.STAMP_KINDS; exports.STICKER_VARIANTS = _chunkOTN6VB5Scjs.STICKER_VARIANTS; exports.StickerBrowser = _chunkXORJ7UZOcjs.StickerBrowser; exports.StickerError = _chunkU67EN7GGcjs.StickerError; exports.StickersSlot = _chunkOTN6VB5Scjs.StickersSlot; exports.checkStickerProvider = checkStickerProvider; exports.checkStickerSearchOptions = _chunkOTN6VB5Scjs.checkStickerSearchOptions; exports.checkStickerVariant = _chunkOTN6VB5Scjs.checkStickerVariant; exports.closestStickerRefs = _chunkOTN6VB5Scjs.closestStickerRefs; exports.combineStickerProviders = combineStickerProviders; exports.createFakeStickerProvider = createFakeStickerProvider; exports.createInsertStickerTool = _chunkPE6YJMQTcjs.createInsertStickerTool; exports.createStickerToolProvider = _chunkPE6YJMQTcjs.createStickerToolProvider; exports.createStickerTools = _chunkPE6YJMQTcjs.createStickerTools; exports.describeStickerRef = _chunkPE6YJMQTcjs.describeStickerRef; exports.editorStampOf = _chunkOTN6VB5Scjs.editorStampOf; exports.editorStickers = _chunkXORJ7UZOcjs.editorStickers; exports.insertSticker = _chunkPE6YJMQTcjs.insertSticker; exports.normalizeStickerRef = _chunkOTN6VB5Scjs.normalizeStickerRef; exports.parseStickerRef = _chunkOTN6VB5Scjs.parseStickerRef; exports.stampCoverage = _chunkOTN6VB5Scjs.stampCoverage; exports.stampOutline = _chunkXORJ7UZOcjs.stampOutline; exports.stampPixels = _chunkXORJ7UZOcjs.stampPixels; exports.stickerCollections = _chunkOTN6VB5Scjs.stickerCollections; exports.stickerForAgent = _chunkPE6YJMQTcjs.stickerForAgent; exports.stickerIdFor = _chunkOTN6VB5Scjs.stickerIdFor; exports.stickerPage = _chunkOTN6VB5Scjs.stickerPage; exports.stickerRefOf = _chunkOTN6VB5Scjs.stickerRefOf; exports.stickerVariantFor = _chunkOTN6VB5Scjs.stickerVariantFor; exports.stickerVariantsOf = _chunkOTN6VB5Scjs.stickerVariantsOf;