@pixerate/editor 0.267.0 → 0.268.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/animation-editor/index.cjs +62 -62
  2. package/dist/animation-editor/index.js +6 -6
  3. package/dist/brushes/index.cjs +198 -72
  4. package/dist/brushes/index.d.cts +4 -3
  5. package/dist/brushes/index.d.ts +4 -3
  6. package/dist/brushes/index.js +136 -10
  7. package/dist/brushes/library.d.cts +27 -1
  8. package/dist/brushes/library.d.ts +27 -1
  9. package/dist/brushes/profiles.d.cts +4 -0
  10. package/dist/brushes/profiles.d.ts +4 -0
  11. package/dist/brushes/schema.d.cts +4 -0
  12. package/dist/brushes/schema.d.ts +4 -0
  13. package/dist/brushes/sources.d.cts +114 -0
  14. package/dist/brushes/sources.d.ts +114 -0
  15. package/dist/brushes/types.d.cts +37 -5
  16. package/dist/brushes/types.d.ts +37 -5
  17. package/dist/brushes/ui.d.cts +40 -4
  18. package/dist/brushes/ui.d.ts +40 -4
  19. package/dist/{brushstrokeRender-NLFPEBGI.js → brushstrokeRender-A727TTNQ.js} +6 -6
  20. package/dist/{brushstrokeRender-L7JYRR2R.cjs → brushstrokeRender-WR2N5HHY.cjs} +22 -22
  21. package/dist/canvas/index.cjs +13 -13
  22. package/dist/canvas/index.js +1 -1
  23. package/dist/{chunk-J2TNIZ55.cjs → chunk-2WOGXEDX.cjs} +1 -1
  24. package/dist/{chunk-JGWVZPT6.js → chunk-3XDSNIGF.js} +72 -5
  25. package/dist/{chunk-EXB2EGBE.cjs → chunk-62MFUHSI.cjs} +16 -16
  26. package/dist/{chunk-FGJHJIMN.js → chunk-74ODPZLX.js} +10 -10
  27. package/dist/{chunk-GCVFEPJG.js → chunk-ACTHSQOI.js} +182 -0
  28. package/dist/{chunk-LHPEG5AU.cjs → chunk-B3G7BKUV.cjs} +90 -90
  29. package/dist/{chunk-EF7LZAXQ.js → chunk-BAZMCFVL.js} +1 -1
  30. package/dist/{chunk-HIO6E7LC.cjs → chunk-BPJSI4NT.cjs} +2 -2
  31. package/dist/{chunk-XVOP3CGQ.cjs → chunk-EU2ZZA6L.cjs} +393 -62
  32. package/dist/{chunk-YIC6MAJ2.js → chunk-HFY5LG5J.js} +1 -1
  33. package/dist/{chunk-7UKFXXWL.js → chunk-HVO6XCI4.js} +1 -1
  34. package/dist/{chunk-OKHKA43L.cjs → chunk-JBB3GDBB.cjs} +330 -216
  35. package/dist/{chunk-AGNUD5BI.js → chunk-KZI3YWKW.js} +351 -20
  36. package/dist/{chunk-EYSHTYLG.js → chunk-LETF75CA.js} +1 -1
  37. package/dist/{chunk-FQPUF2W2.cjs → chunk-NIBFKXMD.cjs} +217 -35
  38. package/dist/{chunk-G7NHE2VD.cjs → chunk-NPWWSCWT.cjs} +35 -35
  39. package/dist/{chunk-OSEXT7JL.js → chunk-QNY7BDFP.js} +1 -1
  40. package/dist/{chunk-PXDIAYB6.cjs → chunk-STLTOISW.cjs} +34 -214
  41. package/dist/{chunk-4IDUYB45.js → chunk-UVTO3D4E.js} +130 -16
  42. package/dist/{chunk-TXFWZ5ZJ.js → chunk-W6JDUXP3.js} +4 -184
  43. package/dist/{chunk-LA5Y4ETA.cjs → chunk-YOVXQHA3.cjs} +23 -23
  44. package/dist/{chunk-DT4YQUGT.cjs → chunk-YXP6X3SY.cjs} +101 -34
  45. package/dist/image-editor/index.cjs +851 -851
  46. package/dist/image-editor/index.js +23 -23
  47. package/dist/paint/index.cjs +5 -5
  48. package/dist/paint/index.js +11 -11
  49. package/dist/paint-editor/brush-library-tools.d.cts +12 -2
  50. package/dist/paint-editor/brush-library-tools.d.ts +12 -2
  51. package/dist/paint-editor/brush-model.d.cts +29 -1
  52. package/dist/paint-editor/brush-model.d.ts +29 -1
  53. package/dist/paint-editor/brush-source-art.d.cts +32 -0
  54. package/dist/paint-editor/brush-source-art.d.ts +32 -0
  55. package/dist/paint-editor/controller.d.cts +6 -1
  56. package/dist/paint-editor/controller.d.ts +6 -1
  57. package/dist/paint-editor/dab-stroke.d.cts +2 -0
  58. package/dist/paint-editor/dab-stroke.d.ts +2 -0
  59. package/dist/paint-editor/engine.d.cts +11 -2
  60. package/dist/paint-editor/engine.d.ts +11 -2
  61. package/dist/paint-editor/index.cjs +576 -481
  62. package/dist/paint-editor/index.d.cts +3 -1
  63. package/dist/paint-editor/index.d.ts +3 -1
  64. package/dist/paint-editor/index.js +127 -32
  65. package/dist/paint-editor/stamp-tip.d.cts +8 -5
  66. package/dist/paint-editor/stamp-tip.d.ts +8 -5
  67. package/dist/paint-editor/thumbnails.d.cts +6 -0
  68. package/dist/paint-editor/thumbnails.d.ts +6 -0
  69. package/dist/paint-editor/tip-grain.d.cts +34 -0
  70. package/dist/paint-editor/tip-grain.d.ts +34 -0
  71. package/dist/raster/index.cjs +2 -2
  72. package/dist/raster/index.js +9 -9
  73. package/dist/vector/index.cjs +1002 -1002
  74. package/dist/vector/index.js +7 -7
  75. package/package.json +1 -1
  76. package/skills/pixerate-brushes/SKILL.md +35 -8
  77. package/skills/pixerate-brushes-design/SKILL.md +8 -1
  78. package/skills/pixerate-paint-editor/SKILL.md +6 -0
  79. package/skills/pixerate-paint-editor/references/brush-facets.md +3 -1
  80. package/skills/pixerate-paint-sketch-to-finish/SKILL.md +1 -0
@@ -16,12 +16,20 @@ var _chunkPFHJS4HEcjs = require('./chunk-PFHJS4HE.cjs');
16
16
 
17
17
 
18
18
 
19
- var _chunkXVOP3CGQcjs = require('./chunk-XVOP3CGQ.cjs');
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+ var _chunkEU2ZZA6Lcjs = require('./chunk-EU2ZZA6L.cjs');
20
28
 
21
29
  // src/brushes/patch.ts
22
30
  var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
23
31
  var copy = (v) => JSON.parse(JSON.stringify(v));
24
- var FACETS = _chunkXVOP3CGQcjs.BRUSH_JSON_SCHEMA.properties;
32
+ var FACETS = _chunkEU2ZZA6Lcjs.BRUSH_JSON_SCHEMA.properties;
25
33
  var BRUSH_PATCH_PROPERTIES = (() => {
26
34
  const { id: _id, name: _name, ...rest } = FACETS;
27
35
  const { required: _r, ...tip } = rest.tip;
@@ -53,17 +61,17 @@ function changedBrushPaths(a, b, path = "") {
53
61
  // src/brushes/tools.ts
54
62
  var READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
55
63
  var WRITE = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
56
- var FACETS2 = _chunkXVOP3CGQcjs.BRUSH_JSON_SCHEMA.properties;
64
+ var FACETS2 = _chunkEU2ZZA6Lcjs.BRUSH_JSON_SCHEMA.properties;
57
65
  var ID = { type: "string", pattern: String(FACETS2.id.pattern), description: "Brush id from list_brush_presets (lowercase letters, digits and dashes)." };
58
66
  var FACET_PROPS = BRUSH_PATCH_PROPERTIES;
59
- var HOSTS = Object.keys(_chunkXVOP3CGQcjs.BRUSH_HOST_PROFILES);
67
+ var HOSTS = Object.keys(_chunkEU2ZZA6Lcjs.BRUSH_HOST_PROFILES);
60
68
  var copy2 = (v) => JSON.parse(JSON.stringify(v));
61
69
  var merge = mergeBrushPatch;
62
70
  var changedPaths = changedBrushPaths;
63
71
  function summary(entry, profile, withCheck) {
64
72
  const b = entry.brush;
65
- const engine = _chunkXVOP3CGQcjs.resolveEngine.call(void 0, b);
66
- const check = withCheck ? _chunkXVOP3CGQcjs.checkBrush.call(void 0, b, profile) : null;
73
+ const engine = _chunkEU2ZZA6Lcjs.resolveEngine.call(void 0, b);
74
+ const check = withCheck ? _chunkEU2ZZA6Lcjs.checkBrush.call(void 0, b, profile) : null;
67
75
  return {
68
76
  id: b.id,
69
77
  name: b.name,
@@ -72,7 +80,7 @@ function summary(entry, profile, withCheck) {
72
80
  ...b.medium ? { medium: b.medium } : {},
73
81
  ...b.variety ? { variety: b.variety } : {},
74
82
  ...b.tool ? { tool: b.tool } : {},
75
- category: _chunkXVOP3CGQcjs.resolveCategory.call(void 0, b, engine),
83
+ category: _chunkEU2ZZA6Lcjs.resolveCategory.call(void 0, b, engine),
76
84
  engine,
77
85
  tip: b.tip.shape,
78
86
  ...b.tip.size !== void 0 ? { size: b.tip.size } : {},
@@ -86,26 +94,33 @@ function summary(entry, profile, withCheck) {
86
94
  var refusal = (check, host) => `${host.name} can't use this brush: ${check.issues.filter((i) => i.severity === "error").map((i) => `${i.facet}: ${i.reason} ${i.fix}`).join(" ")} (check_brush lists every issue; nothing was saved.)`;
87
95
  var warnings = (check) => check.issues.filter((i) => i.severity === "warning");
88
96
  function createBrushLibraryTools(library, options = {}) {
89
- const profile = _nullishCoalesce(options.profile, () => ( _chunkXVOP3CGQcjs.PAINT_PROFILE));
97
+ const profile = _nullishCoalesce(options.profile, () => ( _chunkEU2ZZA6Lcjs.PAINT_PROFILE));
90
98
  const hostOf = (id) => {
91
99
  if (id === void 0) return profile;
92
- const p = _chunkXVOP3CGQcjs.BRUSH_HOST_PROFILES[id];
93
- if (!p) throw new (0, _chunkXVOP3CGQcjs.BrushLibraryError)("invalid", `No host "${id}"; use one of: ${HOSTS.join(", ")}.`);
100
+ const p = _chunkEU2ZZA6Lcjs.BRUSH_HOST_PROFILES[id];
101
+ if (!p) throw new (0, _chunkEU2ZZA6Lcjs.BrushLibraryError)("invalid", `No host "${id}"; use one of: ${HOSTS.join(", ")}.`);
94
102
  return p;
95
103
  };
96
104
  const entry = async (id) => {
97
105
  const found = await library.get(id);
98
106
  if (found) return found;
99
107
  const ids = (await library.list()).map((e) => e.brush.id);
100
- throw new (0, _chunkXVOP3CGQcjs.BrushLibraryError)("unknown_brush", `No brush "${id}". ${_chunkXVOP3CGQcjs.closest.call(void 0, id, ids)}list_brush_presets lists them.`);
108
+ throw new (0, _chunkEU2ZZA6Lcjs.BrushLibraryError)("unknown_brush", `No brush "${id}". ${_chunkEU2ZZA6Lcjs.closest.call(void 0, id, ids)}list_brush_presets lists them.`);
101
109
  };
102
110
  const checkFor = async (brush, p) => {
103
- const check2 = _chunkXVOP3CGQcjs.checkBrush.call(void 0, brush, p);
104
- const provider = _optionalChain([options, 'access', _9 => _9.stickers, 'optionalCall', _10 => _10()]);
105
- if (!provider || brush.tip.shape !== "stamp") return check2;
111
+ const check2 = _chunkEU2ZZA6Lcjs.checkBrush.call(void 0, brush, p);
106
112
  const issues = [...check2.issues];
113
+ const refs = [...(_nullishCoalesce(brush.tip.stamps, () => ( []))).map((r) => ["tip.stamps", r]), ...brush.tip.grain ? [["tip.grain.source", brush.tip.grain.source]] : []];
114
+ for (const [facet, ref] of refs) {
115
+ const id = _chunkEU2ZZA6Lcjs.brushSourceIdOf.call(void 0, ref);
116
+ if (id === null || await library.getSource(id)) continue;
117
+ const ids = (await library.listSources()).map((x) => x.id);
118
+ issues.push({ facet, severity: "error", reason: `There is no image source "${id}" in the brush library. ${_chunkEU2ZZA6Lcjs.closest.call(void 0, id, ids)}`.trim(), fix: "Add it with add_brush_source, or use a ref list_brush_sources lists." });
119
+ }
120
+ const provider = _optionalChain([options, 'access', _9 => _9.stickers, 'optionalCall', _10 => _10()]);
121
+ if (!provider || brush.tip.shape !== "stamp") return { ok: !issues.some((i) => i.severity === "error"), issues };
107
122
  for (const ref of _nullishCoalesce(brush.tip.stamps, () => ( []))) {
108
- if (ref.startsWith("editor:")) continue;
123
+ if (ref.startsWith("editor:") || _chunkEU2ZZA6Lcjs.isBrushSourceRef.call(void 0, ref)) continue;
109
124
  try {
110
125
  await _chunkPFHJS4HEcjs.describeStickerRef.call(void 0, provider, ref);
111
126
  } catch (e) {
@@ -118,12 +133,12 @@ function createBrushLibraryTools(library, options = {}) {
118
133
  const problems = [];
119
134
  let brush;
120
135
  try {
121
- brush = _chunkXVOP3CGQcjs.normalizeBrushSpec.call(void 0, raw, problems);
136
+ brush = _chunkEU2ZZA6Lcjs.normalizeBrushSpec.call(void 0, raw, problems);
122
137
  } catch (e) {
123
- throw new (0, _chunkXVOP3CGQcjs.BrushLibraryError)("invalid", e.message);
138
+ throw new (0, _chunkEU2ZZA6Lcjs.BrushLibraryError)("invalid", e.message);
124
139
  }
125
- const check2 = _chunkXVOP3CGQcjs.withKeptIssues.call(void 0, await checkFor(brush, profile), original ? await checkFor(original, profile) : null);
126
- if (!check2.ok) throw new (0, _chunkXVOP3CGQcjs.BrushLibraryError)("invalid", refusal(check2, profile));
140
+ const check2 = _chunkEU2ZZA6Lcjs.withKeptIssues.call(void 0, await checkFor(brush, profile), original ? await checkFor(original, profile) : null);
141
+ if (!check2.ok) throw new (0, _chunkEU2ZZA6Lcjs.BrushLibraryError)("invalid", refusal(check2, profile));
127
142
  const saved = await library.save(brush, { overwrite });
128
143
  return { ...saved, problems, check: check2 };
129
144
  };
@@ -134,10 +149,10 @@ function createBrushLibraryTools(library, options = {}) {
134
149
  inputSchema: {
135
150
  type: "object",
136
151
  properties: {
137
- kind: { type: "string", enum: [..._chunkXVOP3CGQcjs.BRUSH_KINDS], description: "mark (lays a material) or tool (works on paint already down)." },
138
- medium: { type: "string", enum: [..._chunkXVOP3CGQcjs.BRUSH_MEDIA], description: "Marks laying this medium." },
152
+ kind: { type: "string", enum: [..._chunkEU2ZZA6Lcjs.BRUSH_KINDS], description: "mark (lays a material) or tool (works on paint already down)." },
153
+ medium: { type: "string", enum: [..._chunkEU2ZZA6Lcjs.BRUSH_MEDIA], description: "Marks laying this medium." },
139
154
  variety: { type: "string", maxLength: 40, description: "This variety (oil, watercolor, pencil, highlighter\u2026)." },
140
- tool: { type: "string", enum: [..._chunkXVOP3CGQcjs.BRUSH_TOOLS], description: "Tools that do this." },
155
+ tool: { type: "string", enum: [..._chunkEU2ZZA6Lcjs.BRUSH_TOOLS], description: "Tools that do this." },
141
156
  tag: { type: "string", maxLength: 40, description: "Brushes with this tag." },
142
157
  text: { type: "string", maxLength: 200, description: "Words in the id, name, description, variety or tags (each word must match the start of one)." },
143
158
  source: { type: "string", enum: ["all", "builtin", "user"], description: "all (default), builtin presets only, or user (saved) brushes only." },
@@ -164,8 +179,8 @@ function createBrushLibraryTools(library, options = {}) {
164
179
  annotations: { title: "Describe a brush", ...READ },
165
180
  handler: async ({ id }) => {
166
181
  const e = await entry(id);
167
- const engine = _chunkXVOP3CGQcjs.resolveEngine.call(void 0, e.brush);
168
- return { brush: e.brush, builtin: e.builtin, engine, category: _chunkXVOP3CGQcjs.resolveCategory.call(void 0, e.brush, engine), check: await checkFor(e.brush, profile) };
182
+ const engine = _chunkEU2ZZA6Lcjs.resolveEngine.call(void 0, e.brush);
183
+ return { brush: e.brush, builtin: e.builtin, engine, category: _chunkEU2ZZA6Lcjs.resolveCategory.call(void 0, e.brush, engine), check: await checkFor(e.brush, profile) };
169
184
  }
170
185
  };
171
186
  const check = {
@@ -183,20 +198,20 @@ function createBrushLibraryTools(library, options = {}) {
183
198
  },
184
199
  annotations: { title: "Check a brush for a host", ...READ },
185
200
  handler: async ({ id, brush, host }) => {
186
- if (id === void 0 === (brush === void 0)) throw new (0, _chunkXVOP3CGQcjs.BrushLibraryError)("invalid", "Give either id (a library brush) or brush (a spec), not both or neither.");
201
+ if (id === void 0 === (brush === void 0)) throw new (0, _chunkEU2ZZA6Lcjs.BrushLibraryError)("invalid", "Give either id (a library brush) or brush (a spec), not both or neither.");
187
202
  const p = hostOf(host);
188
203
  let b;
189
204
  const problems = [];
190
205
  if (id !== void 0) b = (await entry(id)).brush;
191
206
  else {
192
207
  try {
193
- b = _chunkXVOP3CGQcjs.normalizeBrushSpec.call(void 0, brush, problems);
208
+ b = _chunkEU2ZZA6Lcjs.normalizeBrushSpec.call(void 0, brush, problems);
194
209
  } catch (e) {
195
- throw new (0, _chunkXVOP3CGQcjs.BrushLibraryError)("invalid", e.message);
210
+ throw new (0, _chunkEU2ZZA6Lcjs.BrushLibraryError)("invalid", e.message);
196
211
  }
197
212
  }
198
213
  const result = await checkFor(b, p);
199
- return { host: p.id, ...result, ...problems.length ? { problems } : {}, ...!result.ok ? { degraded: _chunkXVOP3CGQcjs.degrade.call(void 0, b, p) } : {} };
214
+ return { host: p.id, ...result, ...problems.length ? { problems } : {}, ...!result.ok ? { degraded: _chunkEU2ZZA6Lcjs.degrade.call(void 0, b, p) } : {} };
200
215
  }
201
216
  };
202
217
  const exportTool = {
@@ -207,7 +222,34 @@ function createBrushLibraryTools(library, options = {}) {
207
222
  annotations: { title: "Export brushes", ...READ },
208
223
  handler: async ({ ids }) => ({ file: await library.exportFile(ids) })
209
224
  };
210
- const tools = [list, describe, check, exportTool];
225
+ const listSources = {
226
+ name: "list_brush_sources",
227
+ title: "List brush image sources",
228
+ description: "Lists the image sources in the brush library: the images image tips and image grain are made from. Returns each source's id, ref (source:<id>, for tip.stamps of a stamp tip or tip.grain.source in create_brush / update_brush), name, width and height in px, licence and origin when it has them, and usedBy (the saved brushes that name it), plus total. Built-in grain patterns need no source. Changes nothing.",
229
+ inputSchema: {
230
+ type: "object",
231
+ properties: {
232
+ text: { type: "string", maxLength: 200, description: "Words in the id, name or origin (each word must match the start of one)." },
233
+ offset: { type: "integer", minimum: 0, description: "Skip this many (default 0)." },
234
+ limit: { type: "integer", minimum: 1, maximum: 200, description: "At most this many (default 100)." }
235
+ },
236
+ additionalProperties: false
237
+ },
238
+ annotations: { title: "List brush image sources", ...READ },
239
+ handler: async ({ text, offset, limit }) => {
240
+ const brushes = (await library.list({ source: "user" })).map((e) => e.brush);
241
+ const wordsOf = (v) => v.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean);
242
+ const want = typeof text === "string" ? wordsOf(text) : [];
243
+ const found = (await library.listSources()).filter((x) => {
244
+ const have = wordsOf([x.id, x.name, _nullishCoalesce(x.origin, () => ( ""))].join(" "));
245
+ return want.every((w) => have.some((h) => h.startsWith(w)));
246
+ });
247
+ const start = _nullishCoalesce(offset, () => ( 0));
248
+ const page = found.slice(start, start + (_nullishCoalesce(limit, () => ( 100))));
249
+ return { total: found.length, sources: page.map((x) => ({ ..._chunkEU2ZZA6Lcjs.summarizeBrushSource.call(void 0, x), usedBy: brushes.filter((b) => _chunkEU2ZZA6Lcjs.brushSourceIds.call(void 0, b).includes(x.id)).map((b) => b.id) })) };
250
+ }
251
+ };
252
+ const tools = [list, describe, check, exportTool, listSources];
211
253
  if (options.renderPreview) {
212
254
  const render = options.renderPreview;
213
255
  tools.push({
@@ -255,12 +297,12 @@ function createBrushLibraryTools(library, options = {}) {
255
297
  },
256
298
  annotations: { title: "Save a new brush", ...WRITE },
257
299
  handler: async ({ basedOn, ...spec }) => {
258
- if (library.isBuiltin(spec.id)) throw new (0, _chunkXVOP3CGQcjs.BrushLibraryError)("builtin", `"${spec.id}" is a built-in brush id; pick another id (or basedOn "${spec.id}" with a new id).`);
259
- if (await library.get(spec.id)) throw new (0, _chunkXVOP3CGQcjs.BrushLibraryError)("exists", `A brush "${spec.id}" exists already; pick another id, or change it with update_brush.`);
300
+ if (library.isBuiltin(spec.id)) throw new (0, _chunkEU2ZZA6Lcjs.BrushLibraryError)("builtin", `"${spec.id}" is a built-in brush id; pick another id (or basedOn "${spec.id}" with a new id).`);
301
+ if (await library.get(spec.id)) throw new (0, _chunkEU2ZZA6Lcjs.BrushLibraryError)("exists", `A brush "${spec.id}" exists already; pick another id, or change it with update_brush.`);
260
302
  const base = basedOn !== void 0 ? (await entry(basedOn)).brush : void 0;
261
303
  const raw = base ? merge(base, spec) : spec;
262
304
  const saved = await store(raw, false);
263
- return { id: saved.brush.id, brush: saved.brush, engine: _chunkXVOP3CGQcjs.resolveEngine.call(void 0, saved.brush), problems: saved.problems, warnings: warnings(saved.check) };
305
+ return { id: saved.brush.id, brush: saved.brush, engine: _chunkEU2ZZA6Lcjs.resolveEngine.call(void 0, saved.brush), problems: saved.problems, warnings: warnings(saved.check) };
264
306
  }
265
307
  },
266
308
  {
@@ -281,13 +323,38 @@ function createBrushLibraryTools(library, options = {}) {
281
323
  annotations: { title: "Update a brush", ...WRITE, idempotentHint: true },
282
324
  handler: async ({ id, unset, ...patch }) => {
283
325
  const e = await entry(id);
284
- if (e.builtin) throw new (0, _chunkXVOP3CGQcjs.BrushLibraryError)("builtin", `"${id}" is a built-in brush and can't be changed; duplicate_brush makes a copy you can change.`);
326
+ if (e.builtin) throw new (0, _chunkEU2ZZA6Lcjs.BrushLibraryError)("builtin", `"${id}" is a built-in brush and can't be changed; duplicate_brush makes a copy you can change.`);
285
327
  const base = copy2(e.brush);
286
328
  for (const k of _nullishCoalesce(unset, () => ( []))) delete base[k];
287
329
  const saved = await store(merge(base, { ...patch, id }), true, e.brush);
288
330
  return { id, brush: saved.brush, changed: changedPaths(e.brush, saved.brush), problems: saved.problems, warnings: warnings(saved.check), ...saved.check.kept.length ? { kept: saved.check.kept } : {} };
289
331
  }
290
332
  },
333
+ {
334
+ name: "add_brush_source",
335
+ title: "Add a brush image source",
336
+ description: `Adds an image to the brush library as a source for image tips and image grain (as Procreate's shape and grain sources). The image is a PNG data: URL; the marks are read from its transparency, its dark parts (a scan of marks on paper) or its light parts (white on black), as channel says, and kept as a grey mask at most ${_chunkEU2ZZA6Lcjs.MAX_BRUSH_SOURCE_SIZE} px on a side. Returns id, ref (source:<id>), the stored source's width and height, problems (the channel auto picked, scaling) and replaced. Then make the brush: create_brush or update_brush with tip { shape: "stamp", stamps: [ref] } for an image tip, or tip.grain { source: ref } for grain. A taken id is refused unless overwrite.`,
337
+ inputSchema: {
338
+ type: "object",
339
+ properties: {
340
+ name: { type: "string", minLength: 1, maxLength: 80, description: 'Display name, e.g. "Charcoal scan".' },
341
+ image: { type: "string", pattern: "^data:image/png;base64,", maxLength: 4e7, description: `The image: a PNG data: URL, at most ${_chunkEU2ZZA6Lcjs.MAX_BRUSH_SOURCE_INPUT_PIXELS} pixels; larger than ${_chunkEU2ZZA6Lcjs.MAX_BRUSH_SOURCE_SIZE} px it is scaled down.` },
342
+ id: { type: "string", pattern: String(FACETS2.id.pattern), description: "The source id (default: from the name, made unique): lowercase letters, digits and dashes." },
343
+ channel: { type: "string", enum: [..._chunkEU2ZZA6Lcjs.BRUSH_SOURCE_CHANNELS], description: "Where the marks are: auto (default: the transparency when the image has any, else the dark parts), alpha, darkness (dark marks on light paper) or lightness (light marks on black)." },
344
+ invert: { type: "boolean", description: "Swap painted and empty parts (default false)." },
345
+ licence: { type: "string", maxLength: 2e3, description: "The licence or terms the image came with, kept with the source." },
346
+ origin: { type: "string", maxLength: 200, description: "Where it came from: a file name, a pack or a URL." },
347
+ overwrite: { type: "boolean", description: "Replace the image of a source with the same id (default false); brushes using it change with it." }
348
+ },
349
+ required: ["name", "image"],
350
+ additionalProperties: false
351
+ },
352
+ annotations: { title: "Add a brush image source", ...WRITE },
353
+ handler: async ({ overwrite, ...input }) => {
354
+ const r = await library.addSource(input, { overwrite: !!overwrite });
355
+ return { id: r.source.id, ref: _chunkEU2ZZA6Lcjs.brushSourceRef.call(void 0, r.source.id), width: r.source.width, height: r.source.height, problems: r.problems, replaced: r.replaced };
356
+ }
357
+ },
291
358
  {
292
359
  name: "duplicate_brush",
293
360
  title: "Duplicate a brush",
@@ -340,7 +407,7 @@ function createBrushLibraryTools(library, options = {}) {
340
407
  return tools;
341
408
  }
342
409
  function createBrushLibraryToolProvider(library, options = {}) {
343
- const profile = _nullishCoalesce(options.profile, () => ( _chunkXVOP3CGQcjs.PAINT_PROFILE));
410
+ const profile = _nullishCoalesce(options.profile, () => ( _chunkEU2ZZA6Lcjs.PAINT_PROFILE));
344
411
  const tools = createBrushLibraryTools(library, options);
345
412
  return {
346
413
  id: _nullishCoalesce(options.id, () => ( "brushes")),