@pixerate/editor 0.248.0 → 0.249.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/README.md +1 -0
  2. package/dist/animation-editor/index.cjs +64 -61
  3. package/dist/animation-editor/index.js +8 -5
  4. package/dist/{brushstrokeRender-DTDPFBLC.cjs → brushstrokeRender-GCKK7V2I.cjs} +14 -11
  5. package/dist/{brushstrokeRender-QLCXROJK.js → brushstrokeRender-T3JZST44.js} +7 -4
  6. package/dist/canvas/index.cjs +8 -8
  7. package/dist/canvas/index.js +1 -1
  8. package/dist/{chunk-IBDU6EGZ.cjs → chunk-2SMMZDMG.cjs} +35 -35
  9. package/dist/{chunk-SC2RZ5EF.js → chunk-2VJAVXLI.js} +2 -2
  10. package/dist/{chunk-OLNIPTB3.cjs → chunk-2VX55NFM.cjs} +1 -320
  11. package/dist/chunk-3K6HWEXZ.cjs +92 -0
  12. package/dist/{chunk-UOOK7HTX.js → chunk-3PPJRMIG.js} +1 -1
  13. package/dist/chunk-4D6KP5BV.js +92 -0
  14. package/dist/chunk-5RH3VXAK.js +269 -0
  15. package/dist/{chunk-NZ3O3A5C.js → chunk-6BIDVPED.js} +1 -1
  16. package/dist/{chunk-2ONK3ERD.cjs → chunk-75K3W4D6.cjs} +20 -20
  17. package/dist/{chunk-7T7JWTVE.cjs → chunk-BOHDJJ2O.cjs} +8 -8
  18. package/dist/{chunk-2PBZBRP5.js → chunk-D5KGAO4Z.js} +10 -92
  19. package/dist/{chunk-5GLYUSNT.js → chunk-DOIU7VJG.js} +1 -1
  20. package/dist/{chunk-HB4ICNNA.cjs → chunk-DPYAO5U6.cjs} +42 -42
  21. package/dist/chunk-EJLRBS5Q.js +301 -0
  22. package/dist/{chunk-6G6ZDDDB.cjs → chunk-H4AOUUBE.cjs} +301 -149
  23. package/dist/{chunk-PQ3H2AAN.cjs → chunk-I7NJPHWV.cjs} +122 -204
  24. package/dist/{chunk-L6REDLNB.cjs → chunk-IOFVM3L3.cjs} +7 -7
  25. package/dist/{chunk-42TWUPNJ.js → chunk-IRSTHLQQ.js} +1 -320
  26. package/dist/{chunk-VNBUDVD4.js → chunk-J7T5QZ45.js} +226 -74
  27. package/dist/{chunk-VMROIWLL.cjs → chunk-KBYB7OWB.cjs} +3 -3
  28. package/dist/{chunk-TPWB4Y6L.cjs → chunk-KYX7NZ5V.cjs} +1 -1
  29. package/dist/chunk-LJRCU3BJ.cjs +301 -0
  30. package/dist/{chunk-WVS5KQDH.cjs → chunk-LP2MCSBE.cjs} +246 -244
  31. package/dist/{chunk-XNRBMYNB.js → chunk-O75V4ZS3.js} +1 -1
  32. package/dist/chunk-OTTT5N3K.js +322 -0
  33. package/dist/{chunk-Q2HA52V4.js → chunk-PZP2SZIZ.js} +2 -2
  34. package/dist/{chunk-ZFSLEOSQ.js → chunk-UBRJOIR5.js} +11 -9
  35. package/dist/chunk-W5M3J7PV.cjs +322 -0
  36. package/dist/{chunk-4P6H35HU.js → chunk-WA44NKPF.js} +2 -2
  37. package/dist/{chunk-XWTRATIM.cjs → chunk-WLJZZHUZ.cjs} +23 -23
  38. package/dist/{chunk-P3NIPIKS.js → chunk-WQCNC4DK.js} +1 -1
  39. package/dist/chunk-ZEYMKDYS.cjs +269 -0
  40. package/dist/image-editor/index.cjs +1055 -1050
  41. package/dist/image-editor/index.js +28 -23
  42. package/dist/paint/index.cjs +8 -5
  43. package/dist/paint/index.js +13 -10
  44. package/dist/paint-editor/controller.d.cts +37 -1
  45. package/dist/paint-editor/controller.d.ts +37 -1
  46. package/dist/paint-editor/index.cjs +745 -602
  47. package/dist/paint-editor/index.d.cts +3 -0
  48. package/dist/paint-editor/index.d.ts +3 -0
  49. package/dist/paint-editor/index.js +184 -41
  50. package/dist/paint-editor/sticker-art.d.cts +55 -0
  51. package/dist/paint-editor/sticker-art.d.ts +55 -0
  52. package/dist/paint-editor/stickers.d.cts +30 -17
  53. package/dist/paint-editor/stickers.d.ts +30 -17
  54. package/dist/paint-editor/stroke.d.cts +4 -0
  55. package/dist/paint-editor/stroke.d.ts +4 -0
  56. package/dist/paint-editor/ui.d.cts +10 -2
  57. package/dist/paint-editor/ui.d.ts +10 -2
  58. package/dist/raster/index.cjs +6 -4
  59. package/dist/raster/index.js +12 -10
  60. package/dist/stickers/browser.d.cts +48 -0
  61. package/dist/stickers/browser.d.ts +48 -0
  62. package/dist/stickers/combine.d.cts +10 -0
  63. package/dist/stickers/combine.d.ts +10 -0
  64. package/dist/stickers/conformance.d.cts +18 -0
  65. package/dist/stickers/conformance.d.ts +18 -0
  66. package/dist/stickers/editor.d.cts +14 -0
  67. package/dist/stickers/editor.d.ts +14 -0
  68. package/dist/stickers/errors.d.cts +6 -0
  69. package/dist/stickers/errors.d.ts +6 -0
  70. package/dist/stickers/fake.d.cts +16 -0
  71. package/dist/stickers/fake.d.ts +16 -0
  72. package/dist/stickers/helpers.d.cts +51 -0
  73. package/dist/stickers/helpers.d.ts +51 -0
  74. package/dist/stickers/index.cjs +331 -0
  75. package/dist/stickers/index.d.cts +16 -0
  76. package/dist/stickers/index.d.ts +16 -0
  77. package/dist/stickers/index.js +331 -0
  78. package/dist/stickers/stamps.d.cts +13 -0
  79. package/dist/stickers/stamps.d.ts +13 -0
  80. package/dist/stickers/tools.d.cts +25 -0
  81. package/dist/stickers/tools.d.ts +25 -0
  82. package/dist/stickers/types.d.cts +123 -0
  83. package/dist/stickers/types.d.ts +123 -0
  84. package/dist/vector/index.cjs +978 -975
  85. package/dist/vector/index.js +9 -6
  86. package/package.json +11 -1
  87. package/skills/README.md +1 -0
  88. package/skills/pixerate-paint-editor/SKILL.md +8 -5
  89. package/skills/pixerate-stickers/SKILL.md +45 -0
@@ -0,0 +1,18 @@
1
+ import type { StickerProvider } from './types.cjs';
2
+ export interface StickerConformanceIssue {
3
+ /** The sticker it is about, when it is about one. */
4
+ stickerId?: string;
5
+ message: string;
6
+ }
7
+ export interface StickerConformanceOptions {
8
+ /** Stop walking pages after this many (default 50). */
9
+ maxPages?: number;
10
+ /** Load at most this many stickers in each format (default 3). */
11
+ loads?: number;
12
+ }
13
+ /**
14
+ * Checks a sticker provider against the contract: capabilities, every listed sticker (walking the
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).
17
+ */
18
+ export declare function checkStickerProvider(provider: StickerProvider, options?: StickerConformanceOptions): Promise<StickerConformanceIssue[]>;
@@ -0,0 +1,18 @@
1
+ import type { StickerProvider } from './types.js';
2
+ export interface StickerConformanceIssue {
3
+ /** The sticker it is about, when it is about one. */
4
+ stickerId?: string;
5
+ message: string;
6
+ }
7
+ export interface StickerConformanceOptions {
8
+ /** Stop walking pages after this many (default 50). */
9
+ maxPages?: number;
10
+ /** Load at most this many stickers in each format (default 3). */
11
+ loads?: number;
12
+ }
13
+ /**
14
+ * Checks a sticker provider against the contract: capabilities, every listed sticker (walking the
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).
17
+ */
18
+ export declare function checkStickerProvider(provider: StickerProvider, options?: StickerConformanceOptions): Promise<StickerConformanceIssue[]>;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `editorStickers()`: the editor's own stamps as a sticker provider (id `editor`), the default in
3
+ * every `StickersSlot`, so stickers work with no setup. Each stamp is MIT-licensed and tintable.
4
+ * Its SVG is the stamp's outline, contoured from the coverage function the paint editor draws, so
5
+ * the vector art and the painted stamp have the same shape; its PNG is drawn from the function.
6
+ */
7
+ import { type StampKind } from './stamps.cjs';
8
+ import type { StickerProvider } from './types.cjs';
9
+ /** The stamp's outline as SVG path data in a `BOX` px square (marching squares on the 0.5 level, simplified). */
10
+ export declare function stampOutline(kind: StampKind): string;
11
+ /** The stamp drawn flat in `colour` as RGBA, `size` px square (supersampled). */
12
+ export declare function stampPixels(kind: StampKind, size: number, colour: string): Uint8ClampedArray;
13
+ /** The editor's own stamps as a `StickerProvider` (id `editor`): tintable, MIT, SVG and PNG. */
14
+ export declare function editorStickers(): StickerProvider;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `editorStickers()`: the editor's own stamps as a sticker provider (id `editor`), the default in
3
+ * every `StickersSlot`, so stickers work with no setup. Each stamp is MIT-licensed and tintable.
4
+ * Its SVG is the stamp's outline, contoured from the coverage function the paint editor draws, so
5
+ * the vector art and the painted stamp have the same shape; its PNG is drawn from the function.
6
+ */
7
+ import { type StampKind } from './stamps.js';
8
+ import type { StickerProvider } from './types.js';
9
+ /** The stamp's outline as SVG path data in a `BOX` px square (marching squares on the 0.5 level, simplified). */
10
+ export declare function stampOutline(kind: StampKind): string;
11
+ /** The stamp drawn flat in `colour` as RGBA, `size` px square (supersampled). */
12
+ export declare function stampPixels(kind: StampKind, size: number, colour: string): Uint8ClampedArray;
13
+ /** The editor's own stamps as a `StickerProvider` (id `editor`): tintable, MIT, SVG and PNG. */
14
+ export declare function editorStickers(): StickerProvider;
@@ -0,0 +1,6 @@
1
+ export type StickerErrorCode = 'unknown_sticker' | 'unknown_provider' | 'unsupported_format' | 'no_provider' | 'load_failed' | 'invalid_argument';
2
+ /** A refusal from a sticker provider or the sticker tools. The message says what to do instead. */
3
+ export declare class StickerError extends Error {
4
+ readonly code: StickerErrorCode;
5
+ constructor(code: StickerErrorCode, message: string);
6
+ }
@@ -0,0 +1,6 @@
1
+ export type StickerErrorCode = 'unknown_sticker' | 'unknown_provider' | 'unsupported_format' | 'no_provider' | 'load_failed' | 'invalid_argument';
2
+ /** A refusal from a sticker provider or the sticker tools. The message says what to do instead. */
3
+ export declare class StickerError extends Error {
4
+ readonly code: StickerErrorCode;
5
+ constructor(code: StickerErrorCode, message: string);
6
+ }
@@ -0,0 +1,16 @@
1
+ /**
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.
5
+ */
6
+ import type { StickerProvider } from './types.cjs';
7
+ export interface FakeStickerProviderOptions {
8
+ /** Provider id (default `fake`). */
9
+ id?: string;
10
+ /** Delay every `load` by this many ms (to test loading states). */
11
+ loadDelayMs?: number;
12
+ /** Ids whose `load` rejects (to test failures). */
13
+ failIds?: readonly string[];
14
+ }
15
+ /** A deterministic sticker library for tests and demos. */
16
+ export declare function createFakeStickerProvider(options?: FakeStickerProviderOptions): StickerProvider;
@@ -0,0 +1,16 @@
1
+ /**
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.
5
+ */
6
+ import type { StickerProvider } from './types.js';
7
+ export interface FakeStickerProviderOptions {
8
+ /** Provider id (default `fake`). */
9
+ id?: string;
10
+ /** Delay every `load` by this many ms (to test loading states). */
11
+ loadDelayMs?: number;
12
+ /** Ids whose `load` rejects (to test failures). */
13
+ failIds?: readonly string[];
14
+ }
15
+ /** A deterministic sticker library for tests and demos. */
16
+ export declare function createFakeStickerProvider(options?: FakeStickerProviderOptions): StickerProvider;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Helpers for sticker providers and the editors that use them: paging and searching an in-memory
3
+ * catalogue, `provider:id` refs, and the slot an editor keeps its provider in.
4
+ */
5
+ import { type StampKind } from './stamps.cjs';
6
+ import type { StickerPage, StickerProvider, StickerRef, StickerSearchOptions, StickerSummary } from './types.cjs';
7
+ /** Default and largest page size. */
8
+ export declare const DEFAULT_STICKER_PAGE = 24;
9
+ export declare const MAX_STICKER_PAGE = 100;
10
+ /** The id of the editor's own sticker set (`editorStickers()`). */
11
+ export declare const EDITOR_STICKERS_ID = "editor";
12
+ /** Checks search options, throwing a `StickerError` that names the bad one. */
13
+ export declare function checkStickerSearchOptions(options?: StickerSearchOptions): void;
14
+ /**
15
+ * One page of `items` filtered by `options` (category, every tag, commercial licence) and, when
16
+ * `query` has words, by every word appearing in the name, tags, category or description. Cursors
17
+ * are offsets as strings.
18
+ */
19
+ export declare function stickerPage(items: readonly StickerSummary[], options?: StickerSearchOptions, query?: string): StickerPage;
20
+ /** A ref split into provider and id. A bare editor stamp (`"star"`) is `editor:star`; any other bare id is refused. */
21
+ export declare function parseStickerRef(ref: StickerRef): {
22
+ provider: string;
23
+ id: string;
24
+ };
25
+ /** The canonical form of a ref (`"star"` → `"editor:star"`). */
26
+ export declare function normalizeStickerRef(ref: StickerRef): StickerRef;
27
+ /** The editor stamp a ref names, or `null` for another provider's sticker. */
28
+ export declare function editorStampOf(ref: StickerRef): StampKind | null;
29
+ /** The ref of a sticker `id` listed by `provider`. */
30
+ export declare function stickerRefOf(provider: StickerProvider, id: string): StickerRef;
31
+ /**
32
+ * The id to pass to `provider` for `ref`, or `null` when the ref names another provider. A
33
+ * combined provider takes the whole ref; any other takes the part after its own id.
34
+ */
35
+ export declare function stickerIdFor(provider: StickerProvider, ref: StickerRef): string | null;
36
+ /** Up to `n` of `candidates` closest to `wanted` (shared prefix, then edit distance), for "did you mean" refusals. */
37
+ export declare function closestStickerRefs(wanted: string, candidates: readonly string[], n?: number): string[];
38
+ /**
39
+ * Holds an editor's sticker provider: the editor's own set (`editorStickers()`) until a host sets
40
+ * another. Controllers expose it as `stickersProvider` and `setStickersProvider(provider | null)`
41
+ * (`null` goes back to the default); listeners hear every change.
42
+ */
43
+ export declare class StickersSlot {
44
+ readonly fallback: StickerProvider;
45
+ private current;
46
+ private readonly listeners;
47
+ constructor(fallback: StickerProvider);
48
+ get provider(): StickerProvider;
49
+ set(provider: StickerProvider | null): void;
50
+ subscribe(listener: (provider: StickerProvider) => void): () => void;
51
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Helpers for sticker providers and the editors that use them: paging and searching an in-memory
3
+ * catalogue, `provider:id` refs, and the slot an editor keeps its provider in.
4
+ */
5
+ import { type StampKind } from './stamps.js';
6
+ import type { StickerPage, StickerProvider, StickerRef, StickerSearchOptions, StickerSummary } from './types.js';
7
+ /** Default and largest page size. */
8
+ export declare const DEFAULT_STICKER_PAGE = 24;
9
+ export declare const MAX_STICKER_PAGE = 100;
10
+ /** The id of the editor's own sticker set (`editorStickers()`). */
11
+ export declare const EDITOR_STICKERS_ID = "editor";
12
+ /** Checks search options, throwing a `StickerError` that names the bad one. */
13
+ export declare function checkStickerSearchOptions(options?: StickerSearchOptions): void;
14
+ /**
15
+ * One page of `items` filtered by `options` (category, every tag, commercial licence) and, when
16
+ * `query` has words, by every word appearing in the name, tags, category or description. Cursors
17
+ * are offsets as strings.
18
+ */
19
+ export declare function stickerPage(items: readonly StickerSummary[], options?: StickerSearchOptions, query?: string): StickerPage;
20
+ /** A ref split into provider and id. A bare editor stamp (`"star"`) is `editor:star`; any other bare id is refused. */
21
+ export declare function parseStickerRef(ref: StickerRef): {
22
+ provider: string;
23
+ id: string;
24
+ };
25
+ /** The canonical form of a ref (`"star"` → `"editor:star"`). */
26
+ export declare function normalizeStickerRef(ref: StickerRef): StickerRef;
27
+ /** The editor stamp a ref names, or `null` for another provider's sticker. */
28
+ export declare function editorStampOf(ref: StickerRef): StampKind | null;
29
+ /** The ref of a sticker `id` listed by `provider`. */
30
+ export declare function stickerRefOf(provider: StickerProvider, id: string): StickerRef;
31
+ /**
32
+ * The id to pass to `provider` for `ref`, or `null` when the ref names another provider. A
33
+ * combined provider takes the whole ref; any other takes the part after its own id.
34
+ */
35
+ export declare function stickerIdFor(provider: StickerProvider, ref: StickerRef): string | null;
36
+ /** Up to `n` of `candidates` closest to `wanted` (shared prefix, then edit distance), for "did you mean" refusals. */
37
+ export declare function closestStickerRefs(wanted: string, candidates: readonly string[], n?: number): string[];
38
+ /**
39
+ * Holds an editor's sticker provider: the editor's own set (`editorStickers()`) until a host sets
40
+ * another. Controllers expose it as `stickersProvider` and `setStickersProvider(provider | null)`
41
+ * (`null` goes back to the default); listeners hear every change.
42
+ */
43
+ export declare class StickersSlot {
44
+ readonly fallback: StickerProvider;
45
+ private current;
46
+ private readonly listeners;
47
+ constructor(fallback: StickerProvider);
48
+ get provider(): StickerProvider;
49
+ set(provider: StickerProvider | null): void;
50
+ subscribe(listener: (provider: StickerProvider) => void): () => void;
51
+ }
@@ -0,0 +1,331 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+
4
+
5
+
6
+ var _chunkLJRCU3BJcjs = require('../chunk-LJRCU3BJ.cjs');
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+ var _chunkZEYMKDYScjs = require('../chunk-ZEYMKDYS.cjs');
28
+ require('../chunk-3K6HWEXZ.cjs');
29
+
30
+
31
+ var _chunkW5M3J7PVcjs = require('../chunk-W5M3J7PV.cjs');
32
+ require('../chunk-XFM3OCHU.cjs');
33
+
34
+ // src/stickers/fake.ts
35
+ var CATALOGUE = [
36
+ ...["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 })),
37
+ ...["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 })),
38
+ ...["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 })),
39
+ { id: "art-sketch", name: "Sketch", category: "art", tags: ["drawing", "non-commercial"], shape: "tile", colours: ["#1f2937", "#fde68a"], commercial: false }
40
+ ];
41
+ var W = 120;
42
+ var H = 120;
43
+ function summaryOf(s) {
44
+ return {
45
+ id: s.id,
46
+ name: s.name,
47
+ category: s.category,
48
+ tags: s.tags,
49
+ description: s.shape === "badge" ? "A flat badge (tintable)." : "A two-colour tile.",
50
+ width: W,
51
+ height: H,
52
+ formats: ["svg", "png"],
53
+ 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 },
54
+ tintable: s.shape === "badge"
55
+ };
56
+ }
57
+ function svgOf(s, fill) {
58
+ if (s.shape === "badge") {
59
+ 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>`;
60
+ }
61
+ 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>`;
62
+ }
63
+ function pixelsOf(s, size, fill) {
64
+ const out = new Uint8ClampedArray(size * size * 4);
65
+ const hex = (c) => [parseInt(c.slice(1, 3), 16), parseInt(c.slice(3, 5), 16), parseInt(c.slice(5, 7), 16)];
66
+ for (let y = 0; y < size; y++) {
67
+ for (let x = 0; x < size; x++) {
68
+ const u = (x + 0.5) / size * W;
69
+ const v = (y + 0.5) / size * H;
70
+ let rgb = null;
71
+ if (s.shape === "badge") {
72
+ const inDisc = Math.hypot(u - 60, v - 60) <= 54;
73
+ const inNotch = u >= 45 && u <= 75 && v >= 45 && v <= 75;
74
+ if (inDisc && !inNotch) rgb = hex(fill);
75
+ } else if (u >= 10 && u <= 110 && v >= 10 && v <= 110) rgb = hex(v < 60 ? s.colours[1] : s.colours[0]);
76
+ if (!rgb) continue;
77
+ const i = (y * size + x) * 4;
78
+ out[i] = rgb[0];
79
+ out[i + 1] = rgb[1];
80
+ out[i + 2] = rgb[2];
81
+ out[i + 3] = 255;
82
+ }
83
+ }
84
+ return out;
85
+ }
86
+ function createFakeStickerProvider(options = {}) {
87
+ const summaries = CATALOGUE.map(summaryOf);
88
+ const find = (id) => CATALOGUE.find((s) => s.id === id);
89
+ return {
90
+ id: _nullishCoalesce(options.id, () => ( "fake")),
91
+ name: "Fake stickers",
92
+ version: "1",
93
+ capabilities: { supportsSearch: true, supportsSvg: true, supportsRaster: true, maxSize: 512 },
94
+ list: (o) => _chunkZEYMKDYScjs.stickerPage.call(void 0, summaries, o),
95
+ search: (q, o) => _chunkZEYMKDYScjs.stickerPage.call(void 0, summaries, o, q),
96
+ describe(id) {
97
+ const s = find(id);
98
+ if (!s) return void 0;
99
+ 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] };
100
+ },
101
+ categories: () => [...new Set(CATALOGUE.map((s) => s.category))].map((c) => ({ id: c, name: c, count: CATALOGUE.filter((s) => s.category === c).length })),
102
+ async load(id, o = {}) {
103
+ if (options.loadDelayMs) await new Promise((r) => setTimeout(r, options.loadDelayMs));
104
+ const s = find(id);
105
+ if (!s) throw new (0, _chunkZEYMKDYScjs.StickerError)("unknown_sticker", `No fake sticker "${id}".`);
106
+ if (_optionalChain([options, 'access', _ => _.failIds, 'optionalAccess', _2 => _2.includes, 'call', _3 => _3(id)])) throw new (0, _chunkZEYMKDYScjs.StickerError)("load_failed", `The fake sticker "${id}" failed to load (failIds).`);
107
+ const fill = s.shape === "badge" && o.colour ? o.colour.toLowerCase() : s.colours[0];
108
+ if (o.format === "png") {
109
+ const size = _nullishCoalesce(o.size, () => ( 128));
110
+ if (!(Number.isInteger(size) && size >= 8 && size <= 512)) throw new (0, _chunkZEYMKDYScjs.StickerError)("invalid_argument", `size must be 8..512 px, got ${size}.`);
111
+ return { kind: "image", url: _chunkW5M3J7PVcjs.pngDataUrl.call(void 0, size, size, pixelsOf(s, size, fill)), width: size, height: size };
112
+ }
113
+ return { kind: "svg", svg: svgOf(s, fill) };
114
+ }
115
+ };
116
+ }
117
+
118
+ // src/stickers/combine.ts
119
+ function combineStickerProviders(providers, options = {}) {
120
+ const ids = providers.map((p) => p.id);
121
+ const dup = ids.find((id, i) => ids.indexOf(id) !== i);
122
+ if (dup) throw new (0, _chunkZEYMKDYScjs.StickerError)("invalid_argument", `Two sticker providers have the id "${dup}"; give each a unique id.`);
123
+ const byId = new Map(providers.map((p) => [p.id, p]));
124
+ const owner = (ref) => {
125
+ const { provider, id } = _chunkZEYMKDYScjs.parseStickerRef.call(void 0, ref);
126
+ const p = byId.get(provider);
127
+ if (!p) throw new (0, _chunkZEYMKDYScjs.StickerError)("unknown_provider", `No sticker provider "${provider}"; the providers are ${ids.join(", ") || "none"}.`);
128
+ return { p, id };
129
+ };
130
+ const prefix = (p, s) => ({ ...s, id: _chunkZEYMKDYScjs.stickerRefOf.call(void 0, p, s.id) });
131
+ const page = async (query, o = {}) => {
132
+ const { after, limit = _chunkZEYMKDYScjs.DEFAULT_STICKER_PAGE, ...filters } = o;
133
+ _chunkZEYMKDYScjs.checkStickerSearchOptions.call(void 0, { limit });
134
+ let i = 0;
135
+ let inner;
136
+ if (after !== void 0) {
137
+ const m = /^(\d+)\|(.*)$/.exec(after);
138
+ if (!m || Number(m[1]) >= providers.length) throw new (0, _chunkZEYMKDYScjs.StickerError)("invalid_argument", `after "${after}" is not a cursor from this provider; pass the next value of the previous page.`);
139
+ i = Number(m[1]);
140
+ inner = m[2] || void 0;
141
+ }
142
+ const get = (p, opts) => query === null ? p.list(opts) : p.search(query, opts);
143
+ let total = 0;
144
+ for (const p of providers) total += (await get(p, { ...filters, limit: 1 })).total;
145
+ const items = [];
146
+ let next;
147
+ while (i < providers.length && items.length < limit) {
148
+ const p = providers[i];
149
+ const res = await get(p, { ...filters, limit: limit - items.length, ...inner ? { after: inner } : {} });
150
+ items.push(...res.items.map((s) => prefix(p, s)));
151
+ if (res.next && res.items.length) {
152
+ inner = res.next;
153
+ if (items.length >= limit) next = `${i}|${inner}`;
154
+ continue;
155
+ }
156
+ i++;
157
+ inner = void 0;
158
+ }
159
+ if (!next && items.length >= limit && i < providers.length) next = `${i}|`;
160
+ return { items, total, ...next ? { next } : {} };
161
+ };
162
+ return {
163
+ id: _nullishCoalesce(options.id, () => ( "stickers")),
164
+ name: _nullishCoalesce(options.name, () => ( providers.map((p) => p.name).join(" + "))),
165
+ namespaced: true,
166
+ capabilities: {
167
+ supportsSearch: providers.some((p) => p.capabilities.supportsSearch),
168
+ supportsSvg: providers.some((p) => p.capabilities.supportsSvg),
169
+ supportsRaster: providers.some((p) => p.capabilities.supportsRaster),
170
+ ...providers.every((p) => p.capabilities.maxSize) ? { maxSize: Math.max(...providers.map((p) => p.capabilities.maxSize)) } : {}
171
+ },
172
+ list: (o) => page(null, o),
173
+ search: (q, o) => page(q, o),
174
+ async describe(ref) {
175
+ let o;
176
+ try {
177
+ o = owner(ref);
178
+ } catch (e2) {
179
+ return void 0;
180
+ }
181
+ const d = await o.p.describe(o.id);
182
+ return d ? { ...d, id: _chunkZEYMKDYScjs.stickerRefOf.call(void 0, o.p, d.id) } : void 0;
183
+ },
184
+ async load(ref, o) {
185
+ const { p, id } = owner(ref);
186
+ return p.load(id, o);
187
+ },
188
+ async categories() {
189
+ const counts = /* @__PURE__ */ new Map();
190
+ for (const p of providers) {
191
+ for (const c of await _asyncNullishCoalesce(await _optionalChain([p, 'access', _4 => _4.categories, 'optionalCall', _5 => _5()]), async () => ( []))) {
192
+ const had = counts.get(c.id);
193
+ counts.set(c.id, { id: c.id, name: _nullishCoalesce(_optionalChain([had, 'optionalAccess', _6 => _6.name]), () => ( c.name)), count: (_nullishCoalesce(_optionalChain([had, 'optionalAccess', _7 => _7.count]), () => ( 0))) + c.count });
194
+ }
195
+ }
196
+ return [...counts.values()];
197
+ }
198
+ };
199
+ }
200
+
201
+ // src/stickers/conformance.ts
202
+ var HEX = /^#[0-9a-fA-F]{6}$/;
203
+ function checkSummary(s, provider, issues) {
204
+ const at = (message) => issues.push({ stickerId: s.id, message });
205
+ if (typeof s.id !== "string" || !s.id) at("id is missing");
206
+ if (!provider.namespaced && s.id.includes(":")) at('id contains ":" (refs are provider:id; only a combined provider lists refs)');
207
+ if (typeof s.name !== "string" || !s.name) at("name is missing");
208
+ if (typeof s.category !== "string" || !s.category) at("category is missing");
209
+ if (!(s.width > 0 && s.height > 0)) at(`width and height must be positive, got ${s.width} \xD7 ${s.height}`);
210
+ if (!Array.isArray(s.formats) || !s.formats.length || s.formats.some((f) => f !== "svg" && f !== "png")) at("formats must list svg and / or png");
211
+ 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");
212
+ 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");
213
+ if (!s.licence || typeof s.licence.name !== "string" || !s.licence.name) at("licence.name is missing (every sticker says how it may be used)");
214
+ if (typeof _optionalChain([s, 'access', _14 => _14.licence, 'optionalAccess', _15 => _15.commercial]) !== "boolean") at("licence.commercial is not a boolean");
215
+ if (typeof s.tintable !== "boolean") at("tintable is not a boolean");
216
+ if (s.thumbnail !== void 0 && !/^(data:|https:)/.test(s.thumbnail)) at("thumbnail must be a data: or https: URL");
217
+ }
218
+ async function checkStickerProvider(provider, options = {}) {
219
+ const issues = [];
220
+ const top = (message) => issues.push({ message });
221
+ if (typeof provider.id !== "string" || !provider.id) top("provider has no id");
222
+ else if (provider.id.includes(":")) top('provider id contains ":"');
223
+ if (typeof provider.name !== "string" || !provider.name) top("provider has no name");
224
+ const caps = provider.capabilities;
225
+ for (const k of ["supportsSearch", "supportsSvg", "supportsRaster"]) if (typeof _optionalChain([caps, 'optionalAccess', _16 => _16[k]]) !== "boolean") top(`capabilities.${k} is not a boolean`);
226
+ if (_optionalChain([caps, 'optionalAccess', _17 => _17.maxSize]) !== void 0 && !(caps.maxSize > 0)) top("capabilities.maxSize must be positive");
227
+ const all = [];
228
+ const ids = /* @__PURE__ */ new Set();
229
+ let after;
230
+ let total = -1;
231
+ for (let n = 0; n < (_nullishCoalesce(options.maxPages, () => ( 50))); n++) {
232
+ let page;
233
+ try {
234
+ page = await provider.list({ limit: 10, ...after ? { after } : {} });
235
+ } catch (e) {
236
+ top(`list() rejected: ${e.message}`);
237
+ break;
238
+ }
239
+ if (!Array.isArray(_optionalChain([page, 'optionalAccess', _18 => _18.items])) || typeof page.total !== "number") {
240
+ top("list() did not return { items, total }");
241
+ break;
242
+ }
243
+ if (page.items.length > 10) top(`list({ limit: 10 }) returned ${page.items.length} stickers`);
244
+ if (total === -1) total = page.total;
245
+ for (const s of page.items) {
246
+ if (ids.has(s.id)) top(`duplicate id "${s.id}"`);
247
+ ids.add(s.id);
248
+ all.push(s);
249
+ checkSummary(s, provider, issues);
250
+ }
251
+ if (!page.next) break;
252
+ if (!page.items.length) {
253
+ top("list() returned an empty page with a next cursor");
254
+ break;
255
+ }
256
+ after = page.next;
257
+ }
258
+ if (total !== -1 && all.length !== total) top(`list() pages hold ${all.length} stickers but total says ${total}`);
259
+ try {
260
+ const found = await provider.search("", { limit: 1 });
261
+ if (found.total !== total) top(`search("") total ${found.total} differs from list() total ${total}`);
262
+ } catch (e) {
263
+ top(`search("") rejected: ${e.message}`);
264
+ }
265
+ const first = all[0];
266
+ if (first) {
267
+ const byCategory = await provider.list({ category: first.category, limit: 100 });
268
+ if (byCategory.items.some((s) => s.category !== first.category)) top(`list({ category: "${first.category}" }) returned stickers of other categories`);
269
+ const d = await provider.describe(first.id);
270
+ if (!d) top(`describe("${first.id}") returned nothing for a listed sticker`);
271
+ else if (d.id !== first.id) top(`describe("${first.id}") returned id "${d.id}"`);
272
+ 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`);
273
+ }
274
+ if (await provider.describe("__no_such_sticker__") !== void 0) top("describe of an unknown id returned a sticker");
275
+ let rejected = false;
276
+ try {
277
+ await provider.load("__no_such_sticker__");
278
+ } catch (e3) {
279
+ rejected = true;
280
+ }
281
+ if (!rejected) top("load of an unknown id did not reject");
282
+ for (const s of all.slice(0, _nullishCoalesce(options.loads, () => ( 3)))) {
283
+ for (const format of _nullishCoalesce(s.formats, () => ( []))) {
284
+ try {
285
+ const asset = await provider.load(s.id, { format, size: 32 });
286
+ if (format === "svg" && (asset.kind !== "svg" || !/<svg[\s>]/.test(asset.svg))) issues.push({ stickerId: s.id, message: 'load({ format: "svg" }) did not return SVG' });
287
+ if (format === "png" && (asset.kind !== "image" || !/^(data:|https:)/.test(asset.url) || !(asset.width > 0 && asset.height > 0))) issues.push({ stickerId: s.id, message: 'load({ format: "png" }) did not return an image URL with its size' });
288
+ } catch (e) {
289
+ issues.push({ stickerId: s.id, message: `load({ format: "${format}" }) rejected: ${e.message}` });
290
+ }
291
+ }
292
+ }
293
+ const tintable = all.find((s) => s.tintable);
294
+ if (tintable) {
295
+ try {
296
+ const a = await provider.load(tintable.id, { format: tintable.formats[0], colour: "#123456", size: 32 });
297
+ if (a.kind === "svg" && !/#123456/i.test(a.svg)) issues.push({ stickerId: tintable.id, message: "a tintable sticker loaded with colour #123456 does not use it" });
298
+ } catch (e) {
299
+ issues.push({ stickerId: tintable.id, message: `load with a colour rejected: ${e.message}` });
300
+ }
301
+ }
302
+ return issues;
303
+ }
304
+
305
+
306
+
307
+
308
+
309
+
310
+
311
+
312
+
313
+
314
+
315
+
316
+
317
+
318
+
319
+
320
+
321
+
322
+
323
+
324
+
325
+
326
+
327
+
328
+
329
+
330
+
331
+ exports.DEFAULT_STICKER_PAGE = _chunkZEYMKDYScjs.DEFAULT_STICKER_PAGE; exports.EDITOR_STICKERS_ID = _chunkZEYMKDYScjs.EDITOR_STICKERS_ID; exports.MAX_STICKER_PAGE = _chunkZEYMKDYScjs.MAX_STICKER_PAGE; exports.STAMP_KINDS = _chunkZEYMKDYScjs.STAMP_KINDS; exports.StickerBrowser = _chunkLJRCU3BJcjs.StickerBrowser; exports.StickerError = _chunkZEYMKDYScjs.StickerError; exports.StickersSlot = _chunkZEYMKDYScjs.StickersSlot; exports.checkStickerProvider = checkStickerProvider; exports.checkStickerSearchOptions = _chunkZEYMKDYScjs.checkStickerSearchOptions; exports.closestStickerRefs = _chunkZEYMKDYScjs.closestStickerRefs; exports.combineStickerProviders = combineStickerProviders; exports.createFakeStickerProvider = createFakeStickerProvider; exports.createStickerToolProvider = _chunkZEYMKDYScjs.createStickerToolProvider; exports.createStickerTools = _chunkZEYMKDYScjs.createStickerTools; exports.describeStickerRef = _chunkZEYMKDYScjs.describeStickerRef; exports.editorStampOf = _chunkZEYMKDYScjs.editorStampOf; exports.editorStickers = _chunkLJRCU3BJcjs.editorStickers; exports.normalizeStickerRef = _chunkZEYMKDYScjs.normalizeStickerRef; exports.parseStickerRef = _chunkZEYMKDYScjs.parseStickerRef; exports.stampCoverage = _chunkZEYMKDYScjs.stampCoverage; exports.stampOutline = _chunkLJRCU3BJcjs.stampOutline; exports.stampPixels = _chunkLJRCU3BJcjs.stampPixels; exports.stickerForAgent = _chunkZEYMKDYScjs.stickerForAgent; exports.stickerIdFor = _chunkZEYMKDYScjs.stickerIdFor; exports.stickerPage = _chunkZEYMKDYScjs.stickerPage; exports.stickerRefOf = _chunkZEYMKDYScjs.stickerRefOf;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `@pixerate/editor/stickers`: the sticker provider contract (dependency-free). A host plugs in
3
+ * any sticker library (`StickerProvider`); the editor's own stamps are the built-in set
4
+ * (`editorStickers()`, the default in every `StickersSlot`). Also: a conformance checker, a fake
5
+ * provider for tests, combining providers, and the `find_stickers` / `describe_sticker` tools.
6
+ */
7
+ export * from './types.cjs';
8
+ export * from './errors.cjs';
9
+ export * from './helpers.cjs';
10
+ export * from './stamps.cjs';
11
+ export { editorStickers, stampOutline, stampPixels } from './editor.cjs';
12
+ export { createFakeStickerProvider, type FakeStickerProviderOptions } from './fake.cjs';
13
+ export { combineStickerProviders } from './combine.cjs';
14
+ export { checkStickerProvider, type StickerConformanceIssue, type StickerConformanceOptions } from './conformance.cjs';
15
+ export { createStickerTools, createStickerToolProvider, describeStickerRef, stickerForAgent, type StickerToolsOptions } from './tools.cjs';
16
+ export { StickerBrowser, type StickerBrowserItem, type StickerBrowserState } from './browser.cjs';
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `@pixerate/editor/stickers`: the sticker provider contract (dependency-free). A host plugs in
3
+ * any sticker library (`StickerProvider`); the editor's own stamps are the built-in set
4
+ * (`editorStickers()`, the default in every `StickersSlot`). Also: a conformance checker, a fake
5
+ * provider for tests, combining providers, and the `find_stickers` / `describe_sticker` tools.
6
+ */
7
+ export * from './types.js';
8
+ export * from './errors.js';
9
+ export * from './helpers.js';
10
+ export * from './stamps.js';
11
+ export { editorStickers, stampOutline, stampPixels } from './editor.js';
12
+ export { createFakeStickerProvider, type FakeStickerProviderOptions } from './fake.js';
13
+ export { combineStickerProviders } from './combine.js';
14
+ export { checkStickerProvider, type StickerConformanceIssue, type StickerConformanceOptions } from './conformance.js';
15
+ export { createStickerTools, createStickerToolProvider, describeStickerRef, stickerForAgent, type StickerToolsOptions } from './tools.js';
16
+ export { StickerBrowser, type StickerBrowserItem, type StickerBrowserState } from './browser.js';