@pixerate/editor 0.246.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.
- package/README.md +1 -0
- package/dist/animation-editor/index.cjs +64 -61
- package/dist/animation-editor/index.js +8 -5
- package/dist/{brushstrokeRender-ZDHMXJAE.cjs → brushstrokeRender-GCKK7V2I.cjs} +14 -11
- package/dist/{brushstrokeRender-AKUI3SEJ.js → brushstrokeRender-T3JZST44.js} +7 -4
- package/dist/canvas/index.cjs +8 -8
- package/dist/canvas/index.js +1 -1
- package/dist/{chunk-UVPD55UZ.cjs → chunk-2SMMZDMG.cjs} +35 -35
- package/dist/chunk-2VJAVXLI.js +192 -0
- package/dist/{chunk-OLNIPTB3.cjs → chunk-2VX55NFM.cjs} +1 -320
- package/dist/chunk-3K6HWEXZ.cjs +92 -0
- package/dist/{chunk-UOOK7HTX.js → chunk-3PPJRMIG.js} +1 -1
- package/dist/chunk-4D6KP5BV.js +92 -0
- package/dist/chunk-5RH3VXAK.js +269 -0
- package/dist/{chunk-LDPLEZ5T.js → chunk-6BIDVPED.js} +1 -1
- package/dist/{chunk-2ONK3ERD.cjs → chunk-75K3W4D6.cjs} +20 -20
- package/dist/chunk-BOHDJJ2O.cjs +192 -0
- package/dist/{chunk-2PBZBRP5.js → chunk-D5KGAO4Z.js} +10 -92
- package/dist/{chunk-DQWPMGH2.js → chunk-DOIU7VJG.js} +1 -1
- package/dist/{chunk-HB4ICNNA.cjs → chunk-DPYAO5U6.cjs} +42 -42
- package/dist/chunk-EJLRBS5Q.js +301 -0
- package/dist/{chunk-Q2UYUWAZ.cjs → chunk-H4AOUUBE.cjs} +679 -164
- package/dist/{chunk-PQ3H2AAN.cjs → chunk-I7NJPHWV.cjs} +122 -204
- package/dist/{chunk-FK5LTDOO.cjs → chunk-IOFVM3L3.cjs} +7 -7
- package/dist/{chunk-42TWUPNJ.js → chunk-IRSTHLQQ.js} +1 -320
- package/dist/{chunk-VDAWA546.js → chunk-J7T5QZ45.js} +614 -99
- package/dist/{chunk-VMROIWLL.cjs → chunk-KBYB7OWB.cjs} +3 -3
- package/dist/{chunk-5MFDD7SP.cjs → chunk-KYX7NZ5V.cjs} +1 -1
- package/dist/chunk-LJRCU3BJ.cjs +301 -0
- package/dist/{chunk-XTWXBQD4.cjs → chunk-LP2MCSBE.cjs} +593 -236
- package/dist/{chunk-A23PSOBB.js → chunk-O75V4ZS3.js} +1 -1
- package/dist/chunk-OTTT5N3K.js +322 -0
- package/dist/{chunk-N6WUB3Y5.js → chunk-PZP2SZIZ.js} +2 -2
- package/dist/{chunk-4PMCMWQS.js → chunk-UBRJOIR5.js} +367 -10
- package/dist/chunk-W5M3J7PV.cjs +322 -0
- package/dist/{chunk-4P6H35HU.js → chunk-WA44NKPF.js} +2 -2
- package/dist/{chunk-CEPKXLBS.cjs → chunk-WLJZZHUZ.cjs} +23 -23
- package/dist/{chunk-P3NIPIKS.js → chunk-WQCNC4DK.js} +1 -1
- package/dist/chunk-ZEYMKDYS.cjs +269 -0
- package/dist/image-editor/effect-tools.d.cts +37 -4
- package/dist/image-editor/effect-tools.d.ts +37 -4
- package/dist/image-editor/index.cjs +1340 -1828
- package/dist/image-editor/index.d.cts +1 -1
- package/dist/image-editor/index.d.ts +1 -1
- package/dist/image-editor/index.js +298 -786
- package/dist/paint/index.cjs +8 -5
- package/dist/paint/index.js +13 -10
- package/dist/paint-editor/controller.d.cts +71 -2
- package/dist/paint-editor/controller.d.ts +71 -2
- package/dist/paint-editor/index.cjs +901 -589
- package/dist/paint-editor/index.d.cts +3 -0
- package/dist/paint-editor/index.d.ts +3 -0
- package/dist/paint-editor/index.js +365 -53
- package/dist/paint-editor/procedural.d.cts +82 -5
- package/dist/paint-editor/procedural.d.ts +82 -5
- package/dist/paint-editor/sticker-art.d.cts +55 -0
- package/dist/paint-editor/sticker-art.d.ts +55 -0
- package/dist/paint-editor/stickers.d.cts +30 -17
- package/dist/paint-editor/stickers.d.ts +30 -17
- package/dist/paint-editor/stroke.d.cts +4 -0
- package/dist/paint-editor/stroke.d.ts +4 -0
- package/dist/paint-editor/tools.d.cts +1 -1
- package/dist/paint-editor/tools.d.ts +1 -1
- package/dist/paint-editor/ui.d.cts +25 -4
- package/dist/paint-editor/ui.d.ts +25 -4
- package/dist/{image-editor/layers/effects-ui.d.cts → raster/effects/ui.d.cts} +32 -5
- package/dist/{image-editor/layers/effects-ui.d.ts → raster/effects/ui.d.ts} +32 -5
- package/dist/raster/index.cjs +17 -3
- package/dist/raster/index.d.cts +1 -0
- package/dist/raster/index.d.ts +1 -0
- package/dist/raster/index.js +23 -9
- package/dist/stickers/browser.d.cts +48 -0
- package/dist/stickers/browser.d.ts +48 -0
- package/dist/stickers/combine.d.cts +10 -0
- package/dist/stickers/combine.d.ts +10 -0
- package/dist/stickers/conformance.d.cts +18 -0
- package/dist/stickers/conformance.d.ts +18 -0
- package/dist/stickers/editor.d.cts +14 -0
- package/dist/stickers/editor.d.ts +14 -0
- package/dist/stickers/errors.d.cts +6 -0
- package/dist/stickers/errors.d.ts +6 -0
- package/dist/stickers/fake.d.cts +16 -0
- package/dist/stickers/fake.d.ts +16 -0
- package/dist/stickers/helpers.d.cts +51 -0
- package/dist/stickers/helpers.d.ts +51 -0
- package/dist/stickers/index.cjs +331 -0
- package/dist/stickers/index.d.cts +16 -0
- package/dist/stickers/index.d.ts +16 -0
- package/dist/stickers/index.js +331 -0
- package/dist/stickers/stamps.d.cts +13 -0
- package/dist/stickers/stamps.d.ts +13 -0
- package/dist/stickers/tools.d.cts +25 -0
- package/dist/stickers/tools.d.ts +25 -0
- package/dist/stickers/types.d.cts +123 -0
- package/dist/stickers/types.d.ts +123 -0
- package/dist/vector/index.cjs +978 -975
- package/dist/vector/index.js +9 -6
- package/package.json +11 -1
- package/skills/README.md +1 -0
- package/skills/pixerate-paint-editor/SKILL.md +26 -32
- package/skills/pixerate-paint-editor/references/gradient-paint.md +23 -0
- package/skills/pixerate-paint-editor/references/layer-effects.md +28 -0
- package/skills/pixerate-paint-editor/references/light-paint.md +10 -2
- package/skills/pixerate-paint-editor/references/particle-brushes.md +66 -0
- package/skills/pixerate-stickers/SKILL.md +45 -0
|
@@ -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';
|