@pixerate/editor 0.251.0 → 0.253.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/dist/brushes/index.cjs +240 -1344
- package/dist/brushes/index.d.cts +10 -1
- package/dist/brushes/index.d.ts +10 -1
- package/dist/brushes/index.js +250 -1354
- package/dist/brushes/library.d.cts +104 -0
- package/dist/brushes/library.d.ts +104 -0
- package/dist/brushes/patch.d.cts +23 -0
- package/dist/brushes/patch.d.ts +23 -0
- package/dist/brushes/profiles.d.cts +85 -0
- package/dist/brushes/profiles.d.ts +85 -0
- package/dist/brushes/tools.d.cts +37 -0
- package/dist/brushes/tools.d.ts +37 -0
- package/dist/brushes/ui.d.cts +105 -0
- package/dist/brushes/ui.d.ts +105 -0
- package/dist/chunk-34KLWUH4.cjs +2284 -0
- package/dist/chunk-QPHNVZHO.js +2284 -0
- package/dist/paint-editor/brush-controls.d.cts +57 -0
- package/dist/paint-editor/brush-controls.d.ts +57 -0
- package/dist/paint-editor/brush-library-tools.d.cts +15 -0
- package/dist/paint-editor/brush-library-tools.d.ts +15 -0
- package/dist/paint-editor/brush-library.d.cts +29 -0
- package/dist/paint-editor/brush-library.d.ts +29 -0
- package/dist/paint-editor/controller.d.cts +76 -17
- package/dist/paint-editor/controller.d.ts +76 -17
- package/dist/paint-editor/index.cjs +537 -852
- package/dist/paint-editor/index.d.cts +1 -0
- package/dist/paint-editor/index.d.ts +1 -0
- package/dist/paint-editor/index.js +327 -642
- package/dist/paint-editor/serialize.d.cts +7 -4
- package/dist/paint-editor/serialize.d.ts +7 -4
- package/dist/paint-editor/sticker-tools.d.cts +1 -1
- package/dist/paint-editor/sticker-tools.d.ts +1 -1
- package/dist/paint-editor/strokes.d.cts +10 -1
- package/dist/paint-editor/strokes.d.ts +10 -1
- 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 +3 -51
- package/dist/paint-editor/ui.d.ts +3 -51
- package/dist/paint-editor/wet-tools.d.cts +1 -1
- package/dist/paint-editor/wet-tools.d.ts +1 -1
- package/dist/tools/skill-evals.d.cts +8 -1
- package/dist/tools/skill-evals.d.ts +8 -1
- package/package.json +1 -1
- package/skills/README.md +2 -0
- package/skills/pixerate-brushes/SKILL.md +106 -0
- package/skills/pixerate-brushes-design/SKILL.md +88 -0
- package/skills/pixerate-brushes-design/evals/canvas-chalk.json +15 -0
- package/skills/pixerate-brushes-design/evals/pink-highlighter.json +15 -0
- package/skills/pixerate-brushes-design/evals/port-to-vector.json +17 -0
- package/skills/pixerate-paint-editor/SKILL.md +26 -24
- package/skills/pixerate-paint-editor/references/brush-facets.md +30 -0
- package/skills/pixerate-paint-editor/references/gradient-paint.md +1 -1
- package/skills/pixerate-paint-editor/references/light-paint.md +2 -2
- package/skills/pixerate-paint-editor/references/oils.md +1 -1
- package/skills/pixerate-paint-editor/references/particle-brushes.md +8 -5
- package/skills/pixerate-paint-oil-impasto/SKILL.md +1 -1
- package/skills/pixerate-stickers/SKILL.md +1 -1
- package/skills/pixerate-video-editor/SKILL.md +1 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The brush library: the built-in presets plus the user's brushes, kept on any `StorageAdapter`
|
|
3
|
+
* (`createMemoryStorage`, `createLocalStorageAdapter`, `createIndexedDbAdapter`,
|
|
4
|
+
* `createOpfsAdapter`) or, without one, in memory. One library shared by every editor makes a
|
|
5
|
+
* brush saved in one show up in all of them.
|
|
6
|
+
*
|
|
7
|
+
* The user's brushes are stored as one `pixerate-brushes` version 2 file under one key. Library
|
|
8
|
+
* files of version 1 (the paint editor's flat brushes) still import: each brush is migrated.
|
|
9
|
+
*/
|
|
10
|
+
import type { StorageAdapter } from '../persistence/storage.cjs';
|
|
11
|
+
import { type BrushHostProfile } from './profiles.cjs';
|
|
12
|
+
import type { Brush, BrushKind, BrushMedium, BrushTool } from './types.cjs';
|
|
13
|
+
/** The JSON file a brush library exports to and imports from. */
|
|
14
|
+
export interface BrushLibraryFile {
|
|
15
|
+
format: 'pixerate-brushes';
|
|
16
|
+
version: 2;
|
|
17
|
+
brushes: Brush[];
|
|
18
|
+
}
|
|
19
|
+
export type BrushLibraryErrorCode = 'unknown_brush' | 'builtin' | 'exists' | 'invalid';
|
|
20
|
+
/** A refusal; the message says what to do instead. */
|
|
21
|
+
export declare class BrushLibraryError extends Error {
|
|
22
|
+
readonly code: BrushLibraryErrorCode;
|
|
23
|
+
constructor(code: BrushLibraryErrorCode, message: string);
|
|
24
|
+
}
|
|
25
|
+
export interface BrushLibraryEntry {
|
|
26
|
+
brush: Brush;
|
|
27
|
+
/** A built-in preset: it can be duplicated but not changed or removed. */
|
|
28
|
+
builtin: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface BrushFilter {
|
|
31
|
+
kind?: BrushKind;
|
|
32
|
+
medium?: BrushMedium;
|
|
33
|
+
variety?: string;
|
|
34
|
+
tool?: BrushTool;
|
|
35
|
+
/** Has this tag. */
|
|
36
|
+
tag?: string;
|
|
37
|
+
/** Words in the id, name, description, variety or tags (case-insensitive; every word must match). */
|
|
38
|
+
text?: string;
|
|
39
|
+
/** Only built-in presets or only the user's brushes. */
|
|
40
|
+
source?: 'builtin' | 'user';
|
|
41
|
+
/** Only brushes this host can render (`checkBrush` has no errors). */
|
|
42
|
+
profile?: BrushHostProfile;
|
|
43
|
+
}
|
|
44
|
+
export interface SaveBrushResult {
|
|
45
|
+
/** The brush as stored (normalised). */
|
|
46
|
+
brush: Brush;
|
|
47
|
+
/** What normalising changed. */
|
|
48
|
+
problems: string[];
|
|
49
|
+
/** It replaced a user brush of the same id. */
|
|
50
|
+
replaced: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface ImportBrushesResult {
|
|
53
|
+
added: string[];
|
|
54
|
+
replaced: string[];
|
|
55
|
+
skipped: string[];
|
|
56
|
+
problems: string[];
|
|
57
|
+
}
|
|
58
|
+
export interface BrushLibraryOptions {
|
|
59
|
+
/** Where the user's brushes are kept (default: memory only, gone on reload). */
|
|
60
|
+
storage?: Pick<StorageAdapter, 'get' | 'set'>;
|
|
61
|
+
/** Storage key prefix (default `pixerate.brushes`). */
|
|
62
|
+
namespace?: string;
|
|
63
|
+
/** The built-in presets (default `BRUSH_PRESETS`). */
|
|
64
|
+
presets?: readonly Brush[];
|
|
65
|
+
}
|
|
66
|
+
export interface BrushLibrary {
|
|
67
|
+
/** Built-in presets first (in library order), then the user's brushes (in the order saved). */
|
|
68
|
+
list(filter?: BrushFilter): Promise<BrushLibraryEntry[]>;
|
|
69
|
+
get(id: string): Promise<BrushLibraryEntry | null>;
|
|
70
|
+
isBuiltin(id: string): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Stores a user brush (normalised first). Refuses a built-in id and, without `overwrite`, an
|
|
73
|
+
* id already taken.
|
|
74
|
+
*/
|
|
75
|
+
save(brush: unknown, options?: {
|
|
76
|
+
overwrite?: boolean;
|
|
77
|
+
}): Promise<SaveBrushResult>;
|
|
78
|
+
/** Removes a user brush; refuses a built-in one. False when there was no such brush. */
|
|
79
|
+
remove(id: string): Promise<boolean>;
|
|
80
|
+
/** Copies any brush (built-ins included) to a new user brush: `id` default `<id>-copy`, `-copy-2`…; `name` default `<name> copy`. */
|
|
81
|
+
duplicate(id: string, options?: {
|
|
82
|
+
id?: string;
|
|
83
|
+
name?: string;
|
|
84
|
+
}): Promise<Brush>;
|
|
85
|
+
/** A library file of the user's brushes, or of the given ids (built-ins included). */
|
|
86
|
+
exportFile(ids?: readonly string[]): Promise<BrushLibraryFile>;
|
|
87
|
+
/** Adds the brushes of a library file (version 1 or 2, or a bare array). Built-in ids are skipped; taken ids too, unless `overwrite`. */
|
|
88
|
+
importFile(raw: unknown, options?: {
|
|
89
|
+
overwrite?: boolean;
|
|
90
|
+
}): Promise<ImportBrushesResult>;
|
|
91
|
+
/** Called after every change. Returns an unsubscribe function. */
|
|
92
|
+
subscribe(listener: () => void): () => void;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Brushes read from a library file: `{ format: 'pixerate-brushes', version: 1 | 2, brushes }` or
|
|
96
|
+
* a bare array. Version 1 brushes are migrated (`migrateBrushV1`); every brush is normalised.
|
|
97
|
+
* Brushes without a valid id or name are skipped, and everything fixed or skipped is reported
|
|
98
|
+
* in `problems`.
|
|
99
|
+
*/
|
|
100
|
+
export declare function parseBrushLibrary(raw: unknown, problems?: string[]): Brush[];
|
|
101
|
+
/** A brush library over `options.storage` (see the module comment). */
|
|
102
|
+
export declare function createBrushLibrary(options?: BrushLibraryOptions): BrushLibrary;
|
|
103
|
+
/** "Closest: a, b. " for an unknown id (shared prefix or substring), or "" when nothing is near. */
|
|
104
|
+
export declare function closest(id: string, ids: readonly string[], max?: number): string;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The brush library: the built-in presets plus the user's brushes, kept on any `StorageAdapter`
|
|
3
|
+
* (`createMemoryStorage`, `createLocalStorageAdapter`, `createIndexedDbAdapter`,
|
|
4
|
+
* `createOpfsAdapter`) or, without one, in memory. One library shared by every editor makes a
|
|
5
|
+
* brush saved in one show up in all of them.
|
|
6
|
+
*
|
|
7
|
+
* The user's brushes are stored as one `pixerate-brushes` version 2 file under one key. Library
|
|
8
|
+
* files of version 1 (the paint editor's flat brushes) still import: each brush is migrated.
|
|
9
|
+
*/
|
|
10
|
+
import type { StorageAdapter } from '../persistence/storage.js';
|
|
11
|
+
import { type BrushHostProfile } from './profiles.js';
|
|
12
|
+
import type { Brush, BrushKind, BrushMedium, BrushTool } from './types.js';
|
|
13
|
+
/** The JSON file a brush library exports to and imports from. */
|
|
14
|
+
export interface BrushLibraryFile {
|
|
15
|
+
format: 'pixerate-brushes';
|
|
16
|
+
version: 2;
|
|
17
|
+
brushes: Brush[];
|
|
18
|
+
}
|
|
19
|
+
export type BrushLibraryErrorCode = 'unknown_brush' | 'builtin' | 'exists' | 'invalid';
|
|
20
|
+
/** A refusal; the message says what to do instead. */
|
|
21
|
+
export declare class BrushLibraryError extends Error {
|
|
22
|
+
readonly code: BrushLibraryErrorCode;
|
|
23
|
+
constructor(code: BrushLibraryErrorCode, message: string);
|
|
24
|
+
}
|
|
25
|
+
export interface BrushLibraryEntry {
|
|
26
|
+
brush: Brush;
|
|
27
|
+
/** A built-in preset: it can be duplicated but not changed or removed. */
|
|
28
|
+
builtin: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface BrushFilter {
|
|
31
|
+
kind?: BrushKind;
|
|
32
|
+
medium?: BrushMedium;
|
|
33
|
+
variety?: string;
|
|
34
|
+
tool?: BrushTool;
|
|
35
|
+
/** Has this tag. */
|
|
36
|
+
tag?: string;
|
|
37
|
+
/** Words in the id, name, description, variety or tags (case-insensitive; every word must match). */
|
|
38
|
+
text?: string;
|
|
39
|
+
/** Only built-in presets or only the user's brushes. */
|
|
40
|
+
source?: 'builtin' | 'user';
|
|
41
|
+
/** Only brushes this host can render (`checkBrush` has no errors). */
|
|
42
|
+
profile?: BrushHostProfile;
|
|
43
|
+
}
|
|
44
|
+
export interface SaveBrushResult {
|
|
45
|
+
/** The brush as stored (normalised). */
|
|
46
|
+
brush: Brush;
|
|
47
|
+
/** What normalising changed. */
|
|
48
|
+
problems: string[];
|
|
49
|
+
/** It replaced a user brush of the same id. */
|
|
50
|
+
replaced: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface ImportBrushesResult {
|
|
53
|
+
added: string[];
|
|
54
|
+
replaced: string[];
|
|
55
|
+
skipped: string[];
|
|
56
|
+
problems: string[];
|
|
57
|
+
}
|
|
58
|
+
export interface BrushLibraryOptions {
|
|
59
|
+
/** Where the user's brushes are kept (default: memory only, gone on reload). */
|
|
60
|
+
storage?: Pick<StorageAdapter, 'get' | 'set'>;
|
|
61
|
+
/** Storage key prefix (default `pixerate.brushes`). */
|
|
62
|
+
namespace?: string;
|
|
63
|
+
/** The built-in presets (default `BRUSH_PRESETS`). */
|
|
64
|
+
presets?: readonly Brush[];
|
|
65
|
+
}
|
|
66
|
+
export interface BrushLibrary {
|
|
67
|
+
/** Built-in presets first (in library order), then the user's brushes (in the order saved). */
|
|
68
|
+
list(filter?: BrushFilter): Promise<BrushLibraryEntry[]>;
|
|
69
|
+
get(id: string): Promise<BrushLibraryEntry | null>;
|
|
70
|
+
isBuiltin(id: string): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Stores a user brush (normalised first). Refuses a built-in id and, without `overwrite`, an
|
|
73
|
+
* id already taken.
|
|
74
|
+
*/
|
|
75
|
+
save(brush: unknown, options?: {
|
|
76
|
+
overwrite?: boolean;
|
|
77
|
+
}): Promise<SaveBrushResult>;
|
|
78
|
+
/** Removes a user brush; refuses a built-in one. False when there was no such brush. */
|
|
79
|
+
remove(id: string): Promise<boolean>;
|
|
80
|
+
/** Copies any brush (built-ins included) to a new user brush: `id` default `<id>-copy`, `-copy-2`…; `name` default `<name> copy`. */
|
|
81
|
+
duplicate(id: string, options?: {
|
|
82
|
+
id?: string;
|
|
83
|
+
name?: string;
|
|
84
|
+
}): Promise<Brush>;
|
|
85
|
+
/** A library file of the user's brushes, or of the given ids (built-ins included). */
|
|
86
|
+
exportFile(ids?: readonly string[]): Promise<BrushLibraryFile>;
|
|
87
|
+
/** Adds the brushes of a library file (version 1 or 2, or a bare array). Built-in ids are skipped; taken ids too, unless `overwrite`. */
|
|
88
|
+
importFile(raw: unknown, options?: {
|
|
89
|
+
overwrite?: boolean;
|
|
90
|
+
}): Promise<ImportBrushesResult>;
|
|
91
|
+
/** Called after every change. Returns an unsubscribe function. */
|
|
92
|
+
subscribe(listener: () => void): () => void;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Brushes read from a library file: `{ format: 'pixerate-brushes', version: 1 | 2, brushes }` or
|
|
96
|
+
* a bare array. Version 1 brushes are migrated (`migrateBrushV1`); every brush is normalised.
|
|
97
|
+
* Brushes without a valid id or name are skipped, and everything fixed or skipped is reported
|
|
98
|
+
* in `problems`.
|
|
99
|
+
*/
|
|
100
|
+
export declare function parseBrushLibrary(raw: unknown, problems?: string[]): Brush[];
|
|
101
|
+
/** A brush library over `options.storage` (see the module comment). */
|
|
102
|
+
export declare function createBrushLibrary(options?: BrushLibraryOptions): BrushLibrary;
|
|
103
|
+
/** "Closest: a, b. " for an unknown id (shared prefix or substring), or "" when nothing is near. */
|
|
104
|
+
export declare function closest(id: string, ids: readonly string[], max?: number): string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Facet patches: a partial brush laid over another (`mergeBrushPatch`), the schema of one for
|
|
3
|
+
* tools (`BRUSH_PATCH_PROPERTIES`), and the leaf paths two brushes differ in (`changedBrushPaths`).
|
|
4
|
+
* The library tools and the paint editor's `set_brush` / `paint_stroke` share them, so a patch
|
|
5
|
+
* means the same everywhere.
|
|
6
|
+
*/
|
|
7
|
+
import type { JsonSchema } from '../tools/schema.cjs';
|
|
8
|
+
import type { Brush } from './types.cjs';
|
|
9
|
+
/** A brush's facets, any of them, as a patch takes them (the tip's `shape` may be left out). */
|
|
10
|
+
export type BrushPatch = {
|
|
11
|
+
[K in Exclude<keyof Brush, 'id'>]?: K extends 'tip' ? Partial<Brush['tip']> : Brush[K];
|
|
12
|
+
};
|
|
13
|
+
/** The schema of every facet except `id` and `name`, as tool arguments (a patch may leave out `tip.shape`). */
|
|
14
|
+
export declare const BRUSH_PATCH_PROPERTIES: Readonly<Record<string, JsonSchema>>;
|
|
15
|
+
/** The facets a patch can remove (`unset`), back to the engine's defaults. */
|
|
16
|
+
export declare const UNSETTABLE_BRUSH_FACETS: readonly ["description", "tags", "medium", "variety", "marks", "dynamics", "behaviour", "light", "motion", "colour"];
|
|
17
|
+
export type UnsettableBrushFacet = (typeof UNSETTABLE_BRUSH_FACETS)[number];
|
|
18
|
+
/** The `unset` argument of tools that take a patch. */
|
|
19
|
+
export declare const BRUSH_UNSET_PROPERTY: JsonSchema;
|
|
20
|
+
/** `base` with `patch` laid over it: objects merge, everything else (lists included) is replaced. Marks of another kind replace the marks. */
|
|
21
|
+
export declare function mergeBrushPatch<T extends Record<string, unknown>>(base: T, patch: Record<string, unknown>, path?: string): T;
|
|
22
|
+
/** Leaf paths whose values differ between two brushes (dot paths; lists compare whole). */
|
|
23
|
+
export declare function changedBrushPaths(a: unknown, b: unknown, path?: string): string[];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Facet patches: a partial brush laid over another (`mergeBrushPatch`), the schema of one for
|
|
3
|
+
* tools (`BRUSH_PATCH_PROPERTIES`), and the leaf paths two brushes differ in (`changedBrushPaths`).
|
|
4
|
+
* The library tools and the paint editor's `set_brush` / `paint_stroke` share them, so a patch
|
|
5
|
+
* means the same everywhere.
|
|
6
|
+
*/
|
|
7
|
+
import type { JsonSchema } from '../tools/schema.js';
|
|
8
|
+
import type { Brush } from './types.js';
|
|
9
|
+
/** A brush's facets, any of them, as a patch takes them (the tip's `shape` may be left out). */
|
|
10
|
+
export type BrushPatch = {
|
|
11
|
+
[K in Exclude<keyof Brush, 'id'>]?: K extends 'tip' ? Partial<Brush['tip']> : Brush[K];
|
|
12
|
+
};
|
|
13
|
+
/** The schema of every facet except `id` and `name`, as tool arguments (a patch may leave out `tip.shape`). */
|
|
14
|
+
export declare const BRUSH_PATCH_PROPERTIES: Readonly<Record<string, JsonSchema>>;
|
|
15
|
+
/** The facets a patch can remove (`unset`), back to the engine's defaults. */
|
|
16
|
+
export declare const UNSETTABLE_BRUSH_FACETS: readonly ["description", "tags", "medium", "variety", "marks", "dynamics", "behaviour", "light", "motion", "colour"];
|
|
17
|
+
export type UnsettableBrushFacet = (typeof UNSETTABLE_BRUSH_FACETS)[number];
|
|
18
|
+
/** The `unset` argument of tools that take a patch. */
|
|
19
|
+
export declare const BRUSH_UNSET_PROPERTY: JsonSchema;
|
|
20
|
+
/** `base` with `patch` laid over it: objects merge, everything else (lists included) is replaced. Marks of another kind replace the marks. */
|
|
21
|
+
export declare function mergeBrushPatch<T extends Record<string, unknown>>(base: T, patch: Record<string, unknown>, path?: string): T;
|
|
22
|
+
/** Leaf paths whose values differ between two brushes (dot paths; lists compare whole). */
|
|
23
|
+
export declare function changedBrushPaths(a: unknown, b: unknown, path?: string): string[];
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host capability profiles: what a host (the paint editor, the image editor, brushed canvas and
|
|
3
|
+
* vector strokes, later Swift) can render. `checkBrush` explains each refusal with a fix,
|
|
4
|
+
* `listPresets` filters a library down to what the host can use, and `degrade` strips the facets
|
|
5
|
+
* a host can't render and says what it dropped.
|
|
6
|
+
*
|
|
7
|
+
* A capability is `full` (rendered), `ignored` (the brush is accepted but the host paints without
|
|
8
|
+
* it: a warning) or `none` (the brush is refused: an error).
|
|
9
|
+
*/
|
|
10
|
+
import { type Brush, type BrushKind, type BrushMarksKind, type BrushPaint, type BrushSurface, type BrushTipShape, type BrushTool } from './types.cjs';
|
|
11
|
+
export type BrushSupport = 'full' | 'ignored' | 'none';
|
|
12
|
+
/** What a colour is: a solid colour, a light / dark pair, a gradient or effect paint. */
|
|
13
|
+
export type BrushColourKind = 'solid' | 'dual' | 'gradient' | 'effect';
|
|
14
|
+
export interface BrushHostProfile {
|
|
15
|
+
/** `paint`, `image`, `canvas`, `vector`… */
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
kinds: readonly BrushKind[];
|
|
19
|
+
/** Tools the host has (for `kind: 'tool'`). */
|
|
20
|
+
tools: readonly BrushTool[];
|
|
21
|
+
tipShapes: readonly BrushTipShape[];
|
|
22
|
+
marks: readonly BrushMarksKind[];
|
|
23
|
+
surfaces: readonly BrushSurface[];
|
|
24
|
+
/** Picking paint up and mixing it in (`behaviour.exchange` pickup, mix, dirty). */
|
|
25
|
+
exchange: BrushSupport;
|
|
26
|
+
/** The Mixer Brush (`behaviour.exchange.reservoir`). */
|
|
27
|
+
reservoir: BrushSupport;
|
|
28
|
+
/** Paper grain response (`behaviour.grain`). */
|
|
29
|
+
grain: BrushSupport;
|
|
30
|
+
light: BrushSupport;
|
|
31
|
+
motion: BrushSupport;
|
|
32
|
+
/** Colour kinds the brush's default colour may be (palette entries of other kinds are warned about). */
|
|
33
|
+
colours: readonly BrushColourKind[];
|
|
34
|
+
/** Velocity dynamics (needs timed input). */
|
|
35
|
+
velocity: BrushSupport;
|
|
36
|
+
/** Tilt dynamics (needs pen tilt). */
|
|
37
|
+
tilt: BrushSupport;
|
|
38
|
+
}
|
|
39
|
+
export interface BrushIssue {
|
|
40
|
+
/** The facet path the issue is about, e.g. `tip.shape`, `light`, `behaviour.exchange.reservoir`. */
|
|
41
|
+
facet: string;
|
|
42
|
+
/** `error`: the host can't render the brush; `warning`: it renders it without this setting. */
|
|
43
|
+
severity: 'error' | 'warning';
|
|
44
|
+
reason: string;
|
|
45
|
+
fix: string;
|
|
46
|
+
}
|
|
47
|
+
export interface BrushCheck {
|
|
48
|
+
/** No errors (warnings may remain). */
|
|
49
|
+
ok: boolean;
|
|
50
|
+
issues: BrushIssue[];
|
|
51
|
+
}
|
|
52
|
+
/** The paint editor: every facet. */
|
|
53
|
+
export declare const PAINT_PROFILE: BrushHostProfile;
|
|
54
|
+
/** The image editor's brush: dry dabs on pixels, its Mixer Brush and the eraser. */
|
|
55
|
+
export declare const IMAGE_PROFILE: BrushHostProfile;
|
|
56
|
+
/**
|
|
57
|
+
* Brushed canvas strokes: dry dab brushes along a path. Light paint needs the paint editor's
|
|
58
|
+
* light channel (glow and spill), which canvas strokes don't have, so it is refused.
|
|
59
|
+
*/
|
|
60
|
+
export declare const CANVAS_PROFILE: BrushHostProfile;
|
|
61
|
+
/** Vector brushstrokes: dry dab brushes along a path; light brushes paint as plain colour. */
|
|
62
|
+
export declare const VECTOR_PROFILE: BrushHostProfile;
|
|
63
|
+
/** The built-in profiles by id. */
|
|
64
|
+
export declare const BRUSH_HOST_PROFILES: Readonly<Record<string, BrushHostProfile>>;
|
|
65
|
+
/** What kind of colour a `BrushPaint` is. */
|
|
66
|
+
export declare function brushColourKind(paint: BrushPaint): BrushColourKind;
|
|
67
|
+
/** What a brush needs from a host, and what the host does about it. */
|
|
68
|
+
export declare function checkBrush(brush: Brush, profile: BrushHostProfile): BrushCheck;
|
|
69
|
+
/** The presets (default: the built-in ones) a host can render, in library order. */
|
|
70
|
+
export declare function listPresets(profile: BrushHostProfile, presets?: readonly Brush[]): Brush[];
|
|
71
|
+
export interface DegradedBrush {
|
|
72
|
+
/** The brush with what the host can't render stripped (a copy). */
|
|
73
|
+
brush: Brush;
|
|
74
|
+
/** The errors fixed by stripping a facet. */
|
|
75
|
+
dropped: BrushIssue[];
|
|
76
|
+
/** Errors that stripping cannot fix (an unsupported kind or tool): the host still refuses it. */
|
|
77
|
+
issues: BrushIssue[];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The brush the host can render: facets it refuses are removed (light, motion, a reservoir, a
|
|
81
|
+
* wet or thick surface, particle or object marks, bristle and stamp tips become round, an
|
|
82
|
+
* unsupported default colour), each reported in `dropped`. A tool the host lacks can't be
|
|
83
|
+
* stripped and stays in `issues`.
|
|
84
|
+
*/
|
|
85
|
+
export declare function degrade(brush: Brush, profile: BrushHostProfile): DegradedBrush;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host capability profiles: what a host (the paint editor, the image editor, brushed canvas and
|
|
3
|
+
* vector strokes, later Swift) can render. `checkBrush` explains each refusal with a fix,
|
|
4
|
+
* `listPresets` filters a library down to what the host can use, and `degrade` strips the facets
|
|
5
|
+
* a host can't render and says what it dropped.
|
|
6
|
+
*
|
|
7
|
+
* A capability is `full` (rendered), `ignored` (the brush is accepted but the host paints without
|
|
8
|
+
* it: a warning) or `none` (the brush is refused: an error).
|
|
9
|
+
*/
|
|
10
|
+
import { type Brush, type BrushKind, type BrushMarksKind, type BrushPaint, type BrushSurface, type BrushTipShape, type BrushTool } from './types.js';
|
|
11
|
+
export type BrushSupport = 'full' | 'ignored' | 'none';
|
|
12
|
+
/** What a colour is: a solid colour, a light / dark pair, a gradient or effect paint. */
|
|
13
|
+
export type BrushColourKind = 'solid' | 'dual' | 'gradient' | 'effect';
|
|
14
|
+
export interface BrushHostProfile {
|
|
15
|
+
/** `paint`, `image`, `canvas`, `vector`… */
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
kinds: readonly BrushKind[];
|
|
19
|
+
/** Tools the host has (for `kind: 'tool'`). */
|
|
20
|
+
tools: readonly BrushTool[];
|
|
21
|
+
tipShapes: readonly BrushTipShape[];
|
|
22
|
+
marks: readonly BrushMarksKind[];
|
|
23
|
+
surfaces: readonly BrushSurface[];
|
|
24
|
+
/** Picking paint up and mixing it in (`behaviour.exchange` pickup, mix, dirty). */
|
|
25
|
+
exchange: BrushSupport;
|
|
26
|
+
/** The Mixer Brush (`behaviour.exchange.reservoir`). */
|
|
27
|
+
reservoir: BrushSupport;
|
|
28
|
+
/** Paper grain response (`behaviour.grain`). */
|
|
29
|
+
grain: BrushSupport;
|
|
30
|
+
light: BrushSupport;
|
|
31
|
+
motion: BrushSupport;
|
|
32
|
+
/** Colour kinds the brush's default colour may be (palette entries of other kinds are warned about). */
|
|
33
|
+
colours: readonly BrushColourKind[];
|
|
34
|
+
/** Velocity dynamics (needs timed input). */
|
|
35
|
+
velocity: BrushSupport;
|
|
36
|
+
/** Tilt dynamics (needs pen tilt). */
|
|
37
|
+
tilt: BrushSupport;
|
|
38
|
+
}
|
|
39
|
+
export interface BrushIssue {
|
|
40
|
+
/** The facet path the issue is about, e.g. `tip.shape`, `light`, `behaviour.exchange.reservoir`. */
|
|
41
|
+
facet: string;
|
|
42
|
+
/** `error`: the host can't render the brush; `warning`: it renders it without this setting. */
|
|
43
|
+
severity: 'error' | 'warning';
|
|
44
|
+
reason: string;
|
|
45
|
+
fix: string;
|
|
46
|
+
}
|
|
47
|
+
export interface BrushCheck {
|
|
48
|
+
/** No errors (warnings may remain). */
|
|
49
|
+
ok: boolean;
|
|
50
|
+
issues: BrushIssue[];
|
|
51
|
+
}
|
|
52
|
+
/** The paint editor: every facet. */
|
|
53
|
+
export declare const PAINT_PROFILE: BrushHostProfile;
|
|
54
|
+
/** The image editor's brush: dry dabs on pixels, its Mixer Brush and the eraser. */
|
|
55
|
+
export declare const IMAGE_PROFILE: BrushHostProfile;
|
|
56
|
+
/**
|
|
57
|
+
* Brushed canvas strokes: dry dab brushes along a path. Light paint needs the paint editor's
|
|
58
|
+
* light channel (glow and spill), which canvas strokes don't have, so it is refused.
|
|
59
|
+
*/
|
|
60
|
+
export declare const CANVAS_PROFILE: BrushHostProfile;
|
|
61
|
+
/** Vector brushstrokes: dry dab brushes along a path; light brushes paint as plain colour. */
|
|
62
|
+
export declare const VECTOR_PROFILE: BrushHostProfile;
|
|
63
|
+
/** The built-in profiles by id. */
|
|
64
|
+
export declare const BRUSH_HOST_PROFILES: Readonly<Record<string, BrushHostProfile>>;
|
|
65
|
+
/** What kind of colour a `BrushPaint` is. */
|
|
66
|
+
export declare function brushColourKind(paint: BrushPaint): BrushColourKind;
|
|
67
|
+
/** What a brush needs from a host, and what the host does about it. */
|
|
68
|
+
export declare function checkBrush(brush: Brush, profile: BrushHostProfile): BrushCheck;
|
|
69
|
+
/** The presets (default: the built-in ones) a host can render, in library order. */
|
|
70
|
+
export declare function listPresets(profile: BrushHostProfile, presets?: readonly Brush[]): Brush[];
|
|
71
|
+
export interface DegradedBrush {
|
|
72
|
+
/** The brush with what the host can't render stripped (a copy). */
|
|
73
|
+
brush: Brush;
|
|
74
|
+
/** The errors fixed by stripping a facet. */
|
|
75
|
+
dropped: BrushIssue[];
|
|
76
|
+
/** Errors that stripping cannot fix (an unsupported kind or tool): the host still refuses it. */
|
|
77
|
+
issues: BrushIssue[];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The brush the host can render: facets it refuses are removed (light, motion, a reservoir, a
|
|
81
|
+
* wet or thick surface, particle or object marks, bristle and stamp tips become round, an
|
|
82
|
+
* unsupported default colour), each reported in `dropped`. A tool the host lacks can't be
|
|
83
|
+
* stripped and stays in `issues`.
|
|
84
|
+
*/
|
|
85
|
+
export declare function degrade(brush: Brush, profile: BrushHostProfile): DegradedBrush;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tools for a brush library (`createBrushLibraryToolProvider`): list, describe, create,
|
|
3
|
+
* update, duplicate, remove, export, import and check brushes, and preview one when the host
|
|
4
|
+
* injects a renderer. Writes are checked against the host's profile before anything is stored,
|
|
5
|
+
* and refusals name what to do instead. The provider imports no renderer: `preview_brush` is
|
|
6
|
+
* the host's.
|
|
7
|
+
*/
|
|
8
|
+
import type { AgentTool, AgentToolProvider } from '../tools/registry.cjs';
|
|
9
|
+
import { type BrushLibrary } from './library.cjs';
|
|
10
|
+
import { type BrushHostProfile } from './profiles.cjs';
|
|
11
|
+
import { type Brush } from './types.cjs';
|
|
12
|
+
/** A preview image of a brush: base64 data and its type. */
|
|
13
|
+
export interface BrushPreviewImage {
|
|
14
|
+
data: string;
|
|
15
|
+
mimeType: string;
|
|
16
|
+
}
|
|
17
|
+
/** Draws a sample stroke with a brush (the host's renderer: the paint engine, a canvas…). */
|
|
18
|
+
export type BrushPreviewRenderer = (brush: Brush, options: {
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
color: string;
|
|
22
|
+
}) => BrushPreviewImage | Promise<BrushPreviewImage>;
|
|
23
|
+
export interface BrushLibraryToolProviderOptions {
|
|
24
|
+
/** What this host can render (default `PAINT_PROFILE`): lists are filtered by it and writes checked against it. */
|
|
25
|
+
profile?: BrushHostProfile;
|
|
26
|
+
/** With it, `preview_brush` draws a sample stroke. */
|
|
27
|
+
renderPreview?: BrushPreviewRenderer;
|
|
28
|
+
/** Only the read tools (list, describe, export, check, preview). */
|
|
29
|
+
readOnly?: boolean;
|
|
30
|
+
/** Provider id (default `brushes`), also the tool namespace unless `namespace` is set. */
|
|
31
|
+
id?: string;
|
|
32
|
+
namespace?: string;
|
|
33
|
+
}
|
|
34
|
+
/** The brush library tools for one host (see the module comment). */
|
|
35
|
+
export declare function createBrushLibraryTools(library: BrushLibrary, options?: BrushLibraryToolProviderOptions): AgentTool[];
|
|
36
|
+
/** The brush library tools as a provider (id `brushes` by default; namespaced, so they don't meet the paint editor's brush tools). */
|
|
37
|
+
export declare function createBrushLibraryToolProvider(library: BrushLibrary, options?: BrushLibraryToolProviderOptions): AgentToolProvider;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tools for a brush library (`createBrushLibraryToolProvider`): list, describe, create,
|
|
3
|
+
* update, duplicate, remove, export, import and check brushes, and preview one when the host
|
|
4
|
+
* injects a renderer. Writes are checked against the host's profile before anything is stored,
|
|
5
|
+
* and refusals name what to do instead. The provider imports no renderer: `preview_brush` is
|
|
6
|
+
* the host's.
|
|
7
|
+
*/
|
|
8
|
+
import type { AgentTool, AgentToolProvider } from '../tools/registry.js';
|
|
9
|
+
import { type BrushLibrary } from './library.js';
|
|
10
|
+
import { type BrushHostProfile } from './profiles.js';
|
|
11
|
+
import { type Brush } from './types.js';
|
|
12
|
+
/** A preview image of a brush: base64 data and its type. */
|
|
13
|
+
export interface BrushPreviewImage {
|
|
14
|
+
data: string;
|
|
15
|
+
mimeType: string;
|
|
16
|
+
}
|
|
17
|
+
/** Draws a sample stroke with a brush (the host's renderer: the paint engine, a canvas…). */
|
|
18
|
+
export type BrushPreviewRenderer = (brush: Brush, options: {
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
color: string;
|
|
22
|
+
}) => BrushPreviewImage | Promise<BrushPreviewImage>;
|
|
23
|
+
export interface BrushLibraryToolProviderOptions {
|
|
24
|
+
/** What this host can render (default `PAINT_PROFILE`): lists are filtered by it and writes checked against it. */
|
|
25
|
+
profile?: BrushHostProfile;
|
|
26
|
+
/** With it, `preview_brush` draws a sample stroke. */
|
|
27
|
+
renderPreview?: BrushPreviewRenderer;
|
|
28
|
+
/** Only the read tools (list, describe, export, check, preview). */
|
|
29
|
+
readOnly?: boolean;
|
|
30
|
+
/** Provider id (default `brushes`), also the tool namespace unless `namespace` is set. */
|
|
31
|
+
id?: string;
|
|
32
|
+
namespace?: string;
|
|
33
|
+
}
|
|
34
|
+
/** The brush library tools for one host (see the module comment). */
|
|
35
|
+
export declare function createBrushLibraryTools(library: BrushLibrary, options?: BrushLibraryToolProviderOptions): AgentTool[];
|
|
36
|
+
/** The brush library tools as a provider (id `brushes` by default; namespaced, so they don't meet the paint editor's brush tools). */
|
|
37
|
+
export declare function createBrushLibraryToolProvider(library: BrushLibrary, options?: BrushLibraryToolProviderOptions): AgentToolProvider;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The brush editor as data, for the React and Svelte `BrushEditor` and `BrushLibraryBrowser`:
|
|
3
|
+
* `brushEditorSections` lists a brush's facets as ordered sections of controls (Medium, Shape,
|
|
4
|
+
* Marks, Feel, Behaviour, Light, Motion, Colour), each control a facet path with its value,
|
|
5
|
+
* range and label, leaving out what the host profile can't render. `setBrushEditorValue` applies
|
|
6
|
+
* one control's change; `brushLibraryGroups` groups a library by medium and variety, tools apart.
|
|
7
|
+
*
|
|
8
|
+
* Sliders reuse the paint editor's tables (`BRUSH_SLIDERS`, `brushEngineControls`), mapped from
|
|
9
|
+
* engine paths to facet paths through `BRUSH_FIELDS`, so the panels and the editor agree.
|
|
10
|
+
*/
|
|
11
|
+
import type { BrushLibraryEntry } from './library.cjs';
|
|
12
|
+
import { type BrushHostProfile } from './profiles.cjs';
|
|
13
|
+
import { type Brush } from './types.cjs';
|
|
14
|
+
export type BrushEditorSectionId = 'medium' | 'shape' | 'marks' | 'feel' | 'behaviour' | 'light' | 'motion' | 'colour';
|
|
15
|
+
interface ControlBase {
|
|
16
|
+
/** Facet path the control sets (`tip.size`, `behaviour.exchange.pickup`…). */
|
|
17
|
+
path: string;
|
|
18
|
+
label: string;
|
|
19
|
+
/** Accessible name (stable across hosts, for tests and assistive tech). */
|
|
20
|
+
ariaLabel: string;
|
|
21
|
+
/** Set when the host paints without this setting (its profile ignores it). */
|
|
22
|
+
note?: string;
|
|
23
|
+
}
|
|
24
|
+
export type BrushEditorControl = (ControlBase & {
|
|
25
|
+
kind: 'slider';
|
|
26
|
+
min: number;
|
|
27
|
+
max: number;
|
|
28
|
+
step: number;
|
|
29
|
+
value: number;
|
|
30
|
+
}) | (ControlBase & {
|
|
31
|
+
kind: 'choice';
|
|
32
|
+
options: ReadonlyArray<{
|
|
33
|
+
value: string;
|
|
34
|
+
label: string;
|
|
35
|
+
}>;
|
|
36
|
+
value: string;
|
|
37
|
+
}) | (ControlBase & {
|
|
38
|
+
kind: 'toggle';
|
|
39
|
+
value: boolean;
|
|
40
|
+
}) | (ControlBase & {
|
|
41
|
+
kind: 'text';
|
|
42
|
+
value: string;
|
|
43
|
+
placeholder?: string;
|
|
44
|
+
})
|
|
45
|
+
/** A solid colour (#rrggbb), or `null` when there is none (or the default is a gradient or effect). */
|
|
46
|
+
| (ControlBase & {
|
|
47
|
+
kind: 'colour';
|
|
48
|
+
value: string | null;
|
|
49
|
+
}) | (ControlBase & {
|
|
50
|
+
kind: 'palette';
|
|
51
|
+
value: string[];
|
|
52
|
+
max: number;
|
|
53
|
+
})
|
|
54
|
+
/** Stamp refs (`editor:star`…): `options` are the editor's stamps; hosts may offer more refs. */
|
|
55
|
+
| (ControlBase & {
|
|
56
|
+
kind: 'stamps';
|
|
57
|
+
value: string[];
|
|
58
|
+
max: number;
|
|
59
|
+
options: ReadonlyArray<{
|
|
60
|
+
value: string;
|
|
61
|
+
label: string;
|
|
62
|
+
}>;
|
|
63
|
+
});
|
|
64
|
+
export interface BrushEditorSection {
|
|
65
|
+
id: BrushEditorSectionId;
|
|
66
|
+
title: string;
|
|
67
|
+
controls: BrushEditorControl[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Sections of controls for a brush, in order, as the host `profile` (default the paint editor)
|
|
71
|
+
* can render them: sections and controls it refuses are left out, ones it paints without carry
|
|
72
|
+
* a note. Values are the brush's own, else the defaults of the engine it resolves to.
|
|
73
|
+
*/
|
|
74
|
+
export declare function brushEditorSections(brush: Brush, profile?: BrushHostProfile): BrushEditorSection[];
|
|
75
|
+
/**
|
|
76
|
+
* The brush with one control's value set (`undefined` removes it), normalised. Changing the kind,
|
|
77
|
+
* the marks or the tip shape starts that facet afresh. Returns the brush and what was clamped or
|
|
78
|
+
* dropped; an invalid change returns the brush unchanged with the reason.
|
|
79
|
+
*/
|
|
80
|
+
export declare function setBrushEditorValue(brush: Brush, path: string, value: unknown): {
|
|
81
|
+
brush: Brush;
|
|
82
|
+
problems: string[];
|
|
83
|
+
};
|
|
84
|
+
export interface BrushLibraryGroup {
|
|
85
|
+
/** `tools`, or a medium (`paint`, `ink`, `dry`, `spray`), or `other`. */
|
|
86
|
+
id: string;
|
|
87
|
+
title: string;
|
|
88
|
+
/** Brushes by variety (`''`: no variety), in library order. */
|
|
89
|
+
varieties: Array<{
|
|
90
|
+
variety: string;
|
|
91
|
+
title: string;
|
|
92
|
+
entries: BrushLibraryEntry[];
|
|
93
|
+
}>;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* A library grouped for a browser: marks by medium, then variety; tools as their own group.
|
|
97
|
+
* `text` keeps brushes whose id, name, variety or tags start with each word; `profile` keeps only
|
|
98
|
+
* what that host renders.
|
|
99
|
+
*/
|
|
100
|
+
export declare function brushLibraryGroups(entries: readonly BrushLibraryEntry[], filter?: {
|
|
101
|
+
text?: string;
|
|
102
|
+
profile?: BrushHostProfile;
|
|
103
|
+
source?: 'builtin' | 'user';
|
|
104
|
+
}): BrushLibraryGroup[];
|
|
105
|
+
export {};
|