@pixerate/editor 0.258.0 → 0.259.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.
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The brush module as language-neutral contracts (`@pixerate/conformance` `domains/brushes`), so
3
+ * another implementation (the Swift port) ships the same brushes: the brush JSON Schema, the
4
+ * built-in presets and the engine brushes they resolve to, the host profiles and what each host
5
+ * accepts, v1 migrations, the library file format, the library's agent tools and the brush editor
6
+ * sections. `scripts/export-contracts.mjs` writes them; a core test fails when the committed
7
+ * files differ from what this returns.
8
+ *
9
+ * Everything here is data from this module's own functions; nothing depends on the package
10
+ * version, so a release does not change the files.
11
+ */
12
+ import type { JsonSchema } from '../tools/schema.cjs';
13
+ /** Bumped when a contract file changes shape (not when its data changes). */
14
+ export declare const BRUSH_CONTRACT_FORMAT_VERSION = 1;
15
+ /** JSON Schema of a host profile (`profiles.json`). */
16
+ export declare const BRUSH_PROFILE_JSON_SCHEMA: JsonSchema;
17
+ /** JSON Schema of a brush library file (`pixerate-brushes` version 2). */
18
+ export declare const BRUSH_LIBRARY_FILE_JSON_SCHEMA: JsonSchema;
19
+ /**
20
+ * Every brush contract file, by its path under `domains/brushes/` (plain JSON values). The
21
+ * presets and their resolutions, checks and migrations are what an implementation must reproduce;
22
+ * the schemas and tools are what it must accept and offer.
23
+ */
24
+ export declare function brushContracts(): Record<string, unknown>;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The brush module as language-neutral contracts (`@pixerate/conformance` `domains/brushes`), so
3
+ * another implementation (the Swift port) ships the same brushes: the brush JSON Schema, the
4
+ * built-in presets and the engine brushes they resolve to, the host profiles and what each host
5
+ * accepts, v1 migrations, the library file format, the library's agent tools and the brush editor
6
+ * sections. `scripts/export-contracts.mjs` writes them; a core test fails when the committed
7
+ * files differ from what this returns.
8
+ *
9
+ * Everything here is data from this module's own functions; nothing depends on the package
10
+ * version, so a release does not change the files.
11
+ */
12
+ import type { JsonSchema } from '../tools/schema.js';
13
+ /** Bumped when a contract file changes shape (not when its data changes). */
14
+ export declare const BRUSH_CONTRACT_FORMAT_VERSION = 1;
15
+ /** JSON Schema of a host profile (`profiles.json`). */
16
+ export declare const BRUSH_PROFILE_JSON_SCHEMA: JsonSchema;
17
+ /** JSON Schema of a brush library file (`pixerate-brushes` version 2). */
18
+ export declare const BRUSH_LIBRARY_FILE_JSON_SCHEMA: JsonSchema;
19
+ /**
20
+ * Every brush contract file, by its path under `domains/brushes/` (plain JSON values). The
21
+ * presets and their resolutions, checks and migrations are what an implementation must reproduce;
22
+ * the schemas and tools are what it must accept and offer.
23
+ */
24
+ export declare function brushContracts(): Record<string, unknown>;
@@ -55,6 +55,9 @@ require('../chunk-A2IS7EHE.cjs');
55
55
 
56
56
 
57
57
 
58
+
59
+
60
+
58
61
 
59
62
 
60
63
 
@@ -280,6 +283,118 @@ function brushLibraryGroups(entries, filter = {}) {
280
283
  return [...groups.values()].sort((a, b) => order.indexOf(a.id) - order.indexOf(b.id));
281
284
  }
282
285
 
286
+ // src/brushes/contract.ts
287
+ var BRUSH_CONTRACT_FORMAT_VERSION = 1;
288
+ var SUPPORT = { type: "string", enum: ["full", "ignored", "none"], description: "full: rendered; ignored: accepted, painted without it (a warning); none: refused (an error)." };
289
+ var BRUSH_PROFILE_JSON_SCHEMA = {
290
+ type: "object",
291
+ description: "What a host can render (`BrushHostProfile`).",
292
+ properties: {
293
+ id: { type: "string", description: "paint, image, canvas, vector\u2026" },
294
+ name: { type: "string", description: "Display name, used in messages." },
295
+ kinds: { type: "array", items: { type: "string", enum: [..._chunkRSIKDWOPcjs.BRUSH_KINDS] }, description: "Brush kinds it has." },
296
+ tools: { type: "array", items: { type: "string", enum: [..._chunkRSIKDWOPcjs.BRUSH_TOOLS] }, description: "Tools it has (kind tool)." },
297
+ tipShapes: { type: "array", items: { type: "string", enum: [..._chunkRSIKDWOPcjs.BRUSH_TIP_SHAPES] }, description: "Tip shapes it renders." },
298
+ marks: { type: "array", items: { type: "string", enum: [..._chunkRSIKDWOPcjs.BRUSH_MARKS_KINDS] }, description: "Marks kinds it renders." },
299
+ surfaces: { type: "array", items: { type: "string", enum: [..._chunkRSIKDWOPcjs.BRUSH_SURFACES] }, description: "Surfaces it renders." },
300
+ exchange: { ...SUPPORT, description: "Picking paint up and mixing it in." },
301
+ reservoir: { ...SUPPORT, description: "The Mixer Brush reservoir." },
302
+ grain: { ...SUPPORT, description: "Paper grain response." },
303
+ light: { ...SUPPORT, description: "Light paint." },
304
+ motion: { ...SUPPORT, description: "Motion after painting." },
305
+ colours: { type: "array", items: { type: "string", enum: ["solid", "dual", "gradient", "effect"] }, description: "Colour kinds a default colour may be." },
306
+ velocity: { ...SUPPORT, description: "Velocity dynamics." },
307
+ tilt: { ...SUPPORT, description: "Tilt dynamics." }
308
+ },
309
+ required: ["id", "name", "kinds", "tools", "tipShapes", "marks", "surfaces", "exchange", "reservoir", "grain", "light", "motion", "colours", "velocity", "tilt"],
310
+ additionalProperties: false
311
+ };
312
+ var BRUSH_LIBRARY_FILE_JSON_SCHEMA = {
313
+ type: "object",
314
+ description: "A brush library file, as export_brushes writes it. Readers also accept version 1 files (flat engine brushes, migrated) and a bare array of brushes.",
315
+ properties: {
316
+ format: { type: "string", const: "pixerate-brushes", description: 'Always "pixerate-brushes".' },
317
+ version: { type: "integer", const: 2, description: "Always 2." },
318
+ brushes: { type: "array", items: { $ref: "brush.schema.json" }, description: "The brushes, each a facet brush (brush.schema.json)." }
319
+ },
320
+ required: ["format", "version", "brushes"],
321
+ additionalProperties: false
322
+ };
323
+ function migrationInputs() {
324
+ const pick = (id) => _chunkRSIKDWOPcjs.brushToJSON.call(void 0, _chunkRSIKDWOPcjs.BUILTIN_BRUSHES.find((b) => b.id === id));
325
+ return [
326
+ { id: "plain", name: "Plain" },
327
+ pick("pencil-6b"),
328
+ { ...pick("soft-pastel"), id: "my-pastel", name: "My pastel", category: "custom", grain: 0.2, tip: { ...pick("soft-pastel").tip, size: 50 } },
329
+ pick("mixer-brush"),
330
+ pick("watercolor-wash"),
331
+ pick("impasto-brush"),
332
+ pick("hair-strands"),
333
+ pick("bristle-round"),
334
+ pick("sticker-stars"),
335
+ pick("rainbow-marker"),
336
+ pick("blender"),
337
+ pick("add-water"),
338
+ pick("spray-can"),
339
+ // Fields an engine never reads are dropped on migration.
340
+ { ...pick("ink-pen"), id: "ink-with-mixer", name: "Ink with mixer settings", mixer: { wet: 0.9, load: 0.1, mix: 0.9, loadEachStroke: false, cleanEachStroke: false, mixing: "pigment" } }
341
+ ];
342
+ }
343
+ function editorSectionFixtures() {
344
+ const ids = ["highlighter", "chalk", "watercolor-wash", "hair-strands", "mixer-brush", "blender", "spray-can"];
345
+ return ids.flatMap(
346
+ (id) => Object.keys(_chunkRSIKDWOPcjs.BRUSH_HOST_PROFILES).map((host) => {
347
+ const brush = _chunkRSIKDWOPcjs.BRUSH_PRESETS.find((b) => b.id === id);
348
+ return { brush: id, host, sections: brushEditorSections(brush, _chunkRSIKDWOPcjs.BRUSH_HOST_PROFILES[host]) };
349
+ })
350
+ );
351
+ }
352
+ function toolDefinitions() {
353
+ const library = _chunkRSIKDWOPcjs.createBrushLibrary.call(void 0, );
354
+ const toolsFor = (host) => _chunkMQGQTKVWcjs.createBrushLibraryTools.call(void 0, library, { profile: _chunkRSIKDWOPcjs.BRUSH_HOST_PROFILES[host], renderPreview: () => ({ data: "", mimeType: "image/png" }) });
355
+ return {
356
+ tools: toolsFor("paint").map((t) => ({ name: t.name, title: t.title, description: t.description, inputSchema: t.inputSchema, annotations: t.annotations })),
357
+ hosts: Object.fromEntries(Object.keys(_chunkRSIKDWOPcjs.BRUSH_HOST_PROFILES).map((host) => [host, toolsFor(host).map((t) => t.name)]))
358
+ };
359
+ }
360
+ function brushContracts() {
361
+ const presets = _chunkRSIKDWOPcjs.BRUSH_PRESETS.map((b) => JSON.parse(JSON.stringify(b)));
362
+ const resolved = _chunkRSIKDWOPcjs.BRUSH_PRESETS.map((b) => ({ id: b.id, engine: _chunkRSIKDWOPcjs.resolveEngine.call(void 0, b), category: _chunkRSIKDWOPcjs.resolveCategory.call(void 0, b), brush: _chunkRSIKDWOPcjs.brushToJSON.call(void 0, _chunkRSIKDWOPcjs.resolveBrush.call(void 0, b)) }));
363
+ const profiles = Object.values(_chunkRSIKDWOPcjs.BRUSH_HOST_PROFILES);
364
+ const checks = _chunkRSIKDWOPcjs.BRUSH_PRESETS.map((b) => ({
365
+ id: b.id,
366
+ hosts: Object.fromEntries(profiles.map((p) => {
367
+ const c = _chunkRSIKDWOPcjs.checkBrush.call(void 0, b, p);
368
+ return [p.id, { ok: c.ok, issues: c.issues.map(({ facet, severity }) => ({ facet, severity })) }];
369
+ }))
370
+ }));
371
+ const migrations = migrationInputs().map((input) => ({ input, output: _chunkRSIKDWOPcjs.migrateBrushV1.call(void 0, input), resolved: _chunkRSIKDWOPcjs.brushToJSON.call(void 0, _chunkRSIKDWOPcjs.resolveBrush.call(void 0, _chunkRSIKDWOPcjs.migrateBrushV1.call(void 0, input))), engine: _chunkRSIKDWOPcjs.normalizePaintBrush.call(void 0, input).engine }));
372
+ const files = {
373
+ "brush.schema.json": { $schema: "https://json-schema.org/draft/2020-12/schema", $id: "brush.schema.json", ..._chunkRSIKDWOPcjs.BRUSH_JSON_SCHEMA },
374
+ "profile.schema.json": { $schema: "https://json-schema.org/draft/2020-12/schema", $id: "profile.schema.json", ...BRUSH_PROFILE_JSON_SCHEMA },
375
+ "library-file.schema.json": { $schema: "https://json-schema.org/draft/2020-12/schema", $id: "library-file.schema.json", ...BRUSH_LIBRARY_FILE_JSON_SCHEMA },
376
+ "presets.json": presets,
377
+ "resolved.json": resolved,
378
+ "profiles.json": profiles,
379
+ "checks.json": checks,
380
+ "migrations.json": migrations,
381
+ "tools.json": toolDefinitions(),
382
+ "editor-sections.json": editorSectionFixtures()
383
+ };
384
+ files["manifest.json"] = {
385
+ domain: "brushes",
386
+ formatVersion: BRUSH_CONTRACT_FORMAT_VERSION,
387
+ limits: { maxPalette: _chunkRSIKDWOPcjs.MAX_BRUSH_PALETTE },
388
+ counts: { presets: presets.length, profiles: profiles.length, migrations: migrations.length, editorSections: files["editor-sections.json"].length },
389
+ files: Object.keys(files).sort()
390
+ };
391
+ return files;
392
+ }
393
+
394
+
395
+
396
+
397
+
283
398
 
284
399
 
285
400
 
@@ -322,4 +437,4 @@ function brushLibraryGroups(entries, filter = {}) {
322
437
 
323
438
 
324
439
 
325
- exports.BRUSH_FACET_SCHEMAS = _chunkRSIKDWOPcjs.BRUSH_FACET_SCHEMAS; exports.BRUSH_HOST_PROFILES = _chunkRSIKDWOPcjs.BRUSH_HOST_PROFILES; exports.BRUSH_JSON_SCHEMA = _chunkRSIKDWOPcjs.BRUSH_JSON_SCHEMA; exports.BRUSH_KINDS = _chunkRSIKDWOPcjs.BRUSH_KINDS; exports.BRUSH_MARKS_KINDS = _chunkRSIKDWOPcjs.BRUSH_MARKS_KINDS; exports.BRUSH_MEDIA = _chunkRSIKDWOPcjs.BRUSH_MEDIA; exports.BRUSH_PATCH_PROPERTIES = _chunkMQGQTKVWcjs.BRUSH_PATCH_PROPERTIES; exports.BRUSH_PRESETS = _chunkRSIKDWOPcjs.BRUSH_PRESETS; exports.BRUSH_SURFACES = _chunkRSIKDWOPcjs.BRUSH_SURFACES; exports.BRUSH_TIP_SHAPES = _chunkRSIKDWOPcjs.BRUSH_TIP_SHAPES; exports.BRUSH_TOOLS = _chunkRSIKDWOPcjs.BRUSH_TOOLS; exports.BRUSH_UNSET_PROPERTY = _chunkMQGQTKVWcjs.BRUSH_UNSET_PROPERTY; exports.BRUSH_WORD = _chunkRSIKDWOPcjs.BRUSH_WORD; exports.BrushLibraryError = _chunkRSIKDWOPcjs.BrushLibraryError; exports.CANVAS_PROFILE = _chunkRSIKDWOPcjs.CANVAS_PROFILE; exports.IMAGE_PROFILE = _chunkRSIKDWOPcjs.IMAGE_PROFILE; exports.MAX_BRUSH_PALETTE = _chunkRSIKDWOPcjs.MAX_BRUSH_PALETTE; exports.PAINT_PROFILE = _chunkRSIKDWOPcjs.PAINT_PROFILE; exports.UNSETTABLE_BRUSH_FACETS = _chunkMQGQTKVWcjs.UNSETTABLE_BRUSH_FACETS; exports.VECTOR_PROFILE = _chunkRSIKDWOPcjs.VECTOR_PROFILE; exports.WET_BRUSH_TOOLS = _chunkRSIKDWOPcjs.WET_BRUSH_TOOLS; exports.brushColourHex = _chunkLRTR4ASZcjs.brushColourHex; exports.brushColourKind = _chunkRSIKDWOPcjs.brushColourKind; exports.brushEditorSections = brushEditorSections; exports.brushLibraryGroups = brushLibraryGroups; exports.changedBrushPaths = _chunkMQGQTKVWcjs.changedBrushPaths; exports.checkBrush = _chunkRSIKDWOPcjs.checkBrush; exports.createBrushLibrary = _chunkRSIKDWOPcjs.createBrushLibrary; exports.createBrushLibraryToolProvider = _chunkMQGQTKVWcjs.createBrushLibraryToolProvider; exports.createBrushLibraryTools = _chunkMQGQTKVWcjs.createBrushLibraryTools; exports.degrade = _chunkRSIKDWOPcjs.degrade; exports.listPresets = _chunkRSIKDWOPcjs.listPresets; exports.mergeBrushPatch = _chunkMQGQTKVWcjs.mergeBrushPatch; exports.migrateBrushV1 = _chunkRSIKDWOPcjs.migrateBrushV1; exports.normalizeBrushSpec = _chunkRSIKDWOPcjs.normalizeBrushSpec; exports.parseBrushLibrary = _chunkRSIKDWOPcjs.parseBrushLibrary2; exports.resolveBrush = _chunkRSIKDWOPcjs.resolveBrush; exports.resolveCategory = _chunkRSIKDWOPcjs.resolveCategory; exports.resolveEngine = _chunkRSIKDWOPcjs.resolveEngine; exports.resolveWetMedium = _chunkRSIKDWOPcjs.resolveWetMedium; exports.setBrushEditorValue = setBrushEditorValue; exports.snapshotBrushLibrary = _chunkRSIKDWOPcjs.snapshotBrushLibrary;
440
+ exports.BRUSH_CONTRACT_FORMAT_VERSION = BRUSH_CONTRACT_FORMAT_VERSION; exports.BRUSH_FACET_SCHEMAS = _chunkRSIKDWOPcjs.BRUSH_FACET_SCHEMAS; exports.BRUSH_HOST_PROFILES = _chunkRSIKDWOPcjs.BRUSH_HOST_PROFILES; exports.BRUSH_JSON_SCHEMA = _chunkRSIKDWOPcjs.BRUSH_JSON_SCHEMA; exports.BRUSH_KINDS = _chunkRSIKDWOPcjs.BRUSH_KINDS; exports.BRUSH_LIBRARY_FILE_JSON_SCHEMA = BRUSH_LIBRARY_FILE_JSON_SCHEMA; exports.BRUSH_MARKS_KINDS = _chunkRSIKDWOPcjs.BRUSH_MARKS_KINDS; exports.BRUSH_MEDIA = _chunkRSIKDWOPcjs.BRUSH_MEDIA; exports.BRUSH_PATCH_PROPERTIES = _chunkMQGQTKVWcjs.BRUSH_PATCH_PROPERTIES; exports.BRUSH_PRESETS = _chunkRSIKDWOPcjs.BRUSH_PRESETS; exports.BRUSH_PROFILE_JSON_SCHEMA = BRUSH_PROFILE_JSON_SCHEMA; exports.BRUSH_SURFACES = _chunkRSIKDWOPcjs.BRUSH_SURFACES; exports.BRUSH_TIP_SHAPES = _chunkRSIKDWOPcjs.BRUSH_TIP_SHAPES; exports.BRUSH_TOOLS = _chunkRSIKDWOPcjs.BRUSH_TOOLS; exports.BRUSH_UNSET_PROPERTY = _chunkMQGQTKVWcjs.BRUSH_UNSET_PROPERTY; exports.BRUSH_WORD = _chunkRSIKDWOPcjs.BRUSH_WORD; exports.BrushLibraryError = _chunkRSIKDWOPcjs.BrushLibraryError; exports.CANVAS_PROFILE = _chunkRSIKDWOPcjs.CANVAS_PROFILE; exports.IMAGE_PROFILE = _chunkRSIKDWOPcjs.IMAGE_PROFILE; exports.MAX_BRUSH_PALETTE = _chunkRSIKDWOPcjs.MAX_BRUSH_PALETTE; exports.PAINT_PROFILE = _chunkRSIKDWOPcjs.PAINT_PROFILE; exports.UNSETTABLE_BRUSH_FACETS = _chunkMQGQTKVWcjs.UNSETTABLE_BRUSH_FACETS; exports.VECTOR_PROFILE = _chunkRSIKDWOPcjs.VECTOR_PROFILE; exports.WET_BRUSH_TOOLS = _chunkRSIKDWOPcjs.WET_BRUSH_TOOLS; exports.brushColourHex = _chunkLRTR4ASZcjs.brushColourHex; exports.brushColourKind = _chunkRSIKDWOPcjs.brushColourKind; exports.brushContracts = brushContracts; exports.brushEditorSections = brushEditorSections; exports.brushLibraryGroups = brushLibraryGroups; exports.changedBrushPaths = _chunkMQGQTKVWcjs.changedBrushPaths; exports.checkBrush = _chunkRSIKDWOPcjs.checkBrush; exports.createBrushLibrary = _chunkRSIKDWOPcjs.createBrushLibrary; exports.createBrushLibraryToolProvider = _chunkMQGQTKVWcjs.createBrushLibraryToolProvider; exports.createBrushLibraryTools = _chunkMQGQTKVWcjs.createBrushLibraryTools; exports.degrade = _chunkRSIKDWOPcjs.degrade; exports.listPresets = _chunkRSIKDWOPcjs.listPresets; exports.mergeBrushPatch = _chunkMQGQTKVWcjs.mergeBrushPatch; exports.migrateBrushV1 = _chunkRSIKDWOPcjs.migrateBrushV1; exports.normalizeBrushSpec = _chunkRSIKDWOPcjs.normalizeBrushSpec; exports.parseBrushLibrary = _chunkRSIKDWOPcjs.parseBrushLibrary2; exports.resolveBrush = _chunkRSIKDWOPcjs.resolveBrush; exports.resolveCategory = _chunkRSIKDWOPcjs.resolveCategory; exports.resolveEngine = _chunkRSIKDWOPcjs.resolveEngine; exports.resolveWetMedium = _chunkRSIKDWOPcjs.resolveWetMedium; exports.setBrushEditorValue = setBrushEditorValue; exports.snapshotBrushLibrary = _chunkRSIKDWOPcjs.snapshotBrushLibrary;
@@ -15,6 +15,7 @@ export { resolveBrush, resolveCategory, resolveEngine, resolveWetMedium, type Re
15
15
  export { migrateBrushV1 } from './migrate.cjs';
16
16
  export { BRUSH_PRESETS } from './presets.cjs';
17
17
  export { brushColourHex } from './colour.cjs';
18
+ export { BRUSH_CONTRACT_FORMAT_VERSION, BRUSH_LIBRARY_FILE_JSON_SCHEMA, BRUSH_PROFILE_JSON_SCHEMA, brushContracts } from './contract.cjs';
18
19
  export { BRUSH_HOST_PROFILES, brushColourKind, CANVAS_PROFILE, checkBrush, degrade, IMAGE_PROFILE, listPresets, PAINT_PROFILE, VECTOR_PROFILE, type BrushCheck, type BrushColourKind, type BrushHostProfile, type BrushIssue, type BrushSupport, type DegradedBrush } from './profiles.cjs';
19
20
  export { BrushLibraryError, createBrushLibrary, parseBrushLibrary, snapshotBrushLibrary, type BrushFilter, type BrushLibrary, type BrushLibraryEntry, type BrushLibraryErrorCode, type BrushLibraryFile, type BrushLibrarySnapshot, type BrushLibraryOptions, type ImportBrushesResult, type SaveBrushResult, } from './library.cjs';
20
21
  export { BRUSH_PATCH_PROPERTIES, BRUSH_UNSET_PROPERTY, changedBrushPaths, mergeBrushPatch, UNSETTABLE_BRUSH_FACETS, type BrushPatch, type UnsettableBrushFacet } from './patch.cjs';
@@ -15,6 +15,7 @@ export { resolveBrush, resolveCategory, resolveEngine, resolveWetMedium, type Re
15
15
  export { migrateBrushV1 } from './migrate.js';
16
16
  export { BRUSH_PRESETS } from './presets.js';
17
17
  export { brushColourHex } from './colour.js';
18
+ export { BRUSH_CONTRACT_FORMAT_VERSION, BRUSH_LIBRARY_FILE_JSON_SCHEMA, BRUSH_PROFILE_JSON_SCHEMA, brushContracts } from './contract.js';
18
19
  export { BRUSH_HOST_PROFILES, brushColourKind, CANVAS_PROFILE, checkBrush, degrade, IMAGE_PROFILE, listPresets, PAINT_PROFILE, VECTOR_PROFILE, type BrushCheck, type BrushColourKind, type BrushHostProfile, type BrushIssue, type BrushSupport, type DegradedBrush } from './profiles.js';
19
20
  export { BrushLibraryError, createBrushLibrary, parseBrushLibrary, snapshotBrushLibrary, type BrushFilter, type BrushLibrary, type BrushLibraryEntry, type BrushLibraryErrorCode, type BrushLibraryFile, type BrushLibrarySnapshot, type BrushLibraryOptions, type ImportBrushesResult, type SaveBrushResult, } from './library.js';
20
21
  export { BRUSH_PATCH_PROPERTIES, BRUSH_UNSET_PROPERTY, changedBrushPaths, mergeBrushPatch, UNSETTABLE_BRUSH_FACETS, type BrushPatch, type UnsettableBrushFacet } from './patch.js';
@@ -28,6 +28,7 @@ import {
28
28
  BRUSH_TIP_SHAPES,
29
29
  BRUSH_TOOLS,
30
30
  BRUSH_WORD,
31
+ BUILTIN_BRUSHES,
31
32
  BrushLibraryError,
32
33
  CANVAS_PROFILE,
33
34
  DOT_ROTATIONS,
@@ -45,6 +46,7 @@ import {
45
46
  WET_BRUSH_TOOLS,
46
47
  WET_MEDIA,
47
48
  brushColourKind,
49
+ brushToJSON,
48
50
  checkBrush,
49
51
  createBrushLibrary,
50
52
  degrade,
@@ -52,6 +54,7 @@ import {
52
54
  listPresets,
53
55
  migrateBrushV1,
54
56
  normalizeBrushSpec,
57
+ normalizePaintBrush,
55
58
  parseBrushLibrary2 as parseBrushLibrary,
56
59
  resolveBrush,
57
60
  resolveCategory,
@@ -279,15 +282,126 @@ function brushLibraryGroups(entries, filter = {}) {
279
282
  const order = ["dry", "ink", "paint", "spray", "other", "tools"];
280
283
  return [...groups.values()].sort((a, b) => order.indexOf(a.id) - order.indexOf(b.id));
281
284
  }
285
+
286
+ // src/brushes/contract.ts
287
+ var BRUSH_CONTRACT_FORMAT_VERSION = 1;
288
+ var SUPPORT = { type: "string", enum: ["full", "ignored", "none"], description: "full: rendered; ignored: accepted, painted without it (a warning); none: refused (an error)." };
289
+ var BRUSH_PROFILE_JSON_SCHEMA = {
290
+ type: "object",
291
+ description: "What a host can render (`BrushHostProfile`).",
292
+ properties: {
293
+ id: { type: "string", description: "paint, image, canvas, vector\u2026" },
294
+ name: { type: "string", description: "Display name, used in messages." },
295
+ kinds: { type: "array", items: { type: "string", enum: [...BRUSH_KINDS] }, description: "Brush kinds it has." },
296
+ tools: { type: "array", items: { type: "string", enum: [...BRUSH_TOOLS] }, description: "Tools it has (kind tool)." },
297
+ tipShapes: { type: "array", items: { type: "string", enum: [...BRUSH_TIP_SHAPES] }, description: "Tip shapes it renders." },
298
+ marks: { type: "array", items: { type: "string", enum: [...BRUSH_MARKS_KINDS] }, description: "Marks kinds it renders." },
299
+ surfaces: { type: "array", items: { type: "string", enum: [...BRUSH_SURFACES] }, description: "Surfaces it renders." },
300
+ exchange: { ...SUPPORT, description: "Picking paint up and mixing it in." },
301
+ reservoir: { ...SUPPORT, description: "The Mixer Brush reservoir." },
302
+ grain: { ...SUPPORT, description: "Paper grain response." },
303
+ light: { ...SUPPORT, description: "Light paint." },
304
+ motion: { ...SUPPORT, description: "Motion after painting." },
305
+ colours: { type: "array", items: { type: "string", enum: ["solid", "dual", "gradient", "effect"] }, description: "Colour kinds a default colour may be." },
306
+ velocity: { ...SUPPORT, description: "Velocity dynamics." },
307
+ tilt: { ...SUPPORT, description: "Tilt dynamics." }
308
+ },
309
+ required: ["id", "name", "kinds", "tools", "tipShapes", "marks", "surfaces", "exchange", "reservoir", "grain", "light", "motion", "colours", "velocity", "tilt"],
310
+ additionalProperties: false
311
+ };
312
+ var BRUSH_LIBRARY_FILE_JSON_SCHEMA = {
313
+ type: "object",
314
+ description: "A brush library file, as export_brushes writes it. Readers also accept version 1 files (flat engine brushes, migrated) and a bare array of brushes.",
315
+ properties: {
316
+ format: { type: "string", const: "pixerate-brushes", description: 'Always "pixerate-brushes".' },
317
+ version: { type: "integer", const: 2, description: "Always 2." },
318
+ brushes: { type: "array", items: { $ref: "brush.schema.json" }, description: "The brushes, each a facet brush (brush.schema.json)." }
319
+ },
320
+ required: ["format", "version", "brushes"],
321
+ additionalProperties: false
322
+ };
323
+ function migrationInputs() {
324
+ const pick = (id) => brushToJSON(BUILTIN_BRUSHES.find((b) => b.id === id));
325
+ return [
326
+ { id: "plain", name: "Plain" },
327
+ pick("pencil-6b"),
328
+ { ...pick("soft-pastel"), id: "my-pastel", name: "My pastel", category: "custom", grain: 0.2, tip: { ...pick("soft-pastel").tip, size: 50 } },
329
+ pick("mixer-brush"),
330
+ pick("watercolor-wash"),
331
+ pick("impasto-brush"),
332
+ pick("hair-strands"),
333
+ pick("bristle-round"),
334
+ pick("sticker-stars"),
335
+ pick("rainbow-marker"),
336
+ pick("blender"),
337
+ pick("add-water"),
338
+ pick("spray-can"),
339
+ // Fields an engine never reads are dropped on migration.
340
+ { ...pick("ink-pen"), id: "ink-with-mixer", name: "Ink with mixer settings", mixer: { wet: 0.9, load: 0.1, mix: 0.9, loadEachStroke: false, cleanEachStroke: false, mixing: "pigment" } }
341
+ ];
342
+ }
343
+ function editorSectionFixtures() {
344
+ const ids = ["highlighter", "chalk", "watercolor-wash", "hair-strands", "mixer-brush", "blender", "spray-can"];
345
+ return ids.flatMap(
346
+ (id) => Object.keys(BRUSH_HOST_PROFILES).map((host) => {
347
+ const brush = BRUSH_PRESETS.find((b) => b.id === id);
348
+ return { brush: id, host, sections: brushEditorSections(brush, BRUSH_HOST_PROFILES[host]) };
349
+ })
350
+ );
351
+ }
352
+ function toolDefinitions() {
353
+ const library = createBrushLibrary();
354
+ const toolsFor = (host) => createBrushLibraryTools(library, { profile: BRUSH_HOST_PROFILES[host], renderPreview: () => ({ data: "", mimeType: "image/png" }) });
355
+ return {
356
+ tools: toolsFor("paint").map((t) => ({ name: t.name, title: t.title, description: t.description, inputSchema: t.inputSchema, annotations: t.annotations })),
357
+ hosts: Object.fromEntries(Object.keys(BRUSH_HOST_PROFILES).map((host) => [host, toolsFor(host).map((t) => t.name)]))
358
+ };
359
+ }
360
+ function brushContracts() {
361
+ const presets = BRUSH_PRESETS.map((b) => JSON.parse(JSON.stringify(b)));
362
+ const resolved = BRUSH_PRESETS.map((b) => ({ id: b.id, engine: resolveEngine(b), category: resolveCategory(b), brush: brushToJSON(resolveBrush(b)) }));
363
+ const profiles = Object.values(BRUSH_HOST_PROFILES);
364
+ const checks = BRUSH_PRESETS.map((b) => ({
365
+ id: b.id,
366
+ hosts: Object.fromEntries(profiles.map((p) => {
367
+ const c = checkBrush(b, p);
368
+ return [p.id, { ok: c.ok, issues: c.issues.map(({ facet, severity }) => ({ facet, severity })) }];
369
+ }))
370
+ }));
371
+ const migrations = migrationInputs().map((input) => ({ input, output: migrateBrushV1(input), resolved: brushToJSON(resolveBrush(migrateBrushV1(input))), engine: normalizePaintBrush(input).engine }));
372
+ const files = {
373
+ "brush.schema.json": { $schema: "https://json-schema.org/draft/2020-12/schema", $id: "brush.schema.json", ...BRUSH_JSON_SCHEMA },
374
+ "profile.schema.json": { $schema: "https://json-schema.org/draft/2020-12/schema", $id: "profile.schema.json", ...BRUSH_PROFILE_JSON_SCHEMA },
375
+ "library-file.schema.json": { $schema: "https://json-schema.org/draft/2020-12/schema", $id: "library-file.schema.json", ...BRUSH_LIBRARY_FILE_JSON_SCHEMA },
376
+ "presets.json": presets,
377
+ "resolved.json": resolved,
378
+ "profiles.json": profiles,
379
+ "checks.json": checks,
380
+ "migrations.json": migrations,
381
+ "tools.json": toolDefinitions(),
382
+ "editor-sections.json": editorSectionFixtures()
383
+ };
384
+ files["manifest.json"] = {
385
+ domain: "brushes",
386
+ formatVersion: BRUSH_CONTRACT_FORMAT_VERSION,
387
+ limits: { maxPalette: MAX_BRUSH_PALETTE },
388
+ counts: { presets: presets.length, profiles: profiles.length, migrations: migrations.length, editorSections: files["editor-sections.json"].length },
389
+ files: Object.keys(files).sort()
390
+ };
391
+ return files;
392
+ }
282
393
  export {
394
+ BRUSH_CONTRACT_FORMAT_VERSION,
283
395
  BRUSH_FACET_SCHEMAS,
284
396
  BRUSH_HOST_PROFILES,
285
397
  BRUSH_JSON_SCHEMA,
286
398
  BRUSH_KINDS,
399
+ BRUSH_LIBRARY_FILE_JSON_SCHEMA,
287
400
  BRUSH_MARKS_KINDS,
288
401
  BRUSH_MEDIA,
289
402
  BRUSH_PATCH_PROPERTIES,
290
403
  BRUSH_PRESETS,
404
+ BRUSH_PROFILE_JSON_SCHEMA,
291
405
  BRUSH_SURFACES,
292
406
  BRUSH_TIP_SHAPES,
293
407
  BRUSH_TOOLS,
@@ -303,6 +417,7 @@ export {
303
417
  WET_BRUSH_TOOLS,
304
418
  brushColourHex,
305
419
  brushColourKind,
420
+ brushContracts,
306
421
  brushEditorSections,
307
422
  brushLibraryGroups,
308
423
  changedBrushPaths,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor",
3
- "version": "0.258.0",
3
+ "version": "0.259.0",
4
4
  "description": "UI-agnostic rich text and media editing core library",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",