@pixerate/editor 0.267.0 → 0.268.1

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 (83) 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-GCVFEPJG.js → chunk-ACTHSQOI.js} +182 -0
  27. package/dist/{chunk-EF7LZAXQ.js → chunk-BAZMCFVL.js} +1 -1
  28. package/dist/{chunk-HIO6E7LC.cjs → chunk-BPJSI4NT.cjs} +2 -2
  29. package/dist/{chunk-XVOP3CGQ.cjs → chunk-EU2ZZA6L.cjs} +393 -62
  30. package/dist/{chunk-YIC6MAJ2.js → chunk-HFY5LG5J.js} +1 -1
  31. package/dist/{chunk-7UKFXXWL.js → chunk-HVO6XCI4.js} +1 -1
  32. package/dist/{chunk-LHPEG5AU.cjs → chunk-J4GXP53R.cjs} +140 -91
  33. package/dist/{chunk-OKHKA43L.cjs → chunk-JBB3GDBB.cjs} +330 -216
  34. package/dist/{chunk-AGNUD5BI.js → chunk-KZI3YWKW.js} +351 -20
  35. package/dist/{chunk-FGJHJIMN.js → chunk-LDOD7VCT.js} +59 -10
  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/adjustment-layers.d.cts +8 -0
  46. package/dist/image-editor/adjustment-layers.d.ts +8 -0
  47. package/dist/image-editor/index.cjs +857 -853
  48. package/dist/image-editor/index.js +30 -26
  49. package/dist/paint/index.cjs +5 -5
  50. package/dist/paint/index.js +11 -11
  51. package/dist/paint-editor/brush-library-tools.d.cts +12 -2
  52. package/dist/paint-editor/brush-library-tools.d.ts +12 -2
  53. package/dist/paint-editor/brush-model.d.cts +29 -1
  54. package/dist/paint-editor/brush-model.d.ts +29 -1
  55. package/dist/paint-editor/brush-source-art.d.cts +32 -0
  56. package/dist/paint-editor/brush-source-art.d.ts +32 -0
  57. package/dist/paint-editor/controller.d.cts +6 -1
  58. package/dist/paint-editor/controller.d.ts +6 -1
  59. package/dist/paint-editor/dab-stroke.d.cts +2 -0
  60. package/dist/paint-editor/dab-stroke.d.ts +2 -0
  61. package/dist/paint-editor/engine.d.cts +11 -2
  62. package/dist/paint-editor/engine.d.ts +11 -2
  63. package/dist/paint-editor/index.cjs +576 -481
  64. package/dist/paint-editor/index.d.cts +3 -1
  65. package/dist/paint-editor/index.d.ts +3 -1
  66. package/dist/paint-editor/index.js +127 -32
  67. package/dist/paint-editor/stamp-tip.d.cts +8 -5
  68. package/dist/paint-editor/stamp-tip.d.ts +8 -5
  69. package/dist/paint-editor/thumbnails.d.cts +6 -0
  70. package/dist/paint-editor/thumbnails.d.ts +6 -0
  71. package/dist/paint-editor/tip-grain.d.cts +34 -0
  72. package/dist/paint-editor/tip-grain.d.ts +34 -0
  73. package/dist/raster/index.cjs +2 -2
  74. package/dist/raster/index.js +9 -9
  75. package/dist/vector/index.cjs +1002 -1002
  76. package/dist/vector/index.js +7 -7
  77. package/package.json +110 -1
  78. package/skills/pixerate-brushes/SKILL.md +35 -8
  79. package/skills/pixerate-brushes-design/SKILL.md +8 -1
  80. package/skills/pixerate-image-editor/SKILL.md +1 -1
  81. package/skills/pixerate-paint-editor/SKILL.md +6 -0
  82. package/skills/pixerate-paint-editor/references/brush-facets.md +3 -1
  83. package/skills/pixerate-paint-sketch-to-finish/SKILL.md +1 -0
@@ -8,7 +8,7 @@
8
8
 
9
9
 
10
10
 
11
- var _chunkDT4YQUGTcjs = require('../chunk-DT4YQUGT.cjs');
11
+ var _chunkYXP6X3SYcjs = require('../chunk-YXP6X3SY.cjs');
12
12
  require('../chunk-PFHJS4HE.cjs');
13
13
 
14
14
 
@@ -17,7 +17,7 @@ require('../chunk-PFHJS4HE.cjs');
17
17
 
18
18
 
19
19
 
20
- var _chunkHIO6E7LCcjs = require('../chunk-HIO6E7LC.cjs');
20
+ var _chunkBPJSI4NTcjs = require('../chunk-BPJSI4NT.cjs');
21
21
 
22
22
 
23
23
 
@@ -69,7 +69,29 @@ var _chunkHIO6E7LCcjs = require('../chunk-HIO6E7LC.cjs');
69
69
 
70
70
 
71
71
 
72
- var _chunkXVOP3CGQcjs = require('../chunk-XVOP3CGQ.cjs');
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+ var _chunkEU2ZZA6Lcjs = require('../chunk-EU2ZZA6L.cjs');
73
95
 
74
96
 
75
97
  var _chunkRDFHWAHLcjs = require('../chunk-RDFHWAHL.cjs');
@@ -80,12 +102,17 @@ require('../chunk-U67EN7GG.cjs');
80
102
  require('../chunk-WOFXZ3XU.cjs');
81
103
 
82
104
 
83
- var _chunkFQPUF2W2cjs = require('../chunk-FQPUF2W2.cjs');
105
+ var _chunkNIBFKXMDcjs = require('../chunk-NIBFKXMD.cjs');
84
106
  require('../chunk-3K6HWEXZ.cjs');
85
107
 
86
108
 
87
109
  var _chunk2VX55NFMcjs = require('../chunk-2VX55NFM.cjs');
110
+
111
+
112
+
113
+ var _chunkW5M3J7PVcjs = require('../chunk-W5M3J7PV.cjs');
88
114
  require('../chunk-474IQG74.cjs');
115
+ require('../chunk-XFM3OCHU.cjs');
89
116
 
90
117
 
91
118
  var _chunkMLLTWAXHcjs = require('../chunk-MLLTWAXH.cjs');
@@ -107,6 +134,9 @@ var RENDER_LABELS = { dot: "Dots", line: "Lines", sequence: "Beads", ribbon: "Ri
107
134
  var APPLICATOR_LABELS = { brush: "Brush", tube: "Paint tube", knife: "Palette knife", roller: "Roller", gloop: "Gloop pen" };
108
135
  var WET_MEDIUM_LABELS = { watercolor: "Watercolour", gouache: "Gouache", oil: "Oil" };
109
136
  var STAMP_OPTIONS = _chunkRDFHWAHLcjs.STAMP_KINDS.map((k) => ({ value: `editor:${k}`, label: title(k) }));
137
+ var GRAIN_MODE_LABELS = { texturised: "Texturised (stays put)", moving: "Moving (rolls with the tip)" };
138
+ var FLIP_LABELS = { none: "None", x: "Across", y: "Along", xy: "Both" };
139
+ var DAB_ENGINES = /* @__PURE__ */ new Set(["dab", "eraser", "mixer", "blender", "smudge"]);
110
140
  var ARIA = {
111
141
  "dynamics.rotation": "Brush rotation",
112
142
  "tip.blend": "Brush blend",
@@ -119,23 +149,25 @@ var ARIA = {
119
149
  "marks.stampRotation": "Dot rotation",
120
150
  "behaviour.wet.medium": "Wet medium",
121
151
  "behaviour.thick.applicator": "Impasto tool",
122
- "light.pressure": "Light follows pressure"
152
+ "light.pressure": "Light follows pressure",
153
+ "tip.grain.source": "Tip grain",
154
+ "dynamics.angleJitter": "Brush rotation jitter"
123
155
  };
124
156
  var aria = (path, label) => _nullishCoalesce(ARIA[path], () => ( `Brush ${label.toLowerCase()}`));
125
- function brushEditorSections(brush, profile = _chunkXVOP3CGQcjs.PAINT_PROFILE) {
126
- const resolved = _chunkXVOP3CGQcjs.resolveBrush.call(void 0, brush);
127
- const engine = _chunkXVOP3CGQcjs.resolveEngine.call(void 0, brush);
128
- const fieldOf = (facet) => _chunkXVOP3CGQcjs.BRUSH_FIELDS.find((f) => f.facet === facet && f.engines.includes(engine));
129
- const facetOf = (enginePath) => _optionalChain([(_nullishCoalesce(_chunkXVOP3CGQcjs.BRUSH_FIELDS.find((f) => f.engine === enginePath && f.engines.includes(engine)), () => ( _chunkXVOP3CGQcjs.BRUSH_FIELDS.find((f) => f.engine === enginePath)))), 'optionalAccess', _ => _.facet]);
157
+ function brushEditorSections(brush, profile = _chunkEU2ZZA6Lcjs.PAINT_PROFILE, context = {}) {
158
+ const resolved = _chunkEU2ZZA6Lcjs.resolveBrush.call(void 0, brush);
159
+ const engine = _chunkEU2ZZA6Lcjs.resolveEngine.call(void 0, brush);
160
+ const fieldOf = (facet) => _chunkEU2ZZA6Lcjs.BRUSH_FIELDS.find((f) => f.facet === facet && f.engines.includes(engine));
161
+ const facetOf = (enginePath) => _optionalChain([(_nullishCoalesce(_chunkEU2ZZA6Lcjs.BRUSH_FIELDS.find((f) => f.engine === enginePath && f.engines.includes(engine)), () => ( _chunkEU2ZZA6Lcjs.BRUSH_FIELDS.find((f) => f.engine === enginePath)))), 'optionalAccess', _ => _.facet]);
130
162
  const value = (path) => {
131
- const own = _chunkXVOP3CGQcjs.getPath.call(void 0, brush, path);
163
+ const own = _chunkEU2ZZA6Lcjs.getPath.call(void 0, brush, path);
132
164
  if (own !== void 0) return own;
133
165
  const field = fieldOf(path);
134
166
  if (!field) return void 0;
135
- const v = _chunkXVOP3CGQcjs.getPath.call(void 0, resolved, field.engine);
167
+ const v = _chunkEU2ZZA6Lcjs.getPath.call(void 0, resolved, field.engine);
136
168
  return field.map ? field.map.toFacet(v) : v;
137
169
  };
138
- const issues = _chunkXVOP3CGQcjs.checkBrush.call(void 0, brush, profile).issues;
170
+ const issues = _chunkEU2ZZA6Lcjs.checkBrush.call(void 0, brush, profile).issues;
139
171
  const warned = new Map(issues.filter((i) => i.severity === "warning").map((i) => [i.facet, i.reason]));
140
172
  const refused = new Set(issues.filter((i) => i.severity === "error").map((i) => i.facet));
141
173
  const full = (s) => s === "full";
@@ -148,7 +180,7 @@ function brushEditorSections(brush, profile = _chunkXVOP3CGQcjs.PAINT_PROFILE) {
148
180
  const slider = (c, path = facetOf(c.path)) => {
149
181
  if (!path) return null;
150
182
  const v = value(path);
151
- return withNote({ kind: "slider", path, label: c.label, ariaLabel: aria(path, c.label), min: c.min, max: path === "tip.size" ? Math.min(c.max, _chunkFQPUF2W2cjs.MAX_BRUSH_SIZE) : c.max, step: c.step, value: typeof v === "number" ? v : c.min });
183
+ return withNote({ kind: "slider", path, label: c.label, ariaLabel: aria(path, c.label), min: c.min, max: path === "tip.size" ? Math.min(c.max, _chunkNIBFKXMDcjs.MAX_BRUSH_SIZE) : c.max, step: c.step, value: typeof v === "number" ? v : c.min });
152
184
  };
153
185
  const choice = (path, label, opts, fallback = _nullishCoalesce(_optionalChain([opts, 'access', _5 => _5[0], 'optionalAccess', _6 => _6.value]), () => ( ""))) => withNote({ kind: "choice", path, label, ariaLabel: aria(path, label), options: opts, value: String(_nullishCoalesce(value(path), () => ( fallback))) });
154
186
  const toggle = (path, label) => withNote({ kind: "toggle", path, label, ariaLabel: aria(path, label), value: Boolean(value(path)) });
@@ -160,70 +192,90 @@ function brushEditorSections(brush, profile = _chunkXVOP3CGQcjs.PAINT_PROFILE) {
160
192
  };
161
193
  const isTool = brush.kind === "tool";
162
194
  const shape = brush.tip.shape;
195
+ const sourceOptions = (_nullishCoalesce(context.sources, () => ( []))).map((x) => ({ value: _chunkEU2ZZA6Lcjs.brushSourceRef.call(void 0, x.id), label: x.name }));
163
196
  const marks = _nullishCoalesce(_optionalChain([brush, 'access', _7 => _7.marks, 'optionalAccess', _8 => _8.kind]), () => ( "continuous"));
164
197
  const exchangeFull = full(_optionalChain([brush, 'access', _9 => _9.behaviour, 'optionalAccess', _10 => _10.exchange, 'optionalAccess', _11 => _11.reservoir]) ? profile.reservoir : profile.exchange);
165
198
  add("medium", "Medium", [
166
199
  profile.kinds.length > 1 && profile.kinds.includes(brush.kind) && choice("kind", "Kind", options(profile.kinds, { mark: "Lays a material", tool: "Works on paint" })),
167
- !isTool && choice("medium", "Medium", [{ value: "", label: "Any" }, ...options(_chunkXVOP3CGQcjs.BRUSH_MEDIA, MEDIUM_LABELS)], ""),
200
+ !isTool && choice("medium", "Medium", [{ value: "", label: "Any" }, ...options(_chunkEU2ZZA6Lcjs.BRUSH_MEDIA, MEDIUM_LABELS)], ""),
168
201
  !isTool && withNote({ kind: "text", path: "variety", label: "Variety", ariaLabel: "Brush variety", value: _nullishCoalesce(brush.variety, () => ( "")), placeholder: "pencil, oil, highlighter\u2026" }),
169
202
  isTool && !!brush.tool && profile.tools.includes(brush.tool) && choice("tool", "Tool", options(profile.tools, TOOL_LABELS))
170
203
  ]);
171
- const engineControls = _chunkDT4YQUGTcjs.brushEngineControls.call(void 0, resolved);
172
- const tipSliders = _chunkDT4YQUGTcjs.BRUSH_SLIDERS.filter((c) => c.path.startsWith("tip.") && c.path !== "tip.smoothing");
204
+ const engineControls = _chunkYXP6X3SYcjs.brushEngineControls.call(void 0, resolved);
205
+ const tipSliders = _chunkYXP6X3SYcjs.BRUSH_SLIDERS.filter((c) => c.path.startsWith("tip.") && c.path !== "tip.smoothing");
173
206
  add("shape", "Shape", [
174
207
  !refused.has("tip.shape") && choice("tip.shape", "Tip", options(profile.tipShapes.filter((s) => !isTool || s !== "stamp"), SHAPE_LABELS)),
175
208
  ...tipSliders.map((c) => slider(c)),
176
209
  engine === "dab" && choice("tip.blend", "Blend", _chunk2VX55NFMcjs.BLEND_MODES.map((m) => ({ value: m.id, label: m.label })), "normal"),
177
210
  ...shape === "bristle" && engineOk && engineControls ? engineControls.sliders.filter((c) => c.path.startsWith("bristle.")).map((c) => slider(c)) : [],
178
- shape === "stamp" && engineOk && withNote({ kind: "stamps", path: "tip.stamps", label: "Stamps", ariaLabel: "Brush stamps", value: [..._nullishCoalesce(value("tip.stamps"), () => ( []))], max: marks === "particles" ? _chunkXVOP3CGQcjs.MAX_DOT_TEXTURES : _chunkXVOP3CGQcjs.MAX_STICKER_STAMPS, options: STAMP_OPTIONS })
211
+ shape === "stamp" && engineOk && withNote({ kind: "stamps", path: "tip.stamps", label: "Stamps", ariaLabel: "Brush stamps", value: [..._nullishCoalesce(value("tip.stamps"), () => ( []))], max: marks === "particles" ? _chunkEU2ZZA6Lcjs.MAX_DOT_TEXTURES : _chunkEU2ZZA6Lcjs.MAX_STICKER_STAMPS, options: marks === "objects" ? STAMP_OPTIONS : [...STAMP_OPTIONS, ...sourceOptions] })
179
212
  ]);
213
+ const dabs = DAB_ENGINES.has(engine) && engineOk && shape !== "bristle" && marks !== "particles" && marks !== "objects";
214
+ const grain = brush.tip.grain;
215
+ if (dabs && full(_nullishCoalesce(profile.tipGrain, () => ( "none")))) {
216
+ add("grain", "Tip grain", [
217
+ choice("tip.grain.source", "Texture", [{ value: "", label: "None" }, ...options(_chunkEU2ZZA6Lcjs.TIP_GRAIN_PATTERNS), ...sourceOptions], ""),
218
+ !!grain && choice("tip.grain.mode", "Grain moves", options(_chunkEU2ZZA6Lcjs.BRUSH_GRAIN_MODES, GRAIN_MODE_LABELS), "texturised"),
219
+ !!grain && withNote({ kind: "slider", path: "tip.grain.scale", label: "Grain scale", ariaLabel: "Brush grain scale", min: 0.1, max: 8, step: 0.05, value: _nullishCoalesce(grain.scale, () => ( 1)) }),
220
+ !!grain && withNote({ kind: "slider", path: "tip.grain.depth", label: "Grain depth", ariaLabel: "Brush grain depth", min: 0, max: 1, step: 0.01, value: _nullishCoalesce(grain.depth, () => ( 0.6)) }),
221
+ !!grain && withNote({ kind: "slider", path: "tip.grain.contrast", label: "Grain contrast", ariaLabel: "Brush grain contrast", min: 0, max: 3, step: 0.05, value: _nullishCoalesce(grain.contrast, () => ( 1)) }),
222
+ !!grain && toggle("tip.grain.invert", "Invert grain")
223
+ ]);
224
+ }
180
225
  if (!isTool && profile.marks.length > 1 && engineOk) {
181
226
  const particles = marks === "particles" && engineControls ? engineControls.sliders.filter((c) => c.path.startsWith("particle.") && c.path !== "particle.waveSpeed") : [];
182
227
  const objects = marks === "objects" && engineControls ? engineControls.sliders.filter((c) => c.path.startsWith("sticker.")) : [];
183
228
  const render = value("marks.render");
184
229
  add("marks", "Marks", [
185
230
  choice("marks.kind", "Laid as", options(profile.marks, MARKS_LABELS), "continuous"),
186
- marks === "particles" && choice("marks.physics", "Particles", options(_chunkXVOP3CGQcjs.PARTICLE_KINDS, PHYSICS_LABELS)),
187
- marks === "particles" && choice("marks.render", "Drawn as", options(_chunkXVOP3CGQcjs.PARTICLE_RENDERS, RENDER_LABELS)),
188
- marks === "particles" && render === "ribbon" && choice("marks.ribbonPairs", "Ribbon joins", options(_chunkXVOP3CGQcjs.RIBBON_PAIRS, { adjacent: "Neighbours", "first-last": "Outermost" })),
189
- marks === "particles" && shape === "stamp" && choice("marks.stampOrder", "Texture order", options(_chunkXVOP3CGQcjs.DOT_TEXTURE_ORDERS, { sequence: "In turn", random: "Random" })),
190
- marks === "particles" && shape === "stamp" && choice("marks.stampRotation", "Texture turn", options(_chunkXVOP3CGQcjs.DOT_ROTATIONS, { follow: "Follow motion", random: "Random", fixed: "Tip angle" })),
231
+ marks === "particles" && choice("marks.physics", "Particles", options(_chunkEU2ZZA6Lcjs.PARTICLE_KINDS, PHYSICS_LABELS)),
232
+ marks === "particles" && choice("marks.render", "Drawn as", options(_chunkEU2ZZA6Lcjs.PARTICLE_RENDERS, RENDER_LABELS)),
233
+ marks === "particles" && render === "ribbon" && choice("marks.ribbonPairs", "Ribbon joins", options(_chunkEU2ZZA6Lcjs.RIBBON_PAIRS, { adjacent: "Neighbours", "first-last": "Outermost" })),
234
+ marks === "particles" && shape === "stamp" && choice("marks.stampOrder", "Texture order", options(_chunkEU2ZZA6Lcjs.DOT_TEXTURE_ORDERS, { sequence: "In turn", random: "Random" })),
235
+ marks === "particles" && shape === "stamp" && choice("marks.stampRotation", "Texture turn", options(_chunkEU2ZZA6Lcjs.DOT_ROTATIONS, { follow: "Follow motion", random: "Random", fixed: "Tip angle" })),
191
236
  ...particles.map((c) => slider(c)),
192
237
  ...objects.map((c) => slider(c))
193
238
  ]);
194
239
  }
195
- const feel = _chunkDT4YQUGTcjs.BRUSH_SLIDERS.filter((c) => ["tip.smoothing", "pressureSmoothing", "dynamics.minSize", "dynamics.tiltSize", "dynamics.velocitySize"].includes(c.path)).filter((c) => !(c.path === "dynamics.tiltSize" && !full(profile.tilt)) && !(c.path === "dynamics.velocitySize" && !full(profile.velocity)));
240
+ const feel = _chunkYXP6X3SYcjs.BRUSH_SLIDERS.filter((c) => ["tip.smoothing", "pressureSmoothing", "dynamics.minSize", "dynamics.tiltSize", "dynamics.velocitySize"].includes(c.path)).filter((c) => !(c.path === "dynamics.tiltSize" && !full(profile.tilt)) && !(c.path === "dynamics.velocitySize" && !full(profile.velocity)));
241
+ const stampDabs = dabs && shape === "stamp";
242
+ const jitter = (path, label, max = 1, step = 0.01) => withNote({ kind: "slider", path, label, ariaLabel: aria(path, label), min: 0, max, step, value: Number(_nullishCoalesce(value(path), () => ( 0))) });
196
243
  add("feel", "Feel", [
197
244
  ...feel.map((c) => slider(c)),
198
245
  toggle("dynamics.pressureSize", "Pressure \u2192 size"),
199
246
  toggle("dynamics.pressureOpacity", "Pressure \u2192 opacity"),
200
- choice("dynamics.rotation", "Rotation", options(_chunkXVOP3CGQcjs.BRUSH_ROTATIONS, ROTATION_LABELS))
247
+ choice("dynamics.rotation", "Rotation", options(_chunkEU2ZZA6Lcjs.BRUSH_ROTATIONS, ROTATION_LABELS)),
248
+ stampDabs && full(_nullishCoalesce(profile.dabCount, () => ( "none"))) && withNote({ kind: "slider", path: "dynamics.count", label: "Count", ariaLabel: "Brush dab count", min: 1, max: 16, step: 1, value: _nullishCoalesce(_optionalChain([brush, 'access', _12 => _12.dynamics, 'optionalAccess', _13 => _13.count]), () => ( 1)) }),
249
+ stampDabs && jitter("dynamics.scatter", "Scatter", 4),
250
+ stampDabs && jitter("dynamics.angleJitter", "Rotation jitter"),
251
+ stampDabs && jitter("dynamics.sizeJitter", "Size jitter"),
252
+ stampDabs && choice("dynamics.flip", "Flip", options(_chunkEU2ZZA6Lcjs.BRUSH_FLIPS, FLIP_LABELS), "none")
201
253
  ]);
202
254
  const surfaces = profile.surfaces;
203
- const engineSliders = (engineOk ? _nullishCoalesce(_optionalChain([engineControls, 'optionalAccess', _12 => _12.sliders]), () => ( [])) : []).filter((c) => !/^(bristle|particle|sticker)\./.test(c.path) && c.path !== "light.amount" && c.path !== "impasto.rim" && c.path !== "impasto.clarity");
204
- const exchangeOk = (path) => !_optionalChain([path, 'optionalAccess', _13 => _13.startsWith, 'call', _14 => _14("behaviour.exchange")]) || (path.startsWith("behaviour.exchange.reservoir") ? full(profile.reservoir) : exchangeFull);
205
- add("behaviour", _nullishCoalesce(_optionalChain([engineControls, 'optionalAccess', _15 => _15.title]), () => ( "Behaviour")), [
255
+ const engineSliders = (engineOk ? _nullishCoalesce(_optionalChain([engineControls, 'optionalAccess', _14 => _14.sliders]), () => ( [])) : []).filter((c) => !/^(bristle|particle|sticker)\./.test(c.path) && c.path !== "light.amount" && c.path !== "impasto.rim" && c.path !== "impasto.clarity");
256
+ const exchangeOk = (path) => !_optionalChain([path, 'optionalAccess', _15 => _15.startsWith, 'call', _16 => _16("behaviour.exchange")]) || (path.startsWith("behaviour.exchange.reservoir") ? full(profile.reservoir) : exchangeFull);
257
+ add("behaviour", _nullishCoalesce(_optionalChain([engineControls, 'optionalAccess', _17 => _17.title]), () => ( "Behaviour")), [
206
258
  !isTool && surfaces.length > 1 && !refused.has("behaviour.surface") && choice("behaviour.surface", "Surface", options(surfaces, SURFACE_LABELS), "dry"),
207
259
  full(profile.grain) && slider({ path: "grain", label: "Grain", min: 0, max: 1, step: 0.01 }, "behaviour.grain"),
208
- engine === "wet" && engineOk && !isTool && choice("behaviour.wet.medium", "Medium", options(_chunkXVOP3CGQcjs.WET_MEDIA, WET_MEDIUM_LABELS), "watercolor"),
209
- engine === "impasto" && engineOk && choice("behaviour.thick.applicator", "Tool", options(_chunkXVOP3CGQcjs.IMPASTO_TOOLS, APPLICATOR_LABELS), "brush"),
260
+ engine === "wet" && engineOk && !isTool && choice("behaviour.wet.medium", "Medium", options(_chunkEU2ZZA6Lcjs.WET_MEDIA, WET_MEDIUM_LABELS), "watercolor"),
261
+ engine === "impasto" && engineOk && choice("behaviour.thick.applicator", "Tool", options(_chunkEU2ZZA6Lcjs.IMPASTO_TOOLS, APPLICATOR_LABELS), "brush"),
210
262
  ...engineSliders.map((c) => exchangeOk(facetOf(c.path)) ? slider(c) : null),
211
- engineOk && _optionalChain([engineControls, 'optionalAccess', _16 => _16.mixingPath]) && (engine === "wet" || exchangeFull) && fieldOf("behaviour.mixing") && choice("behaviour.mixing", "Mixing", options(["pigment", "rgb"], { pigment: "Pigment", rgb: "RGB" })),
212
- engineOk && _optionalChain([engineControls, 'optionalAccess', _17 => _17.dirty]) && exchangeFull && fieldOf("behaviour.exchange.dirty") && toggle("behaviour.exchange.dirty", "Dirty brush"),
263
+ engineOk && _optionalChain([engineControls, 'optionalAccess', _18 => _18.mixingPath]) && (engine === "wet" || exchangeFull) && fieldOf("behaviour.mixing") && choice("behaviour.mixing", "Mixing", options(["pigment", "rgb"], { pigment: "Pigment", rgb: "RGB" })),
264
+ engineOk && _optionalChain([engineControls, 'optionalAccess', _19 => _19.dirty]) && exchangeFull && fieldOf("behaviour.exchange.dirty") && toggle("behaviour.exchange.dirty", "Dirty brush"),
213
265
  engine === "mixer" && engineOk && full(profile.reservoir) && toggle("behaviour.exchange.reservoir.refill", "Load each stroke")
214
266
  ]);
215
- if (!isTool && full(profile.light) && engineOk && _optionalChain([engineControls, 'optionalAccess', _18 => _18.light])) {
267
+ if (!isTool && full(profile.light) && engineOk && _optionalChain([engineControls, 'optionalAccess', _20 => _20.light])) {
216
268
  add("light", "Light", [
217
269
  slider({ path: "light.amount", label: "Light", min: 0, max: 1, step: 0.01 }),
218
270
  toggle("light.pressure", "Light follows pressure"),
219
271
  ...engine === "impasto" ? [slider({ path: "impasto.rim", label: "Rim", min: 0, max: 1, step: 0.01 }), slider({ path: "impasto.clarity", label: "Clarity", min: 0, max: 1, step: 0.01 })] : [],
220
- withNote({ kind: "slider", path: "light.spill.radius", label: "Spill", ariaLabel: "Brush light spill", min: 0, max: 128, step: 1, value: _nullishCoalesce(_optionalChain([brush, 'access', _19 => _19.light, 'optionalAccess', _20 => _20.spill, 'optionalAccess', _21 => _21.radius]), () => ( 24)) }),
272
+ withNote({ kind: "slider", path: "light.spill.radius", label: "Spill", ariaLabel: "Brush light spill", min: 0, max: 128, step: 1, value: _nullishCoalesce(_optionalChain([brush, 'access', _21 => _21.light, 'optionalAccess', _22 => _22.spill, 'optionalAccess', _23 => _23.radius]), () => ( 24)) }),
221
273
  toggle("light.glow", "Suggest a glow")
222
274
  ]);
223
275
  }
224
276
  if (full(profile.motion) && engineOk && marks === "particles") add("motion", "Motion", [slider({ path: "particle.waveSpeed", label: "Wave speed", min: 0, max: 1, step: 0.01 })]);
225
277
  if (!isTool) {
226
- const own = _optionalChain([brush, 'access', _22 => _22.colour, 'optionalAccess', _23 => _23.default]);
278
+ const own = _optionalChain([brush, 'access', _24 => _24.colour, 'optionalAccess', _25 => _25.default]);
227
279
  const hex = (c) => typeof c === "string" ? _nullishCoalesce(_chunkJBZRVLXVcjs.brushColourHex.call(void 0, c, "light"), () => ( null)) : null;
228
280
  const pair = _chunkMLLTWAXHcjs.isDualColor.call(void 0, own) ? own : null;
229
281
  add("colour", "Colour", [
@@ -233,8 +285,8 @@ function brushEditorSections(brush, profile = _chunkXVOP3CGQcjs.PAINT_PROFILE) {
233
285
  path: "colour.palette",
234
286
  label: "Palette",
235
287
  ariaLabel: "Brush palette",
236
- value: (_nullishCoalesce(_optionalChain([brush, 'access', _24 => _24.colour, 'optionalAccess', _25 => _25.palette]), () => ( []))).flatMap((c) => _chunkMLLTWAXHcjs.isDualColor.call(void 0, c) ? [{ light: _chunkJBZRVLXVcjs.brushColourHex.call(void 0, c, "light"), dark: _chunkJBZRVLXVcjs.brushColourHex.call(void 0, c, "dark") }] : hex(c) ? [hex(c)] : []),
237
- max: _chunkXVOP3CGQcjs.MAX_BRUSH_PALETTE
288
+ value: (_nullishCoalesce(_optionalChain([brush, 'access', _26 => _26.colour, 'optionalAccess', _27 => _27.palette]), () => ( []))).flatMap((c) => _chunkMLLTWAXHcjs.isDualColor.call(void 0, c) ? [{ light: _chunkJBZRVLXVcjs.brushColourHex.call(void 0, c, "light"), dark: _chunkJBZRVLXVcjs.brushColourHex.call(void 0, c, "dark") }] : hex(c) ? [hex(c)] : []),
289
+ max: _chunkEU2ZZA6Lcjs.MAX_BRUSH_PALETTE
238
290
  }),
239
291
  toggle("colour.own", "Always its own colour")
240
292
  ]);
@@ -250,15 +302,19 @@ function hiddenLabel(issue, b) {
250
302
  case "tip.shape":
251
303
  return `a ${b.tip.shape} tip`;
252
304
  case "marks":
253
- return _nullishCoalesce(MARKS_WORDS[_nullishCoalesce(_optionalChain([b, 'access', _26 => _26.marks, 'optionalAccess', _27 => _27.kind]), () => ( "continuous"))], () => ( "its marks"));
305
+ return _nullishCoalesce(MARKS_WORDS[_nullishCoalesce(_optionalChain([b, 'access', _28 => _28.marks, 'optionalAccess', _29 => _29.kind]), () => ( "continuous"))], () => ( "its marks"));
254
306
  case "behaviour.surface":
255
- return `${_nullishCoalesce(_optionalChain([b, 'access', _28 => _28.behaviour, 'optionalAccess', _29 => _29.surface]), () => ( "dry"))} behaviour`;
307
+ return `${_nullishCoalesce(_optionalChain([b, 'access', _30 => _30.behaviour, 'optionalAccess', _31 => _31.surface]), () => ( "dry"))} behaviour`;
256
308
  case "behaviour.exchange.reservoir":
257
309
  return "a Mixer Brush reservoir";
258
310
  case "behaviour.exchange":
259
311
  return "paint pick-up";
260
312
  case "behaviour.grain":
261
313
  return "paper grain";
314
+ case "tip.grain":
315
+ return "tip grain";
316
+ case "dynamics.count":
317
+ return "several dabs per step";
262
318
  case "light":
263
319
  return "light";
264
320
  case "motion":
@@ -268,15 +324,15 @@ function hiddenLabel(issue, b) {
268
324
  case "dynamics.tilt":
269
325
  return "tilt dynamics";
270
326
  case "colour.default":
271
- return `a ${_chunkXVOP3CGQcjs.brushColourKind.call(void 0, b.colour.default)} default colour`;
327
+ return `a ${_chunkEU2ZZA6Lcjs.brushColourKind.call(void 0, b.colour.default)} default colour`;
272
328
  default:
273
329
  return null;
274
330
  }
275
331
  }
276
332
  var sentence = (words) => words.length < 2 ? _nullishCoalesce(words[0], () => ( "")) : `${words.slice(0, -1).join(", ")} and ${words.at(-1)}`;
277
- function brushEditorScope(brush, profile = _chunkXVOP3CGQcjs.PAINT_PROFILE) {
333
+ function brushEditorScope(brush, profile = _chunkEU2ZZA6Lcjs.PAINT_PROFILE) {
278
334
  const hidden = [];
279
- for (const issue of _chunkXVOP3CGQcjs.checkBrush.call(void 0, brush, profile).issues) {
335
+ for (const issue of _chunkEU2ZZA6Lcjs.checkBrush.call(void 0, brush, profile).issues) {
280
336
  const label = hiddenLabel(issue, brush);
281
337
  if (label && !hidden.some((h) => h.label === label)) hidden.push({ facet: issue.facet, label });
282
338
  }
@@ -289,17 +345,17 @@ function newBrushFor(profile, from, name = "New brush") {
289
345
  const id = brushIdFromName(name);
290
346
  const base = { id, name, kind: profile.kinds.includes("mark") ? "mark" : profile.kinds[0], ...profile.kinds.includes("mark") ? {} : { tool: profile.tools[0] }, tip: { shape: profile.tipShapes.includes("round") ? "round" : profile.tipShapes[0] } };
291
347
  if (from) {
292
- const stripped = _chunkXVOP3CGQcjs.degrade.call(void 0, from, profile, { ignored: true });
348
+ const stripped = _chunkEU2ZZA6Lcjs.degrade.call(void 0, from, profile, { ignored: true });
293
349
  if (!stripped.issues.length) return { ...stripped.brush, id, name };
294
350
  }
295
351
  return base;
296
352
  }
297
353
  async function saveBrushEdit(library, options2) {
298
354
  const { original, profile } = options2;
299
- const check = _chunkXVOP3CGQcjs.checkBrushEdit.call(void 0, original, options2.brush, profile);
355
+ const check = _chunkEU2ZZA6Lcjs.checkBrushEdit.call(void 0, original, options2.brush, profile);
300
356
  if (!check.ok) {
301
357
  const errors = check.issues.filter((i) => i.severity === "error").map((i) => `${i.facet}: ${i.reason} ${i.fix}`);
302
- throw new (0, _chunkXVOP3CGQcjs.BrushLibraryError)("invalid", `${profile.name} can't use this brush: ${errors.join(" ")} Nothing was saved.`);
358
+ throw new (0, _chunkEU2ZZA6Lcjs.BrushLibraryError)("invalid", `${profile.name} can't use this brush: ${errors.join(" ")} Nothing was saved.`);
303
359
  }
304
360
  const inPlace = !!original && options2.name === void 0 && !library.isBuiltin(original.id) && original.id === options2.brush.id && !!await library.get(original.id);
305
361
  let brush = options2.brush;
@@ -330,18 +386,20 @@ function setBrushEditorValue(brush, path, value) {
330
386
  next.marks = value === "continuous" ? void 0 : { kind: value };
331
387
  if (value !== "continuous" && next.tip.shape === "bristle") next.tip = { ...next.tip, shape: "round", bristle: void 0 };
332
388
  if (value === "objects") next.tip = { ...next.tip, shape: "stamp" };
389
+ } else if (path === "tip.grain.source" && (value === void 0 || value === "")) {
390
+ delete next.tip.grain;
333
391
  } else if (path === "tip.shape" && value !== "stamp") {
334
392
  next.tip = { ...next.tip, shape: value, stamps: void 0 };
335
393
  if (value !== "bristle") delete next.tip.bristle;
336
394
  } else if (value === void 0 || value === "") {
337
395
  const keys = path.split(".");
338
- const parent = _chunkXVOP3CGQcjs.getPath.call(void 0, next, keys.slice(0, -1).join("."));
396
+ const parent = _chunkEU2ZZA6Lcjs.getPath.call(void 0, next, keys.slice(0, -1).join("."));
339
397
  if (keys.length === 1) delete next[keys[0]];
340
398
  else if (parent) delete parent[keys[keys.length - 1]];
341
- } else _chunkXVOP3CGQcjs.setPath.call(void 0, next, path, value);
399
+ } else _chunkEU2ZZA6Lcjs.setPath.call(void 0, next, path, value);
342
400
  const problems = [];
343
401
  try {
344
- return { brush: _chunkXVOP3CGQcjs.normalizeBrushSpec.call(void 0, JSON.parse(JSON.stringify(next)), problems), problems };
402
+ return { brush: _chunkEU2ZZA6Lcjs.normalizeBrushSpec.call(void 0, JSON.parse(JSON.stringify(next)), problems), problems };
345
403
  } catch (e) {
346
404
  return { brush, problems: [e.message] };
347
405
  }
@@ -357,7 +415,7 @@ function brushLibraryGroups(entries, filter = {}) {
357
415
  const text = words([b.id, b.name, _nullishCoalesce(b.variety, () => ( "")), _nullishCoalesce(b.medium, () => ( "")), _nullishCoalesce(b.tool, () => ( "")), ..._nullishCoalesce(b.tags, () => ( []))].join(" "));
358
416
  if (!wanted.every((w) => text.some((t) => t.startsWith(w)))) continue;
359
417
  }
360
- if (filter.profile && !_chunkXVOP3CGQcjs.checkBrush.call(void 0, b, filter.profile).ok) continue;
418
+ if (filter.profile && !_chunkEU2ZZA6Lcjs.checkBrush.call(void 0, b, filter.profile).ok) continue;
361
419
  const id = b.kind === "tool" ? "tools" : _nullishCoalesce(b.medium, () => ( "other"));
362
420
  let g = groups.get(id);
363
421
  if (!g) groups.set(id, g = { id, title: id === "tools" ? "Tools" : id === "other" ? "Other" : MEDIUM_LABELS[id], varieties: [] });
@@ -369,6 +427,28 @@ function brushLibraryGroups(entries, filter = {}) {
369
427
  const order = ["dry", "ink", "paint", "spray", "other", "tools"];
370
428
  return [...groups.values()].sort((a, b) => order.indexOf(a.id) - order.indexOf(b.id));
371
429
  }
430
+ async function imageFileToPngDataUrl(file) {
431
+ const bytes = new Uint8Array(await file.arrayBuffer());
432
+ if (_chunkW5M3J7PVcjs.isPng.call(void 0, bytes)) return `data:image/png;base64,${_chunkW5M3J7PVcjs.bytesToBase64.call(void 0, bytes)}`;
433
+ const g = globalThis;
434
+ if (!g.createImageBitmap || !g.OffscreenCanvas) throw new (0, _chunkEU2ZZA6Lcjs.BrushSourceError)("Only PNG images can be read here; convert the image to PNG.");
435
+ const bitmap = await g.createImageBitmap(file);
436
+ const canvas = new g.OffscreenCanvas(bitmap.width, bitmap.height);
437
+ canvas.getContext("2d").drawImage(bitmap, 0, 0);
438
+ const png = new Uint8Array(await (await canvas.convertToBlob({ type: "image/png" })).arrayBuffer());
439
+ return `data:image/png;base64,${_chunkW5M3J7PVcjs.bytesToBase64.call(void 0, png)}`;
440
+ }
441
+ async function addBrushSourceFromFile(library, file, options2 = {}) {
442
+ const name = (_nullishCoalesce(_nullishCoalesce(options2.name, () => ( _optionalChain([file, 'access', _32 => _32.name, 'optionalAccess', _33 => _33.replace, 'call', _34 => _34(/\.[a-z0-9]+$/i, "")]))), () => ( "Image"))).trim() || "Image";
443
+ const result = await library.addSource({ name, image: await imageFileToPngDataUrl(file), origin: file.name, ...options2.channel ? { channel: options2.channel } : {} });
444
+ return { ...result, ref: _chunkEU2ZZA6Lcjs.brushSourceRef.call(void 0, result.source.id) };
445
+ }
446
+ function withBrushSource(brush, ref, as) {
447
+ if (as === "grain") return setBrushEditorValue(brush, "tip.grain.source", ref);
448
+ const stamp = brush.tip.shape === "stamp" ? { brush, problems: [] } : setBrushEditorValue(brush, "tip.shape", "stamp");
449
+ const r = setBrushEditorValue(stamp.brush, "tip.stamps", [ref]);
450
+ return { brush: r.brush, problems: [...stamp.problems, ...r.problems] };
451
+ }
372
452
 
373
453
  // src/brushes/contract.ts
374
454
  var BRUSH_CONTRACT_FORMAT_VERSION = 1;
@@ -379,11 +459,11 @@ var BRUSH_PROFILE_JSON_SCHEMA = {
379
459
  properties: {
380
460
  id: { type: "string", description: "paint, image, canvas, vector\u2026" },
381
461
  name: { type: "string", description: "Display name, used in messages." },
382
- kinds: { type: "array", items: { type: "string", enum: [..._chunkXVOP3CGQcjs.BRUSH_KINDS] }, description: "Brush kinds it has." },
383
- tools: { type: "array", items: { type: "string", enum: [..._chunkXVOP3CGQcjs.BRUSH_TOOLS] }, description: "Tools it has (kind tool)." },
384
- tipShapes: { type: "array", items: { type: "string", enum: [..._chunkXVOP3CGQcjs.BRUSH_TIP_SHAPES] }, description: "Tip shapes it renders." },
385
- marks: { type: "array", items: { type: "string", enum: [..._chunkXVOP3CGQcjs.BRUSH_MARKS_KINDS] }, description: "Marks kinds it renders." },
386
- surfaces: { type: "array", items: { type: "string", enum: [..._chunkXVOP3CGQcjs.BRUSH_SURFACES] }, description: "Surfaces it renders." },
462
+ kinds: { type: "array", items: { type: "string", enum: [..._chunkEU2ZZA6Lcjs.BRUSH_KINDS] }, description: "Brush kinds it has." },
463
+ tools: { type: "array", items: { type: "string", enum: [..._chunkEU2ZZA6Lcjs.BRUSH_TOOLS] }, description: "Tools it has (kind tool)." },
464
+ tipShapes: { type: "array", items: { type: "string", enum: [..._chunkEU2ZZA6Lcjs.BRUSH_TIP_SHAPES] }, description: "Tip shapes it renders." },
465
+ marks: { type: "array", items: { type: "string", enum: [..._chunkEU2ZZA6Lcjs.BRUSH_MARKS_KINDS] }, description: "Marks kinds it renders." },
466
+ surfaces: { type: "array", items: { type: "string", enum: [..._chunkEU2ZZA6Lcjs.BRUSH_SURFACES] }, description: "Surfaces it renders." },
387
467
  exchange: { ...SUPPORT, description: "Picking paint up and mixing it in." },
388
468
  reservoir: { ...SUPPORT, description: "The Mixer Brush reservoir." },
389
469
  grain: { ...SUPPORT, description: "Paper grain response." },
@@ -391,7 +471,9 @@ var BRUSH_PROFILE_JSON_SCHEMA = {
391
471
  motion: { ...SUPPORT, description: "Motion after painting." },
392
472
  colours: { type: "array", items: { type: "string", enum: ["solid", "dual", "gradient", "effect"] }, description: "Colour kinds a default colour may be." },
393
473
  velocity: { ...SUPPORT, description: "Velocity dynamics." },
394
- tilt: { ...SUPPORT, description: "Tilt dynamics." }
474
+ tilt: { ...SUPPORT, description: "Tilt dynamics." },
475
+ tipGrain: { ...SUPPORT, description: "Texture inside the tip (tip.grain); missing: none." },
476
+ dabCount: { ...SUPPORT, description: "Several dabs per spacing step (dynamics.count); missing: none." }
395
477
  },
396
478
  required: ["id", "name", "kinds", "tools", "tipShapes", "marks", "surfaces", "exchange", "reservoir", "grain", "light", "motion", "colours", "velocity", "tilt"],
397
479
  additionalProperties: false
@@ -402,13 +484,33 @@ var BRUSH_LIBRARY_FILE_JSON_SCHEMA = {
402
484
  properties: {
403
485
  format: { type: "string", const: "pixerate-brushes", description: 'Always "pixerate-brushes".' },
404
486
  version: { type: "integer", const: 2, description: "Always 2." },
405
- brushes: { type: "array", items: { $ref: "brush.schema.json" }, description: "The brushes, each a facet brush (brush.schema.json)." }
487
+ brushes: { type: "array", items: { $ref: "brush.schema.json" }, description: "The brushes, each a facet brush (brush.schema.json)." },
488
+ sources: {
489
+ type: "array",
490
+ maxItems: _chunkEU2ZZA6Lcjs.MAX_BRUSH_SOURCES,
491
+ description: "The image sources the brushes name (source:<id> in tip.stamps or tip.grain.source), bytes included.",
492
+ items: {
493
+ type: "object",
494
+ description: "An image source: a grey mask, 255 paints (or is a grain peak).",
495
+ properties: {
496
+ id: { type: "string", pattern: _chunkEU2ZZA6Lcjs.BRUSH_ID.source, description: "Source id." },
497
+ name: { type: "string", maxLength: 80, description: "Display name." },
498
+ width: { type: "integer", minimum: 1, maximum: _chunkEU2ZZA6Lcjs.MAX_BRUSH_SOURCE_SIZE, description: "Width in px." },
499
+ height: { type: "integer", minimum: 1, maximum: _chunkEU2ZZA6Lcjs.MAX_BRUSH_SOURCE_SIZE, description: "Height in px." },
500
+ image: { type: "string", pattern: "^data:image/png;base64,", description: "The mask as a greyscale PNG data: URL." },
501
+ licence: { type: "string", maxLength: 2e3, description: "The licence or terms it came with." },
502
+ origin: { type: "string", maxLength: 200, description: "Where it came from (a file or pack name)." }
503
+ },
504
+ required: ["id", "name", "width", "height", "image"],
505
+ additionalProperties: false
506
+ }
507
+ }
406
508
  },
407
509
  required: ["format", "version", "brushes"],
408
510
  additionalProperties: false
409
511
  };
410
512
  function migrationInputs() {
411
- const pick = (id) => _chunkXVOP3CGQcjs.brushToJSON.call(void 0, _chunkXVOP3CGQcjs.BUILTIN_BRUSHES.find((b) => b.id === id));
513
+ const pick = (id) => _chunkEU2ZZA6Lcjs.brushToJSON.call(void 0, _chunkEU2ZZA6Lcjs.BUILTIN_BRUSHES.find((b) => b.id === id));
412
514
  return [
413
515
  { id: "plain", name: "Plain" },
414
516
  pick("pencil-6b"),
@@ -430,34 +532,34 @@ function migrationInputs() {
430
532
  function editorSectionFixtures() {
431
533
  const ids = ["highlighter", "chalk", "watercolor-wash", "hair-strands", "mixer-brush", "blender", "spray-can"];
432
534
  return ids.flatMap(
433
- (id) => Object.keys(_chunkXVOP3CGQcjs.BRUSH_HOST_PROFILES).map((host) => {
434
- const brush = _chunkXVOP3CGQcjs.BRUSH_PRESETS.find((b) => b.id === id);
435
- return { brush: id, host, sections: brushEditorSections(brush, _chunkXVOP3CGQcjs.BRUSH_HOST_PROFILES[host]) };
535
+ (id) => Object.keys(_chunkEU2ZZA6Lcjs.BRUSH_HOST_PROFILES).map((host) => {
536
+ const brush = _chunkEU2ZZA6Lcjs.BRUSH_PRESETS.find((b) => b.id === id);
537
+ return { brush: id, host, sections: brushEditorSections(brush, _chunkEU2ZZA6Lcjs.BRUSH_HOST_PROFILES[host]) };
436
538
  })
437
539
  );
438
540
  }
439
541
  function toolDefinitions() {
440
- const library = _chunkXVOP3CGQcjs.createBrushLibrary.call(void 0, );
441
- const toolsFor = (host) => _chunkDT4YQUGTcjs.createBrushLibraryTools.call(void 0, library, { profile: _chunkXVOP3CGQcjs.BRUSH_HOST_PROFILES[host], renderPreview: () => ({ data: "", mimeType: "image/png" }) });
542
+ const library = _chunkEU2ZZA6Lcjs.createBrushLibrary.call(void 0, );
543
+ const toolsFor = (host) => _chunkYXP6X3SYcjs.createBrushLibraryTools.call(void 0, library, { profile: _chunkEU2ZZA6Lcjs.BRUSH_HOST_PROFILES[host], renderPreview: () => ({ data: "", mimeType: "image/png" }) });
442
544
  return {
443
545
  tools: toolsFor("paint").map((t) => ({ name: t.name, title: t.title, description: t.description, inputSchema: t.inputSchema, annotations: t.annotations })),
444
- hosts: Object.fromEntries(Object.keys(_chunkXVOP3CGQcjs.BRUSH_HOST_PROFILES).map((host) => [host, toolsFor(host).map((t) => t.name)]))
546
+ hosts: Object.fromEntries(Object.keys(_chunkEU2ZZA6Lcjs.BRUSH_HOST_PROFILES).map((host) => [host, toolsFor(host).map((t) => t.name)]))
445
547
  };
446
548
  }
447
549
  function brushContracts() {
448
- const presets = _chunkXVOP3CGQcjs.BRUSH_PRESETS.map((b) => JSON.parse(JSON.stringify(b)));
449
- const resolved = _chunkXVOP3CGQcjs.BRUSH_PRESETS.map((b) => ({ id: b.id, engine: _chunkXVOP3CGQcjs.resolveEngine.call(void 0, b), category: _chunkXVOP3CGQcjs.resolveCategory.call(void 0, b), brush: _chunkXVOP3CGQcjs.brushToJSON.call(void 0, _chunkXVOP3CGQcjs.resolveBrush.call(void 0, b)) }));
450
- const profiles = Object.values(_chunkXVOP3CGQcjs.BRUSH_HOST_PROFILES);
451
- const checks = _chunkXVOP3CGQcjs.BRUSH_PRESETS.map((b) => ({
550
+ const presets = _chunkEU2ZZA6Lcjs.BRUSH_PRESETS.map((b) => JSON.parse(JSON.stringify(b)));
551
+ const resolved = _chunkEU2ZZA6Lcjs.BRUSH_PRESETS.map((b) => ({ id: b.id, engine: _chunkEU2ZZA6Lcjs.resolveEngine.call(void 0, b), category: _chunkEU2ZZA6Lcjs.resolveCategory.call(void 0, b), brush: _chunkEU2ZZA6Lcjs.brushToJSON.call(void 0, _chunkEU2ZZA6Lcjs.resolveBrush.call(void 0, b)) }));
552
+ const profiles = Object.values(_chunkEU2ZZA6Lcjs.BRUSH_HOST_PROFILES);
553
+ const checks = _chunkEU2ZZA6Lcjs.BRUSH_PRESETS.map((b) => ({
452
554
  id: b.id,
453
555
  hosts: Object.fromEntries(profiles.map((p) => {
454
- const c = _chunkXVOP3CGQcjs.checkBrush.call(void 0, b, p);
556
+ const c = _chunkEU2ZZA6Lcjs.checkBrush.call(void 0, b, p);
455
557
  return [p.id, { ok: c.ok, issues: c.issues.map(({ facet, severity }) => ({ facet, severity })) }];
456
558
  }))
457
559
  }));
458
- const migrations = migrationInputs().map((input) => ({ input, output: _chunkXVOP3CGQcjs.migrateBrushV1.call(void 0, input), resolved: _chunkXVOP3CGQcjs.brushToJSON.call(void 0, _chunkXVOP3CGQcjs.resolveBrush.call(void 0, _chunkXVOP3CGQcjs.migrateBrushV1.call(void 0, input))), engine: _chunkXVOP3CGQcjs.normalizePaintBrush.call(void 0, input).engine }));
560
+ const migrations = migrationInputs().map((input) => ({ input, output: _chunkEU2ZZA6Lcjs.migrateBrushV1.call(void 0, input), resolved: _chunkEU2ZZA6Lcjs.brushToJSON.call(void 0, _chunkEU2ZZA6Lcjs.resolveBrush.call(void 0, _chunkEU2ZZA6Lcjs.migrateBrushV1.call(void 0, input))), engine: _chunkEU2ZZA6Lcjs.normalizePaintBrush.call(void 0, input).engine }));
459
561
  const files = {
460
- "brush.schema.json": { $schema: "https://json-schema.org/draft/2020-12/schema", $id: "brush.schema.json", ..._chunkXVOP3CGQcjs.BRUSH_JSON_SCHEMA },
562
+ "brush.schema.json": { $schema: "https://json-schema.org/draft/2020-12/schema", $id: "brush.schema.json", ..._chunkEU2ZZA6Lcjs.BRUSH_JSON_SCHEMA },
461
563
  "profile.schema.json": { $schema: "https://json-schema.org/draft/2020-12/schema", $id: "profile.schema.json", ...BRUSH_PROFILE_JSON_SCHEMA },
462
564
  "library-file.schema.json": { $schema: "https://json-schema.org/draft/2020-12/schema", $id: "library-file.schema.json", ...BRUSH_LIBRARY_FILE_JSON_SCHEMA },
463
565
  "presets.json": presets,
@@ -471,7 +573,7 @@ function brushContracts() {
471
573
  files["manifest.json"] = {
472
574
  domain: "brushes",
473
575
  formatVersion: BRUSH_CONTRACT_FORMAT_VERSION,
474
- limits: { maxPalette: _chunkXVOP3CGQcjs.MAX_BRUSH_PALETTE },
576
+ limits: { maxPalette: _chunkEU2ZZA6Lcjs.MAX_BRUSH_PALETTE },
475
577
  counts: { presets: presets.length, profiles: profiles.length, migrations: migrations.length, editorSections: files["editor-sections.json"].length },
476
578
  files: Object.keys(files).sort()
477
579
  };
@@ -537,4 +639,28 @@ function brushContracts() {
537
639
 
538
640
 
539
641
 
540
- exports.BRUSH_CONTRACT_FORMAT_VERSION = BRUSH_CONTRACT_FORMAT_VERSION; exports.BRUSH_FACET_SCHEMAS = _chunkXVOP3CGQcjs.BRUSH_FACET_SCHEMAS; exports.BRUSH_HOST_PROFILES = _chunkXVOP3CGQcjs.BRUSH_HOST_PROFILES; exports.BRUSH_JSON_SCHEMA = _chunkXVOP3CGQcjs.BRUSH_JSON_SCHEMA; exports.BRUSH_KINDS = _chunkXVOP3CGQcjs.BRUSH_KINDS; exports.BRUSH_LIBRARY_FILE_JSON_SCHEMA = BRUSH_LIBRARY_FILE_JSON_SCHEMA; exports.BRUSH_MARKS_KINDS = _chunkXVOP3CGQcjs.BRUSH_MARKS_KINDS; exports.BRUSH_MEDIA = _chunkXVOP3CGQcjs.BRUSH_MEDIA; exports.BRUSH_PATCH_PROPERTIES = _chunkDT4YQUGTcjs.BRUSH_PATCH_PROPERTIES; exports.BRUSH_PRESETS = _chunkXVOP3CGQcjs.BRUSH_PRESETS; exports.BRUSH_PREVIEW_DARK_PAPER = _chunkHIO6E7LCcjs.BRUSH_PREVIEW_DARK_PAPER; exports.BRUSH_PREVIEW_FALLBACKS = _chunkHIO6E7LCcjs.BRUSH_PREVIEW_FALLBACKS; exports.BRUSH_PREVIEW_MIN_CONTRAST = _chunkHIO6E7LCcjs.BRUSH_PREVIEW_MIN_CONTRAST; exports.BRUSH_PROFILE_JSON_SCHEMA = BRUSH_PROFILE_JSON_SCHEMA; exports.BRUSH_SURFACES = _chunkXVOP3CGQcjs.BRUSH_SURFACES; exports.BRUSH_TIP_SHAPES = _chunkXVOP3CGQcjs.BRUSH_TIP_SHAPES; exports.BRUSH_TOOLS = _chunkXVOP3CGQcjs.BRUSH_TOOLS; exports.BRUSH_UNSET_PROPERTY = _chunkDT4YQUGTcjs.BRUSH_UNSET_PROPERTY; exports.BRUSH_WORD = _chunkXVOP3CGQcjs.BRUSH_WORD; exports.BrushLibraryError = _chunkXVOP3CGQcjs.BrushLibraryError; exports.CANVAS_PROFILE = _chunkXVOP3CGQcjs.CANVAS_PROFILE; exports.IMAGE_PROFILE = _chunkXVOP3CGQcjs.IMAGE_PROFILE; exports.MAX_BRUSH_PALETTE = _chunkXVOP3CGQcjs.MAX_BRUSH_PALETTE; exports.PAINT_PROFILE = _chunkXVOP3CGQcjs.PAINT_PROFILE; exports.UNSETTABLE_BRUSH_FACETS = _chunkDT4YQUGTcjs.UNSETTABLE_BRUSH_FACETS; exports.VECTOR_PROFILE = _chunkXVOP3CGQcjs.VECTOR_PROFILE; exports.WET_BRUSH_TOOLS = _chunkXVOP3CGQcjs.WET_BRUSH_TOOLS; exports.brushColourHex = _chunkJBZRVLXVcjs.brushColourHex; exports.brushColourKind = _chunkXVOP3CGQcjs.brushColourKind; exports.brushContracts = brushContracts; exports.brushEditorScope = brushEditorScope; exports.brushEditorSections = brushEditorSections; exports.brushIdFromName = brushIdFromName; exports.brushLibraryGroups = brushLibraryGroups; exports.brushPreviewColours = _chunkHIO6E7LCcjs.brushPreviewColours; exports.brushPreviewStrength = _chunkHIO6E7LCcjs.brushPreviewStrength; exports.brushPreviewsOnDark = _chunkHIO6E7LCcjs.brushPreviewsOnDark; exports.changedBrushPaths = _chunkDT4YQUGTcjs.changedBrushPaths; exports.checkBrush = _chunkXVOP3CGQcjs.checkBrush; exports.checkBrushEdit = _chunkXVOP3CGQcjs.checkBrushEdit; exports.createBrushLibrary = _chunkXVOP3CGQcjs.createBrushLibrary; exports.createBrushLibraryToolProvider = _chunkDT4YQUGTcjs.createBrushLibraryToolProvider; exports.createBrushLibraryTools = _chunkDT4YQUGTcjs.createBrushLibraryTools; exports.degrade = _chunkXVOP3CGQcjs.degrade; exports.isBrushLibrary = _chunkXVOP3CGQcjs.isBrushLibrary; exports.listPresets = _chunkXVOP3CGQcjs.listPresets; exports.mergeBrushPatch = _chunkDT4YQUGTcjs.mergeBrushPatch; exports.migrateBrushV1 = _chunkXVOP3CGQcjs.migrateBrushV1; exports.newBrushFor = newBrushFor; exports.normalizeBrushSpec = _chunkXVOP3CGQcjs.normalizeBrushSpec; exports.parseBrushLibrary = _chunkXVOP3CGQcjs.parseBrushLibrary2; exports.resolveBrush = _chunkXVOP3CGQcjs.resolveBrush; exports.resolveCategory = _chunkXVOP3CGQcjs.resolveCategory; exports.resolveEngine = _chunkXVOP3CGQcjs.resolveEngine; exports.resolveWetMedium = _chunkXVOP3CGQcjs.resolveWetMedium; exports.saveBrushEdit = saveBrushEdit; exports.setBrushEditorValue = setBrushEditorValue; exports.snapshotBrushLibrary = _chunkXVOP3CGQcjs.snapshotBrushLibrary; exports.withKeptIssues = _chunkXVOP3CGQcjs.withKeptIssues;
642
+
643
+
644
+
645
+
646
+
647
+
648
+
649
+
650
+
651
+
652
+
653
+
654
+
655
+
656
+
657
+
658
+
659
+
660
+
661
+
662
+
663
+
664
+
665
+
666
+ exports.BRUSH_CONTRACT_FORMAT_VERSION = BRUSH_CONTRACT_FORMAT_VERSION; exports.BRUSH_FACET_SCHEMAS = _chunkEU2ZZA6Lcjs.BRUSH_FACET_SCHEMAS; exports.BRUSH_FLIPS = _chunkEU2ZZA6Lcjs.BRUSH_FLIPS; exports.BRUSH_GRAIN_MODES = _chunkEU2ZZA6Lcjs.BRUSH_GRAIN_MODES; exports.BRUSH_GRAIN_SOURCE_PATTERN = _chunkEU2ZZA6Lcjs.BRUSH_GRAIN_SOURCE_PATTERN; exports.BRUSH_HOST_PROFILES = _chunkEU2ZZA6Lcjs.BRUSH_HOST_PROFILES; exports.BRUSH_JSON_SCHEMA = _chunkEU2ZZA6Lcjs.BRUSH_JSON_SCHEMA; exports.BRUSH_KINDS = _chunkEU2ZZA6Lcjs.BRUSH_KINDS; exports.BRUSH_LIBRARY_FILE_JSON_SCHEMA = BRUSH_LIBRARY_FILE_JSON_SCHEMA; exports.BRUSH_MARKS_KINDS = _chunkEU2ZZA6Lcjs.BRUSH_MARKS_KINDS; exports.BRUSH_MEDIA = _chunkEU2ZZA6Lcjs.BRUSH_MEDIA; exports.BRUSH_PATCH_PROPERTIES = _chunkYXP6X3SYcjs.BRUSH_PATCH_PROPERTIES; exports.BRUSH_PRESETS = _chunkEU2ZZA6Lcjs.BRUSH_PRESETS; exports.BRUSH_PREVIEW_DARK_PAPER = _chunkBPJSI4NTcjs.BRUSH_PREVIEW_DARK_PAPER; exports.BRUSH_PREVIEW_FALLBACKS = _chunkBPJSI4NTcjs.BRUSH_PREVIEW_FALLBACKS; exports.BRUSH_PREVIEW_MIN_CONTRAST = _chunkBPJSI4NTcjs.BRUSH_PREVIEW_MIN_CONTRAST; exports.BRUSH_PROFILE_JSON_SCHEMA = BRUSH_PROFILE_JSON_SCHEMA; exports.BRUSH_SOURCE_CHANNELS = _chunkEU2ZZA6Lcjs.BRUSH_SOURCE_CHANNELS; exports.BRUSH_SOURCE_PROVIDER = _chunkEU2ZZA6Lcjs.BRUSH_SOURCE_PROVIDER; exports.BRUSH_SURFACES = _chunkEU2ZZA6Lcjs.BRUSH_SURFACES; exports.BRUSH_TIP_SHAPES = _chunkEU2ZZA6Lcjs.BRUSH_TIP_SHAPES; exports.BRUSH_TOOLS = _chunkEU2ZZA6Lcjs.BRUSH_TOOLS; exports.BRUSH_UNSET_PROPERTY = _chunkYXP6X3SYcjs.BRUSH_UNSET_PROPERTY; exports.BRUSH_WORD = _chunkEU2ZZA6Lcjs.BRUSH_WORD; exports.BrushLibraryError = _chunkEU2ZZA6Lcjs.BrushLibraryError; exports.BrushSourceError = _chunkEU2ZZA6Lcjs.BrushSourceError; exports.CANVAS_PROFILE = _chunkEU2ZZA6Lcjs.CANVAS_PROFILE; exports.IMAGE_PROFILE = _chunkEU2ZZA6Lcjs.IMAGE_PROFILE; exports.MAX_BRUSH_PALETTE = _chunkEU2ZZA6Lcjs.MAX_BRUSH_PALETTE; exports.MAX_BRUSH_SOURCES = _chunkEU2ZZA6Lcjs.MAX_BRUSH_SOURCES; exports.MAX_BRUSH_SOURCE_INPUT_PIXELS = _chunkEU2ZZA6Lcjs.MAX_BRUSH_SOURCE_INPUT_PIXELS; exports.MAX_BRUSH_SOURCE_SIZE = _chunkEU2ZZA6Lcjs.MAX_BRUSH_SOURCE_SIZE; exports.PAINT_PROFILE = _chunkEU2ZZA6Lcjs.PAINT_PROFILE; exports.TIP_GRAIN_PATTERNS = _chunkEU2ZZA6Lcjs.TIP_GRAIN_PATTERNS; exports.UNSETTABLE_BRUSH_FACETS = _chunkYXP6X3SYcjs.UNSETTABLE_BRUSH_FACETS; exports.VECTOR_PROFILE = _chunkEU2ZZA6Lcjs.VECTOR_PROFILE; exports.WET_BRUSH_TOOLS = _chunkEU2ZZA6Lcjs.WET_BRUSH_TOOLS; exports.addBrushSourceFromFile = addBrushSourceFromFile; exports.brushColourHex = _chunkJBZRVLXVcjs.brushColourHex; exports.brushColourKind = _chunkEU2ZZA6Lcjs.brushColourKind; exports.brushContracts = brushContracts; exports.brushEditorScope = brushEditorScope; exports.brushEditorSections = brushEditorSections; exports.brushIdFromName = brushIdFromName; exports.brushLibraryGroups = brushLibraryGroups; exports.brushPreviewColours = _chunkBPJSI4NTcjs.brushPreviewColours; exports.brushPreviewStrength = _chunkBPJSI4NTcjs.brushPreviewStrength; exports.brushPreviewsOnDark = _chunkBPJSI4NTcjs.brushPreviewsOnDark; exports.brushSourceFromMask = _chunkEU2ZZA6Lcjs.brushSourceFromMask; exports.brushSourceIdFromName = _chunkEU2ZZA6Lcjs.brushSourceIdFromName; exports.brushSourceIdOf = _chunkEU2ZZA6Lcjs.brushSourceIdOf; exports.brushSourceIds = _chunkEU2ZZA6Lcjs.brushSourceIds; exports.brushSourceRef = _chunkEU2ZZA6Lcjs.brushSourceRef; exports.changedBrushPaths = _chunkYXP6X3SYcjs.changedBrushPaths; exports.checkBrush = _chunkEU2ZZA6Lcjs.checkBrush; exports.checkBrushEdit = _chunkEU2ZZA6Lcjs.checkBrushEdit; exports.createBrushLibrary = _chunkEU2ZZA6Lcjs.createBrushLibrary; exports.createBrushLibraryToolProvider = _chunkYXP6X3SYcjs.createBrushLibraryToolProvider; exports.createBrushLibraryTools = _chunkYXP6X3SYcjs.createBrushLibraryTools; exports.decodeBrushSource = _chunkEU2ZZA6Lcjs.decodeBrushSource; exports.degrade = _chunkEU2ZZA6Lcjs.degrade; exports.imageFileToPngDataUrl = imageFileToPngDataUrl; exports.isBrushLibrary = _chunkEU2ZZA6Lcjs.isBrushLibrary; exports.isBrushSourceRef = _chunkEU2ZZA6Lcjs.isBrushSourceRef; exports.listPresets = _chunkEU2ZZA6Lcjs.listPresets; exports.makeBrushSource = _chunkEU2ZZA6Lcjs.makeBrushSource; exports.mergeBrushPatch = _chunkYXP6X3SYcjs.mergeBrushPatch; exports.migrateBrushV1 = _chunkEU2ZZA6Lcjs.migrateBrushV1; exports.newBrushFor = newBrushFor; exports.normalizeBrushSpec = _chunkEU2ZZA6Lcjs.normalizeBrushSpec; exports.parseBrushLibrary = _chunkEU2ZZA6Lcjs.parseBrushLibrary2; exports.parseBrushLibrarySources = _chunkEU2ZZA6Lcjs.parseBrushLibrarySources; exports.readBrushSource = _chunkEU2ZZA6Lcjs.readBrushSource; exports.resolveBrush = _chunkEU2ZZA6Lcjs.resolveBrush; exports.resolveCategory = _chunkEU2ZZA6Lcjs.resolveCategory; exports.resolveEngine = _chunkEU2ZZA6Lcjs.resolveEngine; exports.resolveWetMedium = _chunkEU2ZZA6Lcjs.resolveWetMedium; exports.saveBrushEdit = saveBrushEdit; exports.setBrushEditorValue = setBrushEditorValue; exports.snapshotBrushLibrary = _chunkEU2ZZA6Lcjs.snapshotBrushLibrary; exports.summarizeBrushSource = _chunkEU2ZZA6Lcjs.summarizeBrushSource; exports.withBrushSource = withBrushSource; exports.withKeptIssues = _chunkEU2ZZA6Lcjs.withKeptIssues;
@@ -9,7 +9,8 @@
9
9
  * renderer is bundled.
10
10
  */
11
11
  export * from './types.cjs';
12
- export { BRUSH_FACET_SCHEMAS, BRUSH_JSON_SCHEMA, BRUSH_WORD, MAX_BRUSH_PALETTE } from './schema.cjs';
12
+ export { BRUSH_FACET_SCHEMAS, BRUSH_GRAIN_SOURCE_PATTERN, BRUSH_JSON_SCHEMA, BRUSH_WORD, MAX_BRUSH_PALETTE, TIP_GRAIN_PATTERNS } from './schema.cjs';
13
+ export { BRUSH_SOURCE_CHANNELS, BRUSH_SOURCE_PROVIDER, brushSourceFromMask, brushSourceIdFromName, brushSourceIdOf, brushSourceIds, brushSourceRef, BrushSourceError, decodeBrushSource, isBrushSourceRef, makeBrushSource, MAX_BRUSH_SOURCE_INPUT_PIXELS, MAX_BRUSH_SOURCE_SIZE, MAX_BRUSH_SOURCES, readBrushSource, summarizeBrushSource, type BrushSource, type BrushSourceChannel, type BrushSourceInput, type BrushSourceSummary, type DecodedBrushSource, } from './sources.cjs';
13
14
  export { normalizeBrushSpec, WET_BRUSH_TOOLS } from './normalize.cjs';
14
15
  export { resolveBrush, resolveCategory, resolveEngine, resolveWetMedium, type ResolveBrushOptions } from './resolve.cjs';
15
16
  export { migrateBrushV1 } from './migrate.cjs';
@@ -18,7 +19,7 @@ export { brushColourHex } from './colour.cjs';
18
19
  export { BRUSH_PREVIEW_DARK_PAPER, BRUSH_PREVIEW_FALLBACKS, BRUSH_PREVIEW_MIN_CONTRAST, brushPreviewColours, brushPreviewsOnDark, brushPreviewStrength, type BrushPreviewColours, type BrushPreviewColoursOptions } from './preview.cjs';
19
20
  export { BRUSH_CONTRACT_FORMAT_VERSION, BRUSH_LIBRARY_FILE_JSON_SCHEMA, BRUSH_PROFILE_JSON_SCHEMA, brushContracts } from './contract.cjs';
20
21
  export { BRUSH_HOST_PROFILES, brushColourKind, CANVAS_PROFILE, checkBrush, checkBrushEdit, degrade, withKeptIssues, IMAGE_PROFILE, listPresets, PAINT_PROFILE, VECTOR_PROFILE, type BrushCheck, type BrushEditCheck, type BrushColourKind, type BrushHostProfile, type BrushIssue, type BrushSupport, type DegradedBrush } from './profiles.cjs';
21
- export { BrushLibraryError, createBrushLibrary, isBrushLibrary, parseBrushLibrary, snapshotBrushLibrary, type BrushFilter, type BrushLibrary, type BrushLibraryEntry, type BrushLibraryErrorCode, type BrushLibraryFile, type BrushLibrarySnapshot, type BrushLibraryOptions, type ImportBrushesResult, type SaveBrushResult, } from './library.cjs';
22
+ export { BrushLibraryError, createBrushLibrary, isBrushLibrary, parseBrushLibrary, parseBrushLibrarySources, type AddBrushSourceResult, snapshotBrushLibrary, type BrushFilter, type BrushLibrary, type BrushLibraryEntry, type BrushLibraryErrorCode, type BrushLibraryFile, type BrushLibrarySnapshot, type BrushLibraryOptions, type ImportBrushesResult, type SaveBrushResult, } from './library.cjs';
22
23
  export { BRUSH_PATCH_PROPERTIES, BRUSH_UNSET_PROPERTY, changedBrushPaths, mergeBrushPatch, UNSETTABLE_BRUSH_FACETS, type BrushPatch, type UnsettableBrushFacet } from './patch.cjs';
23
- export { brushEditorScope, brushEditorSections, brushIdFromName, brushLibraryGroups, newBrushFor, saveBrushEdit, setBrushEditorValue, type BrushEditorHiddenFacet, type BrushEditorScope, type SaveBrushEditOptions, type SaveBrushEditResult, type BrushEditorControl, type BrushEditorSection, type BrushEditorSectionId, type BrushLibraryGroup, } from './ui.cjs';
24
+ export { addBrushSourceFromFile, brushEditorScope, brushEditorSections, imageFileToPngDataUrl, withBrushSource, brushIdFromName, brushLibraryGroups, newBrushFor, saveBrushEdit, setBrushEditorValue, type BrushEditorHiddenFacet, type BrushEditorScope, type SaveBrushEditOptions, type SaveBrushEditResult, type BrushEditorContext, type BrushEditorControl, type BrushEditorSection, type BrushEditorSectionId, type BrushLibraryGroup, } from './ui.cjs';
24
25
  export { createBrushLibraryToolProvider, createBrushLibraryTools, type BrushLibraryToolProviderOptions, type BrushPreviewImage, type BrushPreviewRenderer } from './tools.cjs';
@@ -9,7 +9,8 @@
9
9
  * renderer is bundled.
10
10
  */
11
11
  export * from './types.js';
12
- export { BRUSH_FACET_SCHEMAS, BRUSH_JSON_SCHEMA, BRUSH_WORD, MAX_BRUSH_PALETTE } from './schema.js';
12
+ export { BRUSH_FACET_SCHEMAS, BRUSH_GRAIN_SOURCE_PATTERN, BRUSH_JSON_SCHEMA, BRUSH_WORD, MAX_BRUSH_PALETTE, TIP_GRAIN_PATTERNS } from './schema.js';
13
+ export { BRUSH_SOURCE_CHANNELS, BRUSH_SOURCE_PROVIDER, brushSourceFromMask, brushSourceIdFromName, brushSourceIdOf, brushSourceIds, brushSourceRef, BrushSourceError, decodeBrushSource, isBrushSourceRef, makeBrushSource, MAX_BRUSH_SOURCE_INPUT_PIXELS, MAX_BRUSH_SOURCE_SIZE, MAX_BRUSH_SOURCES, readBrushSource, summarizeBrushSource, type BrushSource, type BrushSourceChannel, type BrushSourceInput, type BrushSourceSummary, type DecodedBrushSource, } from './sources.js';
13
14
  export { normalizeBrushSpec, WET_BRUSH_TOOLS } from './normalize.js';
14
15
  export { resolveBrush, resolveCategory, resolveEngine, resolveWetMedium, type ResolveBrushOptions } from './resolve.js';
15
16
  export { migrateBrushV1 } from './migrate.js';
@@ -18,7 +19,7 @@ export { brushColourHex } from './colour.js';
18
19
  export { BRUSH_PREVIEW_DARK_PAPER, BRUSH_PREVIEW_FALLBACKS, BRUSH_PREVIEW_MIN_CONTRAST, brushPreviewColours, brushPreviewsOnDark, brushPreviewStrength, type BrushPreviewColours, type BrushPreviewColoursOptions } from './preview.js';
19
20
  export { BRUSH_CONTRACT_FORMAT_VERSION, BRUSH_LIBRARY_FILE_JSON_SCHEMA, BRUSH_PROFILE_JSON_SCHEMA, brushContracts } from './contract.js';
20
21
  export { BRUSH_HOST_PROFILES, brushColourKind, CANVAS_PROFILE, checkBrush, checkBrushEdit, degrade, withKeptIssues, IMAGE_PROFILE, listPresets, PAINT_PROFILE, VECTOR_PROFILE, type BrushCheck, type BrushEditCheck, type BrushColourKind, type BrushHostProfile, type BrushIssue, type BrushSupport, type DegradedBrush } from './profiles.js';
21
- export { BrushLibraryError, createBrushLibrary, isBrushLibrary, parseBrushLibrary, snapshotBrushLibrary, type BrushFilter, type BrushLibrary, type BrushLibraryEntry, type BrushLibraryErrorCode, type BrushLibraryFile, type BrushLibrarySnapshot, type BrushLibraryOptions, type ImportBrushesResult, type SaveBrushResult, } from './library.js';
22
+ export { BrushLibraryError, createBrushLibrary, isBrushLibrary, parseBrushLibrary, parseBrushLibrarySources, type AddBrushSourceResult, snapshotBrushLibrary, type BrushFilter, type BrushLibrary, type BrushLibraryEntry, type BrushLibraryErrorCode, type BrushLibraryFile, type BrushLibrarySnapshot, type BrushLibraryOptions, type ImportBrushesResult, type SaveBrushResult, } from './library.js';
22
23
  export { BRUSH_PATCH_PROPERTIES, BRUSH_UNSET_PROPERTY, changedBrushPaths, mergeBrushPatch, UNSETTABLE_BRUSH_FACETS, type BrushPatch, type UnsettableBrushFacet } from './patch.js';
23
- export { brushEditorScope, brushEditorSections, brushIdFromName, brushLibraryGroups, newBrushFor, saveBrushEdit, setBrushEditorValue, type BrushEditorHiddenFacet, type BrushEditorScope, type SaveBrushEditOptions, type SaveBrushEditResult, type BrushEditorControl, type BrushEditorSection, type BrushEditorSectionId, type BrushLibraryGroup, } from './ui.js';
24
+ export { addBrushSourceFromFile, brushEditorScope, brushEditorSections, imageFileToPngDataUrl, withBrushSource, brushIdFromName, brushLibraryGroups, newBrushFor, saveBrushEdit, setBrushEditorValue, type BrushEditorHiddenFacet, type BrushEditorScope, type SaveBrushEditOptions, type SaveBrushEditResult, type BrushEditorContext, type BrushEditorControl, type BrushEditorSection, type BrushEditorSectionId, type BrushLibraryGroup, } from './ui.js';
24
25
  export { createBrushLibraryToolProvider, createBrushLibraryTools, type BrushLibraryToolProviderOptions, type BrushPreviewImage, type BrushPreviewRenderer } from './tools.js';