@pixerate/editor 0.281.0 → 0.282.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 (57) hide show
  1. package/dist/animation-editor/index.cjs +59 -59
  2. package/dist/animation-editor/index.js +3 -3
  3. package/dist/brushes/index.cjs +76 -71
  4. package/dist/brushes/index.js +13 -8
  5. package/dist/brushes/schema.d.cts +1 -0
  6. package/dist/brushes/schema.d.ts +1 -0
  7. package/dist/brushes/types.d.cts +29 -3
  8. package/dist/brushes/types.d.ts +29 -3
  9. package/dist/{brushstrokeRender-EIK3625T.js → brushstrokeRender-4MA5CBW3.js} +3 -3
  10. package/dist/{brushstrokeRender-7HOMKZNO.cjs → brushstrokeRender-JVKVKWDA.cjs} +15 -15
  11. package/dist/canvas/index.cjs +13 -13
  12. package/dist/canvas/index.js +1 -1
  13. package/dist/{chunk-NJDEKU5D.js → chunk-2YGYYVSG.js} +1 -1
  14. package/dist/{chunk-JVFGK3DK.cjs → chunk-3JUKN3RM.cjs} +76 -53
  15. package/dist/{chunk-HN57I7KT.js → chunk-CG3FBNXE.js} +1 -1
  16. package/dist/{chunk-WFSYZO5Z.cjs → chunk-DFZ4I7EZ.cjs} +1 -1
  17. package/dist/{chunk-LZE2QZCT.js → chunk-IHMMHZGP.js} +1 -1
  18. package/dist/{chunk-K4ANWBF7.cjs → chunk-IJQ2YH2M.cjs} +35 -35
  19. package/dist/{chunk-HRUK5TFW.cjs → chunk-IUKPQCYQ.cjs} +164 -71
  20. package/dist/{chunk-WCC4NXWY.cjs → chunk-JD63B33V.cjs} +86 -86
  21. package/dist/{chunk-NJLPW4MM.js → chunk-KV7QHPKL.js} +27 -4
  22. package/dist/{chunk-H3EME3S5.cjs → chunk-MRIAR5DY.cjs} +2 -2
  23. package/dist/{chunk-ROURHJYT.js → chunk-NEDS6DSC.js} +1 -1
  24. package/dist/{chunk-FKO3CY2C.js → chunk-ULPXJ3NB.js} +1 -1
  25. package/dist/{chunk-WZYRYYXN.cjs → chunk-UOM5S4W5.cjs} +12 -12
  26. package/dist/{chunk-YOM76H2V.js → chunk-VEX6YVNG.js} +1 -1
  27. package/dist/{chunk-MNHP3AQT.cjs → chunk-WEXWDBUF.cjs} +23 -23
  28. package/dist/{chunk-PPDUCZZV.js → chunk-YRGTQLDN.js} +122 -29
  29. package/dist/image-editor/index.cjs +788 -788
  30. package/dist/image-editor/index.js +4 -4
  31. package/dist/paint-editor/antimatter.d.cts +154 -0
  32. package/dist/paint-editor/antimatter.d.ts +154 -0
  33. package/dist/paint-editor/brush-controls.d.cts +1 -0
  34. package/dist/paint-editor/brush-controls.d.ts +1 -0
  35. package/dist/paint-editor/brush-model.d.cts +9 -5
  36. package/dist/paint-editor/brush-model.d.ts +9 -5
  37. package/dist/paint-editor/controller.d.cts +23 -0
  38. package/dist/paint-editor/controller.d.ts +23 -0
  39. package/dist/paint-editor/index.cjs +1931 -891
  40. package/dist/paint-editor/index.d.cts +1 -0
  41. package/dist/paint-editor/index.d.ts +1 -0
  42. package/dist/paint-editor/index.js +1098 -58
  43. package/dist/paint-editor/mark-settings.d.cts +30 -0
  44. package/dist/paint-editor/mark-settings.d.ts +30 -0
  45. package/dist/paint-editor/stroke.d.cts +16 -0
  46. package/dist/paint-editor/stroke.d.ts +16 -0
  47. package/dist/paint-editor/strokes.d.cts +2 -0
  48. package/dist/paint-editor/strokes.d.ts +2 -0
  49. package/dist/paint-editor/ui.d.cts +4 -2
  50. package/dist/paint-editor/ui.d.ts +4 -2
  51. package/dist/vector/index.cjs +999 -999
  52. package/dist/vector/index.js +4 -4
  53. package/package.json +1 -1
  54. package/skills/pixerate-brushes/SKILL.md +1 -1
  55. package/skills/pixerate-paint-editor/SKILL.md +8 -2
  56. package/skills/pixerate-paint-editor/references/antimatter.md +49 -0
  57. package/skills/pixerate-paint-editor/references/brush-facets.md +1 -0
@@ -18,7 +18,7 @@
18
18
 
19
19
 
20
20
 
21
- var _chunkJVFGK3DKcjs = require('../chunk-JVFGK3DK.cjs');
21
+ var _chunk3JUKN3RMcjs = require('../chunk-3JUKN3RM.cjs');
22
22
  require('../chunk-PFHJS4HE.cjs');
23
23
 
24
24
 
@@ -27,7 +27,7 @@ require('../chunk-PFHJS4HE.cjs');
27
27
 
28
28
 
29
29
 
30
- var _chunkH3EME3S5cjs = require('../chunk-H3EME3S5.cjs');
30
+ var _chunkMRIAR5DYcjs = require('../chunk-MRIAR5DY.cjs');
31
31
 
32
32
 
33
33
 
@@ -102,7 +102,8 @@ var _chunkH3EME3S5cjs = require('../chunk-H3EME3S5.cjs');
102
102
 
103
103
 
104
104
 
105
- var _chunkHRUK5TFWcjs = require('../chunk-HRUK5TFW.cjs');
105
+
106
+ var _chunkIUKPQCYQcjs = require('../chunk-IUKPQCYQ.cjs');
106
107
 
107
108
 
108
109
  var _chunkRDFHWAHLcjs = require('../chunk-RDFHWAHL.cjs');
@@ -138,7 +139,7 @@ var options = (values, labels = {}) => values.map((value) => ({ value, label: _n
138
139
  var MEDIUM_LABELS = { paint: "Paint", ink: "Ink", dry: "Dry media", spray: "Spray" };
139
140
  var TOOL_LABELS = { eraser: "Eraser", blender: "Blender", smudge: "Smudge", water: "Add water", dryer: "Dry", blow: "Blow", blot: "Blot", "wet-blend": "Wet blend", "wet-drag": "Wet smudge" };
140
141
  var SHAPE_LABELS = { round: "Round", flat: "Flat", chisel: "Chisel", bristle: "Bristle", stamp: "Stamp" };
141
- var MARKS_LABELS = { continuous: "Continuous", particles: "Particles", objects: "Stickers" };
142
+ var MARKS_LABELS = { continuous: "Continuous", particles: "Particles", objects: "Stickers", antimatter: "Antimatter" };
142
143
  var SURFACE_LABELS = { dry: "Dry", wet: "Wet (flows)", thick: "Thick (impasto)" };
143
144
  var ROTATION_LABELS = { fixed: "Fixed", stroke: "Follow stroke", barrel: "Pen barrel" };
144
145
  var PHYSICS_LABELS = { string: "Strings", spring: "Springs", orbit: "Orbits", flow: "Flow", elastic: "Elastic", fuzzy: "Fuzzy" };
@@ -159,6 +160,7 @@ var ARIA = {
159
160
  "marks.ribbonPairs": "Ribbon pairs",
160
161
  "marks.stampOrder": "Dot texture order",
161
162
  "marks.stampRotation": "Dot rotation",
163
+ "marks.invertMode": "Inversion mode",
162
164
  "behaviour.wet.medium": "Wet medium",
163
165
  "behaviour.thick.applicator": "Impasto tool",
164
166
  "light.pressure": "Light follows pressure",
@@ -167,20 +169,20 @@ var ARIA = {
167
169
  "livePatterns.pattern": "Live pattern"
168
170
  };
169
171
  var aria = (path, label) => _nullishCoalesce(ARIA[path], () => ( `Brush ${label.toLowerCase()}`));
170
- function brushEditorSections(brush, profile = _chunkHRUK5TFWcjs.PAINT_PROFILE, context = {}) {
171
- const resolved = _chunkHRUK5TFWcjs.resolveBrush.call(void 0, brush);
172
- const engine = _chunkHRUK5TFWcjs.resolveEngine.call(void 0, brush);
173
- const fieldOf = (facet) => _chunkHRUK5TFWcjs.BRUSH_FIELDS.find((f) => f.facet === facet && f.engines.includes(engine));
174
- const facetOf = (enginePath) => _optionalChain([(_nullishCoalesce(_chunkHRUK5TFWcjs.BRUSH_FIELDS.find((f) => f.engine === enginePath && f.engines.includes(engine)), () => ( _chunkHRUK5TFWcjs.BRUSH_FIELDS.find((f) => f.engine === enginePath)))), 'optionalAccess', _ => _.facet]);
172
+ function brushEditorSections(brush, profile = _chunkIUKPQCYQcjs.PAINT_PROFILE, context = {}) {
173
+ const resolved = _chunkIUKPQCYQcjs.resolveBrush.call(void 0, brush);
174
+ const engine = _chunkIUKPQCYQcjs.resolveEngine.call(void 0, brush);
175
+ const fieldOf = (facet) => _chunkIUKPQCYQcjs.BRUSH_FIELDS.find((f) => f.facet === facet && f.engines.includes(engine));
176
+ const facetOf = (enginePath) => _optionalChain([(_nullishCoalesce(_chunkIUKPQCYQcjs.BRUSH_FIELDS.find((f) => f.engine === enginePath && f.engines.includes(engine)), () => ( _chunkIUKPQCYQcjs.BRUSH_FIELDS.find((f) => f.engine === enginePath)))), 'optionalAccess', _ => _.facet]);
175
177
  const value = (path) => {
176
- const own = _chunkHRUK5TFWcjs.getPath.call(void 0, brush, path);
178
+ const own = _chunkIUKPQCYQcjs.getPath.call(void 0, brush, path);
177
179
  if (own !== void 0) return own;
178
180
  const field = fieldOf(path);
179
181
  if (!field) return void 0;
180
- const v = _chunkHRUK5TFWcjs.getPath.call(void 0, resolved, field.engine);
182
+ const v = _chunkIUKPQCYQcjs.getPath.call(void 0, resolved, field.engine);
181
183
  return field.map ? field.map.toFacet(v) : v;
182
184
  };
183
- const issues = _chunkHRUK5TFWcjs.checkBrush.call(void 0, brush, profile).issues;
185
+ const issues = _chunkIUKPQCYQcjs.checkBrush.call(void 0, brush, profile).issues;
184
186
  const warned = new Map(issues.filter((i) => i.severity === "warning").map((i) => [i.facet, i.reason]));
185
187
  const refused = new Set(issues.filter((i) => i.severity === "error").map((i) => i.facet));
186
188
  const full = (s) => s === "full";
@@ -205,30 +207,30 @@ function brushEditorSections(brush, profile = _chunkHRUK5TFWcjs.PAINT_PROFILE, c
205
207
  };
206
208
  const isTool = brush.kind === "tool";
207
209
  const shape = brush.tip.shape;
208
- const sourceOptions = (_nullishCoalesce(context.sources, () => ( []))).map((x) => ({ value: _chunkHRUK5TFWcjs.brushSourceRef.call(void 0, x.id), label: x.name }));
210
+ const sourceOptions = (_nullishCoalesce(context.sources, () => ( []))).map((x) => ({ value: _chunkIUKPQCYQcjs.brushSourceRef.call(void 0, x.id), label: x.name }));
209
211
  const marks = _nullishCoalesce(_optionalChain([brush, 'access', _7 => _7.marks, 'optionalAccess', _8 => _8.kind]), () => ( "continuous"));
210
212
  const exchangeFull = full(_optionalChain([brush, 'access', _9 => _9.behaviour, 'optionalAccess', _10 => _10.exchange, 'optionalAccess', _11 => _11.reservoir]) ? profile.reservoir : profile.exchange);
211
213
  add("medium", "Medium", [
212
214
  profile.kinds.length > 1 && profile.kinds.includes(brush.kind) && choice("kind", "Kind", options(profile.kinds, { mark: "Lays a material", tool: "Works on paint" })),
213
- !isTool && choice("medium", "Medium", [{ value: "", label: "Any" }, ...options(_chunkHRUK5TFWcjs.BRUSH_MEDIA, MEDIUM_LABELS)], ""),
215
+ !isTool && choice("medium", "Medium", [{ value: "", label: "Any" }, ...options(_chunkIUKPQCYQcjs.BRUSH_MEDIA, MEDIUM_LABELS)], ""),
214
216
  !isTool && withNote({ kind: "text", path: "variety", label: "Variety", ariaLabel: "Brush variety", value: _nullishCoalesce(brush.variety, () => ( "")), placeholder: "pencil, oil, highlighter\u2026" }),
215
217
  isTool && !!brush.tool && profile.tools.includes(brush.tool) && choice("tool", "Tool", options(profile.tools, TOOL_LABELS))
216
218
  ]);
217
- const engineControls = _chunkJVFGK3DKcjs.brushEngineControls.call(void 0, resolved);
218
- const tipSliders = _chunkJVFGK3DKcjs.BRUSH_SLIDERS.filter((c) => c.path.startsWith("tip.") && c.path !== "tip.smoothing");
219
+ const engineControls = _chunk3JUKN3RMcjs.brushEngineControls.call(void 0, resolved);
220
+ const tipSliders = _chunk3JUKN3RMcjs.BRUSH_SLIDERS.filter((c) => c.path.startsWith("tip.") && c.path !== "tip.smoothing");
219
221
  add("shape", "Shape", [
220
222
  !refused.has("tip.shape") && choice("tip.shape", "Tip", options(profile.tipShapes.filter((s) => !isTool || s !== "stamp"), SHAPE_LABELS)),
221
223
  ...tipSliders.map((c) => slider(c)),
222
224
  engine === "dab" && choice("tip.blend", "Blend", _chunk2VX55NFMcjs.BLEND_MODES.map((m) => ({ value: m.id, label: m.label })), "normal"),
223
225
  ...shape === "bristle" && engineOk && engineControls ? engineControls.sliders.filter((c) => c.path.startsWith("bristle.")).map((c) => slider(c)) : [],
224
- shape === "stamp" && engineOk && withNote({ kind: "stamps", path: "tip.stamps", label: "Stamps", ariaLabel: "Brush stamps", value: [..._nullishCoalesce(value("tip.stamps"), () => ( []))], max: marks === "particles" ? _chunkHRUK5TFWcjs.MAX_DOT_TEXTURES : _chunkHRUK5TFWcjs.MAX_STICKER_STAMPS, options: marks === "objects" ? STAMP_OPTIONS : [...STAMP_OPTIONS, ...sourceOptions] })
226
+ shape === "stamp" && engineOk && withNote({ kind: "stamps", path: "tip.stamps", label: "Stamps", ariaLabel: "Brush stamps", value: [..._nullishCoalesce(value("tip.stamps"), () => ( []))], max: marks === "particles" ? _chunkIUKPQCYQcjs.MAX_DOT_TEXTURES : _chunkIUKPQCYQcjs.MAX_STICKER_STAMPS, options: marks === "objects" ? STAMP_OPTIONS : [...STAMP_OPTIONS, ...sourceOptions] })
225
227
  ]);
226
228
  const dabs = DAB_ENGINES.has(engine) && engineOk && shape !== "bristle" && marks !== "particles" && marks !== "objects";
227
229
  const grain = brush.tip.grain;
228
230
  if (dabs && full(_nullishCoalesce(profile.tipGrain, () => ( "none")))) {
229
231
  add("grain", "Tip grain", [
230
- choice("tip.grain.source", "Texture", [{ value: "", label: "None" }, ...options(_chunkHRUK5TFWcjs.TIP_GRAIN_PATTERNS), ...sourceOptions], ""),
231
- !!grain && choice("tip.grain.mode", "Grain moves", options(_chunkHRUK5TFWcjs.BRUSH_GRAIN_MODES, GRAIN_MODE_LABELS), "texturised"),
232
+ choice("tip.grain.source", "Texture", [{ value: "", label: "None" }, ...options(_chunkIUKPQCYQcjs.TIP_GRAIN_PATTERNS), ...sourceOptions], ""),
233
+ !!grain && choice("tip.grain.mode", "Grain moves", options(_chunkIUKPQCYQcjs.BRUSH_GRAIN_MODES, GRAIN_MODE_LABELS), "texturised"),
232
234
  !!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)) }),
233
235
  !!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)) }),
234
236
  !!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)) }),
@@ -238,40 +240,43 @@ function brushEditorSections(brush, profile = _chunkHRUK5TFWcjs.PAINT_PROFILE, c
238
240
  if (!isTool && profile.marks.length > 1 && engineOk) {
239
241
  const particles = marks === "particles" && engineControls ? engineControls.sliders.filter((c) => c.path.startsWith("particle.") && c.path !== "particle.waveSpeed") : [];
240
242
  const objects = marks === "objects" && engineControls ? engineControls.sliders.filter((c) => c.path.startsWith("sticker.")) : [];
243
+ const antimatter = marks === "antimatter" && engineControls ? engineControls.sliders.filter((c) => c.path.startsWith("antimatter.")) : [];
241
244
  const render = value("marks.render");
242
245
  add("marks", "Marks", [
243
246
  choice("marks.kind", "Laid as", options(profile.marks, MARKS_LABELS), "continuous"),
244
- marks === "particles" && choice("marks.physics", "Particles", options(_chunkHRUK5TFWcjs.PARTICLE_KINDS, PHYSICS_LABELS)),
245
- marks === "particles" && choice("marks.render", "Drawn as", options(_chunkHRUK5TFWcjs.PARTICLE_RENDERS, RENDER_LABELS)),
246
- marks === "particles" && render === "ribbon" && choice("marks.ribbonPairs", "Ribbon joins", options(_chunkHRUK5TFWcjs.RIBBON_PAIRS, { adjacent: "Neighbours", "first-last": "Outermost" })),
247
- marks === "particles" && shape === "stamp" && choice("marks.stampOrder", "Texture order", options(_chunkHRUK5TFWcjs.DOT_TEXTURE_ORDERS, { sequence: "In turn", random: "Random" })),
248
- marks === "particles" && shape === "stamp" && choice("marks.stampRotation", "Texture turn", options(_chunkHRUK5TFWcjs.DOT_ROTATIONS, { follow: "Follow motion", random: "Random", fixed: "Tip angle" })),
247
+ marks === "particles" && choice("marks.physics", "Particles", options(_chunkIUKPQCYQcjs.PARTICLE_KINDS, PHYSICS_LABELS)),
248
+ marks === "particles" && choice("marks.render", "Drawn as", options(_chunkIUKPQCYQcjs.PARTICLE_RENDERS, RENDER_LABELS)),
249
+ marks === "particles" && render === "ribbon" && choice("marks.ribbonPairs", "Ribbon joins", options(_chunkIUKPQCYQcjs.RIBBON_PAIRS, { adjacent: "Neighbours", "first-last": "Outermost" })),
250
+ marks === "particles" && shape === "stamp" && choice("marks.stampOrder", "Texture order", options(_chunkIUKPQCYQcjs.DOT_TEXTURE_ORDERS, { sequence: "In turn", random: "Random" })),
251
+ marks === "particles" && shape === "stamp" && choice("marks.stampRotation", "Texture turn", options(_chunkIUKPQCYQcjs.DOT_ROTATIONS, { follow: "Follow motion", random: "Random", fixed: "Tip angle" })),
252
+ marks === "antimatter" && choice("marks.invertMode", "Inversion mode", options(_chunkIUKPQCYQcjs.INVERT_MODES, { rgb: "RGB", oklab: "OKLab" }), "rgb"),
249
253
  ...particles.map((c) => slider(c)),
250
- ...objects.map((c) => slider(c))
254
+ ...objects.map((c) => slider(c)),
255
+ ...antimatter.map((c) => slider(c))
251
256
  ]);
252
257
  }
253
- const feel = _chunkJVFGK3DKcjs.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)));
258
+ const feel = _chunk3JUKN3RMcjs.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)));
254
259
  const stampDabs = dabs && shape === "stamp";
255
260
  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))) });
256
261
  add("feel", "Feel", [
257
262
  ...feel.map((c) => slider(c)),
258
263
  toggle("dynamics.pressureSize", "Pressure \u2192 size"),
259
264
  toggle("dynamics.pressureOpacity", "Pressure \u2192 opacity"),
260
- choice("dynamics.rotation", "Rotation", options(_chunkHRUK5TFWcjs.BRUSH_ROTATIONS, ROTATION_LABELS)),
265
+ choice("dynamics.rotation", "Rotation", options(_chunkIUKPQCYQcjs.BRUSH_ROTATIONS, ROTATION_LABELS)),
261
266
  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)) }),
262
267
  stampDabs && jitter("dynamics.scatter", "Scatter", 4),
263
268
  stampDabs && jitter("dynamics.angleJitter", "Rotation jitter"),
264
269
  stampDabs && jitter("dynamics.sizeJitter", "Size jitter"),
265
- stampDabs && choice("dynamics.flip", "Flip", options(_chunkHRUK5TFWcjs.BRUSH_FLIPS, FLIP_LABELS), "none")
270
+ stampDabs && choice("dynamics.flip", "Flip", options(_chunkIUKPQCYQcjs.BRUSH_FLIPS, FLIP_LABELS), "none")
266
271
  ]);
267
272
  const surfaces = profile.surfaces;
268
- const engineSliders = (engineOk ? _nullishCoalesce(_optionalChain([engineControls, 'optionalAccess', _14 => _14.sliders]), () => ( [])) : []).filter((c) => !/^(bristle|particle|sticker|iridescence)\./.test(c.path) && c.path !== "light.amount" && c.path !== "impasto.rim" && c.path !== "impasto.clarity");
273
+ const engineSliders = (engineOk ? _nullishCoalesce(_optionalChain([engineControls, 'optionalAccess', _14 => _14.sliders]), () => ( [])) : []).filter((c) => !/^(bristle|particle|sticker|antimatter|iridescence)\./.test(c.path) && c.path !== "light.amount" && c.path !== "impasto.rim" && c.path !== "impasto.clarity");
269
274
  const exchangeOk = (path) => !_optionalChain([path, 'optionalAccess', _15 => _15.startsWith, 'call', _16 => _16("behaviour.exchange")]) || (path.startsWith("behaviour.exchange.reservoir") ? full(profile.reservoir) : exchangeFull);
270
275
  add("behaviour", _nullishCoalesce(_optionalChain([engineControls, 'optionalAccess', _17 => _17.title]), () => ( "Behaviour")), [
271
276
  !isTool && surfaces.length > 1 && !refused.has("behaviour.surface") && choice("behaviour.surface", "Surface", options(surfaces, SURFACE_LABELS), "dry"),
272
277
  full(profile.grain) && slider({ path: "grain", label: "Grain", min: 0, max: 1, step: 0.01 }, "behaviour.grain"),
273
- engine === "wet" && engineOk && !isTool && choice("behaviour.wet.medium", "Medium", options(_chunkHRUK5TFWcjs.WET_MEDIA, WET_MEDIUM_LABELS), "watercolor"),
274
- engine === "impasto" && engineOk && choice("behaviour.thick.applicator", "Tool", options(_chunkHRUK5TFWcjs.IMPASTO_TOOLS, APPLICATOR_LABELS), "brush"),
278
+ engine === "wet" && engineOk && !isTool && choice("behaviour.wet.medium", "Medium", options(_chunkIUKPQCYQcjs.WET_MEDIA, WET_MEDIUM_LABELS), "watercolor"),
279
+ engine === "impasto" && engineOk && choice("behaviour.thick.applicator", "Tool", options(_chunkIUKPQCYQcjs.IMPASTO_TOOLS, APPLICATOR_LABELS), "brush"),
275
280
  ...engineSliders.map((c) => exchangeOk(facetOf(c.path)) ? slider(c) : null),
276
281
  engineOk && _optionalChain([engineControls, 'optionalAccess', _18 => _18.mixingPath]) && (engine === "wet" || exchangeFull) && fieldOf("behaviour.mixing") && choice("behaviour.mixing", "Mixing", options(["pigment", "rgb"], { pigment: "Pigment", rgb: "RGB" })),
277
282
  engineOk && _optionalChain([engineControls, 'optionalAccess', _19 => _19.dirty]) && exchangeFull && fieldOf("behaviour.exchange.dirty") && toggle("behaviour.exchange.dirty", "Dirty brush"),
@@ -292,7 +297,7 @@ function brushEditorSections(brush, profile = _chunkHRUK5TFWcjs.PAINT_PROFILE, c
292
297
  toggle("fluorescence.invisible", "Invisible in daylight"),
293
298
  withNote({ kind: "colour", path: "thermochromic.hotColor", label: "Hot colour", ariaLabel: "Brush thermochromic hot colour", value: _nullishCoalesce(_optionalChain([brush, 'access', _26 => _26.thermochromic, 'optionalAccess', _27 => _27.hotColor]), () => ( null)), dark: null, gradient: false }),
294
299
  withNote({ kind: "slider", path: "thermochromic.threshold", label: "Heat threshold", ariaLabel: "Brush thermochromic threshold", min: 0.05, max: 0.95, step: 0.01, value: _nullishCoalesce(_optionalChain([brush, 'access', _28 => _28.thermochromic, 'optionalAccess', _29 => _29.threshold]), () => ( 0.5)) }),
295
- full(_nullishCoalesce(profile.iridescence, () => ( "none"))) && marks !== "objects" && withNote({ kind: "toggle", path: "iridescence", label: "Thin-film iridescence", ariaLabel: "Brush thin-film iridescence", value: Boolean(brush.iridescence) }),
300
+ full(_nullishCoalesce(profile.iridescence, () => ( "none"))) && marks !== "objects" && marks !== "antimatter" && withNote({ kind: "toggle", path: "iridescence", label: "Thin-film iridescence", ariaLabel: "Brush thin-film iridescence", value: Boolean(brush.iridescence) }),
296
301
  ...brush.iridescence && full(_nullishCoalesce(profile.iridescence, () => ( "none"))) ? [
297
302
  slider({ path: "iridescence.thickness", label: "Film (nm)", min: 100, max: 1e3, step: 5 }),
298
303
  slider({ path: "iridescence.pressureToThickness", label: "Pressure \u2192 film", min: 0, max: 1, step: 0.01 }),
@@ -313,14 +318,14 @@ function brushEditorSections(brush, profile = _chunkHRUK5TFWcjs.PAINT_PROFILE, c
313
318
  label: "Palette",
314
319
  ariaLabel: "Brush palette",
315
320
  value: (_nullishCoalesce(_optionalChain([brush, 'access', _32 => _32.colour, 'optionalAccess', _33 => _33.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)] : []),
316
- max: _chunkHRUK5TFWcjs.MAX_BRUSH_PALETTE
321
+ max: _chunkIUKPQCYQcjs.MAX_BRUSH_PALETTE
317
322
  }),
318
323
  toggle("colour.own", "Always its own colour")
319
324
  ]);
320
325
  }
321
326
  return sections;
322
327
  }
323
- var MARKS_WORDS = { particles: "particle marks", objects: "sticker marks", continuous: "continuous marks" };
328
+ var MARKS_WORDS = { particles: "particle marks", objects: "sticker marks", continuous: "continuous marks", antimatter: "antimatter marks" };
324
329
  function hiddenLabel(issue, b) {
325
330
  switch (issue.facet) {
326
331
  case "kind":
@@ -363,15 +368,15 @@ function hiddenLabel(issue, b) {
363
368
  case "dynamics.tilt":
364
369
  return "tilt dynamics";
365
370
  case "colour.default":
366
- return `a ${_chunkHRUK5TFWcjs.brushColourKind.call(void 0, b.colour.default)} default colour`;
371
+ return `a ${_chunkIUKPQCYQcjs.brushColourKind.call(void 0, b.colour.default)} default colour`;
367
372
  default:
368
373
  return null;
369
374
  }
370
375
  }
371
376
  var sentence = (words) => words.length < 2 ? _nullishCoalesce(words[0], () => ( "")) : `${words.slice(0, -1).join(", ")} and ${words.at(-1)}`;
372
- function brushEditorScope(brush, profile = _chunkHRUK5TFWcjs.PAINT_PROFILE) {
377
+ function brushEditorScope(brush, profile = _chunkIUKPQCYQcjs.PAINT_PROFILE) {
373
378
  const hidden = [];
374
- for (const issue of _chunkHRUK5TFWcjs.checkBrush.call(void 0, brush, profile).issues) {
379
+ for (const issue of _chunkIUKPQCYQcjs.checkBrush.call(void 0, brush, profile).issues) {
375
380
  const label = hiddenLabel(issue, brush);
376
381
  if (label && !hidden.some((h) => h.label === label)) hidden.push({ facet: issue.facet, label });
377
382
  }
@@ -384,17 +389,17 @@ function newBrushFor(profile, from, name = "New brush") {
384
389
  const id = brushIdFromName(name);
385
390
  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] } };
386
391
  if (from) {
387
- const stripped = _chunkHRUK5TFWcjs.degrade.call(void 0, from, profile, { ignored: true });
392
+ const stripped = _chunkIUKPQCYQcjs.degrade.call(void 0, from, profile, { ignored: true });
388
393
  if (!stripped.issues.length) return { ...stripped.brush, id, name };
389
394
  }
390
395
  return base;
391
396
  }
392
397
  async function saveBrushEdit(library, options2) {
393
398
  const { original, profile } = options2;
394
- const check = _chunkHRUK5TFWcjs.checkBrushEdit.call(void 0, original, options2.brush, profile);
399
+ const check = _chunkIUKPQCYQcjs.checkBrushEdit.call(void 0, original, options2.brush, profile);
395
400
  if (!check.ok) {
396
401
  const errors = check.issues.filter((i) => i.severity === "error").map((i) => `${i.facet}: ${i.reason} ${i.fix}`);
397
- throw new (0, _chunkHRUK5TFWcjs.BrushLibraryError)("invalid", `${profile.name} can't use this brush: ${errors.join(" ")} Nothing was saved.`);
402
+ throw new (0, _chunkIUKPQCYQcjs.BrushLibraryError)("invalid", `${profile.name} can't use this brush: ${errors.join(" ")} Nothing was saved.`);
398
403
  }
399
404
  const inPlace = !!original && options2.name === void 0 && !library.isBuiltin(original.id) && original.id === options2.brush.id && !!await library.get(original.id);
400
405
  let brush = options2.brush;
@@ -443,13 +448,13 @@ function setBrushEditorValue(brush, path, value) {
443
448
  if (value !== "bristle") delete next.tip.bristle;
444
449
  } else if (value === void 0 || value === "") {
445
450
  const keys = path.split(".");
446
- const parent = _chunkHRUK5TFWcjs.getPath.call(void 0, next, keys.slice(0, -1).join("."));
451
+ const parent = _chunkIUKPQCYQcjs.getPath.call(void 0, next, keys.slice(0, -1).join("."));
447
452
  if (keys.length === 1) delete next[keys[0]];
448
453
  else if (parent) delete parent[keys[keys.length - 1]];
449
- } else _chunkHRUK5TFWcjs.setPath.call(void 0, next, path, value);
454
+ } else _chunkIUKPQCYQcjs.setPath.call(void 0, next, path, value);
450
455
  const problems = [];
451
456
  try {
452
- return { brush: _chunkHRUK5TFWcjs.normalizeBrushSpec.call(void 0, JSON.parse(JSON.stringify(next)), problems), problems };
457
+ return { brush: _chunkIUKPQCYQcjs.normalizeBrushSpec.call(void 0, JSON.parse(JSON.stringify(next)), problems), problems };
453
458
  } catch (e) {
454
459
  return { brush, problems: [e.message] };
455
460
  }
@@ -465,7 +470,7 @@ function brushLibraryGroups(entries, filter = {}) {
465
470
  const text = words([b.id, b.name, _nullishCoalesce(b.variety, () => ( "")), _nullishCoalesce(b.medium, () => ( "")), _nullishCoalesce(b.tool, () => ( "")), ..._nullishCoalesce(b.tags, () => ( []))].join(" "));
466
471
  if (!wanted.every((w) => text.some((t) => t.startsWith(w)))) continue;
467
472
  }
468
- if (filter.profile && !_chunkHRUK5TFWcjs.checkBrush.call(void 0, b, filter.profile).ok) continue;
473
+ if (filter.profile && !_chunkIUKPQCYQcjs.checkBrush.call(void 0, b, filter.profile).ok) continue;
469
474
  const id = b.kind === "tool" ? "tools" : _nullishCoalesce(b.medium, () => ( "other"));
470
475
  let g = groups.get(id);
471
476
  if (!g) groups.set(id, g = { id, title: id === "tools" ? "Tools" : id === "other" ? "Other" : MEDIUM_LABELS[id], varieties: [] });
@@ -481,7 +486,7 @@ async function imageFileToPngDataUrl(file) {
481
486
  const bytes = new Uint8Array(await file.arrayBuffer());
482
487
  if (_chunkW5M3J7PVcjs.isPng.call(void 0, bytes)) return `data:image/png;base64,${_chunkW5M3J7PVcjs.bytesToBase64.call(void 0, bytes)}`;
483
488
  const g = globalThis;
484
- if (!g.createImageBitmap || !g.OffscreenCanvas) throw new (0, _chunkHRUK5TFWcjs.BrushSourceError)("Only PNG images can be read here; convert the image to PNG.");
489
+ if (!g.createImageBitmap || !g.OffscreenCanvas) throw new (0, _chunkIUKPQCYQcjs.BrushSourceError)("Only PNG images can be read here; convert the image to PNG.");
485
490
  const bitmap = await g.createImageBitmap(file);
486
491
  const canvas = new g.OffscreenCanvas(bitmap.width, bitmap.height);
487
492
  canvas.getContext("2d").drawImage(bitmap, 0, 0);
@@ -491,7 +496,7 @@ async function imageFileToPngDataUrl(file) {
491
496
  async function addBrushSourceFromFile(library, file, options2 = {}) {
492
497
  const name = (_nullishCoalesce(_nullishCoalesce(options2.name, () => ( _optionalChain([file, 'access', _38 => _38.name, 'optionalAccess', _39 => _39.replace, 'call', _40 => _40(/\.[a-z0-9]+$/i, "")]))), () => ( "Image"))).trim() || "Image";
493
498
  const result = await library.addSource({ name, image: await imageFileToPngDataUrl(file), origin: file.name, ...options2.channel ? { channel: options2.channel } : {} });
494
- return { ...result, ref: _chunkHRUK5TFWcjs.brushSourceRef.call(void 0, result.source.id) };
499
+ return { ...result, ref: _chunkIUKPQCYQcjs.brushSourceRef.call(void 0, result.source.id) };
495
500
  }
496
501
  function withBrushSource(brush, ref, as) {
497
502
  if (as === "grain") return setBrushEditorValue(brush, "tip.grain.source", ref);
@@ -509,11 +514,11 @@ var BRUSH_PROFILE_JSON_SCHEMA = {
509
514
  properties: {
510
515
  id: { type: "string", description: "paint, image, canvas, vector\u2026" },
511
516
  name: { type: "string", description: "Display name, used in messages." },
512
- kinds: { type: "array", items: { type: "string", enum: [..._chunkHRUK5TFWcjs.BRUSH_KINDS] }, description: "Brush kinds it has." },
513
- tools: { type: "array", items: { type: "string", enum: [..._chunkHRUK5TFWcjs.BRUSH_TOOLS] }, description: "Tools it has (kind tool)." },
514
- tipShapes: { type: "array", items: { type: "string", enum: [..._chunkHRUK5TFWcjs.BRUSH_TIP_SHAPES] }, description: "Tip shapes it renders." },
515
- marks: { type: "array", items: { type: "string", enum: [..._chunkHRUK5TFWcjs.BRUSH_MARKS_KINDS] }, description: "Marks kinds it renders." },
516
- surfaces: { type: "array", items: { type: "string", enum: [..._chunkHRUK5TFWcjs.BRUSH_SURFACES] }, description: "Surfaces it renders." },
517
+ kinds: { type: "array", items: { type: "string", enum: [..._chunkIUKPQCYQcjs.BRUSH_KINDS] }, description: "Brush kinds it has." },
518
+ tools: { type: "array", items: { type: "string", enum: [..._chunkIUKPQCYQcjs.BRUSH_TOOLS] }, description: "Tools it has (kind tool)." },
519
+ tipShapes: { type: "array", items: { type: "string", enum: [..._chunkIUKPQCYQcjs.BRUSH_TIP_SHAPES] }, description: "Tip shapes it renders." },
520
+ marks: { type: "array", items: { type: "string", enum: [..._chunkIUKPQCYQcjs.BRUSH_MARKS_KINDS] }, description: "Marks kinds it renders." },
521
+ surfaces: { type: "array", items: { type: "string", enum: [..._chunkIUKPQCYQcjs.BRUSH_SURFACES] }, description: "Surfaces it renders." },
517
522
  exchange: { ...SUPPORT, description: "Picking paint up and mixing it in." },
518
523
  reservoir: { ...SUPPORT, description: "The Mixer Brush reservoir." },
519
524
  grain: { ...SUPPORT, description: "Paper grain response." },
@@ -540,16 +545,16 @@ var BRUSH_LIBRARY_FILE_JSON_SCHEMA = {
540
545
  brushes: { type: "array", items: { $ref: "brush.schema.json" }, description: "The brushes, each a facet brush (brush.schema.json)." },
541
546
  sources: {
542
547
  type: "array",
543
- maxItems: _chunkHRUK5TFWcjs.MAX_BRUSH_SOURCES,
548
+ maxItems: _chunkIUKPQCYQcjs.MAX_BRUSH_SOURCES,
544
549
  description: "The image sources the brushes name (source:<id> in tip.stamps or tip.grain.source), bytes included.",
545
550
  items: {
546
551
  type: "object",
547
552
  description: "An image source: a grey mask, 255 paints (or is a grain peak).",
548
553
  properties: {
549
- id: { type: "string", pattern: _chunkHRUK5TFWcjs.BRUSH_ID.source, description: "Source id." },
554
+ id: { type: "string", pattern: _chunkIUKPQCYQcjs.BRUSH_ID.source, description: "Source id." },
550
555
  name: { type: "string", maxLength: 80, description: "Display name." },
551
- width: { type: "integer", minimum: 1, maximum: _chunkHRUK5TFWcjs.MAX_BRUSH_SOURCE_SIZE, description: "Width in px." },
552
- height: { type: "integer", minimum: 1, maximum: _chunkHRUK5TFWcjs.MAX_BRUSH_SOURCE_SIZE, description: "Height in px." },
556
+ width: { type: "integer", minimum: 1, maximum: _chunkIUKPQCYQcjs.MAX_BRUSH_SOURCE_SIZE, description: "Width in px." },
557
+ height: { type: "integer", minimum: 1, maximum: _chunkIUKPQCYQcjs.MAX_BRUSH_SOURCE_SIZE, description: "Height in px." },
553
558
  image: { type: "string", pattern: "^data:image/png;base64,", description: "The mask as a greyscale PNG data: URL." },
554
559
  licence: { type: "string", maxLength: 2e3, description: "The licence or terms it came with." },
555
560
  origin: { type: "string", maxLength: 200, description: "Where it came from (a file or pack name)." }
@@ -563,7 +568,7 @@ var BRUSH_LIBRARY_FILE_JSON_SCHEMA = {
563
568
  additionalProperties: false
564
569
  };
565
570
  function migrationInputs() {
566
- const pick = (id) => _chunkHRUK5TFWcjs.brushToJSON.call(void 0, _chunkHRUK5TFWcjs.BUILTIN_BRUSHES.find((b) => b.id === id));
571
+ const pick = (id) => _chunkIUKPQCYQcjs.brushToJSON.call(void 0, _chunkIUKPQCYQcjs.BUILTIN_BRUSHES.find((b) => b.id === id));
567
572
  return [
568
573
  { id: "plain", name: "Plain" },
569
574
  pick("pencil-6b"),
@@ -585,34 +590,34 @@ function migrationInputs() {
585
590
  function editorSectionFixtures() {
586
591
  const ids = ["highlighter", "chalk", "watercolor-wash", "hair-strands", "mixer-brush", "blender", "spray-can"];
587
592
  return ids.flatMap(
588
- (id) => Object.keys(_chunkHRUK5TFWcjs.BRUSH_HOST_PROFILES).map((host) => {
589
- const brush = _chunkHRUK5TFWcjs.BRUSH_PRESETS.find((b) => b.id === id);
590
- return { brush: id, host, sections: brushEditorSections(brush, _chunkHRUK5TFWcjs.BRUSH_HOST_PROFILES[host]) };
593
+ (id) => Object.keys(_chunkIUKPQCYQcjs.BRUSH_HOST_PROFILES).map((host) => {
594
+ const brush = _chunkIUKPQCYQcjs.BRUSH_PRESETS.find((b) => b.id === id);
595
+ return { brush: id, host, sections: brushEditorSections(brush, _chunkIUKPQCYQcjs.BRUSH_HOST_PROFILES[host]) };
591
596
  })
592
597
  );
593
598
  }
594
599
  function toolDefinitions() {
595
- const library = _chunkHRUK5TFWcjs.createBrushLibrary.call(void 0, );
596
- const toolsFor = (host) => _chunkJVFGK3DKcjs.createBrushLibraryTools.call(void 0, library, { profile: _chunkHRUK5TFWcjs.BRUSH_HOST_PROFILES[host], renderPreview: () => ({ data: "", mimeType: "image/png" }) });
600
+ const library = _chunkIUKPQCYQcjs.createBrushLibrary.call(void 0, );
601
+ const toolsFor = (host) => _chunk3JUKN3RMcjs.createBrushLibraryTools.call(void 0, library, { profile: _chunkIUKPQCYQcjs.BRUSH_HOST_PROFILES[host], renderPreview: () => ({ data: "", mimeType: "image/png" }) });
597
602
  return {
598
603
  tools: toolsFor("paint").map((t) => ({ name: t.name, title: t.title, description: t.description, inputSchema: t.inputSchema, annotations: t.annotations })),
599
- hosts: Object.fromEntries(Object.keys(_chunkHRUK5TFWcjs.BRUSH_HOST_PROFILES).map((host) => [host, toolsFor(host).map((t) => t.name)]))
604
+ hosts: Object.fromEntries(Object.keys(_chunkIUKPQCYQcjs.BRUSH_HOST_PROFILES).map((host) => [host, toolsFor(host).map((t) => t.name)]))
600
605
  };
601
606
  }
602
607
  function brushContracts() {
603
- const presets = _chunkHRUK5TFWcjs.BRUSH_PRESETS.map((b) => JSON.parse(JSON.stringify(b)));
604
- const resolved = _chunkHRUK5TFWcjs.BRUSH_PRESETS.map((b) => ({ id: b.id, engine: _chunkHRUK5TFWcjs.resolveEngine.call(void 0, b), category: _chunkHRUK5TFWcjs.resolveCategory.call(void 0, b), brush: _chunkHRUK5TFWcjs.brushToJSON.call(void 0, _chunkHRUK5TFWcjs.resolveBrush.call(void 0, b)) }));
605
- const profiles = Object.values(_chunkHRUK5TFWcjs.BRUSH_HOST_PROFILES);
606
- const checks = _chunkHRUK5TFWcjs.BRUSH_PRESETS.map((b) => ({
608
+ const presets = _chunkIUKPQCYQcjs.BRUSH_PRESETS.map((b) => JSON.parse(JSON.stringify(b)));
609
+ const resolved = _chunkIUKPQCYQcjs.BRUSH_PRESETS.map((b) => ({ id: b.id, engine: _chunkIUKPQCYQcjs.resolveEngine.call(void 0, b), category: _chunkIUKPQCYQcjs.resolveCategory.call(void 0, b), brush: _chunkIUKPQCYQcjs.brushToJSON.call(void 0, _chunkIUKPQCYQcjs.resolveBrush.call(void 0, b)) }));
610
+ const profiles = Object.values(_chunkIUKPQCYQcjs.BRUSH_HOST_PROFILES);
611
+ const checks = _chunkIUKPQCYQcjs.BRUSH_PRESETS.map((b) => ({
607
612
  id: b.id,
608
613
  hosts: Object.fromEntries(profiles.map((p) => {
609
- const c = _chunkHRUK5TFWcjs.checkBrush.call(void 0, b, p);
614
+ const c = _chunkIUKPQCYQcjs.checkBrush.call(void 0, b, p);
610
615
  return [p.id, { ok: c.ok, issues: c.issues.map(({ facet, severity }) => ({ facet, severity })) }];
611
616
  }))
612
617
  }));
613
- const migrations = migrationInputs().map((input) => ({ input, output: _chunkHRUK5TFWcjs.migrateBrushV1.call(void 0, input), resolved: _chunkHRUK5TFWcjs.brushToJSON.call(void 0, _chunkHRUK5TFWcjs.resolveBrush.call(void 0, _chunkHRUK5TFWcjs.migrateBrushV1.call(void 0, input))), engine: _chunkHRUK5TFWcjs.normalizePaintBrush.call(void 0, input).engine }));
618
+ const migrations = migrationInputs().map((input) => ({ input, output: _chunkIUKPQCYQcjs.migrateBrushV1.call(void 0, input), resolved: _chunkIUKPQCYQcjs.brushToJSON.call(void 0, _chunkIUKPQCYQcjs.resolveBrush.call(void 0, _chunkIUKPQCYQcjs.migrateBrushV1.call(void 0, input))), engine: _chunkIUKPQCYQcjs.normalizePaintBrush.call(void 0, input).engine }));
614
619
  const files = {
615
- "brush.schema.json": { $schema: "https://json-schema.org/draft/2020-12/schema", $id: "brush.schema.json", ..._chunkHRUK5TFWcjs.BRUSH_JSON_SCHEMA },
620
+ "brush.schema.json": { $schema: "https://json-schema.org/draft/2020-12/schema", $id: "brush.schema.json", ..._chunkIUKPQCYQcjs.BRUSH_JSON_SCHEMA },
616
621
  "profile.schema.json": { $schema: "https://json-schema.org/draft/2020-12/schema", $id: "profile.schema.json", ...BRUSH_PROFILE_JSON_SCHEMA },
617
622
  "library-file.schema.json": { $schema: "https://json-schema.org/draft/2020-12/schema", $id: "library-file.schema.json", ...BRUSH_LIBRARY_FILE_JSON_SCHEMA },
618
623
  "presets.json": presets,
@@ -626,7 +631,7 @@ function brushContracts() {
626
631
  files["manifest.json"] = {
627
632
  domain: "brushes",
628
633
  formatVersion: BRUSH_CONTRACT_FORMAT_VERSION,
629
- limits: { maxPalette: _chunkHRUK5TFWcjs.MAX_BRUSH_PALETTE },
634
+ limits: { maxPalette: _chunkIUKPQCYQcjs.MAX_BRUSH_PALETTE },
630
635
  counts: { presets: presets.length, profiles: profiles.length, migrations: migrations.length, editorSections: files["editor-sections.json"].length },
631
636
  files: Object.keys(files).sort()
632
637
  };
@@ -727,4 +732,4 @@ function brushContracts() {
727
732
 
728
733
 
729
734
 
730
- exports.AbrError = _chunkJVFGK3DKcjs.AbrError; exports.BRUSH_CONTRACT_FORMAT_VERSION = BRUSH_CONTRACT_FORMAT_VERSION; exports.BRUSH_FACET_SCHEMAS = _chunkHRUK5TFWcjs.BRUSH_FACET_SCHEMAS; exports.BRUSH_FLIPS = _chunkHRUK5TFWcjs.BRUSH_FLIPS; exports.BRUSH_GRAIN_MODES = _chunkHRUK5TFWcjs.BRUSH_GRAIN_MODES; exports.BRUSH_GRAIN_SOURCE_PATTERN = _chunkHRUK5TFWcjs.BRUSH_GRAIN_SOURCE_PATTERN; exports.BRUSH_HOST_PROFILES = _chunkHRUK5TFWcjs.BRUSH_HOST_PROFILES; exports.BRUSH_JSON_SCHEMA = _chunkHRUK5TFWcjs.BRUSH_JSON_SCHEMA; exports.BRUSH_KINDS = _chunkHRUK5TFWcjs.BRUSH_KINDS; exports.BRUSH_LIBRARY_FILE_JSON_SCHEMA = BRUSH_LIBRARY_FILE_JSON_SCHEMA; exports.BRUSH_MARKS_KINDS = _chunkHRUK5TFWcjs.BRUSH_MARKS_KINDS; exports.BRUSH_MEDIA = _chunkHRUK5TFWcjs.BRUSH_MEDIA; exports.BRUSH_PACK_FORMATS = _chunkJVFGK3DKcjs.BRUSH_PACK_FORMATS; exports.BRUSH_PATCH_PROPERTIES = _chunkJVFGK3DKcjs.BRUSH_PATCH_PROPERTIES; exports.BRUSH_PRESETS = _chunkHRUK5TFWcjs.BRUSH_PRESETS; exports.BRUSH_PREVIEW_DARK_PAPER = _chunkH3EME3S5cjs.BRUSH_PREVIEW_DARK_PAPER; exports.BRUSH_PREVIEW_FALLBACKS = _chunkH3EME3S5cjs.BRUSH_PREVIEW_FALLBACKS; exports.BRUSH_PREVIEW_MIN_CONTRAST = _chunkH3EME3S5cjs.BRUSH_PREVIEW_MIN_CONTRAST; exports.BRUSH_PROFILE_JSON_SCHEMA = BRUSH_PROFILE_JSON_SCHEMA; exports.BRUSH_SOURCE_CHANNELS = _chunkHRUK5TFWcjs.BRUSH_SOURCE_CHANNELS; exports.BRUSH_SOURCE_PROVIDER = _chunkHRUK5TFWcjs.BRUSH_SOURCE_PROVIDER; exports.BRUSH_SURFACES = _chunkHRUK5TFWcjs.BRUSH_SURFACES; exports.BRUSH_TIP_SHAPES = _chunkHRUK5TFWcjs.BRUSH_TIP_SHAPES; exports.BRUSH_TOOLS = _chunkHRUK5TFWcjs.BRUSH_TOOLS; exports.BRUSH_UNSET_PROPERTY = _chunkJVFGK3DKcjs.BRUSH_UNSET_PROPERTY; exports.BRUSH_WORD = _chunkHRUK5TFWcjs.BRUSH_WORD; exports.BrushLibraryError = _chunkHRUK5TFWcjs.BrushLibraryError; exports.BrushPackError = _chunkJVFGK3DKcjs.BrushPackError; exports.BrushSourceError = _chunkHRUK5TFWcjs.BrushSourceError; exports.CANVAS_PROFILE = _chunkHRUK5TFWcjs.CANVAS_PROFILE; exports.GHOST_TRIGGERS = _chunkHRUK5TFWcjs.GHOST_TRIGGERS2; exports.IMAGE_PROFILE = _chunkHRUK5TFWcjs.IMAGE_PROFILE; exports.MAX_BRUSH_PACK_BYTES = _chunkJVFGK3DKcjs.MAX_BRUSH_PACK_BYTES; exports.MAX_BRUSH_PALETTE = _chunkHRUK5TFWcjs.MAX_BRUSH_PALETTE; exports.MAX_BRUSH_SOURCES = _chunkHRUK5TFWcjs.MAX_BRUSH_SOURCES; exports.MAX_BRUSH_SOURCE_INPUT_PIXELS = _chunkHRUK5TFWcjs.MAX_BRUSH_SOURCE_INPUT_PIXELS; exports.MAX_BRUSH_SOURCE_SIZE = _chunkHRUK5TFWcjs.MAX_BRUSH_SOURCE_SIZE; exports.MAX_PACK_BRUSHES = _chunkJVFGK3DKcjs.MAX_PACK_BRUSHES; exports.PAINT_PROFILE = _chunkHRUK5TFWcjs.PAINT_PROFILE; exports.TIP_GRAIN_PATTERNS = _chunkHRUK5TFWcjs.TIP_GRAIN_PATTERNS; exports.UNSETTABLE_BRUSH_FACETS = _chunkJVFGK3DKcjs.UNSETTABLE_BRUSH_FACETS; exports.VECTOR_PROFILE = _chunkHRUK5TFWcjs.VECTOR_PROFILE; exports.WET_BRUSH_TOOLS = _chunkHRUK5TFWcjs.WET_BRUSH_TOOLS; exports.addBrushSourceFromFile = addBrushSourceFromFile; exports.brushColourHex = _chunkJBZRVLXVcjs.brushColourHex; exports.brushColourKind = _chunkHRUK5TFWcjs.brushColourKind; exports.brushContracts = brushContracts; exports.brushEditorScope = brushEditorScope; exports.brushEditorSections = brushEditorSections; exports.brushIdFromName = brushIdFromName; exports.brushLibraryGroups = brushLibraryGroups; exports.brushPackFormat = _chunkJVFGK3DKcjs.brushPackFormat; exports.brushPreviewColours = _chunkH3EME3S5cjs.brushPreviewColours; exports.brushPreviewStrength = _chunkH3EME3S5cjs.brushPreviewStrength; exports.brushPreviewsOnDark = _chunkH3EME3S5cjs.brushPreviewsOnDark; exports.brushSourceFromMask = _chunkHRUK5TFWcjs.brushSourceFromMask; exports.brushSourceIdFromName = _chunkHRUK5TFWcjs.brushSourceIdFromName; exports.brushSourceIdOf = _chunkHRUK5TFWcjs.brushSourceIdOf; exports.brushSourceIds = _chunkHRUK5TFWcjs.brushSourceIds; exports.brushSourceRef = _chunkHRUK5TFWcjs.brushSourceRef; exports.changedBrushPaths = _chunkJVFGK3DKcjs.changedBrushPaths; exports.checkBrush = _chunkHRUK5TFWcjs.checkBrush; exports.checkBrushEdit = _chunkHRUK5TFWcjs.checkBrushEdit; exports.createBrushLibrary = _chunkHRUK5TFWcjs.createBrushLibrary; exports.createBrushLibraryToolProvider = _chunkJVFGK3DKcjs.createBrushLibraryToolProvider; exports.createBrushLibraryTools = _chunkJVFGK3DKcjs.createBrushLibraryTools; exports.decodeBrushSource = _chunkHRUK5TFWcjs.decodeBrushSource; exports.degrade = _chunkHRUK5TFWcjs.degrade; exports.imageFileToPngDataUrl = imageFileToPngDataUrl; exports.importBrushFile = _chunkJVFGK3DKcjs.importBrushFile; exports.importBrushPack = _chunkJVFGK3DKcjs.importBrushPack; exports.isBrushLibrary = _chunkHRUK5TFWcjs.isBrushLibrary; exports.isBrushSourceRef = _chunkHRUK5TFWcjs.isBrushSourceRef; exports.listPresets = _chunkHRUK5TFWcjs.listPresets; exports.makeBrushSource = _chunkHRUK5TFWcjs.makeBrushSource; exports.mergeBrushPatch = _chunkJVFGK3DKcjs.mergeBrushPatch; exports.migrateBrushV1 = _chunkHRUK5TFWcjs.migrateBrushV1; exports.newBrushFor = newBrushFor; exports.normalizeBrushSpec = _chunkHRUK5TFWcjs.normalizeBrushSpec; exports.parseBrushLibrary = _chunkHRUK5TFWcjs.parseBrushLibrary2; exports.parseBrushLibrarySources = _chunkHRUK5TFWcjs.parseBrushLibrarySources; exports.readAbr = _chunkJVFGK3DKcjs.readAbr; exports.readBrushPack = _chunkJVFGK3DKcjs.readBrushPack; exports.readBrushSource = _chunkHRUK5TFWcjs.readBrushSource; exports.resolveBrush = _chunkHRUK5TFWcjs.resolveBrush; exports.resolveCategory = _chunkHRUK5TFWcjs.resolveCategory; exports.resolveEngine = _chunkHRUK5TFWcjs.resolveEngine; exports.resolveWetMedium = _chunkHRUK5TFWcjs.resolveWetMedium; exports.saveBrushEdit = saveBrushEdit; exports.setBrushEditorValue = setBrushEditorValue; exports.snapshotBrushLibrary = _chunkHRUK5TFWcjs.snapshotBrushLibrary; exports.summarizeBrushSource = _chunkHRUK5TFWcjs.summarizeBrushSource; exports.withBrushSource = withBrushSource; exports.withKeptIssues = _chunkHRUK5TFWcjs.withKeptIssues;
735
+ exports.AbrError = _chunk3JUKN3RMcjs.AbrError; exports.BRUSH_CONTRACT_FORMAT_VERSION = BRUSH_CONTRACT_FORMAT_VERSION; exports.BRUSH_FACET_SCHEMAS = _chunkIUKPQCYQcjs.BRUSH_FACET_SCHEMAS; exports.BRUSH_FLIPS = _chunkIUKPQCYQcjs.BRUSH_FLIPS; exports.BRUSH_GRAIN_MODES = _chunkIUKPQCYQcjs.BRUSH_GRAIN_MODES; exports.BRUSH_GRAIN_SOURCE_PATTERN = _chunkIUKPQCYQcjs.BRUSH_GRAIN_SOURCE_PATTERN; exports.BRUSH_HOST_PROFILES = _chunkIUKPQCYQcjs.BRUSH_HOST_PROFILES; exports.BRUSH_JSON_SCHEMA = _chunkIUKPQCYQcjs.BRUSH_JSON_SCHEMA; exports.BRUSH_KINDS = _chunkIUKPQCYQcjs.BRUSH_KINDS; exports.BRUSH_LIBRARY_FILE_JSON_SCHEMA = BRUSH_LIBRARY_FILE_JSON_SCHEMA; exports.BRUSH_MARKS_KINDS = _chunkIUKPQCYQcjs.BRUSH_MARKS_KINDS; exports.BRUSH_MEDIA = _chunkIUKPQCYQcjs.BRUSH_MEDIA; exports.BRUSH_PACK_FORMATS = _chunk3JUKN3RMcjs.BRUSH_PACK_FORMATS; exports.BRUSH_PATCH_PROPERTIES = _chunk3JUKN3RMcjs.BRUSH_PATCH_PROPERTIES; exports.BRUSH_PRESETS = _chunkIUKPQCYQcjs.BRUSH_PRESETS; exports.BRUSH_PREVIEW_DARK_PAPER = _chunkMRIAR5DYcjs.BRUSH_PREVIEW_DARK_PAPER; exports.BRUSH_PREVIEW_FALLBACKS = _chunkMRIAR5DYcjs.BRUSH_PREVIEW_FALLBACKS; exports.BRUSH_PREVIEW_MIN_CONTRAST = _chunkMRIAR5DYcjs.BRUSH_PREVIEW_MIN_CONTRAST; exports.BRUSH_PROFILE_JSON_SCHEMA = BRUSH_PROFILE_JSON_SCHEMA; exports.BRUSH_SOURCE_CHANNELS = _chunkIUKPQCYQcjs.BRUSH_SOURCE_CHANNELS; exports.BRUSH_SOURCE_PROVIDER = _chunkIUKPQCYQcjs.BRUSH_SOURCE_PROVIDER; exports.BRUSH_SURFACES = _chunkIUKPQCYQcjs.BRUSH_SURFACES; exports.BRUSH_TIP_SHAPES = _chunkIUKPQCYQcjs.BRUSH_TIP_SHAPES; exports.BRUSH_TOOLS = _chunkIUKPQCYQcjs.BRUSH_TOOLS; exports.BRUSH_UNSET_PROPERTY = _chunk3JUKN3RMcjs.BRUSH_UNSET_PROPERTY; exports.BRUSH_WORD = _chunkIUKPQCYQcjs.BRUSH_WORD; exports.BrushLibraryError = _chunkIUKPQCYQcjs.BrushLibraryError; exports.BrushPackError = _chunk3JUKN3RMcjs.BrushPackError; exports.BrushSourceError = _chunkIUKPQCYQcjs.BrushSourceError; exports.CANVAS_PROFILE = _chunkIUKPQCYQcjs.CANVAS_PROFILE; exports.GHOST_TRIGGERS = _chunkIUKPQCYQcjs.GHOST_TRIGGERS2; exports.IMAGE_PROFILE = _chunkIUKPQCYQcjs.IMAGE_PROFILE; exports.MAX_BRUSH_PACK_BYTES = _chunk3JUKN3RMcjs.MAX_BRUSH_PACK_BYTES; exports.MAX_BRUSH_PALETTE = _chunkIUKPQCYQcjs.MAX_BRUSH_PALETTE; exports.MAX_BRUSH_SOURCES = _chunkIUKPQCYQcjs.MAX_BRUSH_SOURCES; exports.MAX_BRUSH_SOURCE_INPUT_PIXELS = _chunkIUKPQCYQcjs.MAX_BRUSH_SOURCE_INPUT_PIXELS; exports.MAX_BRUSH_SOURCE_SIZE = _chunkIUKPQCYQcjs.MAX_BRUSH_SOURCE_SIZE; exports.MAX_PACK_BRUSHES = _chunk3JUKN3RMcjs.MAX_PACK_BRUSHES; exports.PAINT_PROFILE = _chunkIUKPQCYQcjs.PAINT_PROFILE; exports.TIP_GRAIN_PATTERNS = _chunkIUKPQCYQcjs.TIP_GRAIN_PATTERNS; exports.UNSETTABLE_BRUSH_FACETS = _chunk3JUKN3RMcjs.UNSETTABLE_BRUSH_FACETS; exports.VECTOR_PROFILE = _chunkIUKPQCYQcjs.VECTOR_PROFILE; exports.WET_BRUSH_TOOLS = _chunkIUKPQCYQcjs.WET_BRUSH_TOOLS; exports.addBrushSourceFromFile = addBrushSourceFromFile; exports.brushColourHex = _chunkJBZRVLXVcjs.brushColourHex; exports.brushColourKind = _chunkIUKPQCYQcjs.brushColourKind; exports.brushContracts = brushContracts; exports.brushEditorScope = brushEditorScope; exports.brushEditorSections = brushEditorSections; exports.brushIdFromName = brushIdFromName; exports.brushLibraryGroups = brushLibraryGroups; exports.brushPackFormat = _chunk3JUKN3RMcjs.brushPackFormat; exports.brushPreviewColours = _chunkMRIAR5DYcjs.brushPreviewColours; exports.brushPreviewStrength = _chunkMRIAR5DYcjs.brushPreviewStrength; exports.brushPreviewsOnDark = _chunkMRIAR5DYcjs.brushPreviewsOnDark; exports.brushSourceFromMask = _chunkIUKPQCYQcjs.brushSourceFromMask; exports.brushSourceIdFromName = _chunkIUKPQCYQcjs.brushSourceIdFromName; exports.brushSourceIdOf = _chunkIUKPQCYQcjs.brushSourceIdOf; exports.brushSourceIds = _chunkIUKPQCYQcjs.brushSourceIds; exports.brushSourceRef = _chunkIUKPQCYQcjs.brushSourceRef; exports.changedBrushPaths = _chunk3JUKN3RMcjs.changedBrushPaths; exports.checkBrush = _chunkIUKPQCYQcjs.checkBrush; exports.checkBrushEdit = _chunkIUKPQCYQcjs.checkBrushEdit; exports.createBrushLibrary = _chunkIUKPQCYQcjs.createBrushLibrary; exports.createBrushLibraryToolProvider = _chunk3JUKN3RMcjs.createBrushLibraryToolProvider; exports.createBrushLibraryTools = _chunk3JUKN3RMcjs.createBrushLibraryTools; exports.decodeBrushSource = _chunkIUKPQCYQcjs.decodeBrushSource; exports.degrade = _chunkIUKPQCYQcjs.degrade; exports.imageFileToPngDataUrl = imageFileToPngDataUrl; exports.importBrushFile = _chunk3JUKN3RMcjs.importBrushFile; exports.importBrushPack = _chunk3JUKN3RMcjs.importBrushPack; exports.isBrushLibrary = _chunkIUKPQCYQcjs.isBrushLibrary; exports.isBrushSourceRef = _chunkIUKPQCYQcjs.isBrushSourceRef; exports.listPresets = _chunkIUKPQCYQcjs.listPresets; exports.makeBrushSource = _chunkIUKPQCYQcjs.makeBrushSource; exports.mergeBrushPatch = _chunk3JUKN3RMcjs.mergeBrushPatch; exports.migrateBrushV1 = _chunkIUKPQCYQcjs.migrateBrushV1; exports.newBrushFor = newBrushFor; exports.normalizeBrushSpec = _chunkIUKPQCYQcjs.normalizeBrushSpec; exports.parseBrushLibrary = _chunkIUKPQCYQcjs.parseBrushLibrary2; exports.parseBrushLibrarySources = _chunkIUKPQCYQcjs.parseBrushLibrarySources; exports.readAbr = _chunk3JUKN3RMcjs.readAbr; exports.readBrushPack = _chunk3JUKN3RMcjs.readBrushPack; exports.readBrushSource = _chunkIUKPQCYQcjs.readBrushSource; exports.resolveBrush = _chunkIUKPQCYQcjs.resolveBrush; exports.resolveCategory = _chunkIUKPQCYQcjs.resolveCategory; exports.resolveEngine = _chunkIUKPQCYQcjs.resolveEngine; exports.resolveWetMedium = _chunkIUKPQCYQcjs.resolveWetMedium; exports.saveBrushEdit = saveBrushEdit; exports.setBrushEditorValue = setBrushEditorValue; exports.snapshotBrushLibrary = _chunkIUKPQCYQcjs.snapshotBrushLibrary; exports.summarizeBrushSource = _chunkIUKPQCYQcjs.summarizeBrushSource; exports.withBrushSource = withBrushSource; exports.withKeptIssues = _chunkIUKPQCYQcjs.withKeptIssues;
@@ -18,7 +18,7 @@ import {
18
18
  mergeBrushPatch,
19
19
  readAbr,
20
20
  readBrushPack
21
- } from "../chunk-NJLPW4MM.js";
21
+ } from "../chunk-KV7QHPKL.js";
22
22
  import "../chunk-VE6UKYQ2.js";
23
23
  import {
24
24
  BRUSH_PREVIEW_DARK_PAPER,
@@ -27,7 +27,7 @@ import {
27
27
  brushPreviewColours,
28
28
  brushPreviewStrength,
29
29
  brushPreviewsOnDark
30
- } from "../chunk-ROURHJYT.js";
30
+ } from "../chunk-NEDS6DSC.js";
31
31
  import {
32
32
  BRUSH_FACET_SCHEMAS,
33
33
  BRUSH_FIELDS,
@@ -57,6 +57,7 @@ import {
57
57
  GHOST_TRIGGERS2 as GHOST_TRIGGERS,
58
58
  IMAGE_PROFILE,
59
59
  IMPASTO_TOOLS,
60
+ INVERT_MODES,
60
61
  MAX_BRUSH_PALETTE,
61
62
  MAX_BRUSH_SOURCES,
62
63
  MAX_BRUSH_SOURCE_INPUT_PIXELS,
@@ -102,7 +103,7 @@ import {
102
103
  snapshotBrushLibrary,
103
104
  summarizeBrushSource,
104
105
  withKeptIssues
105
- } from "../chunk-PPDUCZZV.js";
106
+ } from "../chunk-YRGTQLDN.js";
106
107
  import {
107
108
  STAMP_KINDS
108
109
  } from "../chunk-JL6YH5WD.js";
@@ -138,7 +139,7 @@ var options = (values, labels = {}) => values.map((value) => ({ value, label: la
138
139
  var MEDIUM_LABELS = { paint: "Paint", ink: "Ink", dry: "Dry media", spray: "Spray" };
139
140
  var TOOL_LABELS = { eraser: "Eraser", blender: "Blender", smudge: "Smudge", water: "Add water", dryer: "Dry", blow: "Blow", blot: "Blot", "wet-blend": "Wet blend", "wet-drag": "Wet smudge" };
140
141
  var SHAPE_LABELS = { round: "Round", flat: "Flat", chisel: "Chisel", bristle: "Bristle", stamp: "Stamp" };
141
- var MARKS_LABELS = { continuous: "Continuous", particles: "Particles", objects: "Stickers" };
142
+ var MARKS_LABELS = { continuous: "Continuous", particles: "Particles", objects: "Stickers", antimatter: "Antimatter" };
142
143
  var SURFACE_LABELS = { dry: "Dry", wet: "Wet (flows)", thick: "Thick (impasto)" };
143
144
  var ROTATION_LABELS = { fixed: "Fixed", stroke: "Follow stroke", barrel: "Pen barrel" };
144
145
  var PHYSICS_LABELS = { string: "Strings", spring: "Springs", orbit: "Orbits", flow: "Flow", elastic: "Elastic", fuzzy: "Fuzzy" };
@@ -159,6 +160,7 @@ var ARIA = {
159
160
  "marks.ribbonPairs": "Ribbon pairs",
160
161
  "marks.stampOrder": "Dot texture order",
161
162
  "marks.stampRotation": "Dot rotation",
163
+ "marks.invertMode": "Inversion mode",
162
164
  "behaviour.wet.medium": "Wet medium",
163
165
  "behaviour.thick.applicator": "Impasto tool",
164
166
  "light.pressure": "Light follows pressure",
@@ -238,6 +240,7 @@ function brushEditorSections(brush, profile = PAINT_PROFILE, context = {}) {
238
240
  if (!isTool && profile.marks.length > 1 && engineOk) {
239
241
  const particles = marks === "particles" && engineControls ? engineControls.sliders.filter((c) => c.path.startsWith("particle.") && c.path !== "particle.waveSpeed") : [];
240
242
  const objects = marks === "objects" && engineControls ? engineControls.sliders.filter((c) => c.path.startsWith("sticker.")) : [];
243
+ const antimatter = marks === "antimatter" && engineControls ? engineControls.sliders.filter((c) => c.path.startsWith("antimatter.")) : [];
241
244
  const render = value("marks.render");
242
245
  add("marks", "Marks", [
243
246
  choice("marks.kind", "Laid as", options(profile.marks, MARKS_LABELS), "continuous"),
@@ -246,8 +249,10 @@ function brushEditorSections(brush, profile = PAINT_PROFILE, context = {}) {
246
249
  marks === "particles" && render === "ribbon" && choice("marks.ribbonPairs", "Ribbon joins", options(RIBBON_PAIRS, { adjacent: "Neighbours", "first-last": "Outermost" })),
247
250
  marks === "particles" && shape === "stamp" && choice("marks.stampOrder", "Texture order", options(DOT_TEXTURE_ORDERS, { sequence: "In turn", random: "Random" })),
248
251
  marks === "particles" && shape === "stamp" && choice("marks.stampRotation", "Texture turn", options(DOT_ROTATIONS, { follow: "Follow motion", random: "Random", fixed: "Tip angle" })),
252
+ marks === "antimatter" && choice("marks.invertMode", "Inversion mode", options(INVERT_MODES, { rgb: "RGB", oklab: "OKLab" }), "rgb"),
249
253
  ...particles.map((c) => slider(c)),
250
- ...objects.map((c) => slider(c))
254
+ ...objects.map((c) => slider(c)),
255
+ ...antimatter.map((c) => slider(c))
251
256
  ]);
252
257
  }
253
258
  const feel = 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)));
@@ -265,7 +270,7 @@ function brushEditorSections(brush, profile = PAINT_PROFILE, context = {}) {
265
270
  stampDabs && choice("dynamics.flip", "Flip", options(BRUSH_FLIPS, FLIP_LABELS), "none")
266
271
  ]);
267
272
  const surfaces = profile.surfaces;
268
- const engineSliders = (engineOk ? engineControls?.sliders ?? [] : []).filter((c) => !/^(bristle|particle|sticker|iridescence)\./.test(c.path) && c.path !== "light.amount" && c.path !== "impasto.rim" && c.path !== "impasto.clarity");
273
+ const engineSliders = (engineOk ? engineControls?.sliders ?? [] : []).filter((c) => !/^(bristle|particle|sticker|antimatter|iridescence)\./.test(c.path) && c.path !== "light.amount" && c.path !== "impasto.rim" && c.path !== "impasto.clarity");
269
274
  const exchangeOk = (path) => !path?.startsWith("behaviour.exchange") || (path.startsWith("behaviour.exchange.reservoir") ? full(profile.reservoir) : exchangeFull);
270
275
  add("behaviour", engineControls?.title ?? "Behaviour", [
271
276
  !isTool && surfaces.length > 1 && !refused.has("behaviour.surface") && choice("behaviour.surface", "Surface", options(surfaces, SURFACE_LABELS), "dry"),
@@ -292,7 +297,7 @@ function brushEditorSections(brush, profile = PAINT_PROFILE, context = {}) {
292
297
  toggle("fluorescence.invisible", "Invisible in daylight"),
293
298
  withNote({ kind: "colour", path: "thermochromic.hotColor", label: "Hot colour", ariaLabel: "Brush thermochromic hot colour", value: brush.thermochromic?.hotColor ?? null, dark: null, gradient: false }),
294
299
  withNote({ kind: "slider", path: "thermochromic.threshold", label: "Heat threshold", ariaLabel: "Brush thermochromic threshold", min: 0.05, max: 0.95, step: 0.01, value: brush.thermochromic?.threshold ?? 0.5 }),
295
- full(profile.iridescence ?? "none") && marks !== "objects" && withNote({ kind: "toggle", path: "iridescence", label: "Thin-film iridescence", ariaLabel: "Brush thin-film iridescence", value: Boolean(brush.iridescence) }),
300
+ full(profile.iridescence ?? "none") && marks !== "objects" && marks !== "antimatter" && withNote({ kind: "toggle", path: "iridescence", label: "Thin-film iridescence", ariaLabel: "Brush thin-film iridescence", value: Boolean(brush.iridescence) }),
296
301
  ...brush.iridescence && full(profile.iridescence ?? "none") ? [
297
302
  slider({ path: "iridescence.thickness", label: "Film (nm)", min: 100, max: 1e3, step: 5 }),
298
303
  slider({ path: "iridescence.pressureToThickness", label: "Pressure \u2192 film", min: 0, max: 1, step: 0.01 }),
@@ -320,7 +325,7 @@ function brushEditorSections(brush, profile = PAINT_PROFILE, context = {}) {
320
325
  }
321
326
  return sections;
322
327
  }
323
- var MARKS_WORDS = { particles: "particle marks", objects: "sticker marks", continuous: "continuous marks" };
328
+ var MARKS_WORDS = { particles: "particle marks", objects: "sticker marks", continuous: "continuous marks", antimatter: "antimatter marks" };
324
329
  function hiddenLabel(issue, b) {
325
330
  switch (issue.facet) {
326
331
  case "kind":
@@ -24,6 +24,7 @@ export declare const BRUSH_FACET_SCHEMAS: {
24
24
  readonly scatter: JsonSchema;
25
25
  readonly particles: JsonSchema;
26
26
  readonly objects: JsonSchema;
27
+ readonly antimatter: JsonSchema;
27
28
  };
28
29
  readonly dynamics: JsonSchema;
29
30
  readonly behaviour: JsonSchema;
@@ -24,6 +24,7 @@ export declare const BRUSH_FACET_SCHEMAS: {
24
24
  readonly scatter: JsonSchema;
25
25
  readonly particles: JsonSchema;
26
26
  readonly objects: JsonSchema;
27
+ readonly antimatter: JsonSchema;
27
28
  };
28
29
  readonly dynamics: JsonSchema;
29
30
  readonly behaviour: JsonSchema;
@@ -16,7 +16,7 @@ import type { MixingModel } from '../paint/exchange.cjs';
16
16
  import type { BlendMode } from '../raster/blend.cjs';
17
17
  import type { BrushRotation, ImpastoTool, LivePatternKind, PaintBrush, WetMediumKind } from '../paint-editor/brushes.cjs';
18
18
  import type { PaintEffectPaint } from '../paint-editor/effect-paint.cjs';
19
- import type { DotRotation, DotTextureOrder, ParticleKind, ParticleRender, RibbonPairs } from '../paint-editor/mark-settings.cjs';
19
+ import type { DotRotation, DotTextureOrder, InvertMode, ParticleKind, ParticleRender, RibbonPairs } from '../paint-editor/mark-settings.cjs';
20
20
  import type { StickerRef } from '../stickers/types.cjs';
21
21
  /** `mark`: lays a material; `tool`: works on paint already down (erases, blends, smudges, wets…). */
22
22
  export type BrushKind = 'mark' | 'tool';
@@ -35,7 +35,7 @@ export declare const BRUSH_TOOLS: readonly BrushTool[];
35
35
  export type BrushTipShape = 'round' | 'flat' | 'chisel' | 'bristle' | 'stamp';
36
36
  export declare const BRUSH_TIP_SHAPES: readonly BrushTipShape[];
37
37
  /** How the tip is laid along the path. */
38
- export type BrushMarksKind = 'continuous' | 'scatter' | 'particles' | 'objects';
38
+ export type BrushMarksKind = 'continuous' | 'scatter' | 'particles' | 'objects' | 'antimatter';
39
39
  export declare const BRUSH_MARKS_KINDS: readonly BrushMarksKind[];
40
40
  /** What the material does on the surface: stays where it lands (`dry`), flows and dries (`wet`) or stands up in relief (`thick`). */
41
41
  export type BrushSurface = 'dry' | 'wet' | 'thick';
@@ -178,7 +178,33 @@ export interface BrushObjectMarks {
178
178
  /** Random colour change, hue and lightness (default 0.2). */
179
179
  colourJitter?: number;
180
180
  }
181
- export type BrushMarks = BrushContinuousMarks | BrushScatterMarks | BrushParticleMarks | BrushObjectMarks;
181
+ /** Animated blasts that invert, scorch and destroy paint across every visible layer where the tip touches paint. */
182
+ export interface BrushAntimatterMarks {
183
+ kind: 'antimatter';
184
+ /** Blast size, in tip radii, 1..6 (default 2.5). */
185
+ blastRadius?: number;
186
+ /** Stroke travel between blasts, in tip diameters, 0.5..8 (default 2). */
187
+ blastSpacing?: number;
188
+ /** Paint needed under the tip to fire, 0..1 (default 0.1). */
189
+ threshold?: number;
190
+ /** Strength of the colour inversion in the blast core, 0..1 (default 1). */
191
+ invert?: number;
192
+ /** Colour inversion mode: `rgb` (default) or `oklab` (flips lightness and hue so mid-grey changes too). */
193
+ invertMode?: InvertMode;
194
+ /** Strength of the soot ring, 0..1 (default 0.6). */
195
+ scorch?: number;
196
+ /** Paint destroyed at the centre — alpha removed, 0..1 (default 0.25). */
197
+ annihilate?: number;
198
+ /** How many sparks fly out from each blast, 0..1 (default 0.5). */
199
+ sparks?: number;
200
+ /** How far sparks fly, in tip diameters, 0.5..8 (default 3). */
201
+ sparkReach?: number;
202
+ /** Depth of the dent on layers with relief, 0..1 (default 0.5). */
203
+ crater?: number;
204
+ /** Seconds a blast plays on screen, 0..2 (default 0.7; 0 shows the result at once). */
205
+ duration?: number;
206
+ }
207
+ export type BrushMarks = BrushContinuousMarks | BrushScatterMarks | BrushParticleMarks | BrushObjectMarks | BrushAntimatterMarks;
182
208
  /** Pen feel. Every amount is 0 (off) .. 1 unless noted. */
183
209
  export interface BrushDynamics {
184
210
  /** Pressure scales the size (default true). */