@pixerate/editor 0.266.2 → 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 (149) hide show
  1. package/dist/agent/index.cjs +2 -2
  2. package/dist/agent/index.js +1 -1
  3. package/dist/animation-editor/controller.d.cts +8 -0
  4. package/dist/animation-editor/controller.d.ts +8 -0
  5. package/dist/animation-editor/effect-tools.d.cts +1 -1
  6. package/dist/animation-editor/effect-tools.d.ts +1 -1
  7. package/dist/animation-editor/index.cjs +175 -108
  8. package/dist/animation-editor/index.js +94 -27
  9. package/dist/brushes/index.cjs +201 -75
  10. package/dist/brushes/index.d.cts +4 -3
  11. package/dist/brushes/index.d.ts +4 -3
  12. package/dist/brushes/index.js +138 -12
  13. package/dist/brushes/library.d.cts +27 -1
  14. package/dist/brushes/library.d.ts +27 -1
  15. package/dist/brushes/profiles.d.cts +10 -1
  16. package/dist/brushes/profiles.d.ts +10 -1
  17. package/dist/brushes/schema.d.cts +4 -0
  18. package/dist/brushes/schema.d.ts +4 -0
  19. package/dist/brushes/sources.d.cts +114 -0
  20. package/dist/brushes/sources.d.ts +114 -0
  21. package/dist/brushes/types.d.cts +37 -5
  22. package/dist/brushes/types.d.ts +37 -5
  23. package/dist/brushes/ui.d.cts +40 -4
  24. package/dist/brushes/ui.d.ts +40 -4
  25. package/dist/{brushstrokeRender-KD453OBX.js → brushstrokeRender-A727TTNQ.js} +7 -7
  26. package/dist/{brushstrokeRender-FSUWXUZY.cjs → brushstrokeRender-WR2N5HHY.cjs} +23 -23
  27. package/dist/canvas/brushTypes.d.cts +12 -0
  28. package/dist/canvas/brushTypes.d.ts +12 -0
  29. package/dist/canvas/brushstroke.d.cts +1 -1
  30. package/dist/canvas/brushstroke.d.ts +1 -1
  31. package/dist/canvas/drawController.d.cts +19 -4
  32. package/dist/canvas/drawController.d.ts +19 -4
  33. package/dist/canvas/index.cjs +144 -105
  34. package/dist/canvas/index.js +46 -7
  35. package/dist/{chunk-TOID45I2.cjs → chunk-2WOGXEDX.cjs} +1 -1
  36. package/dist/{chunk-XORJ7UZO.cjs → chunk-3VHH6V67.cjs} +13 -12
  37. package/dist/{chunk-3C33YTNN.js → chunk-3XDSNIGF.js} +73 -6
  38. package/dist/chunk-62MFUHSI.cjs +802 -0
  39. package/dist/{chunk-R44Y3EFS.js → chunk-74ODPZLX.js} +17 -345
  40. package/dist/{chunk-GCVFEPJG.js → chunk-ACTHSQOI.js} +182 -0
  41. package/dist/chunk-ADMRNWR7.js +340 -0
  42. package/dist/{chunk-MVUQSK5J.cjs → chunk-B3G7BKUV.cjs} +111 -439
  43. package/dist/{chunk-DXYHQ2VN.js → chunk-BAZMCFVL.js} +1 -1
  44. package/dist/{chunk-VRK322ZC.cjs → chunk-BPJSI4NT.cjs} +2 -2
  45. package/dist/{chunk-RFJZGTHA.cjs → chunk-EU2ZZA6L.cjs} +400 -69
  46. package/dist/{chunk-XC6DPRYI.js → chunk-GSTSZPSR.js} +154 -949
  47. package/dist/{chunk-EZOJBP4D.js → chunk-HFY5LG5J.js} +1 -1
  48. package/dist/chunk-HVO6XCI4.js +802 -0
  49. package/dist/{chunk-YHDHFJUC.cjs → chunk-J5VQYK7T.cjs} +217 -1012
  50. package/dist/{chunk-XRXFVRGY.cjs → chunk-JBB3GDBB.cjs} +2483 -208
  51. package/dist/{chunk-ACFSFUQH.js → chunk-JL6YH5WD.js} +4 -0
  52. package/dist/{chunk-CIJBSUM5.js → chunk-KZI3YWKW.js} +353 -22
  53. package/dist/{chunk-4ODRXQRW.js → chunk-LETF75CA.js} +1 -1
  54. package/dist/{chunk-FQPUF2W2.cjs → chunk-NIBFKXMD.cjs} +217 -35
  55. package/dist/{chunk-AQZQWXEU.cjs → chunk-NPWWSCWT.cjs} +35 -35
  56. package/dist/{chunk-DOZ6F2K2.js → chunk-NSETJI74.js} +3 -2
  57. package/dist/{chunk-PE6YJMQT.cjs → chunk-PFHJS4HE.cjs} +20 -19
  58. package/dist/{chunk-7KNW3WXZ.cjs → chunk-QEVLRLUN.cjs} +63 -0
  59. package/dist/{chunk-LK4SXL3V.js → chunk-QNY7BDFP.js} +1 -1
  60. package/dist/{chunk-OTN6VB5S.cjs → chunk-RDFHWAHL.cjs} +5 -1
  61. package/dist/{chunk-PXDIAYB6.cjs → chunk-STLTOISW.cjs} +34 -214
  62. package/dist/{chunk-T2LDTEXK.js → chunk-UVTO3D4E.js} +2333 -58
  63. package/dist/{chunk-6JL2D5SG.js → chunk-VE6UKYQ2.js} +3 -2
  64. package/dist/{chunk-TXFWZ5ZJ.js → chunk-W6JDUXP3.js} +4 -184
  65. package/dist/{chunk-3SBLVGOM.cjs → chunk-YOVXQHA3.cjs} +23 -23
  66. package/dist/{chunk-WLUVZKVD.cjs → chunk-YXP6X3SY.cjs} +103 -36
  67. package/dist/chunk-YZWM6ZIP.cjs +340 -0
  68. package/dist/{chunk-472INMWH.js → chunk-ZA7L5YJB.js} +63 -0
  69. package/dist/commands/builtins.d.cts +12 -1
  70. package/dist/commands/builtins.d.ts +12 -1
  71. package/dist/commands/index.cjs +2 -2
  72. package/dist/commands/index.js +1 -1
  73. package/dist/image-editor/controller.d.cts +1 -1
  74. package/dist/image-editor/controller.d.ts +1 -1
  75. package/dist/image-editor/index.cjs +868 -864
  76. package/dist/image-editor/index.js +123 -119
  77. package/dist/paint/index.cjs +8 -6
  78. package/dist/paint/index.js +56 -54
  79. package/dist/paint-editor/brush-library-tools.d.cts +12 -2
  80. package/dist/paint-editor/brush-library-tools.d.ts +12 -2
  81. package/dist/paint-editor/brush-model.d.cts +29 -1
  82. package/dist/paint-editor/brush-model.d.ts +29 -1
  83. package/dist/paint-editor/brush-source-art.d.cts +32 -0
  84. package/dist/paint-editor/brush-source-art.d.ts +32 -0
  85. package/dist/paint-editor/controller.d.cts +11 -4
  86. package/dist/paint-editor/controller.d.ts +11 -4
  87. package/dist/paint-editor/dab-stroke.d.cts +2 -0
  88. package/dist/paint-editor/dab-stroke.d.ts +2 -0
  89. package/dist/paint-editor/engine.d.cts +11 -2
  90. package/dist/paint-editor/engine.d.ts +11 -2
  91. package/dist/paint-editor/index.cjs +967 -2335
  92. package/dist/paint-editor/index.d.cts +3 -1
  93. package/dist/paint-editor/index.d.ts +3 -1
  94. package/dist/paint-editor/index.js +394 -1762
  95. package/dist/paint-editor/path-samples.d.cts +2 -0
  96. package/dist/paint-editor/path-samples.d.ts +2 -0
  97. package/dist/paint-editor/presence.d.cts +20 -1
  98. package/dist/paint-editor/presence.d.ts +20 -1
  99. package/dist/paint-editor/stamp-tip.d.cts +8 -5
  100. package/dist/paint-editor/stamp-tip.d.ts +8 -5
  101. package/dist/paint-editor/thumbnails.d.cts +6 -0
  102. package/dist/paint-editor/thumbnails.d.ts +6 -0
  103. package/dist/paint-editor/tip-grain.d.cts +34 -0
  104. package/dist/paint-editor/tip-grain.d.ts +34 -0
  105. package/dist/raster/index.cjs +2 -2
  106. package/dist/raster/index.js +9 -9
  107. package/dist/stickers/helpers.d.cts +2 -0
  108. package/dist/stickers/helpers.d.ts +2 -0
  109. package/dist/stickers/index.cjs +41 -35
  110. package/dist/stickers/index.js +22 -16
  111. package/dist/stickers/types.d.cts +4 -2
  112. package/dist/stickers/types.d.ts +4 -2
  113. package/dist/vector/backdrop.d.cts +13 -0
  114. package/dist/vector/backdrop.d.ts +13 -0
  115. package/dist/vector/brushstroke.d.cts +54 -7
  116. package/dist/vector/brushstroke.d.ts +54 -7
  117. package/dist/vector/brushstrokeMedia.d.cts +96 -0
  118. package/dist/vector/brushstrokeMedia.d.ts +96 -0
  119. package/dist/vector/controller.d.cts +17 -3
  120. package/dist/vector/controller.d.ts +17 -3
  121. package/dist/vector/index.cjs +1850 -1390
  122. package/dist/vector/index.d.cts +2 -0
  123. package/dist/vector/index.d.ts +2 -0
  124. package/dist/vector/index.js +520 -60
  125. package/dist/vector/render.d.cts +6 -0
  126. package/dist/vector/render.d.ts +6 -0
  127. package/dist/vector/types.d.cts +14 -0
  128. package/dist/vector/types.d.ts +14 -0
  129. package/package.json +1 -1
  130. package/skills/pixerate-animation-editor/SKILL.md +1 -0
  131. package/skills/pixerate-brushes/SKILL.md +37 -10
  132. package/skills/pixerate-brushes-design/SKILL.md +15 -5
  133. package/skills/pixerate-brushes-design/evals/port-to-vector.json +8 -8
  134. package/skills/pixerate-canvas/SKILL.md +11 -1
  135. package/skills/pixerate-image-editor/SKILL.md +19 -64
  136. package/skills/pixerate-image-editor/references/color-management.md +2 -1
  137. package/skills/pixerate-image-editor/references/free-transform.md +12 -0
  138. package/skills/pixerate-image-editor/references/layer-effects.md +10 -0
  139. package/skills/pixerate-image-editor/references/painting.md +17 -0
  140. package/skills/pixerate-image-editor/references/photoshop-files.md +1 -1
  141. package/skills/pixerate-image-editor/references/shapes-and-type.md +14 -0
  142. package/skills/pixerate-image-editor/references/smart-objects.md +10 -0
  143. package/skills/pixerate-paint-editor/SKILL.md +14 -4
  144. package/skills/pixerate-paint-editor/references/brush-facets.md +3 -1
  145. package/skills/pixerate-paint-editor/references/wet-media.md +9 -0
  146. package/skills/pixerate-paint-sketch-to-finish/SKILL.md +1 -0
  147. package/skills/pixerate-vector/SKILL.md +16 -6
  148. package/skills/pixerate-vector/references/best-practices.md +1 -0
  149. package/skills/pixerate-vector/references/commands.md +19 -0
@@ -46,7 +46,7 @@ import {
46
46
  resolveCanvasBrushSpec,
47
47
  validateCanvasBrush,
48
48
  validateCanvasBrushPaint
49
- } from "../chunk-DXYHQ2VN.js";
49
+ } from "../chunk-BAZMCFVL.js";
50
50
  import {
51
51
  PATHFINDER_OPERATIONS,
52
52
  PlanarMap,
@@ -8080,6 +8080,8 @@ function createCanvasDrawController(options) {
8080
8080
  };
8081
8081
  let activeStroke = null;
8082
8082
  let colorScheme = options.colorScheme === "dark" ? "dark" : "light";
8083
+ let colorLocked = !!options.colorLocked;
8084
+ let brushColour = null;
8083
8085
  const erasedInDrag = /* @__PURE__ */ new Map();
8084
8086
  let dragStartNodes = null;
8085
8087
  function setTool(tool) {
@@ -8091,11 +8093,16 @@ function createCanvasDrawController(options) {
8091
8093
  ...createDefaultStrokeStyle(tool),
8092
8094
  ...options.style
8093
8095
  };
8096
+ brushColour = null;
8094
8097
  options.onStyleChange?.(currentStyle);
8095
8098
  }
8096
8099
  options.onToolChange?.(tool);
8097
8100
  }
8098
8101
  function setStyle(style) {
8102
+ if (style.color !== void 0) brushColour = null;
8103
+ applyStyle(style);
8104
+ }
8105
+ function applyStyle(style) {
8099
8106
  currentStyle = { ...currentStyle, ...style };
8100
8107
  options.onStyleChange?.(currentStyle);
8101
8108
  }
@@ -8312,8 +8319,10 @@ function createCanvasDrawController(options) {
8312
8319
  /**
8313
8320
  * A brush picker's choice: brushes the pen and highlighter with `id` (`null`: a plain vector
8314
8321
  * stroke) at the brush's own size, embedding a saved brush, and makes its default colour
8315
- * (resolved for the colour scheme) the stroke colour unless `keepColour`. Throws for a brush
8316
- * the canvas can't paint. Resolves with the brush chosen.
8322
+ * (resolved for the colour scheme) the stroke colour unless `keepColour` or the colour is
8323
+ * locked (`setColorLocked`). A light / dark default resolves again when the scheme changes,
8324
+ * until another colour is set. Throws for a brush the canvas can't paint. Resolves with the
8325
+ * brush chosen.
8317
8326
  */
8318
8327
  async selectBrush(id, choice = {}) {
8319
8328
  if (id === null) {
@@ -8325,13 +8334,43 @@ function createCanvasDrawController(options) {
8325
8334
  await validateCanvasBrush(id);
8326
8335
  throw new Error(`Brush '${id}' is not one the canvas can paint; listBrushes lists them.`);
8327
8336
  }
8328
- const hex = !choice.keepColour && preset.defaultColor !== void 0 ? brushColourHex(preset.defaultColor, colorScheme) : null;
8329
- setStyle({ brush: id, brushSize: preset.size, brushSpec: preset.spec, ...hex ? { color: hex } : {} });
8337
+ const hex = !choice.keepColour && !colorLocked && preset.defaultColor !== void 0 ? brushColourHex(preset.defaultColor, colorScheme) : null;
8338
+ if (hex) brushColour = preset.defaultColor ?? null;
8339
+ applyStyle({ brush: id, brushSize: preset.size, brushSpec: preset.spec, ...hex ? { color: hex } : {} });
8330
8340
  return preset;
8331
8341
  },
8332
- /** The host's colour scheme, for brushes' light / dark default colours. */
8342
+ get colorScheme() {
8343
+ return colorScheme;
8344
+ },
8345
+ /**
8346
+ * The host's colour scheme, for brushes' light / dark default colours: a stroke colour that
8347
+ * came from one (`selectBrush`) resolves to the new side. Strokes already drawn keep theirs.
8348
+ */
8333
8349
  setColorScheme(scheme) {
8334
- colorScheme = scheme === "dark" ? "dark" : "light";
8350
+ const next = scheme === "dark" ? "dark" : "light";
8351
+ if (next === colorScheme) return;
8352
+ colorScheme = next;
8353
+ const hex = brushColour ? brushColourHex(brushColour, next) : null;
8354
+ if (hex && hex !== currentStyle.color) applyStyle({ color: hex });
8355
+ },
8356
+ get colorLocked() {
8357
+ return colorLocked;
8358
+ },
8359
+ /** Locks the stroke colour ("Keep my colour"): choosing a brush with a default colour keeps it. */
8360
+ setColorLocked(locked) {
8361
+ if (colorLocked === !!locked) return;
8362
+ colorLocked = !!locked;
8363
+ options.onColorLockChange?.(colorLocked);
8364
+ },
8365
+ /** What the brush picker is set to: the brush, colour, scheme and lock (a snapshot). */
8366
+ brushSettings() {
8367
+ return {
8368
+ brush: typeof currentStyle.brush === "string" ? currentStyle.brush : null,
8369
+ color: typeof currentStyle.color === "string" ? currentStyle.color : null,
8370
+ colorScheme,
8371
+ colorLocked,
8372
+ colorFollowsScheme: brushColour !== null && typeof brushColour === "object"
8373
+ };
8335
8374
  },
8336
8375
  /** The saved brush to embed for a brush id (undefined for presets and tip settings); for a brush picker. */
8337
8376
  brushSpec: (brush) => resolveCanvasBrushSpec(brush, options.brushLibrary)
@@ -74,7 +74,7 @@ function profileAlongPolyline(points, values, count = 32) {
74
74
  var round3 = (v) => Math.round(v * 1e3) / 1e3;
75
75
  var loading = null;
76
76
  function loadCanvasBrushstrokeRenderer() {
77
- loading ??= Promise.resolve().then(() => _interopRequireWildcard(require("./brushstrokeRender-FSUWXUZY.cjs"))).catch((error) => {
77
+ loading ??= Promise.resolve().then(() => _interopRequireWildcard(require("./brushstrokeRender-WR2N5HHY.cjs"))).catch((error) => {
78
78
  loading = null;
79
79
  throw error;
80
80
  });
@@ -5,7 +5,8 @@
5
5
 
6
6
 
7
7
 
8
- var _chunkOTN6VB5Scjs = require('./chunk-OTN6VB5S.cjs');
8
+
9
+ var _chunkRDFHWAHLcjs = require('./chunk-RDFHWAHL.cjs');
9
10
 
10
11
 
11
12
  var _chunkU67EN7GGcjs = require('./chunk-U67EN7GG.cjs');
@@ -33,7 +34,7 @@ var HEX = /^#[0-9a-fA-F]{6}$/;
33
34
  function stampOutline(kind) {
34
35
  const n = GRID;
35
36
  const f = new Float32Array((n + 1) * (n + 1));
36
- for (let j = 0; j <= n; j++) for (let i = 0; i <= n; i++) f[j * (n + 1) + i] = _chunkOTN6VB5Scjs.stampCoverage.call(void 0, kind, i / n * 2 - 1, j / n * 2 - 1, SHAPE_SEED) - 0.5;
37
+ for (let j = 0; j <= n; j++) for (let i = 0; i <= n; i++) f[j * (n + 1) + i] = _chunkRDFHWAHLcjs.stampCoverage.call(void 0, kind, i / n * 2 - 1, j / n * 2 - 1, SHAPE_SEED) - 0.5;
37
38
  const at = (i, j) => f[j * (n + 1) + i];
38
39
  const points = /* @__PURE__ */ new Map();
39
40
  const point = (key, i0, j0, i1, j1) => {
@@ -170,7 +171,7 @@ function stampPixels(kind, size, colour) {
170
171
  for (let y = 0; y < size; y++) {
171
172
  for (let x = 0; x < size; x++) {
172
173
  let c = 0;
173
- for (let sy = 0; sy < ss; sy++) for (let sx = 0; sx < ss; sx++) c += _chunkOTN6VB5Scjs.stampCoverage.call(void 0, kind, (x + (sx + 0.5) / ss) / size * 2 - 1, (y + (sy + 0.5) / ss) / size * 2 - 1, SHAPE_SEED);
174
+ for (let sy = 0; sy < ss; sy++) for (let sx = 0; sx < ss; sx++) c += _chunkRDFHWAHLcjs.stampCoverage.call(void 0, kind, (x + (sx + 0.5) / ss) / size * 2 - 1, (y + (sy + 0.5) / ss) / size * 2 - 1, SHAPE_SEED);
174
175
  const a = Math.round(c / (ss * ss) * 255);
175
176
  if (!a) continue;
176
177
  const i = (y * size + x) * 4;
@@ -192,25 +193,25 @@ function editorStickers() {
192
193
  return { id: k, name: m.name, category: m.category, tags: m.tags, description: m.description, width: BOX, height: BOX, thumbnail: thumbs.get(k), formats: ["svg", "png"], licence: { ...LICENCE }, tintable: true };
193
194
  };
194
195
  const kind = (id) => {
195
- if (!_chunkOTN6VB5Scjs.STAMP_KINDS.includes(id)) throw new (0, _chunkU67EN7GGcjs.StickerError)("unknown_sticker", `The editor has no sticker "${id}"; its stickers are ${_chunkOTN6VB5Scjs.STAMP_KINDS.join(", ")}.`);
196
+ if (!_chunkRDFHWAHLcjs.STAMP_KINDS.includes(id)) throw new (0, _chunkU67EN7GGcjs.StickerError)("unknown_sticker", `The editor has no sticker "${id}"; its stickers are ${_chunkRDFHWAHLcjs.STAMP_KINDS.join(", ")}.`);
196
197
  return id;
197
198
  };
198
199
  return {
199
- id: _chunkOTN6VB5Scjs.EDITOR_STICKERS_ID,
200
+ id: _chunkRDFHWAHLcjs.EDITOR_STICKERS_ID,
200
201
  name: "Editor stickers",
201
202
  version: "1",
202
203
  capabilities: { supportsSearch: true, supportsSvg: true, supportsRaster: true, maxSize: 1024 },
203
- list: (options) => _chunkOTN6VB5Scjs.stickerPage.call(void 0, _chunkOTN6VB5Scjs.STAMP_KINDS.map(summary), options),
204
- search: (query, options) => _chunkOTN6VB5Scjs.stickerPage.call(void 0, _chunkOTN6VB5Scjs.STAMP_KINDS.map(summary), options, query),
204
+ list: (options) => _chunkRDFHWAHLcjs.stickerPage.call(void 0, _chunkRDFHWAHLcjs.STAMP_KINDS.map(summary), options),
205
+ search: (query, options) => _chunkRDFHWAHLcjs.stickerPage.call(void 0, _chunkRDFHWAHLcjs.STAMP_KINDS.map(summary), options, query),
205
206
  describe(id) {
206
- if (!_chunkOTN6VB5Scjs.STAMP_KINDS.includes(id)) return void 0;
207
+ if (!_chunkRDFHWAHLcjs.STAMP_KINDS.includes(id)) return void 0;
207
208
  const k = id;
208
209
  return { ...summary(k), silhouette: outline(k), colours: [META[k].colour] };
209
210
  },
210
- categories: () => [...new Set(_chunkOTN6VB5Scjs.STAMP_KINDS.map((k) => META[k].category))].map((c) => ({ id: c, name: c[0].toUpperCase() + c.slice(1), count: _chunkOTN6VB5Scjs.STAMP_KINDS.filter((k) => META[k].category === c).length })),
211
+ categories: () => [...new Set(_chunkRDFHWAHLcjs.STAMP_KINDS.map((k) => META[k].category))].map((c) => ({ id: c, name: c[0].toUpperCase() + c.slice(1), count: _chunkRDFHWAHLcjs.STAMP_KINDS.filter((k) => META[k].category === c).length })),
211
212
  load(id, options = {}) {
212
213
  const k = kind(id);
213
- _chunkOTN6VB5Scjs.checkStickerVariant.call(void 0, { id: k }, options.variant);
214
+ _chunkRDFHWAHLcjs.checkStickerVariant.call(void 0, { id: k }, options.variant);
214
215
  const colour = _nullishCoalesce(options.colour, () => ( META[k].colour));
215
216
  if (!HEX.test(colour)) throw new (0, _chunkU67EN7GGcjs.StickerError)("invalid_argument", `colour ${JSON.stringify(colour)} must be #rrggbb.`);
216
217
  if (options.format === "png") {
@@ -258,7 +259,7 @@ var StickerBrowser = (_class = class {
258
259
  this.set({ loading: true, error: null, items: [], total: 0, hasMore: false, provider: p.name });
259
260
  try {
260
261
  const categories = await _asyncNullishCoalesce(await _optionalChain([p, 'access', _2 => _2.categories, 'optionalCall', _3 => _3()]), async () => ( []));
261
- const collections = await _asyncNullishCoalesce(await _optionalChain([p, 'access', _4 => _4.listCollections, 'optionalCall', _5 => _5()]), async () => ( []));
262
+ const collections = await _asyncNullishCoalesce(await _chunkRDFHWAHLcjs.providerCollections.call(void 0, p), async () => ( []));
262
263
  if (run !== this.run) return;
263
264
  this.set({ categories, collections, ...this.s.collection && !collections.some((c) => c.id === this.s.collection) ? { collection: "" } : {} });
264
265
  await this.page(run, p, true);
@@ -298,7 +299,7 @@ var StickerBrowser = (_class = class {
298
299
  const res = q && p.capabilities.supportsSearch ? await p.search(q, options) : await p.list(options);
299
300
  if (run !== this.run) return;
300
301
  this.next = res.next;
301
- const items = res.items.map((s) => ({ ...s, ref: _chunkOTN6VB5Scjs.stickerRefOf.call(void 0, p, s.id) }));
302
+ const items = res.items.map((s) => ({ ...s, ref: _chunkRDFHWAHLcjs.stickerRefOf.call(void 0, p, s.id) }));
302
303
  this.set({ items: first ? items : [...this.s.items, ...items], total: res.total, hasMore: Boolean(res.next), loading: false });
303
304
  }
304
305
  }, _class);
@@ -1,22 +1,30 @@
1
1
  import {
2
2
  describeStickerRef
3
- } from "./chunk-6JL2D5SG.js";
3
+ } from "./chunk-VE6UKYQ2.js";
4
4
  import {
5
5
  BRUSH_HOST_PROFILES,
6
6
  BRUSH_JSON_SCHEMA,
7
7
  BRUSH_KINDS,
8
8
  BRUSH_MEDIA,
9
+ BRUSH_SOURCE_CHANNELS,
9
10
  BRUSH_TOOLS,
10
11
  BrushLibraryError,
12
+ MAX_BRUSH_SOURCE_INPUT_PIXELS,
13
+ MAX_BRUSH_SOURCE_SIZE,
11
14
  PAINT_PROFILE,
15
+ brushSourceIdOf,
16
+ brushSourceIds,
17
+ brushSourceRef,
12
18
  checkBrush,
13
19
  closest,
14
20
  degrade,
21
+ isBrushSourceRef,
15
22
  normalizeBrushSpec,
16
23
  resolveCategory,
17
24
  resolveEngine,
25
+ summarizeBrushSource,
18
26
  withKeptIssues
19
- } from "./chunk-CIJBSUM5.js";
27
+ } from "./chunk-KZI3YWKW.js";
20
28
 
21
29
  // src/brushes/patch.ts
22
30
  var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
@@ -101,11 +109,18 @@ function createBrushLibraryTools(library, options = {}) {
101
109
  };
102
110
  const checkFor = async (brush, p) => {
103
111
  const check2 = checkBrush(brush, p);
104
- const provider = options.stickers?.();
105
- if (!provider || brush.tip.shape !== "stamp") return check2;
106
112
  const issues = [...check2.issues];
113
+ const refs = [...(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 = brushSourceIdOf(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. ${closest(id, ids)}`.trim(), fix: "Add it with add_brush_source, or use a ref list_brush_sources lists." });
119
+ }
120
+ const provider = options.stickers?.();
121
+ if (!provider || brush.tip.shape !== "stamp") return { ok: !issues.some((i) => i.severity === "error"), issues };
107
122
  for (const ref of brush.tip.stamps ?? []) {
108
- if (ref.startsWith("editor:")) continue;
123
+ if (ref.startsWith("editor:") || isBrushSourceRef(ref)) continue;
109
124
  try {
110
125
  await describeStickerRef(provider, ref);
111
126
  } catch (e) {
@@ -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, x.origin ?? ""].join(" "));
245
+ return want.every((w) => have.some((h) => h.startsWith(w)));
246
+ });
247
+ const start = offset ?? 0;
248
+ const page = found.slice(start, start + (limit ?? 100));
249
+ return { total: found.length, sources: page.map((x) => ({ ...summarizeBrushSource(x), usedBy: brushes.filter((b) => brushSourceIds(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({
@@ -288,6 +330,31 @@ function createBrushLibraryTools(library, options = {}) {
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 ${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 ${MAX_BRUSH_SOURCE_INPUT_PIXELS} pixels; larger than ${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: [...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: brushSourceRef(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",