@pixerate/editor 0.239.0 → 0.241.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 (45) hide show
  1. package/dist/animation-editor/index.cjs +58 -58
  2. package/dist/animation-editor/index.js +3 -3
  3. package/dist/{brushstrokeRender-WB67GMUP.js → brushstrokeRender-MOYGEXOC.js} +2 -2
  4. package/dist/{brushstrokeRender-MPOM7BFW.cjs → brushstrokeRender-UKADOV5D.cjs} +5 -5
  5. package/dist/canvas/index.cjs +8 -8
  6. package/dist/canvas/index.js +1 -1
  7. package/dist/{chunk-NKTNLJUJ.cjs → chunk-5BR7LC24.cjs} +99 -11
  8. package/dist/{chunk-ZXNO3ZOX.cjs → chunk-7F4G74UQ.cjs} +35 -35
  9. package/dist/{chunk-AD7GA3TL.cjs → chunk-AFLMXITL.cjs} +23 -23
  10. package/dist/{chunk-PP6BXVRL.cjs → chunk-HGGJ36HR.cjs} +1 -1
  11. package/dist/{chunk-BBUE3UJL.cjs → chunk-HW72ABGR.cjs} +86 -86
  12. package/dist/{chunk-7YGBSFC6.js → chunk-ILM6GYAX.js} +1 -1
  13. package/dist/{chunk-THXFU3JB.cjs → chunk-KCMV2534.cjs} +2 -2
  14. package/dist/{chunk-3E36VDMJ.js → chunk-NE6OOKWJ.js} +1 -1
  15. package/dist/{chunk-RV5DKZHU.js → chunk-QP2B3MID.js} +1 -1
  16. package/dist/{chunk-UUBO6RB5.js → chunk-UDBDYZET.js} +1 -1
  17. package/dist/{chunk-PJYG6FCF.js → chunk-XBZK47LV.js} +98 -10
  18. package/dist/{chunk-XK4SST43.js → chunk-ZDPLZDTT.js} +1 -1
  19. package/dist/image-editor/index.cjs +775 -775
  20. package/dist/image-editor/index.js +4 -4
  21. package/dist/paint-editor/controller.d.cts +158 -1
  22. package/dist/paint-editor/controller.d.ts +158 -1
  23. package/dist/paint-editor/document.d.cts +7 -2
  24. package/dist/paint-editor/document.d.ts +7 -2
  25. package/dist/paint-editor/effect-paint.d.cts +60 -3
  26. package/dist/paint-editor/effect-paint.d.ts +60 -3
  27. package/dist/paint-editor/index.cjs +1246 -430
  28. package/dist/paint-editor/index.d.cts +1 -0
  29. package/dist/paint-editor/index.d.ts +1 -0
  30. package/dist/paint-editor/index.js +888 -72
  31. package/dist/paint-editor/live-strokes.d.cts +24 -0
  32. package/dist/paint-editor/live-strokes.d.ts +24 -0
  33. package/dist/paint-editor/stickers.d.cts +6 -0
  34. package/dist/paint-editor/stickers.d.ts +6 -0
  35. package/dist/paint-editor/thick.d.cts +4 -0
  36. package/dist/paint-editor/thick.d.ts +4 -0
  37. package/dist/paint-editor/ui.d.cts +24 -4
  38. package/dist/paint-editor/ui.d.ts +24 -4
  39. package/dist/paint-editor/wet.d.cts +8 -0
  40. package/dist/paint-editor/wet.d.ts +8 -0
  41. package/dist/vector/index.cjs +962 -962
  42. package/dist/vector/index.js +4 -4
  43. package/package.json +1 -1
  44. package/skills/pixerate-paint-editor/SKILL.md +38 -27
  45. package/skills/pixerate-paint-editor/references/effect-paint.md +25 -0
@@ -7,7 +7,7 @@ var _chunkZQIWBJGHcjs = require('../chunk-ZQIWBJGH.cjs');
7
7
 
8
8
 
9
9
 
10
- var _chunkBBUE3UJLcjs = require('../chunk-BBUE3UJL.cjs');
10
+ var _chunkHW72ABGRcjs = require('../chunk-HW72ABGR.cjs');
11
11
  require('../chunk-A2IS7EHE.cjs');
12
12
  require('../chunk-SS53NNZO.cjs');
13
13
 
@@ -55,7 +55,7 @@ var _chunkJLWLX52Lcjs = require('../chunk-JLWLX52L.cjs');
55
55
 
56
56
 
57
57
  var _chunkK7ENM55Pcjs = require('../chunk-K7ENM55P.cjs');
58
- require('../chunk-THXFU3JB.cjs');
58
+ require('../chunk-KCMV2534.cjs');
59
59
 
60
60
 
61
61
 
@@ -126,7 +126,12 @@ require('../chunk-THXFU3JB.cjs');
126
126
 
127
127
 
128
128
 
129
- var _chunkNKTNLJUJcjs = require('../chunk-NKTNLJUJ.cjs');
129
+
130
+
131
+
132
+
133
+
134
+ var _chunk5BR7LC24cjs = require('../chunk-5BR7LC24.cjs');
130
135
  require('../chunk-CVFAAZXE.cjs');
131
136
  require('../chunk-GF2RBSDM.cjs');
132
137
  require('../chunk-NM3G4VQT.cjs');
@@ -285,7 +290,7 @@ function normalizeCanvas(raw) {
285
290
  function createPaintDocument(options = {}) {
286
291
  const canvas = normalizeCanvas({ width: _nullishCoalesce(options.width, () => ( 2048)), height: _nullishCoalesce(options.height, () => ( 1536)), dpi: options.dpi });
287
292
  const layer = createPaintLayer("Layer 1");
288
- return { canvas, paper: _chunkNKTNLJUJcjs.normalizePaper.call(void 0, _nullishCoalesce(options.paper, () => ( {}))), layers: [layer], activeLayerId: layer.id, strokes: [] };
293
+ return { canvas, paper: _chunk5BR7LC24cjs.normalizePaper.call(void 0, _nullishCoalesce(options.paper, () => ( {}))), layers: [layer], activeLayerId: layer.id, strokes: [] };
289
294
  }
290
295
 
291
296
  // src/paint-editor/dab-stroke.ts
@@ -339,13 +344,13 @@ var PaintDabStroke = (_class = class {
339
344
  constructor(brush, options) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);_class.prototype.__init9.call(this);_class.prototype.__init10.call(this);
340
345
  this.brush = brush;
341
346
  this.options = options;
342
- this.smoother = new (0, _chunkNKTNLJUJcjs.PenSmoother)(brush.tip.smoothing, brush.pressureSmoothing);
343
- this.copies = _optionalChain([options, 'access', _2 => _2.copies, 'optionalAccess', _3 => _3.length]) ? options.copies : [_chunkNKTNLJUJcjs.IDENTITY_COPY];
347
+ this.smoother = new (0, _chunk5BR7LC24cjs.PenSmoother)(brush.tip.smoothing, brush.pressureSmoothing);
348
+ this.copies = _optionalChain([options, 'access', _2 => _2.copies, 'optionalAccess', _3 => _3.length]) ? options.copies : [_chunk5BR7LC24cjs.IDENTITY_COPY];
344
349
  const exchanging = brush.engine === "mixer" || brush.engine === "dab";
345
350
  const rates = brush.engine === "mixer" ? _chunkPQ3H2AANcjs.mixerRates.call(void 0, brush.mixer) : _chunkPQ3H2AANcjs.exchangeRates.call(void 0, brush.behavior.pickup, brush.behavior.mix, 1, brush.behavior.mixing);
346
351
  this.mixing = brush.engine === "mixer" ? brush.mixer.mixing : brush.behavior.mixing;
347
352
  this.states = this.copies.map((c, i) => ({
348
- walker: new (0, _chunkNKTNLJUJcjs.PenDabWalker)(brush, (_nullishCoalesce(options.seed, () => ( 0))) + i * 7919 | 0, c.linear),
353
+ walker: new (0, _chunk5BR7LC24cjs.PenDabWalker)(brush, (_nullishCoalesce(options.seed, () => ( 0))) + i * 7919 | 0, c.linear),
349
354
  exchange: exchanging ? new (0, _chunkPQ3H2AANcjs.PaintExchange)(rates, options.load) : null,
350
355
  smudge: brush.engine === "smudge" ? new (0, _chunkPQ3H2AANcjs.SmudgeTip)(brush.smudge, brush.tip.size * (1 + brush.dynamics.tiltSize * 1.5)) : null
351
356
  }));
@@ -458,7 +463,7 @@ var PaintDabStroke = (_class = class {
458
463
  let flow = 1;
459
464
  if (this.options.effect) {
460
465
  const rgb = [0, 0, 0];
461
- flow = _chunkNKTNLJUJcjs.sampleEffectAt.call(void 0, this.options.effect, d.x, d.y, rgb);
466
+ flow = _chunk5BR7LC24cjs.sampleEffectAt.call(void 0, this.options.effect, d.x, d.y, rgb, d);
462
467
  ex.setReservoirColor(rgb[0], rgb[1], rgb[2]);
463
468
  } else if (this.sampler && !this.gradient) {
464
469
  const g = this.sampler.dab(d, this.sample);
@@ -686,15 +691,15 @@ function expand(t, channels) {
686
691
  return out;
687
692
  }
688
693
  var ThickStrokeDriver = (_class3 = class {
689
- constructor(init) {;_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this);_class3.prototype.__init16.call(this);_class3.prototype.__init17.call(this);_class3.prototype.__init18.call(this);_class3.prototype.__init19.call(this);_class3.prototype.__init20.call(this);
694
+ constructor(init) {;_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this);_class3.prototype.__init16.call(this);_class3.prototype.__init17.call(this);_class3.prototype.__init18.call(this);_class3.prototype.__init19.call(this);_class3.prototype.__init20.call(this);_class3.prototype.__init21.call(this);
690
695
  this.init = init;
691
696
  const b = init.brush;
692
- this.smoother = new (0, _chunkNKTNLJUJcjs.PenSmoother)(b.tip.smoothing, b.pressureSmoothing);
693
- const copies = init.copies.length ? init.copies : [_chunkNKTNLJUJcjs.IDENTITY_COPY];
697
+ this.smoother = new (0, _chunk5BR7LC24cjs.PenSmoother)(b.tip.smoothing, b.pressureSmoothing);
698
+ const copies = init.copies.length ? init.copies : [_chunk5BR7LC24cjs.IDENTITY_COPY];
694
699
  this.copies = copies;
695
700
  const rates = _chunkPQ3H2AANcjs.exchangeRates.call(void 0, b.behavior.pickup, b.behavior.mix, 1, b.behavior.mixing);
696
701
  this.states = copies.map((c, i) => ({
697
- walker: new (0, _chunkNKTNLJUJcjs.PenDabWalker)(b, init.seed + i * 7919 | 0, c.linear),
702
+ walker: new (0, _chunk5BR7LC24cjs.PenDabWalker)(b, init.seed + i * 7919 | 0, c.linear),
698
703
  exchange: new (0, _chunkPQ3H2AANcjs.PaintExchange)(rates, init.load),
699
704
  smudge: b.impasto.tool === "knife" ? new (0, _chunkPQ3H2AANcjs.SmudgeTip)({ strength: 0.85, length: 0.6 + 0.38 * b.impasto.drag, mixing: b.behavior.mixing }, b.tip.size * 1.2) : null,
700
705
  carried: null,
@@ -711,13 +716,14 @@ var ThickStrokeDriver = (_class3 = class {
711
716
  __init13() {this.height = /* @__PURE__ */ new Map()}
712
717
  __init14() {this.material = /* @__PURE__ */ new Map()}
713
718
  __init15() {this.paint = [0, 0, 0]}
714
- __init16() {this.tip = new Float32Array(0)}
715
- __init17() {this.lastRaw = null}
716
- __init18() {this.lastFed = null}
717
- __init19() {this.version = 0}
719
+ __init16() {this.centre = [0, 0, 0]}
720
+ __init17() {this.tip = new Float32Array(0)}
721
+ __init18() {this.lastRaw = null}
722
+ __init19() {this.lastFed = null}
723
+ __init20() {this.version = 0}
718
724
 
719
725
 
720
- __init20() {this.sample = { rgb: [0, 0, 0], alpha: 1 }}
726
+ __init21() {this.sample = { rgb: [0, 0, 0], alpha: 1 }}
721
727
  /** What copy 0 carries after the dabs so far. */
722
728
  get load() {
723
729
  return this.states[0].exchange.load;
@@ -837,7 +843,11 @@ var ThickStrokeDriver = (_class3 = class {
837
843
  }
838
844
  const ex = st.exchange;
839
845
  let flow = 1;
840
- if (this.sampler) {
846
+ const effect = this.init.effect;
847
+ if (effect) {
848
+ _chunk5BR7LC24cjs.sampleEffectAt.call(void 0, effect, d.x, d.y, this.centre, d);
849
+ ex.setReservoirColor(this.centre[0], this.centre[1], this.centre[2]);
850
+ } else if (this.sampler) {
841
851
  const g = this.sampler.dab(d, this.sample);
842
852
  ex.setReservoirColor(g.rgb[0], g.rgb[1], g.rgb[2]);
843
853
  flow = g.alpha;
@@ -849,7 +859,12 @@ var ThickStrokeDriver = (_class3 = class {
849
859
  if (dep > 0) {
850
860
  const cov = new Float32Array(n);
851
861
  for (let p = 0; p < n; p++) cov[p] = tip[p] > 0 ? Math.min(1, tip[p] * (im.tool === "roller" || im.tool === "gloop" || im.tool === "tube" ? 1.6 : 1.1)) : 0;
852
- this.medium.deposit(rect, cov, dep, this.paint, ex.paintPigment);
862
+ let field = null;
863
+ if (effect) {
864
+ field = new Float32Array(n * 3);
865
+ _chunk5BR7LC24cjs.effectDabField.call(void 0, effect, rect, cov, this.centre, this.paint, field, 0, d);
866
+ }
867
+ this.medium.deposit(rect, cov, dep, this.paint, ex.paintPigment, field);
853
868
  }
854
869
  const seed = this.init.seed;
855
870
  for (let yy = 0, p = 0; yy < rect.height; yy++) {
@@ -986,17 +1001,16 @@ function union(a, b) {
986
1001
  return { x, y, width: Math.max(a.x + a.width, b.x + b.width) - x, height: Math.max(a.y + a.height, b.y + b.height) - y };
987
1002
  }
988
1003
  var PaintStrokeSession = (_class4 = class {
989
- constructor(host, init) {;_class4.prototype.__init21.call(this);_class4.prototype.__init22.call(this);_class4.prototype.__init23.call(this);_class4.prototype.__init24.call(this);_class4.prototype.__init25.call(this);_class4.prototype.__init26.call(this);_class4.prototype.__init27.call(this);_class4.prototype.__init28.call(this);_class4.prototype.__init29.call(this);_class4.prototype.__init30.call(this);_class4.prototype.__init31.call(this);_class4.prototype.__init32.call(this);_class4.prototype.__init33.call(this);_class4.prototype.__init34.call(this);
1004
+ constructor(host, init) {;_class4.prototype.__init22.call(this);_class4.prototype.__init23.call(this);_class4.prototype.__init24.call(this);_class4.prototype.__init25.call(this);_class4.prototype.__init26.call(this);_class4.prototype.__init27.call(this);_class4.prototype.__init28.call(this);_class4.prototype.__init29.call(this);_class4.prototype.__init30.call(this);_class4.prototype.__init31.call(this);_class4.prototype.__init32.call(this);_class4.prototype.__init33.call(this);_class4.prototype.__init34.call(this);_class4.prototype.__init35.call(this);
990
1005
  this.host = host;
991
1006
  this.init = init;
992
1007
  this.layerId = init.layerId;
993
1008
  this.base = init.surface;
994
1009
  this.live = new Map(this.base.tiles);
995
- this.copies = init.copies.length ? init.copies : [_chunkNKTNLJUJcjs.IDENTITY_COPY];
1010
+ this.copies = init.copies.length ? init.copies : [_chunk5BR7LC24cjs.IDENTITY_COPY];
996
1011
  const brush = init.brush;
997
1012
  if (init.wet) this.wetDriver = init.wet.driver;
998
1013
  const effect = _nullishCoalesce(_optionalChain([init, 'access', _15 => _15.effect, 'optionalAccess', _16 => _16.pixels]), () => ( null));
999
- if (effect && (init.wet || init.thick || init.sticker)) throw new Error(`The ${brush.name} brush (${brush.engine} engine) cannot paint with effect paint.`);
1000
1014
  this.apply = {
1001
1015
  mode: brush.engine === "eraser" ? "erase" : "paint",
1002
1016
  color: init.color,
@@ -1009,11 +1023,11 @@ var PaintStrokeSession = (_class4 = class {
1009
1023
  };
1010
1024
  if (this.wetDriver) {
1011
1025
  } else if (init.sticker) {
1012
- this.stickerSpray = new (0, _chunkNKTNLJUJcjs.StickerSpray)({ brush, target: this.tileTarget(), color: init.colorHex, seed: init.seed, copies: this.copies, nextId: init.sticker.nextId, paint: _nullishCoalesce(init.paint, () => ( null)) });
1026
+ this.stickerSpray = new (0, _chunk5BR7LC24cjs.StickerSpray)({ brush, target: this.tileTarget(), color: init.colorHex, seed: init.seed, copies: this.copies, nextId: init.sticker.nextId, paint: _nullishCoalesce(init.paint, () => ( null)), effect });
1013
1027
  } else if (init.thick) {
1014
1028
  const rgb = (c) => [c[0] / 255, c[1] / 255, c[2] / 255];
1015
- this.thickDriver = new ThickStrokeDriver({ brush, target: this.tileTarget(), channels: init.thick.channels, color: rgb(init.color), load: init.mixerLoad, seed: init.seed, copies: this.copies, paint: _nullishCoalesce(init.paint, () => ( null)) });
1016
- } else if (brush.engine === "mixer" || brush.engine === "blender" || brush.engine === "smudge" || _chunkNKTNLJUJcjs.usesPaintExchange.call(void 0, brush, Boolean(init.paint))) {
1029
+ this.thickDriver = new ThickStrokeDriver({ brush, target: this.tileTarget(), channels: init.thick.channels, color: rgb(init.color), load: init.mixerLoad, seed: init.seed, copies: this.copies, paint: _nullishCoalesce(init.paint, () => ( null)), effect });
1030
+ } else if (brush.engine === "mixer" || brush.engine === "blender" || brush.engine === "smudge" || _chunk5BR7LC24cjs.usesPaintExchange.call(void 0, brush, Boolean(init.paint))) {
1017
1031
  const rgb = (c) => [c[0] / 255, c[1] / 255, c[2] / 255];
1018
1032
  this.wet = new PaintDabStroke(brush, {
1019
1033
  target: this.tileTarget(),
@@ -1029,33 +1043,33 @@ var PaintStrokeSession = (_class4 = class {
1029
1043
  });
1030
1044
  } else {
1031
1045
  const opts = { grain: init.grain, paperDepth: init.paperDepth, seed: init.seed, copies: this.copies, paint: _nullishCoalesce(init.paint, () => ( null)), effect };
1032
- this.dry = brush.engine === "particle" ? new (0, _chunkNKTNLJUJcjs.ParticleStroke)(brush, opts) : brush.engine === "bristle" ? new (0, _chunkNKTNLJUJcjs.BristleStroke)(brush, opts) : new (0, _chunkNKTNLJUJcjs.DryStroke)(brush, opts);
1046
+ this.dry = brush.engine === "particle" ? new (0, _chunk5BR7LC24cjs.ParticleStroke)(brush, opts) : brush.engine === "bristle" ? new (0, _chunk5BR7LC24cjs.BristleStroke)(brush, opts) : new (0, _chunk5BR7LC24cjs.DryStroke)(brush, opts);
1033
1047
  }
1034
1048
  }
1035
1049
 
1036
1050
 
1037
1051
 
1038
1052
  /** The raw input so far (what a replay feeds back in). */
1039
- __init21() {this.points = []}
1053
+ __init22() {this.points = []}
1040
1054
 
1041
1055
 
1042
- __init22() {this.touched = /* @__PURE__ */ new Set()}
1043
- __init23() {this.fresh = /* @__PURE__ */ new Set()}
1044
- __init24() {this.dry = null}
1045
- __init25() {this.wet = null}
1046
- __init26() {this.wetDriver = null}
1047
- __init27() {this.thickDriver = null}
1048
- __init28() {this.stickerSpray = null}
1056
+ __init23() {this.touched = /* @__PURE__ */ new Set()}
1057
+ __init24() {this.fresh = /* @__PURE__ */ new Set()}
1058
+ __init25() {this.dry = null}
1059
+ __init26() {this.wet = null}
1060
+ __init27() {this.wetDriver = null}
1061
+ __init28() {this.thickDriver = null}
1062
+ __init29() {this.stickerSpray = null}
1049
1063
  /** A wet stroke that ended while the simulation was mid-batch: it commits when the driver catches up. */
1050
- __init29() {this.wetEndPending = false}
1064
+ __init30() {this.wetEndPending = false}
1051
1065
 
1052
1066
 
1053
- __init30() {this.bounds = null}
1054
- __init31() {this.lastRaw = null}
1055
- __init32() {this.closed = false}
1056
- __init33() {this.result = null}
1067
+ __init31() {this.bounds = null}
1068
+ __init32() {this.lastRaw = null}
1069
+ __init33() {this.closed = false}
1070
+ __init34() {this.result = null}
1057
1071
  /** The mask stencils this stroke was painted with when they are not the editor's (replays). */
1058
- __init34() {this.maskStencils = null}
1072
+ __init35() {this.maskStencils = null}
1059
1073
  tileTarget() {
1060
1074
  return {
1061
1075
  offset: this.init.offset,
@@ -1200,7 +1214,7 @@ function brushLoadForStroke(brush, current, color) {
1200
1214
  if (brush.engine === "mixer") return mixerLoadForStroke(brush, current, color);
1201
1215
  if (brush.engine === "impasto") return { reservoir: [color[0], color[1], color[2]], amount: 1, pickup: brush.behavior.dirty ? current.pickup : null };
1202
1216
  if (brush.engine === "wet") return brush.wet.tool === "paint" ? { reservoir: [color[0], color[1], color[2]], amount: 1, pickup: brush.behavior.dirty ? current.pickup : null } : _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD;
1203
- if (_chunkNKTNLJUJcjs.usesPaintExchange.call(void 0, brush)) return { reservoir: [color[0], color[1], color[2]], amount: 1, pickup: brush.behavior.dirty ? current.pickup : null };
1217
+ if (_chunk5BR7LC24cjs.usesPaintExchange.call(void 0, brush)) return { reservoir: [color[0], color[1], color[2]], amount: 1, pickup: brush.behavior.dirty ? current.pickup : null };
1204
1218
  return _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD;
1205
1219
  }
1206
1220
 
@@ -1468,7 +1482,7 @@ function normalizeBrushStroke(raw) {
1468
1482
  const out = {
1469
1483
  id: r.id,
1470
1484
  layerId: r.layerId,
1471
- brush: _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, _nullishCoalesce(r.brush, () => ( {}))),
1485
+ brush: _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, _nullishCoalesce(r.brush, () => ( {}))),
1472
1486
  color: typeof r.color === "string" && /^#[0-9a-f]{6}$/i.test(r.color) ? r.color.toLowerCase() : "#000000",
1473
1487
  points,
1474
1488
  seed: finite2(r.seed) ? r.seed | 0 : 0
@@ -1487,7 +1501,7 @@ function normalizeBrushStroke(raw) {
1487
1501
  }
1488
1502
  if (st.length) out.stencils = st;
1489
1503
  }
1490
- const effect = _chunkNKTNLJUJcjs.readPaintEffectPaint.call(void 0, r.paint);
1504
+ const effect = _chunk5BR7LC24cjs.readPaintEffectPaint.call(void 0, r.paint);
1491
1505
  const paint = effect ? null : _chunkPQ3H2AANcjs.readPaintSource.call(void 0, r.paint);
1492
1506
  if (effect) out.paint = effect;
1493
1507
  else if (paint && paint.type === "gradient") out.paint = paint;
@@ -1519,17 +1533,19 @@ function variationAffine(v, origin) {
1519
1533
  return composeAffine(back, composeAffine(linear, toOrigin));
1520
1534
  }
1521
1535
  function varyStroke(stroke, m, v) {
1522
- const s = Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2]));
1536
+ const det = Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2]));
1537
+ const s = Math.abs(det - 1) < 1e-9 ? 1 : det;
1523
1538
  const ps = _nullishCoalesce(v.pressureScale, () => ( 1));
1524
1539
  const points = stroke.points.map((p) => {
1525
1540
  const q = mapPenPoint(m, p);
1526
1541
  if (ps !== 1) q.pressure = Math.min(1, Math.max(0, (_nullishCoalesce(p.pressure, () => ( 1))) * ps));
1527
1542
  return q;
1528
1543
  });
1529
- const brush = s !== 1 ? _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, { ...stroke.brush, tip: { ...stroke.brush.tip, size: stroke.brush.tip.size * s } }) : stroke.brush;
1544
+ const brush = s !== 1 ? _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { ...stroke.brush, tip: { ...stroke.brush.tip, size: stroke.brush.tip.size * s } }) : stroke.brush;
1530
1545
  const out = { ...stroke, brush, points };
1531
1546
  if (_optionalChain([stroke, 'access', _59 => _59.paint, 'optionalAccess', _60 => _60.type]) === "gradient" && s !== 1) out.paint = _chunkPQ3H2AANcjs.scalePaintGradient.call(void 0, stroke.paint, s);
1532
- if (_optionalChain([stroke, 'access', _61 => _61.copies, 'optionalAccess', _62 => _62.length])) {
1547
+ if (_optionalChain([stroke, 'access', _61 => _61.paint, 'optionalAccess', _62 => _62.type]) === "effect" && stroke.paint.map && s !== 1) out.paint = { ...stroke.paint, map: { ...stroke.paint.map, length: Math.min(4096, Math.max(16, Math.round(stroke.paint.map.length * s))) } };
1548
+ if (_optionalChain([stroke, 'access', _63 => _63.copies, 'optionalAccess', _64 => _64.length])) {
1533
1549
  const inv = invertAffine(m);
1534
1550
  out.copies = stroke.copies.map((c) => composeAffine(m, composeAffine(c, inv)));
1535
1551
  }
@@ -1548,7 +1564,7 @@ var paperNodes = /* @__PURE__ */ new WeakMap();
1548
1564
  function paperNode(paper, light) {
1549
1565
  let n = paperNodes.get(paper);
1550
1566
  const metal = (_nullishCoalesce(paper.metallic, () => ( 0))) > 0;
1551
- if (!n || metal && (_optionalChain([n, 'access', _63 => _63.light, 'optionalAccess', _64 => _64.azimuth]) !== _optionalChain([light, 'optionalAccess', _65 => _65.azimuth]) || _optionalChain([n, 'access', _66 => _66.light, 'optionalAccess', _67 => _67.elevation]) !== _optionalChain([light, 'optionalAccess', _68 => _68.elevation]))) {
1567
+ if (!n || metal && (_optionalChain([n, 'access', _65 => _65.light, 'optionalAccess', _66 => _66.azimuth]) !== _optionalChain([light, 'optionalAccess', _67 => _67.azimuth]) || _optionalChain([n, 'access', _68 => _68.light, 'optionalAccess', _69 => _69.elevation]) !== _optionalChain([light, 'optionalAccess', _70 => _70.elevation]))) {
1552
1568
  n = { type: "paper", id: PAPER_NODE_ID, name: "Paper", visible: true, opacity: 1, blend: "normal", paper, ...metal && light ? { light: { azimuth: light.azimuth, elevation: light.elevation } } : {} };
1553
1569
  paperNodes.set(paper, n);
1554
1570
  }
@@ -1570,7 +1586,7 @@ function paperTile(paper, r, light) {
1570
1586
  const key = `${paperKey(paper, light)}|${r.x},${r.y},${r.width},${r.height}`;
1571
1587
  let t = tileCache.get(key);
1572
1588
  if (!t) {
1573
- t = _chunkNKTNLJUJcjs.renderPaper.call(void 0, paper, r, paperGrain(paper), light);
1589
+ t = _chunk5BR7LC24cjs.renderPaper.call(void 0, paper, r, paperGrain(paper), light);
1574
1590
  if (tileCache.size > 512) tileCache.delete(tileCache.keys().next().value);
1575
1591
  tileCache.set(key, t);
1576
1592
  }
@@ -1669,17 +1685,17 @@ function header(state, options) {
1669
1685
  if (options.swatches !== void 0) out.swatches = JSON.parse(JSON.stringify(options.swatches));
1670
1686
  if (options.mixingPad !== void 0) out.mixingPad = JSON.parse(JSON.stringify(options.mixingPad));
1671
1687
  if (options.wet !== void 0) out.wet = JSON.parse(JSON.stringify(options.wet));
1672
- if (_optionalChain([state, 'access', _69 => _69.wetEvents, 'optionalAccess', _70 => _70.length])) out.wetEvents = JSON.parse(JSON.stringify(state.wetEvents));
1673
- if (_optionalChain([state, 'access', _71 => _71.paintEvents, 'optionalAccess', _72 => _72.length])) out.paintEvents = JSON.parse(JSON.stringify(state.paintEvents));
1688
+ if (_optionalChain([state, 'access', _71 => _71.wetEvents, 'optionalAccess', _72 => _72.length])) out.wetEvents = JSON.parse(JSON.stringify(state.wetEvents));
1689
+ if (_optionalChain([state, 'access', _73 => _73.paintEvents, 'optionalAccess', _74 => _74.length])) out.paintEvents = JSON.parse(JSON.stringify(state.paintEvents));
1674
1690
  if (state.origin && (state.origin.x || state.origin.y)) out.origin = { ...state.origin };
1675
1691
  return out;
1676
1692
  }
1677
1693
  function serializePaintDocument(state, options = {}) {
1678
- const { layers, assets } = _chunkBBUE3UJLcjs.serializeLayers.call(void 0, state.layers);
1694
+ const { layers, assets } = _chunkHW72ABGRcjs.serializeLayers.call(void 0, state.layers);
1679
1695
  return { ...header(state, options), layers, assets };
1680
1696
  }
1681
1697
  async function serializePaintDocumentExternal(state, store, options = {}) {
1682
- const { layers, assets } = await _chunkBBUE3UJLcjs.serializeLayersExternal.call(void 0, state.layers, store);
1698
+ const { layers, assets } = await _chunkHW72ABGRcjs.serializeLayersExternal.call(void 0, state.layers, store);
1683
1699
  return { ...header(state, options), layers, assets };
1684
1700
  }
1685
1701
  function paintLayersOnly(layers, problems) {
@@ -1687,7 +1703,7 @@ function paintLayersOnly(layers, problems) {
1687
1703
  for (const l of layers) {
1688
1704
  if (l.type === "pixel") {
1689
1705
  const { filters, source, ...rest } = l;
1690
- if (_optionalChain([filters, 'optionalAccess', _73 => _73.length])) problems.push(`Layer "${l.name}": its filters are an image editor feature and were dropped.`);
1706
+ if (_optionalChain([filters, 'optionalAccess', _75 => _75.length])) problems.push(`Layer "${l.name}": its filters are an image editor feature and were dropped.`);
1691
1707
  if (source) problems.push(`Layer "${l.name}" refers to an image editor source image; it loads empty.`);
1692
1708
  out.push(rest);
1693
1709
  } else if (l.type === "group") out.push({ ...l, children: paintLayersOnly(l.children, problems) });
@@ -1714,11 +1730,11 @@ function deserializePaintDocument(raw) {
1714
1730
  }
1715
1731
  const problems = [];
1716
1732
  const canvas = normalizeCanvas({ width: r.width, height: r.height, dpi: _nullishCoalesce(r.dpi, () => ( void 0)) });
1717
- const paper = _chunkNKTNLJUJcjs.normalizePaper.call(void 0, _nullishCoalesce(r.paper, () => ( {})));
1733
+ const paper = _chunk5BR7LC24cjs.normalizePaper.call(void 0, _nullishCoalesce(r.paper, () => ( {})));
1718
1734
  const assets = r.assets && typeof r.assets === "object" ? r.assets : {};
1719
- const layers = paintLayersOnly(_chunkBBUE3UJLcjs.deserializeLayers.call(void 0, r.layers, { assets, problems }), problems);
1735
+ const layers = paintLayersOnly(_chunkHW72ABGRcjs.deserializeLayers.call(void 0, r.layers, { assets, problems }), problems);
1720
1736
  const wanted = typeof r.activeLayerId === "string" ? r.activeLayerId : null;
1721
- const activeLayerId = wanted && _chunkHB4ICNNAcjs.findLayer.call(void 0, layers, wanted) ? wanted : _nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.flattenLayers.call(void 0, layers), 'access', _74 => _74.filter, 'call', _75 => _75((l) => l.type === "pixel"), 'access', _76 => _76.pop, 'call', _77 => _77(), 'optionalAccess', _78 => _78.id]), () => ( null));
1737
+ const activeLayerId = wanted && _chunkHB4ICNNAcjs.findLayer.call(void 0, layers, wanted) ? wanted : _nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.flattenLayers.call(void 0, layers), 'access', _76 => _76.filter, 'call', _77 => _77((l) => l.type === "pixel"), 'access', _78 => _78.pop, 'call', _79 => _79(), 'optionalAccess', _80 => _80.id]), () => ( null));
1722
1738
  const wetEvents = Array.isArray(r.wetEvents) ? r.wetEvents.filter((e) => e && typeof e === "object" && Number.isInteger(e.clock) && typeof e.kind === "string") : [];
1723
1739
  const state = { canvas, paper, layers, activeLayerId, strokes: readStrokes(r.strokes, problems) };
1724
1740
  if (wetEvents.length) state.wetEvents = wetEvents;
@@ -1732,7 +1748,7 @@ async function deserializePaintDocumentAsync(raw, store) {
1732
1748
  if (!raw || typeof raw !== "object") throw new Error("Not a paint document: expected a JSON object.");
1733
1749
  const r = raw;
1734
1750
  const problems = [];
1735
- const assets = await _chunkBBUE3UJLcjs.resolveExternalAssets.call(void 0, _nullishCoalesce(r.assets, () => ( {})), store, problems);
1751
+ const assets = await _chunkHW72ABGRcjs.resolveExternalAssets.call(void 0, _nullishCoalesce(r.assets, () => ( {})), store, problems);
1736
1752
  const loaded = deserializePaintDocument({ ...r, assets });
1737
1753
  return { ...loaded, problems: [...problems, ...loaded.problems] };
1738
1754
  }
@@ -1815,7 +1831,7 @@ function pointerToPenPoint(e, x, y, view) {
1815
1831
  const p = { x, y, pressure: samplePressure(e) };
1816
1832
  if (typeof e.timeStamp === "number" && Number.isFinite(e.timeStamp)) p.t = e.timeStamp;
1817
1833
  if (e.pointerType !== "pen") return p;
1818
- const o = _chunkNKTNLJUJcjs.penOrientation.call(void 0, {
1834
+ const o = _chunk5BR7LC24cjs.penOrientation.call(void 0, {
1819
1835
  x,
1820
1836
  y,
1821
1837
  altitude: typeof e.altitudeAngle === "number" ? e.altitudeAngle : void 0,
@@ -1863,7 +1879,7 @@ async function decodeImageBlob(blob) {
1863
1879
  if (!ctx) throw new Error("No 2D context to decode with.");
1864
1880
  ctx.drawImage(bitmap, 0, 0);
1865
1881
  const data = new Uint8ClampedArray(ctx.getImageData(0, 0, bitmap.width, bitmap.height).data);
1866
- _optionalChain([bitmap, 'access', _79 => _79.close, 'optionalCall', _80 => _80()]);
1882
+ _optionalChain([bitmap, 'access', _81 => _81.close, 'optionalCall', _82 => _82()]);
1867
1883
  return { width: bitmap.width, height: bitmap.height, data };
1868
1884
  }
1869
1885
  function downloadBytes(bytes, fileName, mimeType) {
@@ -1894,16 +1910,16 @@ function wetPaperInput(paper) {
1894
1910
  absorption: paper.absorption,
1895
1911
  permeability: paper.permeability,
1896
1912
  roughness: flat ? 0 : paper.roughness,
1897
- color: _chunkNKTNLJUJcjs.hexToRgb8.call(void 0, paper.color),
1913
+ color: _chunk5BR7LC24cjs.hexToRgb8.call(void 0, paper.color),
1898
1914
  heightAt: flat ? () => 0.5 : (x, y) => grain.at(x, y)
1899
1915
  };
1900
1916
  }
1901
1917
  var WetStrokeDriver = (_class5 = class {
1902
- constructor(init) {;_class5.prototype.__init35.call(this);_class5.prototype.__init36.call(this);_class5.prototype.__init37.call(this);_class5.prototype.__init38.call(this);_class5.prototype.__init39.call(this);_class5.prototype.__init40.call(this);_class5.prototype.__init41.call(this);_class5.prototype.__init42.call(this);_class5.prototype.__init43.call(this);_class5.prototype.__init44.call(this);_class5.prototype.__init45.call(this);_class5.prototype.__init46.call(this);
1918
+ constructor(init) {;_class5.prototype.__init36.call(this);_class5.prototype.__init37.call(this);_class5.prototype.__init38.call(this);_class5.prototype.__init39.call(this);_class5.prototype.__init40.call(this);_class5.prototype.__init41.call(this);_class5.prototype.__init42.call(this);_class5.prototype.__init43.call(this);_class5.prototype.__init44.call(this);_class5.prototype.__init45.call(this);_class5.prototype.__init46.call(this);_class5.prototype.__init47.call(this);_class5.prototype.__init48.call(this);
1903
1919
  this.init = init;
1904
1920
  const brush = init.brush;
1905
- this.smoother = new (0, _chunkNKTNLJUJcjs.PenSmoother)(brush.tip.smoothing, brush.pressureSmoothing);
1906
- const copies = init.copies.length ? init.copies : [_chunkNKTNLJUJcjs.IDENTITY_COPY];
1921
+ this.smoother = new (0, _chunk5BR7LC24cjs.PenSmoother)(brush.tip.smoothing, brush.pressureSmoothing);
1922
+ const copies = init.copies.length ? init.copies : [_chunk5BR7LC24cjs.IDENTITY_COPY];
1907
1923
  const wet = brush.wet;
1908
1924
  const spacing = Math.max(0.02, brush.tip.spacing);
1909
1925
  const perDab = Math.min(1, spacing * 1.2);
@@ -1915,7 +1931,7 @@ var WetStrokeDriver = (_class5 = class {
1915
1931
  });
1916
1932
  const rates = _chunkPQ3H2AANcjs.exchangeRates.call(void 0, brush.behavior.pickup, brush.behavior.mix, wet.load, brush.behavior.mixing);
1917
1933
  this.states = copies.map((c, i) => ({
1918
- walker: new (0, _chunkNKTNLJUJcjs.PenDabWalker)(brush, init.seed + i * 7919 | 0, c.linear),
1934
+ walker: new (0, _chunk5BR7LC24cjs.PenDabWalker)(brush, init.seed + i * 7919 | 0, c.linear),
1919
1935
  exchange: wet.tool === "paint" ? new (0, _chunkPQ3H2AANcjs.PaintExchange)(rates, init.load) : null,
1920
1936
  last: null
1921
1937
  }));
@@ -1925,28 +1941,29 @@ var WetStrokeDriver = (_class5 = class {
1925
1941
  this.sampler = g ? new (0, _chunkPQ3H2AANcjs.PaintGradientSampler)(g.map.by === "across-tip" ? { ...g, map: { by: "distance", length: 300, repeat: "mirror" } } : g, init.seed) : null;
1926
1942
  }
1927
1943
 
1928
- __init35() {this.dabCount = 0}
1929
- __init36() {this.bounds = null}
1944
+ __init36() {this.dabCount = 0}
1945
+ __init37() {this.bounds = null}
1930
1946
  /** The world's clock at which each raw point was applied (recorded with the stroke). */
1931
- __init37() {this.steps = []}
1947
+ __init38() {this.steps = []}
1932
1948
 
1933
1949
 
1934
1950
 
1935
- __init38() {this.queue = []}
1936
- __init39() {this.finishing = false}
1937
- __init40() {this.finished = false}
1938
- __init41() {this.lastRaw = null}
1939
- __init42() {this.lastFed = null}
1940
- __init43() {this.tip = new Float32Array(0)}
1941
- __init44() {this.cov = new Float32Array(0)}
1942
- __init45() {this.paint = [0, 0, 0]}
1951
+ __init39() {this.queue = []}
1952
+ __init40() {this.finishing = false}
1953
+ __init41() {this.finished = false}
1954
+ __init42() {this.lastRaw = null}
1955
+ __init43() {this.lastFed = null}
1956
+ __init44() {this.tip = new Float32Array(0)}
1957
+ __init45() {this.cov = new Float32Array(0)}
1958
+ __init46() {this.paint = [0, 0, 0]}
1959
+ __init47() {this.centre = [0, 0, 0]}
1943
1960
 
1944
1961
 
1945
- __init46() {this.sample = { rgb: [0, 0, 0], alpha: 1 }}
1962
+ __init48() {this.sample = { rgb: [0, 0, 0], alpha: 1 }}
1946
1963
 
1947
1964
  /** What copy 0's brush carries after the dabs so far. */
1948
1965
  get load() {
1949
- return _nullishCoalesce(_optionalChain([this, 'access', _81 => _81.states, 'access', _82 => _82[0], 'access', _83 => _83.exchange, 'optionalAccess', _84 => _84.load]), () => ( null));
1966
+ return _nullishCoalesce(_optionalChain([this, 'access', _83 => _83.states, 'access', _84 => _84[0], 'access', _85 => _85.exchange, 'optionalAccess', _86 => _86.load]), () => ( null));
1950
1967
  }
1951
1968
  /** Whether every point (and the end) has been applied. */
1952
1969
  get done() {
@@ -2040,14 +2057,23 @@ var WetStrokeDriver = (_class5 = class {
2040
2057
  case "paint": {
2041
2058
  const ex = st.exchange;
2042
2059
  let flow = 1;
2043
- if (this.sampler) {
2060
+ const effect = this.init.effect;
2061
+ if (effect) {
2062
+ _chunk5BR7LC24cjs.sampleEffectAt.call(void 0, effect, d.x, d.y, this.centre, d);
2063
+ ex.setReservoirColor(this.centre[0], this.centre[1], this.centre[2]);
2064
+ } else if (this.sampler) {
2044
2065
  const g = this.sampler.dab(d, this.sample);
2045
2066
  ex.setReservoirColor(g.rgb[0], g.rgb[1], g.rgb[2]);
2046
2067
  flow = g.alpha;
2047
2068
  }
2048
2069
  ex.pickUp(this.medium.sample(rect, tip));
2049
2070
  const total = ex.paint(this.paint);
2050
- if (total > 0 && flow > 0) this.medium.deposit(rect, cov, Math.min(1, total) * k * flow, this.paint, null);
2071
+ if (total > 0 && flow > 0 && effect) {
2072
+ const ec = cov.slice();
2073
+ const field = new Float32Array(ec.length * 3);
2074
+ _chunk5BR7LC24cjs.effectDabField.call(void 0, effect, rect, ec, this.centre, this.paint, field, COVERAGE_STEPS, d);
2075
+ this.medium.deposit(rect, ec, Math.min(1, total) * k, this.paint, null, field);
2076
+ } else if (total > 0 && flow > 0) this.medium.deposit(rect, cov, Math.min(1, total) * k * flow, this.paint, null);
2051
2077
  else world.paint(laneId, rect, cov, this.medium.options.water * k, null);
2052
2078
  ex.drain();
2053
2079
  if (wet.rewet > 0) world.rewet(laneId, rect, cov, wet.rewet * k * 0.15, this.init.paperColor);
@@ -2089,7 +2115,7 @@ var WET_GPU_MODES = ["auto", "off"];
2089
2115
  var GPU_LEAD = 16;
2090
2116
  var DISPLAY_TILES_PER_TICK = 24;
2091
2117
  var PaintWetLayers = (_class6 = class {
2092
- constructor(host, options = {}) {;_class6.prototype.__init47.call(this);_class6.prototype.__init48.call(this);_class6.prototype.__init49.call(this);_class6.prototype.__init50.call(this);_class6.prototype.__init51.call(this);_class6.prototype.__init52.call(this);_class6.prototype.__init53.call(this);_class6.prototype.__init54.call(this);_class6.prototype.__init55.call(this);_class6.prototype.__init56.call(this);_class6.prototype.__init57.call(this);_class6.prototype.__init58.call(this);_class6.prototype.__init59.call(this);_class6.prototype.__init60.call(this);_class6.prototype.__init61.call(this);_class6.prototype.__init62.call(this);
2118
+ constructor(host, options = {}) {;_class6.prototype.__init49.call(this);_class6.prototype.__init50.call(this);_class6.prototype.__init51.call(this);_class6.prototype.__init52.call(this);_class6.prototype.__init53.call(this);_class6.prototype.__init54.call(this);_class6.prototype.__init55.call(this);_class6.prototype.__init56.call(this);_class6.prototype.__init57.call(this);_class6.prototype.__init58.call(this);_class6.prototype.__init59.call(this);_class6.prototype.__init60.call(this);_class6.prototype.__init61.call(this);_class6.prototype.__init62.call(this);_class6.prototype.__init63.call(this);_class6.prototype.__init64.call(this);
2093
2119
  this.host = host;
2094
2120
  this.options = options;
2095
2121
  this.speed = Math.max(1, Math.min(2e3, _nullishCoalesce(options.speed, () => ( 120))));
@@ -2099,27 +2125,27 @@ var PaintWetLayers = (_class6 = class {
2099
2125
  }
2100
2126
 
2101
2127
 
2102
- __init47() {this.world = null}
2103
- __init48() {this.tilt = { ..._chunkXFYLA3ZOcjs.FLAT }}
2104
- __init49() {this.settings = /* @__PURE__ */ new Map()}
2105
- __init50() {this.paused = false}
2128
+ __init49() {this.world = null}
2129
+ __init50() {this.tilt = { ..._chunkXFYLA3ZOcjs.FLAT }}
2130
+ __init51() {this.settings = /* @__PURE__ */ new Map()}
2131
+ __init52() {this.paused = false}
2106
2132
  /** Wet strokes dry as soon as they end (one undo step with the stroke). */
2107
- __init51() {this.instantDry = false}
2133
+ __init53() {this.instantDry = false}
2108
2134
 
2109
- __init52() {this.overlay = false}
2135
+ __init54() {this.overlay = false}
2110
2136
 
2111
2137
  /** The live wet stroke, if any. */
2112
- __init53() {this.driver = null}
2113
- __init54() {this.lastTick = null}
2114
- __init55() {this.ticking = null}
2115
- __init56() {this.stepsOwed = 0}
2116
- __init57() {this.displaying = null}
2117
- __init58() {this.leadAt = 0}
2138
+ __init55() {this.driver = null}
2139
+ __init56() {this.lastTick = null}
2140
+ __init57() {this.ticking = null}
2141
+ __init58() {this.stepsOwed = 0}
2142
+ __init59() {this.displaying = null}
2143
+ __init60() {this.leadAt = 0}
2118
2144
 
2119
2145
 
2120
- __init59() {this.gpu = null}
2121
- __init60() {this.gpuSupport = null}
2122
- __init61() {this.gpuStarting = null}
2146
+ __init61() {this.gpu = null}
2147
+ __init62() {this.gpuSupport = null}
2148
+ __init63() {this.gpuStarting = null}
2123
2149
  // --- Engine B ------------------------------------------------------------------
2124
2150
  /** Starts the GPU engine for the world (once; it may turn out unavailable). */
2125
2151
  startGpu() {
@@ -2155,8 +2181,8 @@ var PaintWetLayers = (_class6 = class {
2155
2181
  if (mode === this.gpuMode) return this.engineInfo();
2156
2182
  this.gpuMode = mode;
2157
2183
  if (mode === "off") {
2158
- _optionalChain([this, 'access', _85 => _85.world, 'optionalAccess', _86 => _86.attachGpu, 'call', _87 => _87(null)]);
2159
- _optionalChain([this, 'access', _88 => _88.gpu, 'optionalAccess', _89 => _89.destroy, 'call', _90 => _90()]);
2184
+ _optionalChain([this, 'access', _87 => _87.world, 'optionalAccess', _88 => _88.attachGpu, 'call', _89 => _89(null)]);
2185
+ _optionalChain([this, 'access', _90 => _90.gpu, 'optionalAccess', _91 => _91.destroy, 'call', _92 => _92()]);
2160
2186
  this.gpu = null;
2161
2187
  this.gpuSupport = null;
2162
2188
  } else if (this.world) this.startGpu();
@@ -2164,13 +2190,13 @@ var PaintWetLayers = (_class6 = class {
2164
2190
  }
2165
2191
  engineInfo() {
2166
2192
  const w = this.world;
2167
- const base = { mode: this.gpuMode, adapter: _nullishCoalesce(_optionalChain([this, 'access', _91 => _91.gpuSupport, 'optionalAccess', _92 => _92.adapter]), () => ( null)), maxTiles: _nullishCoalesce(_optionalChain([this, 'access', _93 => _93.gpuSupport, 'optionalAccess', _94 => _94.maxSlots]), () => ( 0)), minTiles: this.gpuMinTiles, activeTiles: _nullishCoalesce(_optionalChain([w, 'optionalAccess', _95 => _95.activeTiles, 'call', _96 => _96()]), () => ( 0)), cpu: this.backend() };
2193
+ const base = { mode: this.gpuMode, adapter: _nullishCoalesce(_optionalChain([this, 'access', _93 => _93.gpuSupport, 'optionalAccess', _94 => _94.adapter]), () => ( null)), maxTiles: _nullishCoalesce(_optionalChain([this, 'access', _95 => _95.gpuSupport, 'optionalAccess', _96 => _96.maxSlots]), () => ( 0)), minTiles: this.gpuMinTiles, activeTiles: _nullishCoalesce(_optionalChain([w, 'optionalAccess', _97 => _97.activeTiles, 'call', _98 => _98()]), () => ( 0)), cpu: this.backend() };
2168
2194
  if (this.gpuMode === "off") return { ...base, state: "off", reason: "Forced to the CPU." };
2169
2195
  if (this.gpuStarting) return { ...base, state: "starting", reason: null };
2170
- if (_optionalChain([w, 'optionalAccess', _97 => _97.gpuError])) return { ...base, state: "failed", reason: w.gpuError };
2196
+ if (_optionalChain([w, 'optionalAccess', _99 => _99.gpuError])) return { ...base, state: "failed", reason: w.gpuError };
2171
2197
  if (this.gpuSupport && !this.gpuSupport.supported) return { ...base, state: "unavailable", reason: this.gpuSupport.reason };
2172
2198
  if (!this.gpu) return { ...base, state: "not-started", reason: "Starts with the first wet stroke." };
2173
- if (_optionalChain([w, 'optionalAccess', _98 => _98.gpuActive])) return { ...base, state: "active", reason: null };
2199
+ if (_optionalChain([w, 'optionalAccess', _100 => _100.gpuActive])) return { ...base, state: "active", reason: null };
2174
2200
  return { ...base, state: "standby", reason: `The GPU takes over at ${this.gpuMinTiles} wet tiles (256 px).` };
2175
2201
  }
2176
2202
  /** The world (made on first use, at the canvas size). */
@@ -2209,13 +2235,13 @@ var PaintWetLayers = (_class6 = class {
2209
2235
  if (!this.canExtend(left, top)) throw new Error("Wet paint only moves by whole 256 px tiles, between strokes.");
2210
2236
  for (const [id, s] of this.settings) if (s.flow.center) this.settings.set(id, { ...s, flow: { ...s.flow, center: { x: s.flow.center.x + left, y: s.flow.center.y + top } } });
2211
2237
  const old = this.world;
2212
- const snap = _optionalChain([old, 'optionalAccess', _99 => _99.laneIds, 'call', _100 => _100(), 'access', _101 => _101.length]) ? old.snapshot() : null;
2238
+ const snap = _optionalChain([old, 'optionalAccess', _101 => _101.laneIds, 'call', _102 => _102(), 'access', _103 => _103.length]) ? old.snapshot() : null;
2213
2239
  this.dropWorld();
2214
2240
  this.lastTick = null;
2215
2241
  this.stepsOwed = 0;
2216
2242
  if (!old || !snap) return;
2217
2243
  const moved = _chunkXFYLA3ZOcjs.moveWetSnapshot.call(void 0, snap, old, { width, height }, left, top, (l) => {
2218
- const flow = _optionalChain([this, 'access', _102 => _102.settings, 'access', _103 => _103.get, 'call', _104 => _104(l.id), 'optionalAccess', _105 => _105.flow]);
2244
+ const flow = _optionalChain([this, 'access', _104 => _104.settings, 'access', _105 => _105.get, 'call', _106 => _106(l.id), 'optionalAccess', _107 => _107.flow]);
2219
2245
  return {
2220
2246
  base: _chunk474IQG74cjs.translateSurface.call(void 0, l.base, left, top),
2221
2247
  flow: l.flow && flow && flow.kind !== "none" ? _chunkXFYLA3ZOcjs.flowField.call(void 0, flow, width, height) : l.flow
@@ -2233,11 +2259,11 @@ var PaintWetLayers = (_class6 = class {
2233
2259
  }
2234
2260
  /** Layers holding a wet layer (wet or drying). */
2235
2261
  wetLayerIds() {
2236
- return _nullishCoalesce(_optionalChain([this, 'access', _106 => _106.world, 'optionalAccess', _107 => _107.laneIds, 'call', _108 => _108()]), () => ( []));
2262
+ return _nullishCoalesce(_optionalChain([this, 'access', _108 => _108.world, 'optionalAccess', _109 => _109.laneIds, 'call', _110 => _110()]), () => ( []));
2237
2263
  }
2238
2264
  /** Whether a batch is running (edits wait). */
2239
2265
  get busy() {
2240
- return Boolean(_optionalChain([this, 'access', _109 => _109.world, 'optionalAccess', _110 => _110.busy]));
2266
+ return Boolean(_optionalChain([this, 'access', _111 => _111.world, 'optionalAccess', _112 => _112.busy]));
2241
2267
  }
2242
2268
  /** Resolves when no batch is running. */
2243
2269
  async idle() {
@@ -2266,13 +2292,13 @@ var PaintWetLayers = (_class6 = class {
2266
2292
  setParams(layerId, update) {
2267
2293
  const s = this.settingsOf(layerId);
2268
2294
  s.params = _chunkXFYLA3ZOcjs.normalizeWetParams.call(void 0, update, s.params);
2269
- if (_optionalChain([this, 'access', _111 => _111.world, 'optionalAccess', _112 => _112.hasLane, 'call', _113 => _113(layerId)])) this.world.setParams(layerId, s.params);
2295
+ if (_optionalChain([this, 'access', _113 => _113.world, 'optionalAccess', _114 => _114.hasLane, 'call', _115 => _115(layerId)])) this.world.setParams(layerId, s.params);
2270
2296
  return { ...s.params };
2271
2297
  }
2272
2298
  setFlow(layerId, update) {
2273
2299
  const s = this.settingsOf(layerId);
2274
2300
  s.flow = _chunkXFYLA3ZOcjs.normalizeFlow.call(void 0, update, s.flow);
2275
- if (_optionalChain([this, 'access', _114 => _114.world, 'optionalAccess', _115 => _115.hasLane, 'call', _116 => _116(layerId)])) {
2301
+ if (_optionalChain([this, 'access', _116 => _116.world, 'optionalAccess', _117 => _117.hasLane, 'call', _118 => _118(layerId)])) {
2276
2302
  const { width, height } = this.host.canvas();
2277
2303
  this.world.setFlow(layerId, _chunkXFYLA3ZOcjs.flowField.call(void 0, s.flow, width, height), s.flow.strength);
2278
2304
  }
@@ -2280,29 +2306,29 @@ var PaintWetLayers = (_class6 = class {
2280
2306
  }
2281
2307
  setTilt(update) {
2282
2308
  this.tilt = _chunkXFYLA3ZOcjs.normalizeTilt.call(void 0, update, this.tilt);
2283
- _optionalChain([this, 'access', _117 => _117.world, 'optionalAccess', _118 => _118.setTilt, 'call', _119 => _119(this.tilt)]);
2309
+ _optionalChain([this, 'access', _119 => _119.world, 'optionalAccess', _120 => _120.setTilt, 'call', _121 => _121(this.tilt)]);
2284
2310
  return { ...this.tilt };
2285
2311
  }
2286
2312
  /** The paper changed. */
2287
2313
  paperChanged() {
2288
- _optionalChain([this, 'access', _120 => _120.world, 'optionalAccess', _121 => _121.setPaper, 'call', _122 => _122(wetPaperInput(this.host.paper()))]);
2314
+ _optionalChain([this, 'access', _122 => _122.world, 'optionalAccess', _123 => _123.setPaper, 'call', _124 => _124(wetPaperInput(this.host.paper()))]);
2289
2315
  }
2290
2316
  /** A layer as tools describe it. */
2291
2317
  info(layerId) {
2292
2318
  const s = this.settingsOf(layerId);
2293
- const st = _optionalChain([this, 'access', _123 => _123.world, 'optionalAccess', _124 => _124.hasLane, 'call', _125 => _125(layerId)]) ? this.world.stats(layerId) : null;
2319
+ const st = _optionalChain([this, 'access', _125 => _125.world, 'optionalAccess', _126 => _126.hasLane, 'call', _127 => _127(layerId)]) ? this.world.stats(layerId) : null;
2294
2320
  return {
2295
2321
  layerId,
2296
2322
  wet: Boolean(st && st.wetCells > 0),
2297
- wetCells: _nullishCoalesce(_optionalChain([st, 'optionalAccess', _126 => _126.wetCells]), () => ( 0)),
2298
- wetBounds: _nullishCoalesce(_optionalChain([st, 'optionalAccess', _127 => _127.wetBounds]), () => ( null)),
2299
- activeTiles: _nullishCoalesce(_optionalChain([st, 'optionalAccess', _128 => _128.activeTiles]), () => ( 0)),
2323
+ wetCells: _nullishCoalesce(_optionalChain([st, 'optionalAccess', _128 => _128.wetCells]), () => ( 0)),
2324
+ wetBounds: _nullishCoalesce(_optionalChain([st, 'optionalAccess', _129 => _129.wetBounds]), () => ( null)),
2325
+ activeTiles: _nullishCoalesce(_optionalChain([st, 'optionalAccess', _130 => _130.activeTiles]), () => ( 0)),
2300
2326
  params: { ...s.params },
2301
2327
  flow: { ...s.flow }
2302
2328
  };
2303
2329
  }
2304
2330
  backend() {
2305
- return _nullishCoalesce(_optionalChain([this, 'access', _129 => _129.world, 'optionalAccess', _130 => _130.backend, 'call', _131 => _131()]), () => ( null));
2331
+ return _nullishCoalesce(_optionalChain([this, 'access', _131 => _131.world, 'optionalAccess', _132 => _132.backend, 'call', _133 => _133()]), () => ( null));
2306
2332
  }
2307
2333
  /** Shows the current wet pixels (at most `maxTiles` reshaded per layer). */
2308
2334
  syncDisplays(maxTiles = Infinity) {
@@ -2338,7 +2364,7 @@ var PaintWetLayers = (_class6 = class {
2338
2364
  const w = this.world;
2339
2365
  if (!w) return;
2340
2366
  for (const id of w.laneIds()) {
2341
- if (w.isActive(id) || _optionalChain([this, 'access', _132 => _132.driver, 'optionalAccess', _133 => _133.init, 'access', _134 => _134.laneId]) === id) continue;
2367
+ if (w.isActive(id) || _optionalChain([this, 'access', _134 => _134.driver, 'optionalAccess', _135 => _135.init, 'access', _136 => _136.laneId]) === id) continue;
2342
2368
  const base = w.getBase(id);
2343
2369
  w.removeLane(id);
2344
2370
  this.host.updateSurfaces(/* @__PURE__ */ new Map([[id, base]]), []);
@@ -2418,10 +2444,10 @@ var PaintWetLayers = (_class6 = class {
2418
2444
  if (!d || !this.world) return;
2419
2445
  const rects = d.flush();
2420
2446
  if (rects.length) this.syncDisplays(DISPLAY_TILES_PER_TICK);
2421
- if (d.done) _optionalChain([this, 'access', _135 => _135.onDriverDone, 'optionalCall', _136 => _136(d)]);
2447
+ if (d.done) _optionalChain([this, 'access', _137 => _137.onDriverDone, 'optionalCall', _138 => _138(d)]);
2422
2448
  }
2423
2449
  /** Called when a live stroke that ended mid-batch has finished applying. */
2424
- __init62() {this.onDriverDone = null}
2450
+ __init64() {this.onDriverDone = null}
2425
2451
  /** Steps `steps` times now (agents, tests). Returns the steps run. */
2426
2452
  async advance(steps) {
2427
2453
  await this.idle();
@@ -2462,7 +2488,7 @@ var PaintWetLayers = (_class6 = class {
2462
2488
  /** The layer's pixels with all its wet paint dried (deterministic), without changing anything. */
2463
2489
  bakedSurface(layerId) {
2464
2490
  const w = this.world;
2465
- if (!_optionalChain([w, 'optionalAccess', _137 => _137.hasLane, 'call', _138 => _138(layerId)])) return null;
2491
+ if (!_optionalChain([w, 'optionalAccess', _139 => _139.hasLane, 'call', _140 => _140(layerId)])) return null;
2466
2492
  return w.bakedSurface(layerId);
2467
2493
  }
2468
2494
  /** A snapshot of the world for undo (`null` when there is none). */
@@ -2483,13 +2509,13 @@ var PaintWetLayers = (_class6 = class {
2483
2509
  this.syncDisplays();
2484
2510
  }
2485
2511
  dropWorld() {
2486
- _optionalChain([this, 'access', _139 => _139.world, 'optionalAccess', _140 => _140.destroy, 'call', _141 => _141()]);
2512
+ _optionalChain([this, 'access', _141 => _141.world, 'optionalAccess', _142 => _142.destroy, 'call', _143 => _143()]);
2487
2513
  this.world = null;
2488
2514
  this.driver = null;
2489
2515
  }
2490
2516
  /** Forgets everything wet (a new canvas, a resize, opening a document). */
2491
2517
  reset() {
2492
- _optionalChain([this, 'access', _142 => _142.world, 'optionalAccess', _143 => _143.destroy, 'call', _144 => _144()]);
2518
+ _optionalChain([this, 'access', _144 => _144.world, 'optionalAccess', _145 => _145.destroy, 'call', _146 => _146()]);
2493
2519
  this.world = null;
2494
2520
  this.driver = null;
2495
2521
  this.settings.clear();
@@ -2519,14 +2545,14 @@ var PaintWetLayers = (_class6 = class {
2519
2545
  return out;
2520
2546
  }
2521
2547
  destroy() {
2522
- _optionalChain([this, 'access', _145 => _145.world, 'optionalAccess', _146 => _146.destroy, 'call', _147 => _147()]);
2548
+ _optionalChain([this, 'access', _147 => _147.world, 'optionalAccess', _148 => _148.destroy, 'call', _149 => _149()]);
2523
2549
  this.world = null;
2524
- _optionalChain([this, 'access', _148 => _148.gpu, 'optionalAccess', _149 => _149.destroy, 'call', _150 => _150()]);
2550
+ _optionalChain([this, 'access', _150 => _150.gpu, 'optionalAccess', _151 => _151.destroy, 'call', _152 => _152()]);
2525
2551
  this.gpu = null;
2526
2552
  }
2527
2553
  }, _class6);
2528
2554
  function wetPigmentOf(hex, body) {
2529
- const [r, g, b] = _chunkNKTNLJUJcjs.hexToRgb8.call(void 0, hex);
2555
+ const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, hex);
2530
2556
  return Array.from(_chunkXFYLA3ZOcjs.pigmentOf.call(void 0, [r / 255, g / 255, b / 255], body));
2531
2557
  }
2532
2558
 
@@ -2534,13 +2560,13 @@ function wetPigmentOf(hex, body) {
2534
2560
  var thumbWorld = null;
2535
2561
  function paintWetTestStroke(brush, paper, color, width, height) {
2536
2562
  if (!thumbWorld || thumbWorld.width !== width || thumbWorld.height !== height) {
2537
- _optionalChain([thumbWorld, 'optionalAccess', _151 => _151.destroy, 'call', _152 => _152()]);
2563
+ _optionalChain([thumbWorld, 'optionalAccess', _153 => _153.destroy, 'call', _154 => _154()]);
2538
2564
  thumbWorld = new (0, _chunkXFYLA3ZOcjs.WetWorld)(width, height, wetPaperInput(paper), { kernel: "typescript", threads: 0 });
2539
2565
  } else thumbWorld.setPaper(wetPaperInput(paper));
2540
2566
  const w = thumbWorld;
2541
2567
  if (w.hasLane("thumb")) w.removeLane("thumb");
2542
2568
  w.ensureLane("thumb");
2543
- const [r, g, b] = _chunkNKTNLJUJcjs.hexToRgb8.call(void 0, color);
2569
+ const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, color);
2544
2570
  if (brush.wet.tool !== "paint" && brush.wet.tool !== "water") {
2545
2571
  const half = Math.ceil(height / 2);
2546
2572
  w.paint("thumb", { x: 0, y: 0, width, height: half }, null, 0.4, Array.from(_chunkXFYLA3ZOcjs.pigmentOf.call(void 0, [r / 255, g / 255, b / 255], 0), (v) => v * 0.05));
@@ -2554,9 +2580,9 @@ function paintWetTestStroke(brush, paper, color, width, height) {
2554
2580
  load: brushLoadForStroke(brush, _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD, [r / 255, g / 255, b / 255]),
2555
2581
  grain: paperGrain(paper),
2556
2582
  paperDepth: paper.depth,
2557
- paperColor: _chunkNKTNLJUJcjs.hexToRgb8.call(void 0, paper.color),
2583
+ paperColor: _chunk5BR7LC24cjs.hexToRgb8.call(void 0, paper.color),
2558
2584
  seed: 7,
2559
- copies: [_chunkNKTNLJUJcjs.IDENTITY_COPY]
2585
+ copies: [_chunk5BR7LC24cjs.IDENTITY_COPY]
2560
2586
  });
2561
2587
  driver.push(thumbnailStroke(width, height));
2562
2588
  driver.finish();
@@ -2576,7 +2602,7 @@ function thumbnailStroke(width, height) {
2576
2602
  return pts;
2577
2603
  }
2578
2604
  function paintTestStroke(surface, brush, paper, color, width, height) {
2579
- const [r, g, b] = _chunkNKTNLJUJcjs.hexToRgb8.call(void 0, color);
2605
+ const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, color);
2580
2606
  let result = surface;
2581
2607
  let thick = null;
2582
2608
  const session = new PaintStrokeSession(
@@ -2595,13 +2621,13 @@ function paintTestStroke(surface, brush, paper, color, width, height) {
2595
2621
  lockTransparency: false,
2596
2622
  brush,
2597
2623
  color: [r, g, b, 255],
2598
- background: [..._chunkNKTNLJUJcjs.hexToRgb8.call(void 0, paper.color), 255],
2624
+ background: [..._chunk5BR7LC24cjs.hexToRgb8.call(void 0, paper.color), 255],
2599
2625
  grain: paperGrain(paper),
2600
2626
  paperDepth: paper.depth,
2601
2627
  seed: 7,
2602
2628
  colorHex: color,
2603
2629
  affineCopies: [],
2604
- copies: [_chunkNKTNLJUJcjs.IDENTITY_COPY],
2630
+ copies: [_chunk5BR7LC24cjs.IDENTITY_COPY],
2605
2631
  mixerLoad: brushLoadForStroke(brush, _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD, [r / 255, g / 255, b / 255]),
2606
2632
  thick: brush.engine === "impasto" ? { channels: emptyThickChannels() } : void 0,
2607
2633
  sticker: brush.engine === "sticker" ? { nextId: 1 } : void 0
@@ -2615,8 +2641,8 @@ function renderBrushThumbnail(brush, options) {
2615
2641
  const width = Math.max(16, Math.min(512, Math.round(_nullishCoalesce(options.width, () => ( 160)))));
2616
2642
  const height = Math.max(16, Math.min(512, Math.round(_nullishCoalesce(options.height, () => ( 48)))));
2617
2643
  const maxSize = height * 0.42;
2618
- const b = brush.tip.size > maxSize ? _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, { tip: { ...brush.tip, size: maxSize } }, brush) : brush;
2619
- const rgba = [..._chunkNKTNLJUJcjs.hexToRgb8.call(void 0, options.color), 255];
2644
+ const b = brush.tip.size > maxSize ? _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { tip: { ...brush.tip, size: maxSize } }, brush) : brush;
2645
+ const rgba = [..._chunk5BR7LC24cjs.hexToRgb8.call(void 0, options.color), 255];
2620
2646
  let surface = _chunk474IQG74cjs.emptySurface.call(void 0, 4);
2621
2647
  if (b.engine === "eraser") {
2622
2648
  surface = _chunk474IQG74cjs.fillRect.call(void 0, surface, { x: 0, y: 0, width, height }, rgba);
@@ -2758,12 +2784,12 @@ function perspectiveDirections(g, p) {
2758
2784
  }
2759
2785
  var SNAP_DECIDE_DISTANCE = 6;
2760
2786
  var PerspectiveSnapper = (_class7 = class {
2761
- constructor(guide) {;_class7.prototype.__init63.call(this);_class7.prototype.__init64.call(this);
2787
+ constructor(guide) {;_class7.prototype.__init65.call(this);_class7.prototype.__init66.call(this);
2762
2788
  this.guide = guide;
2763
2789
  }
2764
2790
 
2765
- __init63() {this.start = null}
2766
- __init64() {this.dir = null}
2791
+ __init65() {this.start = null}
2792
+ __init66() {this.dir = null}
2767
2793
  /** The line the stroke follows, once chosen. */
2768
2794
  get line() {
2769
2795
  return this.start && this.dir ? { from: this.start, direction: this.dir } : null;
@@ -2869,11 +2895,11 @@ function readSwatch(raw) {
2869
2895
  if (!raw || typeof raw !== "object") return null;
2870
2896
  const r = raw;
2871
2897
  const g = _chunkPQ3H2AANcjs.readPaintSource.call(void 0, r.paint);
2872
- const color = typeof r.color === "string" && /^#[0-9a-f]{6}$/i.test(r.color) ? r.color.toLowerCase() : _optionalChain([g, 'optionalAccess', _153 => _153.type]) === "gradient" ? _chunkPQ3H2AANcjs.paintSourceColor.call(void 0, g) : null;
2898
+ const color = typeof r.color === "string" && /^#[0-9a-f]{6}$/i.test(r.color) ? r.color.toLowerCase() : _optionalChain([g, 'optionalAccess', _155 => _155.type]) === "gradient" ? _chunkPQ3H2AANcjs.paintSourceColor.call(void 0, g) : null;
2873
2899
  if (!color) return null;
2874
2900
  const out = { color };
2875
2901
  if (typeof r.name === "string" && r.name.trim()) out.name = r.name.trim().slice(0, 80);
2876
- if (_optionalChain([g, 'optionalAccess', _154 => _154.type]) === "gradient") out.paint = g;
2902
+ if (_optionalChain([g, 'optionalAccess', _156 => _156.type]) === "gradient") out.paint = g;
2877
2903
  return out;
2878
2904
  }
2879
2905
  var DEFAULT_SWATCHES = [
@@ -2892,7 +2918,7 @@ var DEFAULT_SWATCHES = [
2892
2918
  // src/paint-editor/mixing-pad.ts
2893
2919
  var MIXING_PAD_WIDTH = 360;
2894
2920
  var MIXING_PAD_HEIGHT = 220;
2895
- var PAD_PAINT = _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, {
2921
+ var PAD_PAINT = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, {
2896
2922
  id: "pad-paint",
2897
2923
  name: "Pad paint",
2898
2924
  category: "mixer",
@@ -2900,7 +2926,7 @@ var PAD_PAINT = _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, {
2900
2926
  tip: { size: 28, hardness: 0.75, spacing: 0.1, opacity: 1, flow: 0.7, pressureSize: false, pressureOpacity: false },
2901
2927
  mixer: { wet: 0.45, load: 1, mix: 0.4, loadEachStroke: true, cleanEachStroke: true, mixing: "pigment" }
2902
2928
  });
2903
- var PAD_KNIFE = _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, {
2929
+ var PAD_KNIFE = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, {
2904
2930
  id: "pad-knife",
2905
2931
  name: "Palette knife",
2906
2932
  category: "smudge",
@@ -2910,19 +2936,19 @@ var PAD_KNIFE = _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, {
2910
2936
  // A knife drags streaks of paint and folds them into each other, mixing them as pigment.
2911
2937
  smudge: { strength: 0.6, length: 0.8, mixing: "pigment" }
2912
2938
  });
2913
- var PAD_BLOB = _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, {
2939
+ var PAD_BLOB = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, {
2914
2940
  id: "pad-blob",
2915
2941
  name: "Pad blob",
2916
2942
  category: "custom",
2917
2943
  engine: "dab",
2918
2944
  tip: { size: 20, hardness: 1, spacing: 0.1, opacity: 1, flow: 1, pressureSize: false, pressureOpacity: false }
2919
2945
  });
2920
- var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init65.call(this);_class8.prototype.__init66.call(this);_class8.prototype.__init67.call(this);_class8.prototype.__init68.call(this); }
2921
- __init65() {this.width = MIXING_PAD_WIDTH}
2922
- __init66() {this.height = MIXING_PAD_HEIGHT}
2923
- __init67() {this.surface = _chunk474IQG74cjs.emptySurface.call(void 0, 4)}
2946
+ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init67.call(this);_class8.prototype.__init68.call(this);_class8.prototype.__init69.call(this);_class8.prototype.__init70.call(this); }
2947
+ __init67() {this.width = MIXING_PAD_WIDTH}
2948
+ __init68() {this.height = MIXING_PAD_HEIGHT}
2949
+ __init69() {this.surface = _chunk474IQG74cjs.emptySurface.call(void 0, 4)}
2924
2950
  /** Increments on every change (for redrawing). */
2925
- __init68() {this.version = 0}
2951
+ __init70() {this.version = 0}
2926
2952
  /** Paints (`paint`, with `color`) or smears (`knife`) through `points` in pad pixels. */
2927
2953
  stroke(points, options) {
2928
2954
  if (!points.length) throw new Error("points is empty; give at least one {x, y} in pad pixels.");
@@ -2932,12 +2958,12 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init65.cal
2932
2958
  }
2933
2959
  }
2934
2960
  let brush = options.tool === "knife" ? PAD_KNIFE : PAD_PAINT;
2935
- if (options.size !== void 0) brush = _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, { tip: { ...brush.tip, size: options.size } }, brush);
2961
+ if (options.size !== void 0) brush = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { tip: { ...brush.tip, size: options.size } }, brush);
2936
2962
  return this.run(brush, points, options.color);
2937
2963
  }
2938
2964
  /** Paints `points` with `brush` in `color` on the pad. */
2939
2965
  run(brush, points, color) {
2940
- const [r, g, b] = _chunkNKTNLJUJcjs.hexToRgb8.call(void 0, color);
2966
+ const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, color);
2941
2967
  let result = null;
2942
2968
  const session = new PaintStrokeSession(
2943
2969
  { update: () => {
@@ -2955,7 +2981,7 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init65.cal
2955
2981
  seed: 1,
2956
2982
  colorHex: color,
2957
2983
  affineCopies: [],
2958
- copies: [_chunkNKTNLJUJcjs.IDENTITY_COPY],
2984
+ copies: [_chunk5BR7LC24cjs.IDENTITY_COPY],
2959
2985
  mixerLoad: brushLoadForStroke(brush, _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD, [r / 255, g / 255, b / 255])
2960
2986
  }
2961
2987
  );
@@ -3031,10 +3057,10 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init65.cal
3031
3057
  const off = used.length === 1 ? 0 : 0.45 * R;
3032
3058
  const x = Math.min(cx + R - r, Math.max(cx - R + r, cx + Math.cos(a) * off));
3033
3059
  const y = Math.min(cy + R - r, Math.max(cy - R + r, cy + Math.sin(a) * off));
3034
- const blob = _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_BLOB.tip, size: Math.max(2, 2 * r) } }, PAD_BLOB);
3060
+ const blob = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_BLOB.tip, size: Math.max(2, 2 * r) } }, PAD_BLOB);
3035
3061
  this.run(blob, [{ x, y }], p.color.toLowerCase());
3036
3062
  });
3037
- const knife = _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_KNIFE.tip, size: Math.max(12, R * 0.5) } }, PAD_KNIFE);
3063
+ const knife = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_KNIFE.tip, size: Math.max(12, R * 0.5) } }, PAD_KNIFE);
3038
3064
  for (let k = 0; k < passes; k++) {
3039
3065
  const stir = [];
3040
3066
  const turns = 1.6;
@@ -3046,10 +3072,10 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init65.cal
3046
3072
  this.run(knife, stir, "#000000");
3047
3073
  }
3048
3074
  const mixed = _chunkOLNIPTB3cjs.mixPigments.call(void 0,
3049
- paints.map((p) => _chunkNKTNLJUJcjs.hexToRgb8.call(void 0, p.color).map((v) => v / 255)),
3075
+ paints.map((p) => _chunk5BR7LC24cjs.hexToRgb8.call(void 0, p.color).map((v) => v / 255)),
3050
3076
  paints.map((p) => p.parts)
3051
3077
  );
3052
- const core = _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_BLOB.tip, size: R * 1.1, hardness: 0.55 } }, PAD_BLOB);
3078
+ const core = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_BLOB.tip, size: R * 1.1, hardness: 0.55 } }, PAD_BLOB);
3053
3079
  const hex = "#" + mixed.map((v) => Math.round(v * 255).toString(16).padStart(2, "0")).join("");
3054
3080
  this.run(core, [{ x: cx, y: cy }], hex);
3055
3081
  return { color: this.pick(cx, cy, 2), bounds };
@@ -3060,7 +3086,7 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init65.cal
3060
3086
  }
3061
3087
  /** Replaces the pad with a PNG (data URL or base64) of the pad's size, as `toPng` writes. */
3062
3088
  fromPng(png) {
3063
- const bytes = png.startsWith("data:") ? _optionalChain([_chunkOLNIPTB3cjs.parseDataUrl.call(void 0, png), 'optionalAccess', _155 => _155.bytes]) : _chunkOLNIPTB3cjs.base64ToBytes.call(void 0, png);
3089
+ const bytes = png.startsWith("data:") ? _optionalChain([_chunkOLNIPTB3cjs.parseDataUrl.call(void 0, png), 'optionalAccess', _157 => _157.bytes]) : _chunkOLNIPTB3cjs.base64ToBytes.call(void 0, png);
3064
3090
  if (!bytes || !_chunkOLNIPTB3cjs.isPng.call(void 0, bytes)) throw new Error("The pad image is not a PNG data URL or base64 PNG.");
3065
3091
  const img = _chunkOLNIPTB3cjs.decodePng.call(void 0, bytes);
3066
3092
  if (img.width !== this.width || img.height !== this.height) throw new Error(`The pad image is ${img.width}\xD7${img.height}; a mixing pad is ${this.width}\xD7${this.height}.`);
@@ -3085,6 +3111,43 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init65.cal
3085
3111
  }
3086
3112
  }, _class8);
3087
3113
 
3114
+ // src/paint-editor/live-strokes.ts
3115
+ var LIVE_STROKE_ENGINES = ["dab", "particle", "bristle", "mixer"];
3116
+ function liveStrokeRefusal(brush) {
3117
+ switch (brush.engine) {
3118
+ case "wet":
3119
+ return `The ${brush.name} brush paints wet media, which a stroke layer can't redraw (the paint depends on the simulation's history); paint it on a pixel layer (add_layer).`;
3120
+ case "impasto":
3121
+ return `The ${brush.name} brush paints thick paint with relief, which a stroke layer doesn't keep; paint it on a pixel layer (add_layer).`;
3122
+ case "sticker":
3123
+ return `The ${brush.name} brush sprays stickers, which are their own objects; spray them on an empty pixel layer (add_layer).`;
3124
+ case "eraser":
3125
+ return `Erasers don't paint on a stroke layer; delete strokes instead (remove_strokes), or rasterize the layer first (rasterize_stroke_layer).`;
3126
+ case "blender":
3127
+ case "smudge":
3128
+ return `The ${brush.name} brush moves the paint under it, so it can't be redrawn on its own; rasterize the layer first (rasterize_stroke_layer) or blend on a pixel layer.`;
3129
+ }
3130
+ if (brush.behavior.pickup > 0) return `The ${brush.name} brush picks up the paint under it (pickup ${brush.behavior.pickup}), so it can't be redrawn on its own; set behavior.pickup to 0 (set_brush) or paint on a pixel layer.`;
3131
+ return null;
3132
+ }
3133
+ function liveStrokeBounds(stroke) {
3134
+ let x0 = Infinity;
3135
+ let y0 = Infinity;
3136
+ let x1 = -Infinity;
3137
+ let y1 = -Infinity;
3138
+ for (const m of [null, ..._nullishCoalesce(stroke.copies, () => ( []))]) {
3139
+ for (const p of stroke.points) {
3140
+ const q = m ? applyAffine(m, p.x, p.y) : p;
3141
+ if (q.x < x0) x0 = q.x;
3142
+ if (q.y < y0) y0 = q.y;
3143
+ if (q.x > x1) x1 = q.x;
3144
+ if (q.y > y1) y1 = q.y;
3145
+ }
3146
+ }
3147
+ const pad = stroke.brush.tip.size * (stroke.brush.engine === "particle" ? 2 : 1) + 2;
3148
+ return { x: Math.floor(x0 - pad), y: Math.floor(y0 - pad), width: Math.ceil(x1 - x0 + 2 * pad), height: Math.ceil(y1 - y0 + 2 * pad) };
3149
+ }
3150
+
3088
3151
  // src/paint-editor/replay.ts
3089
3152
  function replayItems(doc) {
3090
3153
  const items = [];
@@ -3107,7 +3170,7 @@ async function replayPainting(source, target, options = {}) {
3107
3170
  const maxStrokes = _nullishCoalesce(options.strokes, () => ( source.strokes.length));
3108
3171
  const chunk = Math.max(1, _nullishCoalesce(options.wetChunk, () => ( 0))) || Infinity;
3109
3172
  let strokes = 0;
3110
- await target.prepareEffectPaints(source.strokes.slice(0, maxStrokes).flatMap((st) => _optionalChain([st, 'access', _156 => _156.paint, 'optionalAccess', _157 => _157.type]) === "effect" ? [st.paint] : []));
3173
+ await target.prepareEffectPaints(source.strokes.slice(0, maxStrokes).flatMap((st) => _optionalChain([st, 'access', _158 => _158.paint, 'optionalAccess', _159 => _159.type]) === "effect" ? [st.paint] : []));
3111
3174
  const advanceTo = async (clock) => {
3112
3175
  const now = target.getState().wet.clock;
3113
3176
  let left = clock - now;
@@ -3115,7 +3178,7 @@ async function replayPainting(source, target, options = {}) {
3115
3178
  const n = Math.min(left, chunk);
3116
3179
  await target.advanceWet({ steps: n });
3117
3180
  left -= n;
3118
- if (Number.isFinite(chunk)) await _optionalChain([options, 'access', _158 => _158.onWetSteps, 'optionalCall', _159 => _159(target.getState().wet.clock)]);
3181
+ if (Number.isFinite(chunk)) await _optionalChain([options, 'access', _160 => _160.onWetSteps, 'optionalCall', _161 => _161(target.getState().wet.clock)]);
3119
3182
  }
3120
3183
  };
3121
3184
  const sr = (r) => ({ x: r.x * s, y: r.y * s, width: r.width * s, height: r.height * s });
@@ -3126,8 +3189,16 @@ async function replayPainting(source, target, options = {}) {
3126
3189
  if (!target.getLayer(e.layerId)) continue;
3127
3190
  if (e.kind === "fill") target.fillLayer({ layerId: e.layerId, ...e.data });
3128
3191
  else if (e.kind === "clear") target.clearLayer(e.layerId);
3129
- else if (e.kind === "stickers") {
3130
- const list = (_nullishCoalesce(_optionalChain([e, 'access', _160 => _160.data, 'optionalAccess', _161 => _161.stickers]), () => ( []))).map((x) => s === 1 ? x : { ...x, x: x.x * s, y: x.y * s, size: x.size * s });
3192
+ else if (e.kind === "live-strokes") {
3193
+ const d = e.data;
3194
+ if (_optionalChain([d, 'optionalAccess', _162 => _162.rasterized])) target.replaceLiveStrokes(e.layerId, null);
3195
+ else {
3196
+ const list = (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _163 => _163.strokes]), () => ( []))).map((st2) => s === 1 ? st2 : { ...varyStroke(st2, scaleM, { scale: s }), ...st2.stencils ? { stencils: st2.stencils.map((x) => scaleStencil(x, s)) } : {} });
3197
+ await target.prepareEffectPaints(list.flatMap((st2) => _optionalChain([st2, 'access', _164 => _164.paint, 'optionalAccess', _165 => _165.type]) === "effect" ? [st2.paint] : []));
3198
+ target.replaceLiveStrokes(e.layerId, list);
3199
+ }
3200
+ } else if (e.kind === "stickers") {
3201
+ const list = (_nullishCoalesce(_optionalChain([e, 'access', _166 => _166.data, 'optionalAccess', _167 => _167.stickers]), () => ( []))).map((x) => s === 1 ? x : { ...x, x: x.x * s, y: x.y * s, size: x.size * s });
3131
3202
  target.replaceStickers(e.layerId, list);
3132
3203
  } else target.flattenRelief(e.layerId);
3133
3204
  continue;
@@ -3140,8 +3211,8 @@ async function replayPainting(source, target, options = {}) {
3140
3211
  switch (e.kind) {
3141
3212
  case "dry": {
3142
3213
  const rect = e.data;
3143
- const ids = _optionalChain([e, 'access', _162 => _162.layerId, 'optionalAccess', _163 => _163.split, 'call', _164 => _164(","), 'access', _165 => _165.filter, 'call', _166 => _166(Boolean)]);
3144
- if (_optionalChain([ids, 'optionalAccess', _167 => _167.length])) for (const id of ids) target.dryPaint({ layerId: id, rect: rect ? sr(rect) : void 0 });
3214
+ const ids = _optionalChain([e, 'access', _168 => _168.layerId, 'optionalAccess', _169 => _169.split, 'call', _170 => _170(","), 'access', _171 => _171.filter, 'call', _172 => _172(Boolean)]);
3215
+ if (_optionalChain([ids, 'optionalAccess', _173 => _173.length])) for (const id of ids) target.dryPaint({ layerId: id, rect: rect ? sr(rect) : void 0 });
3145
3216
  else target.dryPaint({ rect: rect ? sr(rect) : void 0 });
3146
3217
  break;
3147
3218
  }
@@ -3172,7 +3243,7 @@ async function replayPainting(source, target, options = {}) {
3172
3243
  strokes++;
3173
3244
  if (!target.getLayer(st.layerId)) continue;
3174
3245
  const init = { layerId: st.layerId, color: st.color, brush: st.brush, seed: st.seed, copies: _nullishCoalesce(st.copies, () => ( [])), ...st.load ? { load: st.load } : {}, ...st.paint ? { paint: st.paint } : {}, snapToPerspective: false, stencils: false, maskStencils: (_nullishCoalesce(st.stencils, () => ( []))).map((x) => s === 1 ? x : scaleStencil(x, s)) };
3175
- if (st.brush.engine === "wet" && _optionalChain([st, 'access', _168 => _168.steps, 'optionalAccess', _169 => _169.length]) === st.points.length) {
3246
+ if (st.brush.engine === "wet" && _optionalChain([st, 'access', _174 => _174.steps, 'optionalAccess', _175 => _175.length]) === st.points.length) {
3176
3247
  await target.wetIdle();
3177
3248
  await advanceTo(st.steps[0]);
3178
3249
  const session = target.beginStroke({ ...init, start: st.points[0] });
@@ -3192,7 +3263,7 @@ async function replayPainting(source, target, options = {}) {
3192
3263
  await target.wetIdle();
3193
3264
  target.paintStroke({ ...init, points: st.points });
3194
3265
  }
3195
- await _optionalChain([options, 'access', _170 => _170.onStroke, 'optionalCall', _171 => _171(strokes)]);
3266
+ await _optionalChain([options, 'access', _176 => _176.onStroke, 'optionalCall', _177 => _177(strokes)]);
3196
3267
  }
3197
3268
  void IDENTITY_AFFINE;
3198
3269
  }
@@ -3237,7 +3308,7 @@ async function buildTimeLapse(source, target, options = {}) {
3237
3308
  wetChunk: wetEvery > 0 ? wetEvery : 0,
3238
3309
  onStroke: (i) => {
3239
3310
  if (i % perFrame === 0 || i === total) shoot(i);
3240
- _optionalChain([options, 'access', _172 => _172.onProgress, 'optionalCall', _173 => _173(total ? i / total : 1)]);
3311
+ _optionalChain([options, 'access', _178 => _178.onProgress, 'optionalCall', _179 => _179(total ? i / total : 1)]);
3241
3312
  },
3242
3313
  onWetSteps: wetEvery > 0 ? () => shoot(-1) : void 0
3243
3314
  });
@@ -3324,10 +3395,10 @@ function mixPixels(before, after, amount) {
3324
3395
  }
3325
3396
  return out;
3326
3397
  }
3327
- var PaintEffectsRenderer = (_class9 = class {constructor() { _class9.prototype.__init69.call(this);_class9.prototype.__init70.call(this);_class9.prototype.__init71.call(this); }
3328
- __init69() {this.pool = new (0, _chunkPY4GWSE2cjs.EffectRendererPool)({ maxRenderers: 3 })}
3329
- __init70() {this.descriptions = /* @__PURE__ */ new Map()}
3330
- __init71() {this.provider = null}
3398
+ var PaintEffectsRenderer = (_class9 = class {constructor() { _class9.prototype.__init71.call(this);_class9.prototype.__init72.call(this);_class9.prototype.__init73.call(this); }
3399
+ __init71() {this.pool = new (0, _chunkPY4GWSE2cjs.EffectRendererPool)({ maxRenderers: 3 })}
3400
+ __init72() {this.descriptions = /* @__PURE__ */ new Map()}
3401
+ __init73() {this.provider = null}
3331
3402
  /** Forgets cached descriptions and renderers when the provider changes. */
3332
3403
  follow(provider) {
3333
3404
  if (provider === this.provider) return;
@@ -3412,7 +3483,7 @@ function parseColor(value, what = "color") {
3412
3483
  return value.trim().toLowerCase();
3413
3484
  }
3414
3485
  function rgbaOf(hex) {
3415
- const [r, g, b] = _chunkNKTNLJUJcjs.hexToRgb8.call(void 0, hex);
3486
+ const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, hex);
3416
3487
  return [r, g, b, 255];
3417
3488
  }
3418
3489
  function rgbToHex(r, g, b) {
@@ -3425,75 +3496,87 @@ function unionRect(a, b) {
3425
3496
  const y = Math.min(a.y, b.y);
3426
3497
  return { x, y, width: Math.max(a.x + a.width, b.x + b.width) - x, height: Math.max(a.y + a.height, b.y + b.height) - y };
3427
3498
  }
3428
- var isPixelLayer = (l) => _optionalChain([l, 'optionalAccess', _174 => _174.type]) === "pixel";
3499
+ var isPixelLayer = (l) => _optionalChain([l, 'optionalAccess', _180 => _180.type]) === "pixel";
3429
3500
  var PaintEditorController = (_class10 = class _PaintEditorController {
3430
3501
 
3431
- __init72() {this.extensions = {}}
3432
- __init73() {this.undoStack = []}
3433
- __init74() {this.redoStack = []}
3502
+ __init74() {this.extensions = {}}
3503
+ __init75() {this.undoStack = []}
3504
+ __init76() {this.redoStack = []}
3434
3505
 
3435
3506
 
3436
3507
 
3437
3508
  /** The library list in the state (the same array until the user brushes change, so thumbnails are not redrawn). */
3438
- __init75() {this.library = null}
3509
+ __init77() {this.library = null}
3439
3510
 
3440
- __init76() {this.secondaryColor = "#ffffff"}
3511
+ __init78() {this.secondaryColor = "#ffffff"}
3441
3512
  /** Gradient paint chosen with `setPaint` (`null`: the paint colour). */
3442
- __init77() {this.gradient = null}
3513
+ __init79() {this.gradient = null}
3443
3514
  /** Effect paint chosen with `setEffectPaint` (rendered), and one still rendering. */
3444
- __init78() {this.effectPaint = null}
3445
- __init79() {this.effectPaintPending = null}
3515
+ __init80() {this.effectPaint = null}
3516
+ __init81() {this.effectPaintPending = null}
3446
3517
  /** Changes whenever the paint is chosen, so an effect paint that finishes rendering late does not take over. */
3447
- __init80() {this.paintEpoch = 0}
3518
+ __init82() {this.paintEpoch = 0}
3448
3519
  /** Rendered effect paints by `effectPaintKey` (most recently used last). */
3449
- __init81() {this.effectPaintPixels = /* @__PURE__ */ new Map()}
3450
- __init82() {this.mixerLoad = _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD}
3451
- __init83() {this.view = { ...DEFAULT_VIEW, pan: { ...DEFAULT_VIEW.pan } }}
3520
+ __init83() {this.effectPaintPixels = /* @__PURE__ */ new Map()}
3521
+ __init84() {this.mixerLoad = _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD}
3522
+ __init85() {this.view = { ...DEFAULT_VIEW, pan: { ...DEFAULT_VIEW.pan } }}
3452
3523
 
3453
- __init84() {this.selectedSticker = null}
3524
+ __init86() {this.selectedStroke = null}
3525
+ __init87() {this.selectedSticker = null}
3454
3526
  /** A sticker drag in progress (`previewSticker`): the document before it, so the drop undoes in one step, and the one it showed last. */
3455
- __init85() {this.stickerPreview = null}
3527
+ __init88() {this.stickerPreview = null}
3456
3528
 
3457
- __init86() {this.listeners = /* @__PURE__ */ new Set()}
3458
- __init87() {this.version = 0}
3459
- __init88() {this.cached = null}
3460
- __init89() {this.session = null}
3461
- __init90() {this.live = null}
3462
- __init91() {this.liveLayers = null}
3463
- __init92() {this.dirty = "all"}
3464
- __init93() {this.seedCounter = 1}
3465
- __init94() {this.strokeCounter = 1}
3529
+ __init89() {this.listeners = /* @__PURE__ */ new Set()}
3530
+ __init90() {this.version = 0}
3531
+ __init91() {this.cached = null}
3532
+ __init92() {this.session = null}
3533
+ __init93() {this.live = null}
3534
+ __init94() {this.liveLayers = null}
3535
+ __init95() {this.dirty = "all"}
3536
+ __init96() {this.seedCounter = 1}
3537
+ __init97() {this.strokeCounter = 1}
3538
+ /** Live effect paint: playing and the frame rate cap (editor state), and the time the view shows. */
3539
+ __init98() {this.effectPlaybackState = { playing: true, fps: 30 }}
3540
+ __init99() {this.effectTime = 0}
3541
+ /** While a frame is drawn: the time live effect strokes are drawn at (else their own time). */
3542
+ __init100() {this.liveEffectAt = null}
3543
+ /** The view's stroke layers at `effectTime`, each with the document surface it was drawn from (stale once that changes). */
3544
+ __init101() {this.animatedLayers = /* @__PURE__ */ new Map()}
3545
+ /** `renderAnimationFrame` / `withEffectTime`: layer surfaces at another time, for one render. */
3546
+ __init102() {this.frameOverride = null}
3547
+ /** The area each stroke painted (document pixels; `null`: nothing), learnt as it is painted or drawn, so a stroke layer edit redraws only where it changes (see `drawLiveStrokes`). */
3548
+ __init103() {this.paintedBounds = /* @__PURE__ */ new WeakMap()}
3466
3549
 
3467
- __init95() {this.swatches = DEFAULT_SWATCHES.map((s) => ({ ...s }))}
3468
- __init96() {this.pad = new MixingPad()}
3469
- __init97() {this.savedPads = []}
3470
- __init98() {this.nextPadId = 1}
3471
- __init99() {this.tracingVersion = 1}
3472
- __init100() {this.tracingCache = null}
3473
- __init101() {this.imagePixels = /* @__PURE__ */ new Map()}
3474
- __init102() {this.nextReferenceId = 1}
3475
- __init103() {this.viewCompositor = createPaintCompositor()}
3476
- __init104() {this.exportCompositor = createPaintCompositor()}
3550
+ __init104() {this.swatches = DEFAULT_SWATCHES.map((s) => ({ ...s }))}
3551
+ __init105() {this.pad = new MixingPad()}
3552
+ __init106() {this.savedPads = []}
3553
+ __init107() {this.nextPadId = 1}
3554
+ __init108() {this.tracingVersion = 1}
3555
+ __init109() {this.tracingCache = null}
3556
+ __init110() {this.imagePixels = /* @__PURE__ */ new Map()}
3557
+ __init111() {this.nextReferenceId = 1}
3558
+ __init112() {this.viewCompositor = createPaintCompositor()}
3559
+ __init113() {this.exportCompositor = createPaintCompositor()}
3477
3560
  /** Wet media: the simulation, its settings and time. */
3478
3561
 
3479
3562
 
3480
3563
  /** Where the light comes from for thick paint (saved with the document, not undo steps). */
3481
- __init105() {this.lighting = { ...DEFAULT_LIGHTING }}
3482
- __init106() {this.litCaches = /* @__PURE__ */ new Map()}
3564
+ __init114() {this.lighting = { ...DEFAULT_LIGHTING }}
3565
+ __init115() {this.litCaches = /* @__PURE__ */ new Map()}
3483
3566
  /** The live impasto stroke's channels. */
3484
- __init107() {this.liveThick = null}
3567
+ __init116() {this.liveThick = null}
3485
3568
  /** Set while an operation that changes wet paint runs: the simulation before it (for undo). */
3486
- __init108() {this.wetOp = null}
3569
+ __init117() {this.wetOp = null}
3487
3570
  /** The effects provider slot (see `setEffectsProvider`). */
3488
- __init109() {this.effects = new (0, _chunkJAOSL334cjs.EffectsSlot)()}
3489
- __init110() {this.effectsRenderer = new PaintEffectsRenderer()}
3490
- __init111() {this.paperEffectNodeCache = null}
3491
- constructor(options = {}) {;_class10.prototype.__init72.call(this);_class10.prototype.__init73.call(this);_class10.prototype.__init74.call(this);_class10.prototype.__init75.call(this);_class10.prototype.__init76.call(this);_class10.prototype.__init77.call(this);_class10.prototype.__init78.call(this);_class10.prototype.__init79.call(this);_class10.prototype.__init80.call(this);_class10.prototype.__init81.call(this);_class10.prototype.__init82.call(this);_class10.prototype.__init83.call(this);_class10.prototype.__init84.call(this);_class10.prototype.__init85.call(this);_class10.prototype.__init86.call(this);_class10.prototype.__init87.call(this);_class10.prototype.__init88.call(this);_class10.prototype.__init89.call(this);_class10.prototype.__init90.call(this);_class10.prototype.__init91.call(this);_class10.prototype.__init92.call(this);_class10.prototype.__init93.call(this);_class10.prototype.__init94.call(this);_class10.prototype.__init95.call(this);_class10.prototype.__init96.call(this);_class10.prototype.__init97.call(this);_class10.prototype.__init98.call(this);_class10.prototype.__init99.call(this);_class10.prototype.__init100.call(this);_class10.prototype.__init101.call(this);_class10.prototype.__init102.call(this);_class10.prototype.__init103.call(this);_class10.prototype.__init104.call(this);_class10.prototype.__init105.call(this);_class10.prototype.__init106.call(this);_class10.prototype.__init107.call(this);_class10.prototype.__init108.call(this);_class10.prototype.__init109.call(this);_class10.prototype.__init110.call(this);_class10.prototype.__init111.call(this);_class10.prototype.__init112.call(this);_class10.prototype.__init113.call(this);_class10.prototype.__init114.call(this);_class10.prototype.__init115.call(this);
3571
+ __init118() {this.effects = new (0, _chunkJAOSL334cjs.EffectsSlot)()}
3572
+ __init119() {this.effectsRenderer = new PaintEffectsRenderer()}
3573
+ __init120() {this.paperEffectNodeCache = null}
3574
+ constructor(options = {}) {;_class10.prototype.__init74.call(this);_class10.prototype.__init75.call(this);_class10.prototype.__init76.call(this);_class10.prototype.__init77.call(this);_class10.prototype.__init78.call(this);_class10.prototype.__init79.call(this);_class10.prototype.__init80.call(this);_class10.prototype.__init81.call(this);_class10.prototype.__init82.call(this);_class10.prototype.__init83.call(this);_class10.prototype.__init84.call(this);_class10.prototype.__init85.call(this);_class10.prototype.__init86.call(this);_class10.prototype.__init87.call(this);_class10.prototype.__init88.call(this);_class10.prototype.__init89.call(this);_class10.prototype.__init90.call(this);_class10.prototype.__init91.call(this);_class10.prototype.__init92.call(this);_class10.prototype.__init93.call(this);_class10.prototype.__init94.call(this);_class10.prototype.__init95.call(this);_class10.prototype.__init96.call(this);_class10.prototype.__init97.call(this);_class10.prototype.__init98.call(this);_class10.prototype.__init99.call(this);_class10.prototype.__init100.call(this);_class10.prototype.__init101.call(this);_class10.prototype.__init102.call(this);_class10.prototype.__init103.call(this);_class10.prototype.__init104.call(this);_class10.prototype.__init105.call(this);_class10.prototype.__init106.call(this);_class10.prototype.__init107.call(this);_class10.prototype.__init108.call(this);_class10.prototype.__init109.call(this);_class10.prototype.__init110.call(this);_class10.prototype.__init111.call(this);_class10.prototype.__init112.call(this);_class10.prototype.__init113.call(this);_class10.prototype.__init114.call(this);_class10.prototype.__init115.call(this);_class10.prototype.__init116.call(this);_class10.prototype.__init117.call(this);_class10.prototype.__init118.call(this);_class10.prototype.__init119.call(this);_class10.prototype.__init120.call(this);_class10.prototype.__init121.call(this);_class10.prototype.__init122.call(this);_class10.prototype.__init123.call(this);_class10.prototype.__init124.call(this);
3492
3575
  this.historyLimit = Math.max(1, _nullishCoalesce(options.historyLimit, () => ( 200)));
3493
3576
  this.doc = createPaintDocument({ width: options.width, height: options.height, dpi: options.dpi, paper: this.resolvePaper(options.paper) });
3494
- this.userBrushes = (_nullishCoalesce(options.brushes, () => ( []))).map((b) => _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, { ...b, builtin: false }));
3495
- const start = _nullishCoalesce(this.findBrush(_nullishCoalesce(options.brushId, () => ( _chunkNKTNLJUJcjs.DEFAULT_BRUSH_ID))), () => ( this.findBrush(_chunkNKTNLJUJcjs.DEFAULT_BRUSH_ID)));
3496
- this.brush = _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, start);
3577
+ this.userBrushes = (_nullishCoalesce(options.brushes, () => ( []))).map((b) => _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { ...b, builtin: false }));
3578
+ const start = _nullishCoalesce(this.findBrush(_nullishCoalesce(options.brushId, () => ( _chunk5BR7LC24cjs.DEFAULT_BRUSH_ID))), () => ( this.findBrush(_chunk5BR7LC24cjs.DEFAULT_BRUSH_ID)));
3579
+ this.brush = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, start);
3497
3580
  this.color = options.color ? parseColor(options.color) : "#1f2937";
3498
3581
  this.hand = options.hand === "left" ? "left" : "right";
3499
3582
  this.encodeImage = options.encodeImage;
@@ -3504,7 +3587,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3504
3587
  canvas: () => this.doc.canvas,
3505
3588
  paper: () => this.doc.paper,
3506
3589
  layerSurface: (id) => {
3507
- const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _175 => _175.layer]);
3590
+ const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _181 => _181.layer]);
3508
3591
  return isPixelLayer(l) ? l.surface : null;
3509
3592
  },
3510
3593
  updateSurfaces: (surfaces, rects) => {
@@ -3518,7 +3601,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3518
3601
  },
3519
3602
  options.wet
3520
3603
  );
3521
- this.wet.onDriverDone = () => _optionalChain([this, 'access', _176 => _176.session, 'optionalAccess', _177 => _177.completeWetEnd, 'call', _178 => _178()]);
3604
+ this.wet.onDriverDone = () => _optionalChain([this, 'access', _182 => _182.session, 'optionalAccess', _183 => _183.completeWetEnd, 'call', _184 => _184()]);
3522
3605
  }
3523
3606
  // --- State and events ----------------------------------------------------------
3524
3607
  subscribe(listener) {
@@ -3540,7 +3623,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3540
3623
  this.cached = {
3541
3624
  document: this.doc,
3542
3625
  brush: this.brush,
3543
- brushes: this.library ??= [..._chunkNKTNLJUJcjs.BUILTIN_BRUSHES, ...this.userBrushes],
3626
+ brushes: this.library ??= [..._chunk5BR7LC24cjs.BUILTIN_BRUSHES, ...this.userBrushes],
3544
3627
  color: this.color,
3545
3628
  secondaryColor: this.secondaryColor,
3546
3629
  paint: _nullishCoalesce(this.gradient, () => ( { type: "color", color: this.color })),
@@ -3549,11 +3632,13 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3549
3632
  mixerLoad: this.mixerLoad,
3550
3633
  view: this.view,
3551
3634
  hand: this.hand,
3635
+ effectPlayback: { ...this.effectPlaybackState, time: this.effectTime },
3636
+ selectedStroke: this.selectedStroke,
3552
3637
  selectedSticker: this.selectedSticker,
3553
3638
  canUndo: this.undoStack.length > 0 && !this.session,
3554
3639
  canRedo: this.redoStack.length > 0 && !this.session,
3555
- undoLabel: _nullishCoalesce(_optionalChain([last, 'optionalAccess', _179 => _179.label]), () => ( null)),
3556
- redoLabel: _nullishCoalesce(_optionalChain([next, 'optionalAccess', _180 => _180.label]), () => ( null)),
3640
+ undoLabel: _nullishCoalesce(_optionalChain([last, 'optionalAccess', _185 => _185.label]), () => ( null)),
3641
+ redoLabel: _nullishCoalesce(_optionalChain([next, 'optionalAccess', _186 => _186.label]), () => ( null)),
3557
3642
  guides: this.guides,
3558
3643
  swatches: this.swatches,
3559
3644
  mixingPadVersion: this.pad.version,
@@ -3566,10 +3651,10 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3566
3651
  overlay: this.wet.overlay,
3567
3652
  instantDry: this.wet.instantDry,
3568
3653
  speed: this.wet.speed,
3569
- clock: _nullishCoalesce(_optionalChain([this, 'access', _181 => _181.wet, 'access', _182 => _182.world, 'optionalAccess', _183 => _183.clock]), () => ( 0)),
3654
+ clock: _nullishCoalesce(_optionalChain([this, 'access', _187 => _187.wet, 'access', _188 => _188.world, 'optionalAccess', _189 => _189.clock]), () => ( 0)),
3570
3655
  tilt: { ...this.wet.tilt },
3571
3656
  gpu: this.wet.gpuMode,
3572
- gpuActive: Boolean(_optionalChain([this, 'access', _184 => _184.wet, 'access', _185 => _185.world, 'optionalAccess', _186 => _186.gpuActive]))
3657
+ gpuActive: Boolean(_optionalChain([this, 'access', _190 => _190.wet, 'access', _191 => _191.world, 'optionalAccess', _192 => _192.gpuActive]))
3573
3658
  },
3574
3659
  version: this.version
3575
3660
  };
@@ -3588,7 +3673,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3588
3673
  return d;
3589
3674
  }
3590
3675
  // --- History -------------------------------------------------------------------
3591
- __init112() {this.docRevision = 0}
3676
+ __init121() {this.docRevision = 0}
3592
3677
  /**
3593
3678
  * Bumped on every change to the painting (a committed or amended step, undo, redo, opening a
3594
3679
  * document), not on wet-paint frames or view changes. `createPaintEditorDocument` uses it.
@@ -3671,9 +3756,9 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3671
3756
  return wetIds;
3672
3757
  }
3673
3758
  /** Records a wet event (for replays) in the document being built. */
3674
- __init113() {this.pendingWetEvents = []}
3759
+ __init122() {this.pendingWetEvents = []}
3675
3760
  logWet(e) {
3676
- this.pendingWetEvents.push({ clock: _nullishCoalesce(_optionalChain([this, 'access', _187 => _187.wet, 'access', _188 => _188.world, 'optionalAccess', _189 => _189.clock]), () => ( 0)), at: this.doc.strokes.length, ...e });
3761
+ this.pendingWetEvents.push({ clock: _nullishCoalesce(_optionalChain([this, 'access', _193 => _193.wet, 'access', _194 => _194.world, 'optionalAccess', _195 => _195.clock]), () => ( 0)), at: this.doc.strokes.length, ...e });
3677
3762
  }
3678
3763
  /** The document with a paint event appended (for replays). */
3679
3764
  withPaintEvent(doc, e) {
@@ -3734,7 +3819,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3734
3819
  // --- Canvas and paper ----------------------------------------------------------
3735
3820
  resolvePaper(paper) {
3736
3821
  if (typeof paper === "string") {
3737
- const p = _chunkNKTNLJUJcjs.paperPreset.call(void 0, paper);
3822
+ const p = _chunk5BR7LC24cjs.paperPreset.call(void 0, paper);
3738
3823
  if (!p) throw new PaintEditorError(`Unknown paper "${paper}". Call list_papers for the ids.`);
3739
3824
  return p;
3740
3825
  }
@@ -3817,7 +3902,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3817
3902
  }
3818
3903
  let next = { ...shiftDocument(this.withWetEvents(this.doc), left, top), canvas: { ...was, width, height } };
3819
3904
  for (const id of keptWet) {
3820
- const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, next.layers, id), 'optionalAccess', _190 => _190.layer]);
3905
+ const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, next.layers, id), 'optionalAccess', _196 => _196.layer]);
3821
3906
  if (isPixelLayer(l)) next = { ...next, layers: _chunkHB4ICNNAcjs.mapLayer.call(void 0, next.layers, id, () => ({ ...l, surface: _chunk474IQG74cjs.translateSurface.call(void 0, l.surface, l.offset.x, l.offset.y), offset: { x: 0, y: 0 } })) };
3822
3907
  }
3823
3908
  if (this.wet.world) {
@@ -3845,23 +3930,23 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3845
3930
  this.requireIdle("changing the paper");
3846
3931
  let base = this.doc.paper;
3847
3932
  if (update.preset !== void 0 && update.preset !== this.doc.paper.preset) {
3848
- const p = _chunkNKTNLJUJcjs.paperPreset.call(void 0, update.preset);
3933
+ const p = _chunk5BR7LC24cjs.paperPreset.call(void 0, update.preset);
3849
3934
  if (!p) throw new PaintEditorError(`Unknown paper "${update.preset}". Call list_papers for the ids.`);
3850
3935
  base = p;
3851
3936
  }
3852
3937
  const { preset: _p, ...rest } = update;
3853
3938
  if (rest.color !== void 0) parseColor(rest.color, "paper color");
3854
- const paper = _chunkNKTNLJUJcjs.normalizePaper.call(void 0, { ...rest, preset: base.preset, offset: _nullishCoalesce(rest.offset, () => ( this.doc.paper.offset)) }, base);
3939
+ const paper = _chunk5BR7LC24cjs.normalizePaper.call(void 0, { ...rest, preset: base.preset, offset: _nullishCoalesce(rest.offset, () => ( this.doc.paper.offset)) }, base);
3855
3940
  const changedKeys = Object.keys(rest).filter((k) => k !== "name");
3856
3941
  if (changedKeys.length && update.preset === void 0) paper.preset = base.preset;
3857
3942
  if (this.wet.world) this.beginWetOp();
3858
3943
  this.commit("Paper", { ...this.doc, paper });
3859
3944
  this.wet.paperChanged();
3860
- return _chunkNKTNLJUJcjs.clonePaper.call(void 0, paper);
3945
+ return _chunk5BR7LC24cjs.clonePaper.call(void 0, paper);
3861
3946
  }
3862
3947
  // --- Layers --------------------------------------------------------------------
3863
3948
  getLayer(id) {
3864
- return _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _191 => _191.layer]);
3949
+ return _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _197 => _197.layer]);
3865
3950
  }
3866
3951
  /** The active pixel layer (or `undefined`). */
3867
3952
  getActiveLayer() {
@@ -3880,7 +3965,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3880
3965
  walk(this.doc.layers, null, 0);
3881
3966
  return out;
3882
3967
  }
3883
- describeLayer(l, parentId = _nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, l.id), 'optionalAccess', _192 => _192.parent, 'optionalAccess', _193 => _193.id]), () => ( null)), depth = _nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, l.id), 'optionalAccess', _194 => _194.path, 'access', _195 => _195.length]), () => ( 0))) {
3968
+ describeLayer(l, parentId = _nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, l.id), 'optionalAccess', _198 => _198.parent, 'optionalAccess', _199 => _199.id]), () => ( null)), depth = _nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, l.id), 'optionalAccess', _200 => _200.path, 'access', _201 => _201.length]), () => ( 0))) {
3884
3969
  let bounds = null;
3885
3970
  if (l.type === "pixel") {
3886
3971
  const b = _chunk474IQG74cjs.contentBounds.call(void 0, l.surface);
@@ -3897,6 +3982,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3897
3982
  locks: { ..._nullishCoalesce(l.locks, () => ( {})) },
3898
3983
  active: l.id === this.doc.activeLayerId,
3899
3984
  bounds,
3985
+ ..._optionalChain([this, 'access', _202 => _202.doc, 'access', _203 => _203.liveStrokes, 'optionalAccess', _204 => _204[l.id]]) ? { liveStrokes: this.doc.liveStrokes[l.id].length } : {},
3900
3986
  parentId,
3901
3987
  depth
3902
3988
  };
@@ -3923,11 +4009,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3923
4009
  this.emit();
3924
4010
  }
3925
4011
  /** Adds an empty pixel layer or group above `aboveId` (default: the active layer) and makes it active. */
4012
+ /** Adds a layer above the active one: `pixel` (default), `group`, or `strokes`, a live stroke layer whose strokes stay editable. */
3926
4013
  addLayer(options = {}) {
3927
4014
  this.requireIdle("adding a layer");
3928
4015
  const count = _chunkHB4ICNNAcjs.flattenLayers.call(void 0, this.doc.layers).length;
3929
4016
  const type = _nullishCoalesce(options.type, () => ( "pixel"));
3930
- const name = _optionalChain([options, 'access', _196 => _196.name, 'optionalAccess', _197 => _197.trim, 'call', _198 => _198()]) || (type === "group" ? `Group ${count + 1}` : `Layer ${count + 1}`);
4017
+ const name = _optionalChain([options, 'access', _205 => _205.name, 'optionalAccess', _206 => _206.trim, 'call', _207 => _207()]) || (type === "group" ? `Group ${count + 1}` : type === "strokes" ? `Strokes ${count + 1}` : `Layer ${count + 1}`);
3931
4018
  const layer = type === "group" ? { type: "group", id: newPaintLayerId(), name, visible: true, opacity: 1, blend: "pass-through", children: [] } : createPaintLayer(name);
3932
4019
  let parentId = null;
3933
4020
  let index;
@@ -3939,11 +4026,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3939
4026
  } else if (above) {
3940
4027
  const loc = _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, above);
3941
4028
  if (!loc) throw new PaintEditorError(`Unknown layer "${above}". Call list_layers for the ids.`);
3942
- parentId = _nullishCoalesce(_optionalChain([loc, 'access', _199 => _199.parent, 'optionalAccess', _200 => _200.id]), () => ( null));
4029
+ parentId = _nullishCoalesce(_optionalChain([loc, 'access', _208 => _208.parent, 'optionalAccess', _209 => _209.id]), () => ( null));
3943
4030
  index = loc.index + 1;
3944
4031
  }
3945
4032
  const layers = _chunkHB4ICNNAcjs.insertLayer.call(void 0, this.doc.layers, layer, parentId, index);
3946
- this.commit(type === "group" ? "Add group" : "Add layer", { ...this.doc, layers, activeLayerId: layer.id });
4033
+ const live = type === "strokes" ? { liveStrokes: { ..._nullishCoalesce(this.doc.liveStrokes, () => ( {})), [layer.id]: [] } } : {};
4034
+ this.commit(type === "group" ? "Add group" : type === "strokes" ? "Add stroke layer" : "Add layer", { ...this.doc, layers, activeLayerId: layer.id, ...live });
3947
4035
  return this.describeLayer(layer);
3948
4036
  }
3949
4037
  updateLayer(id, update) {
@@ -3979,8 +4067,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3979
4067
  }
3980
4068
  const layers = _chunkHB4ICNNAcjs.mapLayer.call(void 0, this.doc.layers, id, () => null);
3981
4069
  let active = this.doc.activeLayerId;
3982
- if (active && !_chunkHB4ICNNAcjs.findLayer.call(void 0, layers, active)) active = _nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.flattenLayers.call(void 0, layers), 'access', _201 => _201.filter, 'call', _202 => _202((x) => x.type === "pixel"), 'access', _203 => _203.pop, 'call', _204 => _204(), 'optionalAccess', _205 => _205.id]), () => ( null));
3983
- this.commit("Remove layer", { ...this.doc, layers, activeLayerId: active, ...this.thickWithout(_chunkHB4ICNNAcjs.flattenLayers.call(void 0, [l]).map((x) => x.id)), ...this.stickersWithout(_chunkHB4ICNNAcjs.flattenLayers.call(void 0, [l]).map((x) => x.id)) });
4070
+ if (active && !_chunkHB4ICNNAcjs.findLayer.call(void 0, layers, active)) active = _nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.flattenLayers.call(void 0, layers), 'access', _210 => _210.filter, 'call', _211 => _211((x) => x.type === "pixel"), 'access', _212 => _212.pop, 'call', _213 => _213(), 'optionalAccess', _214 => _214.id]), () => ( null));
4071
+ this.commit("Remove layer", { ...this.doc, layers, activeLayerId: active, ...this.thickWithout(_chunkHB4ICNNAcjs.flattenLayers.call(void 0, [l]).map((x) => x.id)), ...this.stickersWithout(_chunkHB4ICNNAcjs.flattenLayers.call(void 0, [l]).map((x) => x.id)), ...this.liveStrokesWithout(_chunkHB4ICNNAcjs.flattenLayers.call(void 0, [l]).map((x) => x.id)) });
3984
4072
  return { removed: id, activeLayerId: active };
3985
4073
  }
3986
4074
  duplicateLayer(id) {
@@ -3993,15 +4081,17 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3993
4081
  if (!loc) throw new PaintEditorError(`Unknown layer "${id}". Call list_layers for the ids.`);
3994
4082
  const thick = { ..._nullishCoalesce(this.doc.thick, () => ( {})) };
3995
4083
  const stickers = { ..._nullishCoalesce(this.doc.stickers, () => ( {})) };
4084
+ const liveStrokes = { ..._nullishCoalesce(this.doc.liveStrokes, () => ( {})) };
3996
4085
  const copy = (l) => {
3997
4086
  const id2 = newPaintLayerId();
3998
4087
  if (thick[l.id]) thick[id2] = thick[l.id];
3999
4088
  if (stickers[l.id]) stickers[id2] = stickers[l.id].map((x) => ({ ...x }));
4089
+ if (liveStrokes[l.id]) liveStrokes[id2] = liveStrokes[l.id].map((x) => ({ ...x, id: `s${this.strokeCounter++}`, layerId: id2 }));
4000
4090
  return l.type === "group" ? { ...l, id: id2, children: l.children.map(copy) } : { ...l, id: id2 };
4001
4091
  };
4002
4092
  const dup = { ...copy(loc.layer), name: `${loc.layer.name} copy` };
4003
- const layers = _chunkHB4ICNNAcjs.insertLayer.call(void 0, this.doc.layers, dup, _nullishCoalesce(_optionalChain([loc, 'access', _206 => _206.parent, 'optionalAccess', _207 => _207.id]), () => ( null)), loc.index + 1);
4004
- this.commit("Duplicate layer", this.withWetEvents({ ...this.doc, layers, activeLayerId: dup.id, ...this.doc.thick ? { thick } : {}, ...this.doc.stickers ? { stickers } : {} }));
4093
+ const layers = _chunkHB4ICNNAcjs.insertLayer.call(void 0, this.doc.layers, dup, _nullishCoalesce(_optionalChain([loc, 'access', _215 => _215.parent, 'optionalAccess', _216 => _216.id]), () => ( null)), loc.index + 1);
4094
+ this.commit("Duplicate layer", this.withWetEvents({ ...this.doc, layers, activeLayerId: dup.id, ...this.doc.thick ? { thick } : {}, ...this.doc.stickers ? { stickers } : {}, ...this.doc.liveStrokes ? { liveStrokes } : {} }));
4005
4095
  return this.describeLayer(dup);
4006
4096
  }
4007
4097
  /** Moves a layer to `index` (0 = bottom) within `parentId` (default its own parent; `null` = top level). */
@@ -4009,7 +4099,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4009
4099
  this.requireIdle("moving a layer");
4010
4100
  const loc = _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id);
4011
4101
  if (!loc) throw new PaintEditorError(`Unknown layer "${id}". Call list_layers for the ids.`);
4012
- const target = parentId === void 0 ? _nullishCoalesce(_optionalChain([loc, 'access', _208 => _208.parent, 'optionalAccess', _209 => _209.id]), () => ( null)) : parentId;
4102
+ const target = parentId === void 0 ? _nullishCoalesce(_optionalChain([loc, 'access', _217 => _217.parent, 'optionalAccess', _218 => _218.id]), () => ( null)) : parentId;
4013
4103
  if (target) {
4014
4104
  const g = this.requireLayer(target);
4015
4105
  if (g.type !== "group") throw new PaintEditorError(`Layer "${g.name}" is not a group; pass a group id as parentId.`);
@@ -4028,7 +4118,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4028
4118
  const pre = _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id);
4029
4119
  if (pre) {
4030
4120
  const sib = pre.parent ? pre.parent.children : this.doc.layers;
4031
- this.dryLayersFirst([id, _optionalChain([sib, 'access', _210 => _210[pre.index - 1], 'optionalAccess', _211 => _211.id])].filter((x) => Boolean(x)));
4121
+ this.dryLayersFirst([id, _optionalChain([sib, 'access', _219 => _219[pre.index - 1], 'optionalAccess', _220 => _220.id])].filter((x) => Boolean(x)));
4032
4122
  }
4033
4123
  const loc = _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id);
4034
4124
  if (!loc) throw new PaintEditorError(`Unknown layer "${id}". Call list_layers for the ids.`);
@@ -4047,7 +4137,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4047
4137
  }
4048
4138
  const merged = { ...below, surface, offset: { x: 0, y: 0 } };
4049
4139
  const layers = _chunkHB4ICNNAcjs.mapLayer.call(void 0, _chunkHB4ICNNAcjs.mapLayer.call(void 0, this.doc.layers, upper.id, () => null), below.id, () => merged);
4050
- this.commit("Merge down", this.withWetEvents({ ...this.doc, layers, activeLayerId: below.id, ...this.thickWithout([upper.id]), ...this.stickersWithout([upper.id, below.id]) }));
4140
+ this.commit("Merge down", this.withWetEvents({ ...this.doc, layers, activeLayerId: below.id, ...this.thickWithout([upper.id]), ...this.stickersWithout([upper.id, below.id]), ...this.liveStrokesWithout([upper.id, below.id]) }));
4051
4141
  return this.describeLayer(merged);
4052
4142
  }
4053
4143
  /** Removes every pixel of a layer. */
@@ -4059,7 +4149,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4059
4149
  this.wet.world.removeLane(l.id);
4060
4150
  }
4061
4151
  const bounds = this.describeLayer(l).bounds;
4062
- this.commit("Clear layer", this.withPaintEvent({ ...this.doc, layers: _chunkHB4ICNNAcjs.mapLayer.call(void 0, this.doc.layers, l.id, () => ({ ...l, surface: _chunk474IQG74cjs.emptySurface.call(void 0, 4) })), ...this.thickWithout([l.id]), ...this.stickersWithout([l.id]) }, { kind: "clear", layerId: l.id }));
4152
+ this.commit("Clear layer", this.withPaintEvent({ ...this.doc, layers: _chunkHB4ICNNAcjs.mapLayer.call(void 0, this.doc.layers, l.id, () => ({ ...l, surface: _chunk474IQG74cjs.emptySurface.call(void 0, 4) })), ...this.thickWithout([l.id]), ...this.stickersWithout([l.id]), ..._optionalChain([this, 'access', _221 => _221.doc, 'access', _222 => _222.liveStrokes, 'optionalAccess', _223 => _223[l.id]]) ? { liveStrokes: { ...this.doc.liveStrokes, [l.id]: [] } } : {} }, { kind: "clear", layerId: l.id }));
4063
4153
  return { layerId: l.id, bounds };
4064
4154
  }
4065
4155
  /**
@@ -4069,7 +4159,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4069
4159
  fillLayer(options = {}) {
4070
4160
  this.requireIdle("filling");
4071
4161
  const fillTarget = this.paintTarget(options.layerId, "fill");
4072
- if (_optionalChain([this, 'access', _212 => _212.doc, 'access', _213 => _213.stickers, 'optionalAccess', _214 => _214[fillTarget.id], 'optionalAccess', _215 => _215.length])) throw new PaintEditorError(`Layer "${fillTarget.name}" is a sticker layer; fill another layer (add_layer).`);
4162
+ if (_optionalChain([this, 'access', _224 => _224.doc, 'access', _225 => _225.stickers, 'optionalAccess', _226 => _226[fillTarget.id], 'optionalAccess', _227 => _227.length])) throw new PaintEditorError(`Layer "${fillTarget.name}" is a sticker layer; fill another layer (add_layer).`);
4163
+ if (_optionalChain([this, 'access', _228 => _228.doc, 'access', _229 => _229.liveStrokes, 'optionalAccess', _230 => _230[fillTarget.id]])) throw new PaintEditorError(`Layer "${fillTarget.name}" is a stroke layer (its pixels are drawn from its strokes); fill another layer (add_layer), or rasterize it first (rasterize_stroke_layer).`);
4073
4164
  this.dryLayersFirst([_nullishCoalesce(_nullishCoalesce(options.layerId, () => ( this.doc.activeLayerId)), () => ( ""))]);
4074
4165
  const l = this.paintTarget(options.layerId, "fill");
4075
4166
  const color = options.color === void 0 ? this.color : parseColor(options.color);
@@ -4110,7 +4201,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4110
4201
  const tiles = new Map(base.tiles);
4111
4202
  for (const { tx, ty } of _chunk474IQG74cjs.tilesInRect.call(void 0, { x: rect.x - l.offset.x, y: rect.y - l.offset.y, width: rect.width, height: rect.height })) {
4112
4203
  const key = _chunk474IQG74cjs.tileKey.call(void 0, tx, ty);
4113
- const src = _nullishCoalesce(_optionalChain([base, 'access', _216 => _216.tiles, 'access', _217 => _217.get, 'call', _218 => _218(key), 'optionalAccess', _219 => _219.data]), () => ( new Uint8ClampedArray(_chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE * 4)));
4204
+ const src = _nullishCoalesce(_optionalChain([base, 'access', _231 => _231.tiles, 'access', _232 => _232.get, 'call', _233 => _233(key), 'optionalAccess', _234 => _234.data]), () => ( new Uint8ClampedArray(_chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE * 4)));
4114
4205
  const tile = _chunk474IQG74cjs.finishTile.call(void 0, _chunkPQ3H2AANcjs.applyStrokeToTile.call(void 0, coverage, src, 4, tx, ty, apply), 4, 0);
4115
4206
  if (tile) tiles.set(key, tile);
4116
4207
  else tiles.delete(key);
@@ -4143,7 +4234,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4143
4234
  }
4144
4235
  /** The effect a layer is drawn from, or `undefined` for other layers. */
4145
4236
  getEffectLayer(layerId) {
4146
- return _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, layerId) ? _optionalChain([this, 'access', _220 => _220.doc, 'access', _221 => _221.effectLayers, 'optionalAccess', _222 => _222[layerId]]) : void 0;
4237
+ return _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, layerId) ? _optionalChain([this, 'access', _235 => _235.doc, 'access', _236 => _236.effectLayers, 'optionalAccess', _237 => _237[layerId]]) : void 0;
4147
4238
  }
4148
4239
  /** The effect paper (without its pixels), or `null`. */
4149
4240
  getPaperEffect() {
@@ -4180,7 +4271,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4180
4271
  /** A pixel layer that takes effect pixels: unlocked, visible, no sticker layer, no wet paint. */
4181
4272
  effectTarget(layerId, what) {
4182
4273
  const l = this.paintTarget(layerId, what);
4183
- if (_optionalChain([this, 'access', _223 => _223.doc, 'access', _224 => _224.stickers, 'optionalAccess', _225 => _225[l.id], 'optionalAccess', _226 => _226.length])) throw new PaintEditorError(`Layer "${l.name}" is a sticker layer; ${what} another layer (add_layer).`);
4274
+ if (_optionalChain([this, 'access', _238 => _238.doc, 'access', _239 => _239.stickers, 'optionalAccess', _240 => _240[l.id], 'optionalAccess', _241 => _241.length])) throw new PaintEditorError(`Layer "${l.name}" is a sticker layer; ${what} another layer (add_layer).`);
4275
+ if (_optionalChain([this, 'access', _242 => _242.doc, 'access', _243 => _243.liveStrokes, 'optionalAccess', _244 => _244[l.id]])) throw new PaintEditorError(`Layer "${l.name}" is a stroke layer (its pixels are drawn from its strokes); ${what} another layer (add_layer), or rasterize it first (rasterize_stroke_layer).`);
4184
4276
  if (this.wet.wetLayerIds().includes(l.id)) throw new PaintEditorError(`Layer "${l.name}" holds wet paint; effects go on dried paint, so dry it first (dry_paint) and try again.`);
4185
4277
  return l;
4186
4278
  }
@@ -4219,8 +4311,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4219
4311
  const was = this.doc.paperEffect;
4220
4312
  const paperEffect = {
4221
4313
  ...this.sourceOf(d, values, options.time),
4222
- opacity: _nullishCoalesce(_nullishCoalesce(options.opacity, () => ( _optionalChain([was, 'optionalAccess', _227 => _227.opacity]))), () => ( 1)),
4223
- blend: _nullishCoalesce(_nullishCoalesce(options.blend, () => ( _optionalChain([was, 'optionalAccess', _228 => _228.blend]))), () => ( "multiply")),
4314
+ opacity: _nullishCoalesce(_nullishCoalesce(options.opacity, () => ( _optionalChain([was, 'optionalAccess', _245 => _245.opacity]))), () => ( 1)),
4315
+ blend: _nullishCoalesce(_nullishCoalesce(options.blend, () => ( _optionalChain([was, 'optionalAccess', _246 => _246.blend]))), () => ( "multiply")),
4224
4316
  image: { width: pixels.width, height: pixels.height, data: new Uint8ClampedArray(pixels.data) },
4225
4317
  version: nextPaperEffectVersion()
4226
4318
  };
@@ -4273,8 +4365,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4273
4365
  const above = _nullishCoalesce(options.aboveId, () => ( this.doc.activeLayerId));
4274
4366
  const loc = above ? _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, above) : null;
4275
4367
  if (options.aboveId !== void 0 && !loc) throw new PaintEditorError(`Layer "${options.aboveId}" was removed while the effect rendered.`);
4276
- const layer = { ...createPaintLayer(_optionalChain([options, 'access', _229 => _229.name, 'optionalAccess', _230 => _230.trim, 'call', _231 => _231()]) || d.name), surface: _chunk474IQG74cjs.surfaceFromPixels.call(void 0, pixels, 4), opacity: _nullishCoalesce(options.opacity, () => ( 1)), blend: _nullishCoalesce(options.blend, () => ( "normal")) };
4277
- const layers = _chunkHB4ICNNAcjs.insertLayer.call(void 0, this.doc.layers, layer, _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _232 => _232.parent, 'optionalAccess', _233 => _233.id]), () => ( null)), loc ? loc.index + 1 : void 0);
4368
+ const layer = { ...createPaintLayer(_optionalChain([options, 'access', _247 => _247.name, 'optionalAccess', _248 => _248.trim, 'call', _249 => _249()]) || d.name), surface: _chunk474IQG74cjs.surfaceFromPixels.call(void 0, pixels, 4), opacity: _nullishCoalesce(options.opacity, () => ( 1)), blend: _nullishCoalesce(options.blend, () => ( "normal")) };
4369
+ const layers = _chunkHB4ICNNAcjs.insertLayer.call(void 0, this.doc.layers, layer, _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _250 => _250.parent, 'optionalAccess', _251 => _251.id]), () => ( null)), loc ? loc.index + 1 : void 0);
4278
4370
  const source = this.sourceOf(d, values, options.time);
4279
4371
  this.commit("Add effect layer", { ...this.doc, layers, activeLayerId: layer.id, effectLayers: { ..._nullishCoalesce(this.doc.effectLayers, () => ( {})), [layer.id]: source } });
4280
4372
  return { layer: this.describeLayer(layer), source };
@@ -4344,16 +4436,25 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4344
4436
  * size, and brushes then take each pixel's colour (and alpha) from it, fixed to the canvas, as if
4345
4437
  * painting a hidden picture into view. `setColor` and `setPaint` go back to colour. Editor state,
4346
4438
  * not an undo step. Strokes that would paint with it throw while it renders. Returns the paint.
4439
+ * With `map: { by: 'stroke' }` the effect lies along each stroke instead of the canvas; with
4440
+ * `live`, strokes on a stroke layer keep playing it (see `setEffectPlayback`).
4347
4441
  */
4348
4442
  async setEffectPaint(source) {
4349
4443
  if (!source || typeof source.effect !== "string" || !source.effect) throw new PaintEditorError("effect is missing; pass an effect id from search_effects (use generator, overlay or title).");
4350
4444
  this.checkTime(source.time);
4445
+ let map;
4446
+ try {
4447
+ map = _chunk5BR7LC24cjs.readEffectStrokeMap.call(void 0, source.map);
4448
+ } catch (e) {
4449
+ throw new PaintEditorError(e.message);
4450
+ }
4451
+ if (source.live !== void 0 && typeof source.live !== "boolean") throw new PaintEditorError("live must be true or false.");
4351
4452
  const epoch = ++this.paintEpoch;
4352
4453
  const changed = (name) => new PaintEditorError(`The paint changed while "${name}" rendered (set_color, set_paint or another set_effect_paint); call set_effect_paint again to paint with it.`);
4353
4454
  const d = await this.describeEffect(source.effect, "paint");
4354
4455
  const values = this.effectValues(d, { ...d.defaults }, source.values);
4355
4456
  if (epoch !== this.paintEpoch) throw changed(d.name);
4356
- const paint = { type: "effect", effect: d.id, provider: this.effects.require().id, values, time: _nullishCoalesce(source.time, () => ( 0)) };
4457
+ const paint = { type: "effect", effect: d.id, provider: this.effects.require().id, values, time: _nullishCoalesce(source.time, () => ( 0)), ...map ? { map } : {}, ...source.live ? { live: true } : {} };
4357
4458
  this.effectPaintPending = paint;
4358
4459
  this.emit([]);
4359
4460
  try {
@@ -4374,9 +4475,10 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4374
4475
  }
4375
4476
  /** The rendered pixels of an effect paint at the current canvas size, or `null` when it has not been rendered. */
4376
4477
  effectPaintImage(p) {
4377
- const key = _chunkNKTNLJUJcjs.effectPaintKey.call(void 0, p);
4478
+ const key = _chunk5BR7LC24cjs.effectPaintKey.call(void 0, p);
4378
4479
  const img = this.effectPaintPixels.get(key);
4379
- if (!img || img.width !== this.doc.canvas.width || img.height !== this.doc.canvas.height) return null;
4480
+ const size = _chunk5BR7LC24cjs.effectPaintSize.call(void 0, p, this.doc.canvas);
4481
+ if (!img || img.width !== size.width || img.height !== size.height) return null;
4380
4482
  this.effectPaintPixels.delete(key);
4381
4483
  this.effectPaintPixels.set(key, img);
4382
4484
  return img;
@@ -4389,15 +4491,15 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4389
4491
  const what = `the effect "${p.effect}"${p.provider ? ` from provider "${p.provider}"` : ""}`;
4390
4492
  if (!provider) throw new PaintEditorError(`Effect paint needs ${what}, but no effects provider is registered; register it (setEffectsProvider) and try again.`);
4391
4493
  if (p.provider && provider.id !== p.provider) throw new PaintEditorError(`Effect paint needs ${what}, but the registered provider is "${provider.id}"; register "${p.provider}" (setEffectsProvider) and try again.`);
4392
- const { width, height } = this.doc.canvas;
4494
+ const { width, height } = _chunk5BR7LC24cjs.effectPaintSize.call(void 0, p, this.doc.canvas);
4393
4495
  let pixels;
4394
4496
  try {
4395
4497
  pixels = await this.effectsRenderer.render(provider, p.effect, { width, height }, { time: p.time, values: p.values });
4396
4498
  } catch (e) {
4397
4499
  throw new PaintEditorError(`Effect paint could not render ${what}: ${e instanceof Error ? e.message : String(e)}`);
4398
4500
  }
4399
- const img = { width, height, data: new Uint8ClampedArray(pixels.data) };
4400
- this.effectPaintPixels.set(_chunkNKTNLJUJcjs.effectPaintKey.call(void 0, p), img);
4501
+ const img = { width, height, data: new Uint8ClampedArray(pixels.data), ...p.map ? { map: p.map } : {} };
4502
+ this.effectPaintPixels.set(_chunk5BR7LC24cjs.effectPaintKey.call(void 0, p), img);
4401
4503
  while (this.effectPaintPixels.size > _PaintEditorController.EFFECT_PAINT_CACHE) this.effectPaintPixels.delete(this.effectPaintPixels.keys().next().value);
4402
4504
  return img;
4403
4505
  }
@@ -4408,7 +4510,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4408
4510
  async prepareEffectPaints(paints) {
4409
4511
  const seen = /* @__PURE__ */ new Set();
4410
4512
  for (const p of paints) {
4411
- const key = _chunkNKTNLJUJcjs.effectPaintKey.call(void 0, p);
4513
+ const key = _chunk5BR7LC24cjs.effectPaintKey.call(void 0, p);
4412
4514
  if (seen.has(key)) continue;
4413
4515
  seen.add(key);
4414
4516
  await this.renderEffectPaint(p);
@@ -4417,7 +4519,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4417
4519
  /** Renders the effect paints of recorded strokes (default all of them) before a replay. */
4418
4520
  async prepareStrokeEffects(strokeIds) {
4419
4521
  const ids = strokeIds ? new Set(strokeIds) : null;
4420
- const paints = this.doc.strokes.filter((s) => (!ids || ids.has(s.id)) && _optionalChain([s, 'access', _234 => _234.paint, 'optionalAccess', _235 => _235.type]) === "effect").map((s) => s.paint);
4522
+ const paints = this.doc.strokes.filter((s) => (!ids || ids.has(s.id)) && _optionalChain([s, 'access', _252 => _252.paint, 'optionalAccess', _253 => _253.type]) === "effect").map((s) => s.paint);
4421
4523
  await this.prepareEffectPaints(paints);
4422
4524
  }
4423
4525
  /** A small preview of the effect paint (a PNG data URL `size` px wide), or `null` without a rendered one. */
@@ -4446,22 +4548,17 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4446
4548
  * its pixels are not rendered.
4447
4549
  */
4448
4550
  strokeEffect(init, brush) {
4449
- if (!_chunkNKTNLJUJcjs.brushTakesPaint.call(void 0, brush)) return null;
4551
+ if (!_chunk5BR7LC24cjs.brushTakesPaint.call(void 0, brush)) return null;
4450
4552
  let source = null;
4451
4553
  if (init.paint !== void 0) {
4452
- if (!_chunkNKTNLJUJcjs.isPaintEffectPaint.call(void 0, init.paint)) return null;
4453
- source = _chunkNKTNLJUJcjs.readPaintEffectPaint.call(void 0, init.paint);
4554
+ if (!_chunk5BR7LC24cjs.isPaintEffectPaint.call(void 0, init.paint)) return null;
4555
+ source = _chunk5BR7LC24cjs.readPaintEffectPaint.call(void 0, init.paint);
4454
4556
  if (!source) throw new PaintEditorError("The stroke's effect paint needs an effect id and values (getEffectPaint returns one).");
4455
4557
  } else if (init.color === void 0 && !brush.paint) {
4456
4558
  if (this.effectPaintPending) throw new PaintEditorError(`The effect paint "${this.effectPaintPending.effect}" is still rendering; wait for set_effect_paint to finish, then paint.`);
4457
4559
  source = this.effectPaint;
4458
4560
  }
4459
4561
  if (!source) return null;
4460
- if (brush.engine === "wet" || brush.engine === "impasto" || brush.engine === "sticker") {
4461
- throw new PaintEditorError(
4462
- `The ${brush.name} brush (${brush.engine} engine) cannot paint with effect paint: it carries one colour as ${brush.engine === "sticker" ? "stickers" : brush.engine === "wet" ? "wet pigment" : "thick paint"}. Paint with a dry brush (a dab, particle, bristle or mixer brush), or paint the shape first and fill it with fill_layer_with_effect {lockTransparency: true}.`
4463
- );
4464
- }
4465
4562
  const pixels = this.effectPaintImage(source);
4466
4563
  if (!pixels) throw new PaintEditorError(`The effect paint "${source.effect}"${source.provider ? ` from provider "${source.provider}"` : ""} is not rendered at this canvas size; call set_effect_paint (or prepareEffectPaints) first.`);
4467
4564
  return { source, pixels };
@@ -4485,14 +4582,14 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4485
4582
  const before = _chunk474IQG74cjs.readRegion.call(void 0, l.surface, rect);
4486
4583
  const inputs = { source: { width: rect.width, height: rect.height, data: before } };
4487
4584
  const heightInput = d.inputs.find((i) => i.kind === "image" && /^(height|depth|relief)$/i.test(i.name));
4488
- const relief = _optionalChain([this, 'access', _236 => _236.doc, 'access', _237 => _237.thick, 'optionalAccess', _238 => _238[l.id]]);
4585
+ const relief = _optionalChain([this, 'access', _254 => _254.doc, 'access', _255 => _255.thick, 'optionalAccess', _256 => _256[l.id]]);
4489
4586
  if (heightInput && relief) {
4490
4587
  const h = _chunk474IQG74cjs.readRegion.call(void 0, relief.height, rect);
4491
4588
  const rgba = new Uint8ClampedArray(rect.width * rect.height * 4);
4492
4589
  for (let i = 0; i < h.length; i++) rgba.set([h[i], h[i], h[i], 255], i * 4);
4493
4590
  inputs[heightInput.name] = { width: rect.width, height: rect.height, data: rgba };
4494
4591
  }
4495
- const sourceInput = _nullishCoalesce(_optionalChain([d, 'access', _239 => _239.inputs, 'access', _240 => _240.find, 'call', _241 => _241((i) => i.kind === "image" && i.name !== _optionalChain([heightInput, 'optionalAccess', _242 => _242.name])), 'optionalAccess', _243 => _243.name]), () => ( "source"));
4592
+ const sourceInput = _nullishCoalesce(_optionalChain([d, 'access', _257 => _257.inputs, 'access', _258 => _258.find, 'call', _259 => _259((i) => i.kind === "image" && i.name !== _optionalChain([heightInput, 'optionalAccess', _260 => _260.name])), 'optionalAccess', _261 => _261.name]), () => ( "source"));
4496
4593
  if (sourceInput !== "source") {
4497
4594
  inputs[sourceInput] = inputs.source;
4498
4595
  delete inputs.source;
@@ -4505,12 +4602,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4505
4602
  }
4506
4603
  // --- Brushes -------------------------------------------------------------------
4507
4604
  findBrush(id) {
4508
- return _nullishCoalesce(_chunkNKTNLJUJcjs.BUILTIN_BRUSHES.find((b) => b.id === id), () => ( this.userBrushes.find((b) => b.id === id)));
4605
+ return _nullishCoalesce(_chunk5BR7LC24cjs.BUILTIN_BRUSHES.find((b) => b.id === id), () => ( this.userBrushes.find((b) => b.id === id)));
4509
4606
  }
4510
4607
  /** A library brush by id (`undefined` when unknown). */
4511
4608
  getBrush(id) {
4512
4609
  const b = this.findBrush(id);
4513
- return b ? _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, b, b) : void 0;
4610
+ return b ? _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, b, b) : void 0;
4514
4611
  }
4515
4612
  requireBrush(id) {
4516
4613
  const b = this.findBrush(id);
@@ -4525,7 +4622,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4525
4622
  const { id, ...changes } = update;
4526
4623
  const base = id !== void 0 ? this.requireBrush(id) : this.brush;
4527
4624
  if (changes.paint !== void 0 && changes.paint !== null) this.readStrokePaint(changes.paint);
4528
- const next = _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, { ...changes, id: base.id }, base);
4625
+ const next = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { ...changes, id: base.id }, base);
4529
4626
  if (base.builtin && !Object.keys(changes).length) next.builtin = true;
4530
4627
  else delete next.builtin;
4531
4628
  this.brush = next;
@@ -4534,10 +4631,10 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4534
4631
  }
4535
4632
  /** Saves the active brush (or `brush`) into the user library under `id` / `name`. */
4536
4633
  saveBrush(options) {
4537
- if (!_chunkNKTNLJUJcjs.BRUSH_ID.test(options.id)) throw new PaintEditorError(`Brush id "${options.id}" is invalid; use lowercase letters, digits and dashes (e.g. "my-pencil").`);
4538
- if (_chunkNKTNLJUJcjs.BUILTIN_BRUSHES.some((b) => b.id === options.id)) throw new PaintEditorError(`"${options.id}" is a built-in brush; save under another id.`);
4539
- const source = options.brush ? _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, options.brush, this.brush) : this.brush;
4540
- const saved = _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, { ...source, id: options.id, name: _nullishCoalesce(options.name, () => ( source.name)) });
4634
+ if (!_chunk5BR7LC24cjs.BRUSH_ID.test(options.id)) throw new PaintEditorError(`Brush id "${options.id}" is invalid; use lowercase letters, digits and dashes (e.g. "my-pencil").`);
4635
+ if (_chunk5BR7LC24cjs.BUILTIN_BRUSHES.some((b) => b.id === options.id)) throw new PaintEditorError(`"${options.id}" is a built-in brush; save under another id.`);
4636
+ const source = options.brush ? _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, options.brush, this.brush) : this.brush;
4637
+ const saved = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { ...source, id: options.id, name: _nullishCoalesce(options.name, () => ( source.name)) });
4541
4638
  delete saved.builtin;
4542
4639
  const i = this.userBrushes.findIndex((b) => b.id === options.id);
4543
4640
  if (i >= 0) this.userBrushes[i] = saved;
@@ -4547,7 +4644,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4547
4644
  return saved;
4548
4645
  }
4549
4646
  removeBrush(id) {
4550
- if (_chunkNKTNLJUJcjs.BUILTIN_BRUSHES.some((b) => b.id === id)) throw new PaintEditorError(`"${id}" is a built-in brush and cannot be removed.`);
4647
+ if (_chunk5BR7LC24cjs.BUILTIN_BRUSHES.some((b) => b.id === id)) throw new PaintEditorError(`"${id}" is a built-in brush and cannot be removed.`);
4551
4648
  const i = this.userBrushes.findIndex((b) => b.id === id);
4552
4649
  if (i < 0) throw new PaintEditorError(`Unknown brush "${id}". Call list_brushes for the ids.`);
4553
4650
  this.userBrushes.splice(i, 1);
@@ -4556,12 +4653,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4556
4653
  }
4557
4654
  /** The user's saved brushes. */
4558
4655
  getUserBrushes() {
4559
- return this.userBrushes.map((b) => _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, b, b));
4656
+ return this.userBrushes.map((b) => _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, b, b));
4560
4657
  }
4561
4658
  /** A brush library file of the given brushes (default: the user's brushes). */
4562
4659
  exportBrushes(ids) {
4563
4660
  const list = ids ? ids.map((id) => this.requireBrush(id)) : this.userBrushes;
4564
- return { format: "pixerate-brushes", version: 1, brushes: list.map(_chunkNKTNLJUJcjs.brushToJSON) };
4661
+ return { format: "pixerate-brushes", version: 1, brushes: list.map(_chunk5BR7LC24cjs.brushToJSON) };
4565
4662
  }
4566
4663
  /**
4567
4664
  * Adds the brushes of a library file to the user library. A brush whose id is taken is skipped
@@ -4571,14 +4668,14 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4571
4668
  const problems = [];
4572
4669
  let list;
4573
4670
  try {
4574
- list = _chunkNKTNLJUJcjs.parseBrushLibrary.call(void 0, file, problems);
4671
+ list = _chunk5BR7LC24cjs.parseBrushLibrary.call(void 0, file, problems);
4575
4672
  } catch (e) {
4576
4673
  throw new PaintEditorError(e.message);
4577
4674
  }
4578
4675
  const added = [];
4579
4676
  const skipped = [];
4580
4677
  for (const b of list) {
4581
- if (_chunkNKTNLJUJcjs.BUILTIN_BRUSHES.some((x) => x.id === b.id)) {
4678
+ if (_chunk5BR7LC24cjs.BUILTIN_BRUSHES.some((x) => x.id === b.id)) {
4582
4679
  skipped.push(b.id);
4583
4680
  problems.push(`"${b.id}" is a built-in brush id; rename it to import it.`);
4584
4681
  continue;
@@ -4643,10 +4740,10 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4643
4740
  * brush's gradient, else the editor's gradient paint. Erasers, blenders and smudges take none.
4644
4741
  */
4645
4742
  strokeGradient(init, brush, color) {
4646
- if (!_chunkNKTNLJUJcjs.brushTakesPaint.call(void 0, brush)) return null;
4743
+ if (!_chunk5BR7LC24cjs.brushTakesPaint.call(void 0, brush)) return null;
4647
4744
  let g = null;
4648
4745
  if (init.paint !== void 0) {
4649
- if (_chunkNKTNLJUJcjs.isPaintEffectPaint.call(void 0, init.paint)) return null;
4746
+ if (_chunk5BR7LC24cjs.isPaintEffectPaint.call(void 0, init.paint)) return null;
4650
4747
  const p = this.readStrokePaint(init.paint);
4651
4748
  g = p.type === "gradient" ? p : null;
4652
4749
  } else g = _nullishCoalesce(brush.paint, () => ( (init.color === void 0 ? this.gradient : null)));
@@ -4690,7 +4787,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4690
4787
  // --- Mixer Brush ---------------------------------------------------------------
4691
4788
  /** Fills the Mixer Brush reservoir with a colour (default the editor colour). */
4692
4789
  loadMixerBrush(color) {
4693
- const [r, g, b] = _chunkNKTNLJUJcjs.hexToRgb8.call(void 0, color === void 0 ? this.color : parseColor(color));
4790
+ const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, color === void 0 ? this.color : parseColor(color));
4694
4791
  this.mixerLoad = _chunkPQ3H2AANcjs.loadMixer.call(void 0, this.mixerLoad, [r / 255, g / 255, b / 255]);
4695
4792
  this.emit([]);
4696
4793
  return this.mixerLoad;
@@ -4720,7 +4817,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4720
4817
  // --- Strokes -------------------------------------------------------------------
4721
4818
  strokeBrush(init) {
4722
4819
  let brush = init.brushId !== void 0 ? this.requireBrush(init.brushId) : this.brush;
4723
- if (init.brush) brush = _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, { ...init.brush, id: brush.id }, brush);
4820
+ if (init.brush) brush = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { ...init.brush, id: brush.id }, brush);
4724
4821
  return brush;
4725
4822
  }
4726
4823
  /**
@@ -4736,7 +4833,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4736
4833
  return symmetryCopies(this.guides.symmetry);
4737
4834
  }
4738
4835
  sessionFor(doc, init, host) {
4739
- if (init.paint !== void 0 && !_chunkNKTNLJUJcjs.isPaintEffectPaint.call(void 0, init.paint)) {
4836
+ if (init.paint !== void 0 && !_chunk5BR7LC24cjs.isPaintEffectPaint.call(void 0, init.paint)) {
4740
4837
  const p = this.readStrokePaint(init.paint);
4741
4838
  if (p.type === "color") init = { ...init, color: p.color, paint: void 0 };
4742
4839
  else init = { ...init, paint: p };
@@ -4754,14 +4851,25 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4754
4851
  })();
4755
4852
  const brush = this.strokeBrush(init);
4756
4853
  const effect = this.strokeEffect(init, brush);
4757
- const stickerLayer = Boolean(_optionalChain([doc, 'access', _244 => _244.stickers, 'optionalAccess', _245 => _245[layer.id], 'optionalAccess', _246 => _246.length]));
4854
+ if (_optionalChain([doc, 'access', _262 => _262.liveStrokes, 'optionalAccess', _263 => _263[layer.id]])) {
4855
+ const why = liveStrokeRefusal(brush);
4856
+ if (why) throw new PaintEditorError(`Layer "${layer.name}" is a stroke layer. ${why}`);
4857
+ }
4858
+ if (_optionalChain([doc, 'access', _264 => _264.liveStrokes, 'optionalAccess', _265 => _265[layer.id]]) && _optionalChain([effect, 'optionalAccess', _266 => _266.source, 'access', _267 => _267.live])) {
4859
+ const key = _chunk5BR7LC24cjs.effectPaintKey.call(void 0, effect.source);
4860
+ const { sources } = this.liveEffects(doc);
4861
+ if (!sources.some((p) => _chunk5BR7LC24cjs.effectPaintKey.call(void 0, p) === key) && sources.length >= MAX_LIVE_EFFECT_SOURCES) {
4862
+ throw new PaintEditorError(`The painting already plays ${MAX_LIVE_EFFECT_SOURCES} live effects, the most it can; paint with one of them, or set_effect_paint with live false to bake this one.`);
4863
+ }
4864
+ }
4865
+ const stickerLayer = Boolean(_optionalChain([doc, 'access', _268 => _268.stickers, 'optionalAccess', _269 => _269[layer.id], 'optionalAccess', _270 => _270.length]));
4758
4866
  if (brush.engine === "sticker") {
4759
4867
  if (!stickerLayer && _chunk474IQG74cjs.contentBounds.call(void 0, layer.surface)) throw new PaintEditorError(`Layer "${layer.name}" already has paint; spray stickers onto an empty layer (add_layer) so they stay editable.`);
4760
4868
  } else if (stickerLayer) {
4761
4869
  throw new PaintEditorError(`Layer "${layer.name}" is a sticker layer (its pixels are drawn from its stickers); paint on another layer (add_layer).`);
4762
4870
  }
4763
4871
  let wet;
4764
- if (_chunkNKTNLJUJcjs.isWetBrush.call(void 0, brush)) {
4872
+ if (_chunk5BR7LC24cjs.isWetBrush.call(void 0, brush)) {
4765
4873
  this.requireWetIdle("painting");
4766
4874
  if (layer.offset.x || layer.offset.y) {
4767
4875
  const moved = { ...layer, surface: _chunk474IQG74cjs.translateSurface.call(void 0, layer.surface, layer.offset.x, layer.offset.y), offset: { x: 0, y: 0 } };
@@ -4776,7 +4884,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4776
4884
  const world = this.wet.ensureLane(layer.id, brush.wet.medium);
4777
4885
  const id = layer.id;
4778
4886
  const wetColor = init.color === void 0 ? this.color : parseColor(init.color);
4779
- const [r, g, b] = _chunkNKTNLJUJcjs.hexToRgb8.call(void 0, wetColor);
4887
+ const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, wetColor);
4780
4888
  const wetGradient = this.strokeGradient(init, brush, wetColor);
4781
4889
  const load = _nullishCoalesce(init.load, () => ( brushLoadForStroke(brush, this.mixerLoad, [r / 255, g / 255, b / 255])));
4782
4890
  const seedW = _nullishCoalesce(init.seed, () => ( _chunkPQ3H2AANcjs.mix32.call(void 0, this.seedCounter * 2654435761 >>> 0) & 2147483647));
@@ -4789,11 +4897,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4789
4897
  load,
4790
4898
  grain: paperGrain(doc.paper),
4791
4899
  paperDepth: doc.paper.depth,
4792
- paperColor: _chunkNKTNLJUJcjs.hexToRgb8.call(void 0, doc.paper.color),
4900
+ paperColor: _chunk5BR7LC24cjs.hexToRgb8.call(void 0, doc.paper.color),
4793
4901
  seed: seedW,
4794
4902
  copies: [IDENTITY_AFFINE, ...copiesW].map(affineCopy),
4795
4903
  mask: init.maskStencils ? this.stencilSelection(init.maskStencils) : init.stencils !== false ? this.stencilSelection() : null,
4796
- paint: wetGradient
4904
+ paint: effect ? null : wetGradient,
4905
+ effect: _nullishCoalesce(_optionalChain([effect, 'optionalAccess', _271 => _271.pixels]), () => ( null))
4797
4906
  });
4798
4907
  init = { ...init, seed: seedW, load };
4799
4908
  wet = { driver, surface: () => world.display(id).surface };
@@ -4804,7 +4913,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4804
4913
  }
4805
4914
  let color = init.color === void 0 ? this.color : parseColor(init.color);
4806
4915
  const tracing = this.guides.tracing;
4807
- if (init.color === void 0 && init.start && _optionalChain([tracing, 'optionalAccess', _247 => _247.visible]) && tracing.sampleColors) color = _nullishCoalesce(this.sampleTracingColor(init.start.x, init.start.y), () => ( color));
4916
+ if (init.color === void 0 && init.start && _optionalChain([tracing, 'optionalAccess', _272 => _272.visible]) && tracing.sampleColors) color = _nullishCoalesce(this.sampleTracingColor(init.start.x, init.start.y), () => ( color));
4808
4917
  const perspective = this.guides.perspective;
4809
4918
  const snapper = init.snapToPerspective && perspective.enabled && perspective.snap ? new PerspectiveSnapper(perspective) : null;
4810
4919
  const useStencils = init.stencils !== false;
@@ -4837,7 +4946,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4837
4946
  mixerLoad: _nullishCoalesce(init.load, () => ( brushLoadForStroke(brush, this.mixerLoad, [rgba[0] / 255, rgba[1] / 255, rgba[2] / 255]))),
4838
4947
  secondary: rgbaOf(this.secondaryColor),
4839
4948
  wet,
4840
- thick: brush.engine === "impasto" ? { channels: _nullishCoalesce(_optionalChain([doc, 'access', _248 => _248.thick, 'optionalAccess', _249 => _249[layer.id]]), () => ( emptyThickChannels())) } : void 0,
4949
+ thick: brush.engine === "impasto" ? { channels: _nullishCoalesce(_optionalChain([doc, 'access', _273 => _273.thick, 'optionalAccess', _274 => _274[layer.id]]), () => ( emptyThickChannels())) } : void 0,
4841
4950
  sticker: brush.engine === "sticker" ? { nextId: this.nextStickerNumber(doc) } : void 0,
4842
4951
  paint: effect ? null : this.strokeGradient(init, brush, color),
4843
4952
  effect
@@ -4851,7 +4960,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4851
4960
  const stroke = {
4852
4961
  id: `s${this.strokeCounter++}`,
4853
4962
  layerId: session.layerId,
4854
- brush: _chunkNKTNLJUJcjs.brushToJSON.call(void 0, init.brush),
4963
+ brush: _chunk5BR7LC24cjs.brushToJSON.call(void 0, init.brush),
4855
4964
  color: init.colorHex,
4856
4965
  points: session.points.map((p) => ({ ...p })),
4857
4966
  seed: init.seed
@@ -4876,12 +4985,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4876
4985
  const before = this.doc;
4877
4986
  const host = {
4878
4987
  update: (layerId, surface, rects) => {
4879
- if (_optionalChain([this, 'access', _250 => _250.session, 'optionalAccess', _251 => _251.init, 'access', _252 => _252.wet])) {
4988
+ if (_optionalChain([this, 'access', _275 => _275.session, 'optionalAccess', _276 => _276.init, 'access', _277 => _277.wet])) {
4880
4989
  this.wet.syncDisplays();
4881
4990
  return;
4882
4991
  }
4883
4992
  this.live = { layerId, surface };
4884
- const ch = _optionalChain([this, 'access', _253 => _253.session, 'optionalAccess', _254 => _254.thickChannels, 'call', _255 => _255()]);
4993
+ const ch = _optionalChain([this, 'access', _278 => _278.session, 'optionalAccess', _279 => _279.thickChannels, 'call', _280 => _280()]);
4885
4994
  this.liveThick = ch ? { layerId, channels: ch } : null;
4886
4995
  this.emit(rects);
4887
4996
  },
@@ -4893,7 +5002,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4893
5002
  const wetStroke = Boolean(s.init.wet);
4894
5003
  if (wetStroke) this.wet.driver = null;
4895
5004
  const brush = s.init.brush;
4896
- if (!cancelled && _optionalChain([s, 'access', _256 => _256.result, 'optionalAccess', _257 => _257.mixerLoad])) this.keepLoad(brush, s.result.mixerLoad);
5005
+ if (!cancelled && _optionalChain([s, 'access', _281 => _281.result, 'optionalAccess', _282 => _282.mixerLoad])) this.keepLoad(brush, s.result.mixerLoad);
4897
5006
  if (cancelled || !surface) {
4898
5007
  if (this.wetOp) {
4899
5008
  const snap = this.wetOp.before;
@@ -4908,6 +5017,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4908
5017
  }
4909
5018
  const stroke = this.record(s);
4910
5019
  s.result.strokeId = stroke.id;
5020
+ this.paintedBounds.set(stroke, _nullishCoalesce(_optionalChain([s, 'access', _283 => _283.result, 'optionalAccess', _284 => _284.bounds]), () => ( null)));
4911
5021
  if (wetStroke && this.wet.instantDry) {
4912
5022
  this.wet.dry(s.layerId);
4913
5023
  this.logWet({ kind: "dry", layerId: s.layerId, at: this.doc.strokes.length + 1 });
@@ -4918,8 +5028,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4918
5028
  const ch = s.thickChannels();
4919
5029
  const thick = ch ? { ..._nullishCoalesce(this.doc.thick, () => ( {})), [l.id]: ch } : this.doc.thick;
4920
5030
  const sprayed = s.stickers();
4921
- const stickers = sprayed ? { ..._nullishCoalesce(this.doc.stickers, () => ( {})), [l.id]: [..._nullishCoalesce(_optionalChain([this, 'access', _258 => _258.doc, 'access', _259 => _259.stickers, 'optionalAccess', _260 => _260[l.id]]), () => ( [])), ...sprayed] } : this.doc.stickers;
4922
- this.commit(`${brush.name} stroke`, this.withWetEvents({ ...this.doc, layers: _chunkHB4ICNNAcjs.mapLayer.call(void 0, this.doc.layers, l.id, () => ({ ...l, surface })), strokes: [...this.doc.strokes, stroke], ...thick ? { thick } : {}, ...stickers ? { stickers } : {} }), wetStroke ? before : this.doc);
5031
+ const stickers = sprayed ? { ..._nullishCoalesce(this.doc.stickers, () => ( {})), [l.id]: [..._nullishCoalesce(_optionalChain([this, 'access', _285 => _285.doc, 'access', _286 => _286.stickers, 'optionalAccess', _287 => _287[l.id]]), () => ( [])), ...sprayed] } : this.doc.stickers;
5032
+ this.commit(`${brush.name} stroke`, this.withWetEvents(withLiveStroke({ ...this.doc, layers: _chunkHB4ICNNAcjs.mapLayer.call(void 0, this.doc.layers, l.id, () => ({ ...l, surface })), strokes: [...this.doc.strokes, stroke], ...thick ? { thick } : {}, ...stickers ? { stickers } : {} }, stroke)), wetStroke ? before : this.doc);
4923
5033
  }
4924
5034
  };
4925
5035
  let session;
@@ -4947,17 +5057,18 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4947
5057
  if (cancelled || !surface) return;
4948
5058
  const stroke = this.record(s);
4949
5059
  s.result.strokeId = stroke.id;
5060
+ this.paintedBounds.set(stroke, _nullishCoalesce(_optionalChain([s, 'access', _288 => _288.result, 'optionalAccess', _289 => _289.bounds]), () => ( null)));
4950
5061
  const l = _chunkHB4ICNNAcjs.findLayer.call(void 0, out.layers, s.layerId).layer;
4951
5062
  const offset = s.init.wet ? { x: 0, y: 0 } : l.offset;
4952
5063
  const ch = s.thickChannels();
4953
5064
  const sprayed = s.stickers();
4954
- out = {
5065
+ out = withLiveStroke({
4955
5066
  ...out,
4956
5067
  layers: _chunkHB4ICNNAcjs.mapLayer.call(void 0, out.layers, l.id, () => ({ ...l, surface, offset })),
4957
5068
  strokes: [...out.strokes, stroke],
4958
5069
  ...ch ? { thick: { ..._nullishCoalesce(out.thick, () => ( {})), [l.id]: ch } } : {},
4959
- ...sprayed ? { stickers: { ..._nullishCoalesce(out.stickers, () => ( {})), [l.id]: [..._nullishCoalesce(_optionalChain([out, 'access', _261 => _261.stickers, 'optionalAccess', _262 => _262[l.id]]), () => ( [])), ...sprayed] } } : {}
4960
- };
5070
+ ...sprayed ? { stickers: { ..._nullishCoalesce(out.stickers, () => ( {})), [l.id]: [..._nullishCoalesce(_optionalChain([out, 'access', _290 => _290.stickers, 'optionalAccess', _291 => _291[l.id]]), () => ( [])), ...sprayed] } } : {}
5071
+ }, stroke);
4961
5072
  }
4962
5073
  };
4963
5074
  const session = this.sessionFor(doc, init, host);
@@ -5014,7 +5125,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5014
5125
  */
5015
5126
  replayStrokes(options) {
5016
5127
  this.requireIdle("replaying");
5017
- if (!_optionalChain([options, 'access', _263 => _263.strokeIds, 'optionalAccess', _264 => _264.length])) throw new PaintEditorError("strokeIds is empty; pass ids from list_strokes.");
5128
+ if (!_optionalChain([options, 'access', _292 => _292.strokeIds, 'optionalAccess', _293 => _293.length])) throw new PaintEditorError("strokeIds is empty; pass ids from list_strokes.");
5018
5129
  const strokes = options.strokeIds.map((id) => {
5019
5130
  const s = this.doc.strokes.find((x) => x.id === id);
5020
5131
  if (!s) throw new PaintEditorError(`Unknown stroke "${id}". Call list_strokes for the ids.`);
@@ -5028,7 +5139,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5028
5139
  if (options.layerId !== void 0) this.paintTarget(options.layerId, "replay onto");
5029
5140
  if (options.color === void 0) {
5030
5141
  for (const s of strokes) {
5031
- if (_optionalChain([s, 'access', _265 => _265.paint, 'optionalAccess', _266 => _266.type]) === "effect" && !this.effectPaintImage(s.paint)) {
5142
+ if (_optionalChain([s, 'access', _294 => _294.paint, 'optionalAccess', _295 => _295.type]) === "effect" && !this.effectPaintImage(s.paint)) {
5032
5143
  throw new PaintEditorError(`Stroke "${s.id}" paints with the effect "${s.paint.effect}"${s.paint.provider ? ` from provider "${s.paint.provider}"` : ""}, which is not rendered in this editor; render it first (prepareStrokeEffects; the replay_strokes tool does this) or recolour the replay with color.`);
5033
5144
  }
5034
5145
  }
@@ -5051,13 +5162,13 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5051
5162
  let brush = v.brush;
5052
5163
  if (options.brushId !== void 0) {
5053
5164
  const b = this.requireBrush(options.brushId);
5054
- brush = _chunkNKTNLJUJcjs.normalizePaintBrush.call(void 0, { tip: { ...b.tip, size: b.tip.size * (v.brush.tip.size / s.brush.tip.size) } }, b);
5165
+ brush = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { tip: { ...b.tip, size: b.tip.size * (v.brush.tip.size / s.brush.tip.size) } }, b);
5055
5166
  }
5056
5167
  const layerId = _nullishCoalesce(options.layerId, () => ( s.layerId));
5057
5168
  if (!_chunkHB4ICNNAcjs.findLayer.call(void 0, doc.layers, layerId)) throw new PaintEditorError(`Stroke "${s.id}" was painted on layer "${layerId}", which no longer exists; pass layerId to replay it elsewhere.`);
5058
5169
  let load = s.load;
5059
5170
  if (load && options.color !== void 0 && load.reservoir) {
5060
- const [r, g, b] = _chunkNKTNLJUJcjs.hexToRgb8.call(void 0, options.color);
5171
+ const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, options.color);
5061
5172
  load = { ...load, reservoir: [r / 255, g / 255, b / 255] };
5062
5173
  }
5063
5174
  const paint = v.paint && options.color === void 0 ? { paint: v.paint } : {};
@@ -5097,7 +5208,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5097
5208
  const id = _nullishCoalesce(layerId, () => ( this.doc.activeLayerId));
5098
5209
  if (!id) return null;
5099
5210
  this.requireLayer(id);
5100
- const ch = _optionalChain([this, 'access', _267 => _267.doc, 'access', _268 => _268.thick, 'optionalAccess', _269 => _269[id]]);
5211
+ const ch = _optionalChain([this, 'access', _296 => _296.doc, 'access', _297 => _297.thick, 'optionalAccess', _298 => _298[id]]);
5101
5212
  if (!ch) return null;
5102
5213
  const r = reliefInfo(ch);
5103
5214
  return { layerId: id, bounds: r.bounds, maxHeight: r.maxHeight };
@@ -5205,7 +5316,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5205
5316
  wetState() {
5206
5317
  const st = this.getState().wet;
5207
5318
  const backend = this.wet.backend();
5208
- return { ...st, layers: this.wet.wetLayerIds().map((id) => this.wet.info(id)), backend, message: _nullishCoalesce(_optionalChain([backend, 'optionalAccess', _270 => _270.message]), () => ( _chunkXFYLA3ZOcjs.wetSupport.call(void 0, ).message)), engine: this.wet.engineInfo() };
5319
+ return { ...st, layers: this.wet.wetLayerIds().map((id) => this.wet.info(id)), backend, message: _nullishCoalesce(_optionalChain([backend, 'optionalAccess', _299 => _299.message]), () => ( _chunkXFYLA3ZOcjs.wetSupport.call(void 0, ).message)), engine: this.wet.engineInfo() };
5209
5320
  }
5210
5321
  /**
5211
5322
  * Engine B, the WebGPU accelerator for large wet areas: whether it may run, its state (`off`,
@@ -5290,7 +5401,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5290
5401
  }
5291
5402
  /** Records a settings change as a wet event (not an undo step). */
5292
5403
  recordWetSetting(e) {
5293
- this.doc = { ...this.doc, wetEvents: [..._nullishCoalesce(this.doc.wetEvents, () => ( [])), { clock: _nullishCoalesce(_optionalChain([this, 'access', _271 => _271.wet, 'access', _272 => _272.world, 'optionalAccess', _273 => _273.clock]), () => ( 0)), at: this.doc.strokes.length, ...e }] };
5404
+ this.doc = { ...this.doc, wetEvents: [..._nullishCoalesce(this.doc.wetEvents, () => ( [])), { clock: _nullishCoalesce(_optionalChain([this, 'access', _300 => _300.wet, 'access', _301 => _301.world, 'optionalAccess', _302 => _302.clock]), () => ( 0)), at: this.doc.strokes.length, ...e }] };
5294
5405
  }
5295
5406
  /** Changes a layer's wet settings (viscosity, evaporation, granulation…). Editor settings saved with the document, not an undo step. */
5296
5407
  setWetParams(layerId, update) {
@@ -5355,9 +5466,11 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5355
5466
  }
5356
5467
  /** The layers with the live stroke's surface in place. */
5357
5468
  currentLayers() {
5469
+ const override = this.frameOverride;
5470
+ if (override) return Array.from(override).reduce((ls, [id, surface]) => _chunkHB4ICNNAcjs.mapLayer.call(void 0, ls, id, (l) => ({ ...l, surface })), this.doc.layers);
5358
5471
  const live = this.live;
5359
- if (!live) return this.doc.layers;
5360
- if (_optionalChain([this, 'access', _274 => _274.liveLayers, 'optionalAccess', _275 => _275.key]) === live.surface) return this.liveLayers.layers;
5472
+ if (!live) return this.animatedView();
5473
+ if (_optionalChain([this, 'access', _303 => _303.liveLayers, 'optionalAccess', _304 => _304.key]) === live.surface) return this.liveLayers.layers;
5361
5474
  const layers = _chunkHB4ICNNAcjs.mapLayer.call(void 0, this.doc.layers, live.layerId, (l) => ({ ...l, surface: live.surface }));
5362
5475
  this.liveLayers = { key: live.surface, layers };
5363
5476
  return layers;
@@ -5380,7 +5493,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5380
5493
  tracingNode(t) {
5381
5494
  const { width, height } = this.doc.canvas;
5382
5495
  const key = this.tracingVersion * 100003 + width * 7 + height;
5383
- if (_optionalChain([this, 'access', _276 => _276.tracingCache, 'optionalAccess', _277 => _277.key]) === key && this.tracingCache.node.opacity === t.opacity) return this.tracingCache.node;
5496
+ if (_optionalChain([this, 'access', _305 => _305.tracingCache, 'optionalAccess', _306 => _306.key]) === key && this.tracingCache.node.opacity === t.opacity) return this.tracingCache.node;
5384
5497
  const src = _nullishCoalesce(this.imagePixels.get(t.src), () => ( this.decodeImage(t.src)));
5385
5498
  const image = resampleInto(src, t.rect, width, height);
5386
5499
  const node = { type: "image", id: TRACING_NODE_ID, name: "Tracing", visible: true, opacity: t.opacity, blend: "normal", image, version: key + Math.round(t.opacity * 1e3) / 1e4 };
@@ -5407,8 +5520,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5407
5520
  let out = layers;
5408
5521
  const ids = /* @__PURE__ */ new Set([...Object.keys(_nullishCoalesce(thick, () => ( {}))), ...live ? [live.layerId] : []]);
5409
5522
  for (const id of ids) {
5410
- const ch = _optionalChain([live, 'optionalAccess', _278 => _278.layerId]) === id ? live.channels : _optionalChain([thick, 'optionalAccess', _279 => _279[id]]);
5411
- const l = ch && _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, out, id), 'optionalAccess', _280 => _280.layer]);
5523
+ const ch = _optionalChain([live, 'optionalAccess', _307 => _307.layerId]) === id ? live.channels : _optionalChain([thick, 'optionalAccess', _308 => _308[id]]);
5524
+ const l = ch && _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, out, id), 'optionalAccess', _309 => _309.layer]);
5412
5525
  if (!ch || !isPixelLayer(l)) continue;
5413
5526
  let cache = this.litCaches.get(id);
5414
5527
  if (!cache) this.litCaches.set(id, cache = new LitCache());
@@ -5421,7 +5534,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5421
5534
  let layers = this.litLayers((_nullishCoalesce(options.wet, () => ( "display"))) === "dried" ? this.driedLayers() : this.currentLayers());
5422
5535
  if (options.layerIds) {
5423
5536
  const picked = [];
5424
- for (const id of options.layerIds) picked.push(_nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, layers, id), 'optionalAccess', _281 => _281.layer]), () => ( this.requireLayer(id))));
5537
+ for (const id of options.layerIds) picked.push(_nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, layers, id), 'optionalAccess', _310 => _310.layer]), () => ( this.requireLayer(id))));
5425
5538
  layers = picked.map((l) => ({ ...l, visible: true }));
5426
5539
  }
5427
5540
  const under = options.paper === false ? [] : [paperNode(this.doc.paper, this.lighting)];
@@ -5437,7 +5550,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5437
5550
  */
5438
5551
  compositeTiles(rect, onTile, options = {}) {
5439
5552
  const dirty = options.dirty === "all" ? void 0 : options.dirty;
5440
- this.viewCompositor.compositeTiles(this.renderLayers({ paper: options.paper }, true), rect, this.sources(), onTile, { dirty, checkpointLayerId: _nullishCoalesce(_optionalChain([this, 'access', _282 => _282.live, 'optionalAccess', _283 => _283.layerId]), () => ( this.doc.activeLayerId)) });
5553
+ this.viewCompositor.compositeTiles(this.renderLayers({ paper: options.paper }, true), rect, this.sources(), onTile, { dirty, checkpointLayerId: _nullishCoalesce(_optionalChain([this, 'access', _311 => _311.live, 'optionalAccess', _312 => _312.layerId]), () => ( this.doc.activeLayerId)) });
5441
5554
  }
5442
5555
  /** The canvas as canvas-sized RGBA (paper included by default). */
5443
5556
  compositePixels(options = {}) {
@@ -5501,7 +5614,11 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5501
5614
  for (const [id, list] of Object.entries(_nullishCoalesce(this.doc.stickers, () => ( {})))) if (list.length && _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id)) stickers[id] = list.map((x) => ({ ...x }));
5502
5615
  if (Object.keys(stickers).length) ext.stickers = stickers;
5503
5616
  else delete ext.stickers;
5504
- const effectLayers = effectLayersToJSON(_nullishCoalesce(this.doc.effectLayers, () => ( {})), (id) => isPixelLayer(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _284 => _284.layer])));
5617
+ const liveStrokes = {};
5618
+ for (const [id, list] of Object.entries(_nullishCoalesce(this.doc.liveStrokes, () => ( {})))) if (_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id)) liveStrokes[id] = list.map((x) => JSON.parse(JSON.stringify(x)));
5619
+ if (Object.keys(liveStrokes).length) ext.liveStrokes = liveStrokes;
5620
+ else delete ext.liveStrokes;
5621
+ const effectLayers = effectLayersToJSON(_nullishCoalesce(this.doc.effectLayers, () => ( {})), (id) => isPixelLayer(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _313 => _313.layer])));
5505
5622
  if (effectLayers) ext.effectLayers = effectLayers;
5506
5623
  else delete ext.effectLayers;
5507
5624
  if (this.doc.paperEffect) ext.paperEffect = paperEffectToJSON(this.doc.paperEffect);
@@ -5518,16 +5635,26 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5518
5635
  const stickers = {};
5519
5636
  for (const [id, list] of Object.entries(ext.stickers)) {
5520
5637
  if (!_chunkHB4ICNNAcjs.findLayer.call(void 0, state.layers, id) || !Array.isArray(list)) continue;
5521
- const ok = list.filter((x) => x && typeof x.id === "string" && _chunkNKTNLJUJcjs.STAMP_KINDS.includes(x.stamp) && Number.isFinite(x.x) && Number.isFinite(x.y) && x.size > 0);
5638
+ const ok = list.filter((x) => x && typeof x.id === "string" && _chunk5BR7LC24cjs.STAMP_KINDS.includes(x.stamp) && Number.isFinite(x.x) && Number.isFinite(x.y) && x.size > 0);
5522
5639
  if (ok.length !== list.length) problems.push(`Some stickers of layer "${id}" could not be read; they were dropped.`);
5523
5640
  stickers[id] = ok.map((x) => ({ ...x }));
5524
5641
  }
5525
5642
  if (Object.keys(stickers).length) state = { ...state, stickers };
5526
5643
  }
5644
+ if (ext.liveStrokes && typeof ext.liveStrokes === "object") {
5645
+ const liveStrokes = {};
5646
+ for (const [id, list] of Object.entries(ext.liveStrokes)) {
5647
+ if (!isPixelLayer(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, state.layers, id), 'optionalAccess', _314 => _314.layer])) || !Array.isArray(list)) continue;
5648
+ const ok = list.map(normalizeBrushStroke).filter((x) => x !== null).map((x) => ({ ...x, layerId: id }));
5649
+ if (ok.length !== list.length) problems.push(`Some strokes of stroke layer "${id}" could not be read; they were dropped (the layer's pixels are kept until a stroke is edited).`);
5650
+ liveStrokes[id] = ok;
5651
+ }
5652
+ if (Object.keys(liveStrokes).length) state = { ...state, liveStrokes };
5653
+ }
5527
5654
  if (ext.effectLayers && typeof ext.effectLayers === "object") {
5528
5655
  const effectLayers = {};
5529
5656
  for (const [id, v] of Object.entries(ext.effectLayers)) {
5530
- if (!isPixelLayer(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, state.layers, id), 'optionalAccess', _285 => _285.layer]))) continue;
5657
+ if (!isPixelLayer(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, state.layers, id), 'optionalAccess', _315 => _315.layer]))) continue;
5531
5658
  const source = readPaintEffectSource(v);
5532
5659
  if (source) effectLayers[id] = source;
5533
5660
  else problems.push(`The effect of layer "${id}" could not be read; it loads as a plain layer.`);
@@ -5568,16 +5695,23 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5568
5695
  this.readGuides(loaded.guides, loaded.state, loaded.problems);
5569
5696
  if (Array.isArray(loaded.swatches)) this.swatches = loaded.swatches.map(readSwatch).filter((x) => x !== null);
5570
5697
  for (const s of loaded.state.strokes) {
5571
- const n = Number(_nullishCoalesce(_optionalChain([/^s(\d+)$/, 'access', _286 => _286.exec, 'call', _287 => _287(s.id), 'optionalAccess', _288 => _288[1]]), () => ( 0)));
5698
+ const n = Number(_nullishCoalesce(_optionalChain([/^s(\d+)$/, 'access', _316 => _316.exec, 'call', _317 => _317(s.id), 'optionalAccess', _318 => _318[1]]), () => ( 0)));
5572
5699
  if (n >= this.strokeCounter) this.strokeCounter = n + 1;
5573
5700
  }
5574
5701
  this.undoStack = [];
5575
5702
  this.redoStack = [];
5576
5703
  this.doc = this.readThick(loaded.state, loaded.extensions, loaded.problems);
5704
+ for (const list of Object.values(_nullishCoalesce(this.doc.liveStrokes, () => ( {})))) {
5705
+ for (const s of list) {
5706
+ const n = Number(_nullishCoalesce(_optionalChain([/^s(\d+)$/, 'access', _319 => _319.exec, 'call', _320 => _320(s.id), 'optionalAccess', _321 => _321[1]]), () => ( 0)));
5707
+ if (n >= this.strokeCounter) this.strokeCounter = n + 1;
5708
+ }
5709
+ }
5577
5710
  this.docRevision++;
5578
5711
  delete this.extensions.layerChannels;
5579
5712
  delete this.extensions.lighting;
5580
5713
  delete this.extensions.stickers;
5714
+ delete this.extensions.liveStrokes;
5581
5715
  delete this.extensions.effectLayers;
5582
5716
  delete this.extensions.paperEffect;
5583
5717
  this.viewCompositor.clear();
@@ -5660,10 +5794,10 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5660
5794
  return next.type === "group" ? { ...next, children: toStandard(next.children) } : next;
5661
5795
  });
5662
5796
  const layers = toStandard(dried);
5663
- const result = _chunkBBUE3UJLcjs.exportPsd.call(void 0, { width, height, layers: [paperLayer, ...layers], merged, resolution: dpi });
5797
+ const result = _chunkHW72ABGRcjs.exportPsd.call(void 0, { width, height, layers: [paperLayer, ...layers], merged, resolution: dpi });
5664
5798
  for (const [mode, names] of changed) {
5665
- const label = _nullishCoalesce(_optionalChain([_chunkOLNIPTB3cjs.BLEND_MODES, 'access', _289 => _289.find, 'call', _290 => _290((m) => m.id === mode), 'optionalAccess', _291 => _291.label]), () => ( mode));
5666
- const std = _nullishCoalesce(_optionalChain([_chunkOLNIPTB3cjs.BLEND_MODES, 'access', _292 => _292.find, 'call', _293 => _293((m) => m.id === _chunkOLNIPTB3cjs.standardBlendMode.call(void 0, mode)), 'optionalAccess', _294 => _294.label]), () => ( _chunkOLNIPTB3cjs.standardBlendMode.call(void 0, mode)));
5799
+ const label = _nullishCoalesce(_optionalChain([_chunkOLNIPTB3cjs.BLEND_MODES, 'access', _322 => _322.find, 'call', _323 => _323((m) => m.id === mode), 'optionalAccess', _324 => _324.label]), () => ( mode));
5800
+ const std = _nullishCoalesce(_optionalChain([_chunkOLNIPTB3cjs.BLEND_MODES, 'access', _325 => _325.find, 'call', _326 => _326((m) => m.id === _chunkOLNIPTB3cjs.standardBlendMode.call(void 0, mode)), 'optionalAccess', _327 => _327.label]), () => ( _chunkOLNIPTB3cjs.standardBlendMode.call(void 0, mode)));
5667
5801
  const list = names.map((n) => `"${n}"`).join(", ");
5668
5802
  const one = names.length === 1;
5669
5803
  result.warnings.push(`${label} blending has no PSD equivalent; ${list} ${one ? "was" : "were"} written as ${std}, the nearest Photoshop mode, so ${one ? "it looks" : "they look"} different in Photoshop (the merged image is exact).`);
@@ -5779,7 +5913,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5779
5913
  const { src, pixels } = this.importImage(options, "The reference image");
5780
5914
  const ref2 = {
5781
5915
  id: `ref${this.nextReferenceId++}`,
5782
- name: _optionalChain([options, 'access', _295 => _295.name, 'optionalAccess', _296 => _296.trim, 'call', _297 => _297()]) || `Reference ${this.guides.references.length + 1}`,
5916
+ name: _optionalChain([options, 'access', _328 => _328.name, 'optionalAccess', _329 => _329.trim, 'call', _330 => _330()]) || `Reference ${this.guides.references.length + 1}`,
5783
5917
  src,
5784
5918
  width: pixels.width,
5785
5919
  height: pixels.height,
@@ -5858,7 +5992,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5858
5992
  try {
5859
5993
  const px = this.decodeImage(ref2.src, `Reference "${ref2.name}"`);
5860
5994
  guides.references.push({ id: ref2.id, name: String(_nullishCoalesce(ref2.name, () => ( ref2.id))), src: ref2.src, width: px.width, height: px.height, x: Number(ref2.x) || 0, y: Number(ref2.y) || 0, displayWidth: Number(ref2.displayWidth) || 240, visible: ref2.visible !== false });
5861
- const n = Number(_nullishCoalesce(_optionalChain([/^ref(\d+)$/, 'access', _298 => _298.exec, 'call', _299 => _299(ref2.id), 'optionalAccess', _300 => _300[1]]), () => ( 0)));
5995
+ const n = Number(_nullishCoalesce(_optionalChain([/^ref(\d+)$/, 'access', _331 => _331.exec, 'call', _332 => _332(ref2.id), 'optionalAccess', _333 => _333[1]]), () => ( 0)));
5862
5996
  if (n >= this.nextReferenceId) this.nextReferenceId = n + 1;
5863
5997
  } catch (e) {
5864
5998
  problems.push(e.message);
@@ -5869,7 +6003,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5869
6003
  guides.stencils = [];
5870
6004
  r.stencils.forEach((raw2, i) => {
5871
6005
  try {
5872
- const id = typeof _optionalChain([raw2, 'optionalAccess', _301 => _301.id]) === "string" && /^stencil\d+$/.test(raw2.id) ? raw2.id : `stencil${this.nextStencilId}`;
6006
+ const id = typeof _optionalChain([raw2, 'optionalAccess', _334 => _334.id]) === "string" && /^stencil\d+$/.test(raw2.id) ? raw2.id : `stencil${this.nextStencilId}`;
5873
6007
  guides.stencils.push(normalizeStencil(raw2, id));
5874
6008
  const n = Number(id.slice(7));
5875
6009
  if (n >= this.nextStencilId) this.nextStencilId = n + 1;
@@ -5892,12 +6026,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5892
6026
  /** The next free sticker number (ids `st<n>` are unique across the document). */
5893
6027
  nextStickerNumber(doc = this.doc) {
5894
6028
  let n = 0;
5895
- for (const list of Object.values(_nullishCoalesce(doc.stickers, () => ( {})))) for (const st of list) n = Math.max(n, Number(_nullishCoalesce(_optionalChain([/^st(\d+)$/, 'access', _302 => _302.exec, 'call', _303 => _303(st.id), 'optionalAccess', _304 => _304[1]]), () => ( 0))));
6029
+ for (const list of Object.values(_nullishCoalesce(doc.stickers, () => ( {})))) for (const st of list) n = Math.max(n, Number(_nullishCoalesce(_optionalChain([/^st(\d+)$/, 'access', _335 => _335.exec, 'call', _336 => _336(st.id), 'optionalAccess', _337 => _337[1]]), () => ( 0))));
5896
6030
  return n + 1;
5897
6031
  }
5898
6032
  stickerLayer(layerId) {
5899
6033
  const l = this.wetLayer(layerId, "edit the stickers of");
5900
- const stickers = _optionalChain([this, 'access', _305 => _305.doc, 'access', _306 => _306.stickers, 'optionalAccess', _307 => _307[l.id]]);
6034
+ const stickers = _optionalChain([this, 'access', _338 => _338.doc, 'access', _339 => _339.stickers, 'optionalAccess', _340 => _340[l.id]]);
5901
6035
  if (!stickers) throw new PaintEditorError(`Layer "${l.name}" has no stickers; spray some with a sticker brush (list_brushes category sticker).`);
5902
6036
  return { layer: l, stickers };
5903
6037
  }
@@ -5906,7 +6040,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5906
6040
  const id = _nullishCoalesce(layerId, () => ( this.doc.activeLayerId));
5907
6041
  if (!id) return [];
5908
6042
  this.requireLayer(id);
5909
- return (_nullishCoalesce(_optionalChain([this, 'access', _308 => _308.doc, 'access', _309 => _309.stickers, 'optionalAccess', _310 => _310[id]]), () => ( []))).map((s) => ({ ...s }));
6043
+ return (_nullishCoalesce(_optionalChain([this, 'access', _341 => _341.doc, 'access', _342 => _342.stickers, 'optionalAccess', _343 => _343[id]]), () => ( []))).map((s) => ({ ...s }));
5910
6044
  }
5911
6045
  /**
5912
6046
  * Replaces a layer's stickers and redraws its pixels from them (one undo step). With `dirty`,
@@ -5922,15 +6056,15 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5922
6056
  }
5923
6057
  redrawStickers(layer, stickers, dirty) {
5924
6058
  const partial = dirty && !layer.offset.x && !layer.offset.y;
5925
- return partial ? _chunkNKTNLJUJcjs.stickerSurface.call(void 0, stickers, layer.surface, dirty) : _chunkNKTNLJUJcjs.stickerSurface.call(void 0, stickers);
6059
+ return partial ? _chunk5BR7LC24cjs.stickerSurface.call(void 0, stickers, layer.surface, dirty) : _chunk5BR7LC24cjs.stickerSurface.call(void 0, stickers);
5926
6060
  }
5927
6061
  /** The document from before the sticker preview on screen, or `null` when none is (anything else changed the document since). */
5928
6062
  previewBase() {
5929
- return _optionalChain([this, 'access', _311 => _311.stickerPreview, 'optionalAccess', _312 => _312.shown]) === this.doc ? this.stickerPreview.base : null;
6063
+ return _optionalChain([this, 'access', _344 => _344.stickerPreview, 'optionalAccess', _345 => _345.shown]) === this.doc ? this.stickerPreview.base : null;
5930
6064
  }
5931
6065
  readStickerUpdate(update) {
5932
6066
  try {
5933
- return _chunkNKTNLJUJcjs.normalizeStickerUpdate.call(void 0, update);
6067
+ return _chunk5BR7LC24cjs.normalizeStickerUpdate.call(void 0, update);
5934
6068
  } catch (e) {
5935
6069
  throw new PaintEditorError(e.message);
5936
6070
  }
@@ -5941,7 +6075,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5941
6075
  const id = _nullishCoalesce(layerId, () => ( this.doc.activeLayerId));
5942
6076
  if (!id) return null;
5943
6077
  this.requireLayer(id);
5944
- const hit = _chunkNKTNLJUJcjs.stickerAt.call(void 0, _nullishCoalesce(_optionalChain([this, 'access', _313 => _313.doc, 'access', _314 => _314.stickers, 'optionalAccess', _315 => _315[id]]), () => ( [])), x, y);
6078
+ const hit = _chunk5BR7LC24cjs.stickerAt.call(void 0, _nullishCoalesce(_optionalChain([this, 'access', _346 => _346.doc, 'access', _347 => _347.stickers, 'optionalAccess', _348 => _348[id]]), () => ( [])), x, y);
5945
6079
  return hit ? { ...hit } : null;
5946
6080
  }
5947
6081
  /** Picks a sticker for the panel and canvas (`null` for none). Editor state, not an undo step. */
@@ -5962,7 +6096,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5962
6096
  if (i < 0) throw new PaintEditorError(`Layer "${layer.name}" has no sticker "${id}". Call list_stickers for the ids.`);
5963
6097
  const next = { ...stickers[i], ...this.readStickerUpdate(update) };
5964
6098
  const list = stickers.map((s, j) => j === i ? next : s);
5965
- const dirty = [_chunkNKTNLJUJcjs.stickerRect.call(void 0, stickers[i]), _chunkNKTNLJUJcjs.stickerRect.call(void 0, next)];
6099
+ const dirty = [_chunk5BR7LC24cjs.stickerRect.call(void 0, stickers[i]), _chunk5BR7LC24cjs.stickerRect.call(void 0, next)];
5966
6100
  const base = _nullishCoalesce(this.previewBase(), () => ( this.doc));
5967
6101
  const surface = this.redrawStickers(layer, list, dirty);
5968
6102
  this.doc = { ...this.doc, layers: _chunkHB4ICNNAcjs.mapLayer.call(void 0, this.doc.layers, layer.id, () => ({ ...layer, surface, offset: { x: 0, y: 0 } })), stickers: { ..._nullishCoalesce(this.doc.stickers, () => ( {})), [layer.id]: list } };
@@ -5985,7 +6119,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5985
6119
  if (i < 0) throw new PaintEditorError(`Layer "${layer.name}" has no sticker "${id}". Call list_stickers for the ids.`);
5986
6120
  const u = this.readStickerUpdate(update);
5987
6121
  const next = { ...stickers[i], ...u };
5988
- this.commitStickers("Edit sticker", layer, stickers.map((s, j) => j === i ? next : s), [_chunkNKTNLJUJcjs.stickerRect.call(void 0, stickers[i]), _chunkNKTNLJUJcjs.stickerRect.call(void 0, next)]);
6122
+ this.commitStickers("Edit sticker", layer, stickers.map((s, j) => j === i ? next : s), [_chunk5BR7LC24cjs.stickerRect.call(void 0, stickers[i]), _chunk5BR7LC24cjs.stickerRect.call(void 0, next)]);
5989
6123
  return { ...next };
5990
6124
  }
5991
6125
  /**
@@ -5997,8 +6131,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5997
6131
  const layer = this.wetLayer(layerId, "add stickers to");
5998
6132
  if (!input.length) throw new PaintEditorError("Pass at least one sticker (stamp, x, y).");
5999
6133
  if (input.length > MAX_ADD_STICKERS) throw new PaintEditorError(`At most ${MAX_ADD_STICKERS} stickers per call; split them over several calls.`);
6000
- const existing = _optionalChain([this, 'access', _316 => _316.doc, 'access', _317 => _317.stickers, 'optionalAccess', _318 => _318[layer.id]]);
6001
- if (!_optionalChain([existing, 'optionalAccess', _319 => _319.length])) {
6134
+ const existing = _optionalChain([this, 'access', _349 => _349.doc, 'access', _350 => _350.stickers, 'optionalAccess', _351 => _351[layer.id]]);
6135
+ if (!_optionalChain([existing, 'optionalAccess', _352 => _352.length])) {
6002
6136
  if (this.wet.wetLayerIds().includes(layer.id) || _chunk474IQG74cjs.contentBounds.call(void 0, layer.surface)) throw new PaintEditorError(`Layer "${layer.name}" already has paint; add stickers to an empty layer (add_layer) or a sticker layer so they stay editable.`);
6003
6137
  }
6004
6138
  let n = this.nextStickerNumber();
@@ -6014,7 +6148,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6014
6148
  const id = n++;
6015
6149
  return { id: `st${id}`, size: 48, rotation: 0, color: this.color, opacity: 1, seed: _nullishCoalesce(raw.seed, () => ( _chunkPQ3H2AANcjs.mix32.call(void 0, id * 2654435761 >>> 0) & 2147483647)), ...u };
6016
6150
  });
6017
- this.commitStickers("Add stickers", layer, [..._nullishCoalesce(existing, () => ( [])), ...added], _optionalChain([existing, 'optionalAccess', _320 => _320.length]) ? added.map(_chunkNKTNLJUJcjs.stickerRect) : void 0);
6151
+ this.commitStickers("Add stickers", layer, [..._nullishCoalesce(existing, () => ( [])), ...added], _optionalChain([existing, 'optionalAccess', _353 => _353.length]) ? added.map(_chunk5BR7LC24cjs.stickerRect) : void 0);
6018
6152
  return added.map((s) => ({ ...s }));
6019
6153
  }
6020
6154
  /** Brings stickers to the top (`front`) or bottom of their layer (one undo step). */
@@ -6024,7 +6158,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6024
6158
  const picked = stickers.filter((s) => ids.includes(s.id));
6025
6159
  const rest = stickers.filter((s) => !ids.includes(s.id));
6026
6160
  const next = to === "front" ? [...rest, ...picked] : [...picked, ...rest];
6027
- this.commitStickers("Arrange stickers", layer, next, picked.map(_chunkNKTNLJUJcjs.stickerRect));
6161
+ this.commitStickers("Arrange stickers", layer, next, picked.map(_chunk5BR7LC24cjs.stickerRect));
6028
6162
  return next.map((s) => s.id);
6029
6163
  }
6030
6164
  /** Deletes stickers (`ids`, or all of them on the layer) (one undo step). Returns the ids removed. */
@@ -6034,7 +6168,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6034
6168
  for (const id of ids) if (!stickers.some((s) => s.id === id)) throw new PaintEditorError(`Layer "${layer.name}" has no sticker "${id}". Call list_stickers for the ids.`);
6035
6169
  }
6036
6170
  const removed = ids ? [...ids] : stickers.map((s) => s.id);
6037
- this.commitStickers("Remove stickers", layer, stickers.filter((s) => !removed.includes(s.id)), ids ? stickers.filter((s) => removed.includes(s.id)).map(_chunkNKTNLJUJcjs.stickerRect) : void 0);
6171
+ this.commitStickers("Remove stickers", layer, stickers.filter((s) => !removed.includes(s.id)), ids ? stickers.filter((s) => removed.includes(s.id)).map(_chunk5BR7LC24cjs.stickerRect) : void 0);
6038
6172
  return removed;
6039
6173
  }
6040
6174
  /** Sets a layer's stickers (replays). */
@@ -6042,14 +6176,428 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6042
6176
  const l = this.wetLayer(layerId, "set the stickers of");
6043
6177
  this.commitStickers("Stickers", l, stickers.map((s) => ({ ...s })));
6044
6178
  }
6179
+ // --- Live stroke layers ---------------------------------------------------------
6180
+ /** The document's `liveStrokes` without these layers (as a spread: `{}` when unchanged). */
6181
+ liveStrokesWithout(ids) {
6182
+ const t = this.doc.liveStrokes;
6183
+ if (!t || !ids.some((id) => t[id])) return {};
6184
+ const out = { ...t };
6185
+ for (const id of ids) delete out[id];
6186
+ return { liveStrokes: out };
6187
+ }
6188
+ liveStrokeLayer(layerId, what) {
6189
+ const l = this.wetLayer(layerId, what);
6190
+ const strokes = _optionalChain([this, 'access', _354 => _354.doc, 'access', _355 => _355.liveStrokes, 'optionalAccess', _356 => _356[l.id]]);
6191
+ if (!strokes) throw new PaintEditorError(`Layer "${l.name}" is not a stroke layer; add one with add_layer {type: "strokes"} (or convert_to_stroke_layer on an empty layer), then paint on it.`);
6192
+ return { layer: l, strokes };
6193
+ }
6194
+ /** Whether a layer is a live stroke layer. */
6195
+ isLiveStrokeLayer(layerId) {
6196
+ return Boolean(_optionalChain([this, 'access', _357 => _357.doc, 'access', _358 => _358.liveStrokes, 'optionalAccess', _359 => _359[layerId]]));
6197
+ }
6198
+ /** Makes an empty pixel layer a live stroke layer (one undo step). */
6199
+ convertToLiveStrokes(layerId) {
6200
+ this.requireIdle("converting a layer");
6201
+ const l = this.paintTarget(layerId, "convert");
6202
+ if (_optionalChain([this, 'access', _360 => _360.doc, 'access', _361 => _361.liveStrokes, 'optionalAccess', _362 => _362[l.id]])) return this.describeLayer(l);
6203
+ if (_optionalChain([this, 'access', _363 => _363.doc, 'access', _364 => _364.stickers, 'optionalAccess', _365 => _365[l.id], 'optionalAccess', _366 => _366.length])) throw new PaintEditorError(`Layer "${l.name}" is a sticker layer; add a stroke layer instead (add_layer {type: "strokes"}).`);
6204
+ if (_chunk474IQG74cjs.contentBounds.call(void 0, l.surface) || this.wet.wetLayerIds().includes(l.id)) throw new PaintEditorError(`Layer "${l.name}" already has paint, which isn't kept as strokes; convert an empty layer or add a stroke layer (add_layer {type: "strokes"}).`);
6205
+ this.commit("Convert to stroke layer", { ...this.doc, liveStrokes: { ..._nullishCoalesce(this.doc.liveStrokes, () => ( {})), [l.id]: [] } });
6206
+ return this.describeLayer(this.getLayer(l.id));
6207
+ }
6208
+ /** A stroke layer's strokes, bottom first. */
6209
+ listLiveStrokes(layerId) {
6210
+ return this.liveStrokeLayer(layerId, "list the strokes of").strokes.map((x) => ({ ...x }));
6211
+ }
6212
+ /** The stroke layer holding a live stroke, and where. */
6213
+ findLiveStroke(id) {
6214
+ for (const [layerId, strokes] of Object.entries(_nullishCoalesce(this.doc.liveStrokes, () => ( {})))) {
6215
+ const index = strokes.findIndex((x) => x.id === id);
6216
+ const layer = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, layerId), 'optionalAccess', _367 => _367.layer]);
6217
+ if (index >= 0 && isPixelLayer(layer)) return { layer, strokes, index };
6218
+ }
6219
+ const baked = this.doc.strokes.some((x) => x.id === id);
6220
+ throw new PaintEditorError(
6221
+ baked ? `Stroke "${id}" was painted on a pixel layer, so its pixels are baked; only strokes on a stroke layer can be edited (list_strokes {live: true}). Replay it onto a stroke layer (replay_strokes layerId) to edit the copy.` : `Unknown stroke "${id}". Call list_strokes {live: true} for the ids of editable strokes.`
6222
+ );
6223
+ }
6224
+ /**
6225
+ * Changes a live stroke's brush (a preset by `brushId`, keeping the stroke's size, and/or brush
6226
+ * settings), colour, paint or seed, and redraws its layer (one undo step). A new colour replaces
6227
+ * gradient or effect paint. Returns the stroke and the area that changed.
6228
+ */
6229
+ updateStroke(id, update) {
6230
+ this.requireIdle("editing a stroke");
6231
+ const { layer, strokes, index } = this.findLiveStroke(id);
6232
+ const was = strokes[index];
6233
+ let brush = was.brush;
6234
+ if (update.brushId !== void 0) {
6235
+ const b = this.requireBrush(update.brushId);
6236
+ brush = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { tip: { ...b.tip, size: was.brush.tip.size } }, b);
6237
+ }
6238
+ if (update.brush !== void 0) {
6239
+ if (!update.brush || typeof update.brush !== "object") throw new PaintEditorError("brush must be an object of brush settings (as set_brush takes).");
6240
+ brush = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { ...update.brush, id: brush.id }, brush);
6241
+ }
6242
+ const why = liveStrokeRefusal(brush);
6243
+ if (why) throw new PaintEditorError(why);
6244
+ const next = { ...was, brush: _chunk5BR7LC24cjs.brushToJSON.call(void 0, brush) };
6245
+ if (update.seed !== void 0) {
6246
+ if (!Number.isInteger(update.seed) || update.seed < 0) throw new PaintEditorError(`seed ${update.seed} must be a whole number of 0 or more.`);
6247
+ next.seed = update.seed;
6248
+ }
6249
+ if (update.color !== void 0) {
6250
+ next.color = parseColor(update.color);
6251
+ delete next.paint;
6252
+ }
6253
+ if (update.paint !== void 0) {
6254
+ if (update.paint === null) delete next.paint;
6255
+ else if (_chunk5BR7LC24cjs.isPaintEffectPaint.call(void 0, update.paint)) {
6256
+ if (!this.effectPaintImage(update.paint)) throw new PaintEditorError(`The effect "${update.paint.effect}" is not rendered yet; render it first (prepareEffectPaints; the update_stroke tool does this).`);
6257
+ next.paint = JSON.parse(JSON.stringify(update.paint));
6258
+ } else {
6259
+ const p = this.readStrokePaint(update.paint);
6260
+ if (p.type === "color") {
6261
+ next.color = p.color;
6262
+ delete next.paint;
6263
+ } else next.paint = JSON.parse(JSON.stringify(p));
6264
+ }
6265
+ }
6266
+ const rgb = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, next.color).map((v) => v / 255);
6267
+ const load = brushLoadForStroke(brush, _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD, rgb);
6268
+ if (load.reservoir || load.pickup) next.load = load;
6269
+ else delete next.load;
6270
+ const list = strokes.map((x, i) => i === index ? next : x);
6271
+ this.commitLiveStrokes("Edit stroke", layer, list, this.strokeAreas([was]));
6272
+ return { stroke: { ...next }, bounds: _nullishCoalesce(unionRect(this.strokeArea(was), this.strokeArea(next)), () => ( { x: 0, y: 0, width: 0, height: 0 })) };
6273
+ }
6274
+ /**
6275
+ * Replaces a live stroke's points, or moves, scales, rotates or flips them (`transform`, about
6276
+ * the stroke's centre unless an origin is given; the brush size scales too), and redraws its
6277
+ * layer (one undo step). Returns the stroke and the area that changed.
6278
+ */
6279
+ editStrokePoints(id, edit) {
6280
+ this.requireIdle("editing a stroke");
6281
+ if (edit.points === void 0 === (edit.transform === void 0)) throw new PaintEditorError("Pass either points (the new pen samples) or transform (dx, dy, scale, rotate, flipX), not both.");
6282
+ const { layer, strokes, index } = this.findLiveStroke(id);
6283
+ const was = strokes[index];
6284
+ let next;
6285
+ if (edit.points !== void 0) {
6286
+ const points = this.checkPoints(edit.points);
6287
+ next = { ...was, points: points.map((p) => ({ ...p })) };
6288
+ } else {
6289
+ const t = edit.transform;
6290
+ if (t.scale !== void 0 && !(t.scale > 0 && t.scale <= 20)) throw new PaintEditorError(`scale ${t.scale} is out of range; use above 0 up to 20.`);
6291
+ for (const k of ["dx", "dy", "rotate"]) if (t[k] !== void 0 && !Number.isFinite(t[k])) throw new PaintEditorError(`${k} must be a number.`);
6292
+ const b = strokeBounds(was.points);
6293
+ const m = variationAffine(t, _nullishCoalesce(t.origin, () => ( { x: b.x + b.width / 2, y: b.y + b.height / 2 })));
6294
+ next = varyStroke(was, m, {});
6295
+ if (was.stencils) next.stencils = was.stencils;
6296
+ }
6297
+ const list = strokes.map((x, i) => i === index ? next : x);
6298
+ this.commitLiveStrokes("Edit stroke points", layer, list, this.strokeAreas([was]));
6299
+ return { stroke: { ...next }, bounds: _nullishCoalesce(unionRect(this.strokeArea(was), this.strokeArea(next)), () => ( { x: 0, y: 0, width: 0, height: 0 })) };
6300
+ }
6301
+ /** Deletes live strokes (one undo step). Returns the ids removed and the area that changed. */
6302
+ removeStrokes(ids) {
6303
+ this.requireIdle("removing strokes");
6304
+ if (!ids.length) throw new PaintEditorError("Pass at least one stroke id (list_strokes {live: true}).");
6305
+ const found = ids.map((id) => this.findLiveStroke(id));
6306
+ let doc = this.doc;
6307
+ let bounds = null;
6308
+ const layers = /* @__PURE__ */ new Map();
6309
+ for (const f of found) layers.set(f.layer.id, f.layer);
6310
+ for (const [layerId, layer] of layers) {
6311
+ const gone = (_nullishCoalesce(_optionalChain([doc, 'access', _368 => _368.liveStrokes, 'optionalAccess', _369 => _369[layerId]]), () => ( []))).filter((x) => ids.includes(x.id));
6312
+ for (const x of gone) bounds = unionRect(bounds, this.strokeArea(x));
6313
+ doc = this.withLiveStrokes(doc, layer, (_nullishCoalesce(_optionalChain([doc, 'access', _370 => _370.liveStrokes, 'optionalAccess', _371 => _371[layerId]]), () => ( []))).filter((x) => !ids.includes(x.id)), this.strokeAreas(gone));
6314
+ }
6315
+ this.commit(ids.length === 1 ? "Remove stroke" : "Remove strokes", doc);
6316
+ return { removed: [...ids], bounds };
6317
+ }
6318
+ /** Moves a live stroke to `index` in its layer (0 = bottom) and redraws (one undo step). Returns the new order. */
6319
+ reorderStroke(id, index) {
6320
+ this.requireIdle("reordering strokes");
6321
+ const { layer, strokes, index: from } = this.findLiveStroke(id);
6322
+ if (!Number.isInteger(index) || index < 0 || index >= strokes.length) throw new PaintEditorError(`index ${index} is out of range; the layer has ${strokes.length} strokes, so use 0..${strokes.length - 1} (0 = bottom).`);
6323
+ const list = [...strokes];
6324
+ const [moved] = list.splice(from, 1);
6325
+ list.splice(index, 0, moved);
6326
+ if (from !== index) this.commitLiveStrokes("Reorder stroke", layer, list, this.strokeAreas([moved]));
6327
+ return list.map((x) => x.id);
6328
+ }
6329
+ /** Turns a stroke layer into a plain pixel layer with the same pixels (one undo step). */
6330
+ rasterizeLiveStrokes(layerId) {
6331
+ this.requireIdle("rasterizing");
6332
+ const { layer } = this.liveStrokeLayer(layerId, "rasterize");
6333
+ this.commit("Rasterize stroke layer", this.withPaintEvent({ ...this.doc, ...this.liveStrokesWithout([layer.id]) }, { kind: "live-strokes", layerId: layer.id, data: { rasterized: true } }));
6334
+ return this.describeLayer(this.getLayer(layer.id));
6335
+ }
6336
+ /** Sets a layer's live strokes and redraws it (replays). `null` makes it a plain pixel layer. */
6337
+ replaceLiveStrokes(layerId, strokes) {
6338
+ const l = this.wetLayer(layerId, "set the strokes of");
6339
+ if (strokes === null) {
6340
+ this.commit("Rasterize stroke layer", { ...this.doc, ...this.liveStrokesWithout([l.id]) });
6341
+ return;
6342
+ }
6343
+ this.commitLiveStrokes("Strokes", l, strokes.map((x) => ({ ...x, layerId: l.id })));
6344
+ }
6345
+ /**
6346
+ * The top-most stroke of a stroke layer (default the active layer) that painted the pixel at
6347
+ * (x, y) document pixels, or `null`. Strokes whose area holds the point are drawn alone, top
6348
+ * first, until one covers it.
6349
+ */
6350
+ liveStrokeAt(layerId, x, y) {
6351
+ if (!Number.isFinite(x) || !Number.isFinite(y)) throw new PaintEditorError("x and y must be numbers of document pixels.");
6352
+ const { layer, strokes } = this.liveStrokeLayer(layerId, "pick a stroke on");
6353
+ const plain = { ...layer, visible: true, locks: void 0, offset: { x: 0, y: 0 } };
6354
+ const doc = { ...this.doc, liveStrokes: void 0, stickers: void 0 };
6355
+ const px = Math.floor(x);
6356
+ const py = Math.floor(y);
6357
+ for (let i = strokes.length - 1; i >= 0; i--) {
6358
+ const area = this.strokeArea(strokes[i]);
6359
+ if (!area || px < area.x || py < area.y || px >= area.x + area.width || py >= area.y + area.height) continue;
6360
+ const drawn = this.drawLiveStroke(doc, plain, _chunk474IQG74cjs.emptySurface.call(void 0, 4), strokes[i]).surface;
6361
+ if (_chunk474IQG74cjs.readPixel.call(void 0, drawn, px, py)[3] > 24) return { ...strokes[i] };
6362
+ }
6363
+ return null;
6364
+ }
6365
+ /** Picks a stroke for the stroke panel and canvas (`null` for none). Editor state, not an undo step. */
6366
+ selectStroke(id) {
6367
+ if (id === this.selectedStroke) return;
6368
+ this.selectedStroke = id;
6369
+ this.emit([]);
6370
+ }
6371
+ /** Where a live stroke painted (document pixels), or `null`. */
6372
+ liveStrokeArea(id) {
6373
+ return this.strokeArea(this.findLiveStroke(id).strokes.find((s) => s.id === id));
6374
+ }
6375
+ /** Commits a layer's new stroke list with its redrawn pixels, recorded for replays (one undo step). */
6376
+ commitLiveStrokes(label, layer, list, dirty) {
6377
+ this.commit(label, this.withLiveStrokes(this.doc, layer, list, dirty));
6378
+ }
6379
+ withLiveStrokes(doc, layer, list, dirty) {
6380
+ if (_chunkHB4ICNNAcjs.isLayerLocked.call(void 0, layer, "pixels")) throw new PaintEditorError(`Layer "${layer.name}" has its pixels locked; unlock it (update_layer locks) first.`);
6381
+ const current = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, doc.layers, layer.id), 'optionalAccess', _372 => _372.layer]);
6382
+ const surface = this.drawLiveStrokes(_nullishCoalesce(current, () => ( layer)), list, dirty);
6383
+ const next = { ...doc, layers: _chunkHB4ICNNAcjs.mapLayer.call(void 0, doc.layers, layer.id, (l) => ({ ...l, surface, offset: { x: 0, y: 0 } })), liveStrokes: { ..._nullishCoalesce(doc.liveStrokes, () => ( {})), [layer.id]: [...list] } };
6384
+ return this.withPaintEvent(next, { kind: "live-strokes", layerId: layer.id, data: { strokes: list.map((x) => JSON.parse(JSON.stringify(x))) } });
6385
+ }
6386
+ /** Where a stroke painted (document pixels): as learnt when it was painted or drawn, else an estimate from its points and brush. */
6387
+ strokeArea(s) {
6388
+ return this.paintedBounds.has(s) ? this.paintedBounds.get(s) : liveStrokeBounds(s);
6389
+ }
6390
+ /** The areas strokes painted, or `null` when one is not known (a full redraw then). */
6391
+ strokeAreas(strokes) {
6392
+ const out = [];
6393
+ for (const s of strokes) {
6394
+ if (!this.paintedBounds.has(s)) return null;
6395
+ const b = this.paintedBounds.get(s);
6396
+ if (b) out.push(b);
6397
+ }
6398
+ return out;
6399
+ }
6400
+ /** Draws one stroke onto `surface` of a plain copy of the layer; returns the result and the area it painted. */
6401
+ drawLiveStroke(doc, plain, surface, st) {
6402
+ let out = surface;
6403
+ const host = {
6404
+ update: () => {
6405
+ },
6406
+ commit: (_s, drawn, cancelled) => {
6407
+ if (!cancelled && drawn) out = drawn;
6408
+ }
6409
+ };
6410
+ const paint = st.paint && this.liveEffectAt !== null && isLiveEffect(st) ? framePaint(st.paint, this.liveEffectAt) : st.paint;
6411
+ const init = { layerId: plain.id, color: st.color, brush: st.brush, seed: st.seed, copies: _nullishCoalesce(st.copies, () => ( [])), ...st.load ? { load: st.load } : {}, ...paint ? { paint } : {}, snapToPerspective: false, stencils: false, maskStencils: _nullishCoalesce(st.stencils, () => ( [])) };
6412
+ const session = this.sessionFor({ ...doc, layers: _chunkHB4ICNNAcjs.mapLayer.call(void 0, doc.layers, plain.id, () => ({ ...plain, surface })) }, init, host);
6413
+ session.push(st.points);
6414
+ const result = session.end();
6415
+ const had = this.paintedBounds.get(st);
6416
+ this.paintedBounds.set(st, had && result.bounds ? unionRect(had, result.bounds) : _nullishCoalesce(_nullishCoalesce(result.bounds, () => ( had)), () => ( null)));
6417
+ return { surface: out, bounds: result.bounds };
6418
+ }
6419
+ /**
6420
+ * A stroke layer's pixels drawn from its strokes, bottom first. With `dirty` (the areas the old
6421
+ * strokes that changed had painted), only the tiles there and under any stroke whose area isn't
6422
+ * known yet are drawn again, from the strokes that reach them (a pixel depends only on the
6423
+ * strokes over it); the other tiles are kept. Without it, or with `null`, every stroke is drawn.
6424
+ */
6425
+ drawLiveStrokes(layer, list, dirty) {
6426
+ const plain = { ...layer, visible: true, locks: void 0, offset: { x: 0, y: 0 } };
6427
+ const doc = { ...this.doc, liveStrokes: void 0, stickers: void 0 };
6428
+ const unknown = list.filter((s) => !this.paintedBounds.has(s));
6429
+ const partial = dirty && !layer.offset.x && !layer.offset.y && unknown.length <= 4;
6430
+ if (!partial) {
6431
+ let surface2 = _chunk474IQG74cjs.emptySurface.call(void 0, 4);
6432
+ for (const st of list) surface2 = this.drawLiveStroke(doc, plain, surface2, st).surface;
6433
+ return surface2;
6434
+ }
6435
+ const areas = [...dirty];
6436
+ for (const st of unknown) {
6437
+ const b = this.drawLiveStroke(doc, plain, _chunk474IQG74cjs.emptySurface.call(void 0, 4), st).bounds;
6438
+ if (b) areas.push(b);
6439
+ }
6440
+ const { width, height } = this.doc.canvas;
6441
+ const rects = areas.map((r) => clipRect(r, width, height)).filter((r) => r !== null);
6442
+ if (!rects.length) return layer.surface;
6443
+ const overlaps = (a, b) => a.x < b.x + b.width && b.x < a.x + a.width && a.y < b.y + b.height && b.y < a.y + a.height;
6444
+ let drawn = _chunk474IQG74cjs.emptySurface.call(void 0, 4);
6445
+ for (const st of list) {
6446
+ const b = this.paintedBounds.get(st);
6447
+ if (b && rects.some((r) => overlaps(r, b))) drawn = this.drawLiveStroke(doc, plain, drawn, st).surface;
6448
+ }
6449
+ let surface = layer.surface;
6450
+ for (const r of rects) surface = _chunk474IQG74cjs.writeRegion.call(void 0, surface, r, _chunk474IQG74cjs.readRegion.call(void 0, drawn, r));
6451
+ return surface;
6452
+ }
6453
+ // --- Live effect paint ------------------------------------------------------------
6454
+ /** The view's layers: stroke layers playing live effects at `effectTime`, the rest as they are. */
6455
+ animatedView() {
6456
+ if (!this.animatedLayers.size) return this.doc.layers;
6457
+ let layers = this.doc.layers;
6458
+ for (const [id, a] of this.animatedLayers) {
6459
+ const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, layers, id), 'optionalAccess', _373 => _373.layer]);
6460
+ if (isPixelLayer(l) && l.surface === a.base) layers = _chunkHB4ICNNAcjs.mapLayer.call(void 0, layers, id, () => ({ ...l, surface: a.surface }));
6461
+ }
6462
+ return layers;
6463
+ }
6464
+ /** Stroke layers with live effect strokes, and the distinct live sources (frame 0). */
6465
+ liveEffects(doc = this.doc) {
6466
+ const layers = [];
6467
+ const sources = /* @__PURE__ */ new Map();
6468
+ for (const [id, strokes] of Object.entries(_nullishCoalesce(doc.liveStrokes, () => ( {})))) {
6469
+ const layer = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, doc.layers, id), 'optionalAccess', _374 => _374.layer]);
6470
+ const live = strokes.filter(isLiveEffect);
6471
+ if (!isPixelLayer(layer) || !live.length) continue;
6472
+ layers.push({ layer, strokes });
6473
+ for (const st of live) sources.set(_chunk5BR7LC24cjs.effectPaintKey.call(void 0, st.paint), st.paint);
6474
+ }
6475
+ return { layers, sources: [...sources.values()] };
6476
+ }
6477
+ /** What plays: the live effect strokes and their layers, or `null` when nothing does. */
6478
+ getAnimation() {
6479
+ const { layers, sources } = this.liveEffects();
6480
+ if (!layers.length) return null;
6481
+ return { layerIds: layers.map((x) => x.layer.id), strokes: layers.reduce((n, x) => n + x.strokes.filter(isLiveEffect).length, 0), sources: sources.length };
6482
+ }
6483
+ /** Plays or pauses live effect paint, and caps its frame rate (1..60). Editor state, not an undo step. */
6484
+ setEffectPlayback(update) {
6485
+ if (update.playing !== void 0 && typeof update.playing !== "boolean") throw new PaintEditorError("playing must be true or false.");
6486
+ if (update.fps !== void 0 && !(Number.isInteger(update.fps) && update.fps >= 1 && update.fps <= 60)) throw new PaintEditorError(`fps ${update.fps} is out of range; use a whole number 1..60.`);
6487
+ this.effectPlaybackState = { ...this.effectPlaybackState, ...update };
6488
+ this.emit([]);
6489
+ return { ...this.effectPlaybackState, time: this.effectTime };
6490
+ }
6491
+ /** The surfaces of every stroke layer with live effects at `time` ms, drawn in full (frames rendered first). */
6492
+ async liveSurfacesAt(time) {
6493
+ const { layers, sources } = this.liveEffects();
6494
+ await this.prepareEffectPaints(sources.map((p) => framePaint(p, time)));
6495
+ const out = /* @__PURE__ */ new Map();
6496
+ this.liveEffectAt = time;
6497
+ try {
6498
+ for (const { layer, strokes } of layers) out.set(layer.id, this.drawLiveStrokes({ ...layer, offset: { x: 0, y: 0 } }, strokes));
6499
+ } finally {
6500
+ this.liveEffectAt = null;
6501
+ }
6502
+ return out;
6503
+ }
6504
+ /**
6505
+ * Shows the live effects at `time` ms (each live stroke's effect at its own time plus `time`):
6506
+ * renders their frames, then redraws only where live effect strokes paint. Nothing in the
6507
+ * document changes. Resolves `false` when the document changed while frames rendered (the next
6508
+ * frame catches up).
6509
+ */
6510
+ async showEffectTime(time) {
6511
+ this.checkTime(time);
6512
+ const { layers, sources } = this.liveEffects();
6513
+ const revision = this.docRevision;
6514
+ await this.prepareEffectPaints(sources.map((p) => framePaint(p, time)));
6515
+ if (revision !== this.docRevision || this.session) return false;
6516
+ const next = /* @__PURE__ */ new Map();
6517
+ const dirty = [];
6518
+ this.liveEffectAt = time;
6519
+ try {
6520
+ for (const { layer, strokes } of layers) {
6521
+ const shown = this.animatedLayers.get(layer.id);
6522
+ const from = shown && shown.base === layer.surface ? shown.surface : layer.surface;
6523
+ const areas = strokes.filter(isLiveEffect).flatMap((st) => [liveStrokeBounds(st), ...this.paintedBounds.get(st) ? [this.paintedBounds.get(st)] : []]);
6524
+ const surface = this.drawLiveStrokes({ ...layer, surface: from, offset: { x: 0, y: 0 } }, strokes, layer.offset.x || layer.offset.y ? null : areas);
6525
+ next.set(layer.id, { base: layer.surface, surface });
6526
+ dirty.push(...areas);
6527
+ }
6528
+ } finally {
6529
+ this.liveEffectAt = null;
6530
+ }
6531
+ this.animatedLayers = next;
6532
+ this.effectTime = time;
6533
+ this.emit(dirty);
6534
+ return true;
6535
+ }
6536
+ /**
6537
+ * Plays live effect paint in the view until `stop`: a frame per tick of `clock` (default
6538
+ * animation frames) at most `fps` a second, one render in flight. Idle while paused, while a
6539
+ * stroke is painted, or when nothing is live. The bindings start it while the editor is shown.
6540
+ */
6541
+ playEffects(clock = defaultEffectClock) {
6542
+ let stopped = false;
6543
+ let cancel = () => {
6544
+ };
6545
+ let last = -Infinity;
6546
+ let started = clock.now() - this.effectTime;
6547
+ const tick = () => {
6548
+ if (stopped) return;
6549
+ const now = clock.now();
6550
+ const { playing, fps } = this.effectPlaybackState;
6551
+ if (!playing) started = now - this.effectTime;
6552
+ if (!playing || this.session || now - last < 1e3 / fps - 1 || !this.liveEffects().layers.length) {
6553
+ cancel = clock.frame(tick);
6554
+ return;
6555
+ }
6556
+ last = now;
6557
+ this.showEffectTime(Math.max(0, now - started)).then(
6558
+ () => {
6559
+ if (!stopped) cancel = clock.frame(tick);
6560
+ },
6561
+ () => {
6562
+ if (!stopped) cancel = clock.frame(tick);
6563
+ }
6564
+ );
6565
+ };
6566
+ cancel = clock.frame(tick);
6567
+ return {
6568
+ stop() {
6569
+ stopped = true;
6570
+ cancel();
6571
+ }
6572
+ };
6573
+ }
6574
+ /**
6575
+ * Runs `fn` with the painting as it is `time` ms into its live effects (see `getAnimation`): for
6576
+ * exports at a moment. Nothing in the document changes.
6577
+ */
6578
+ async withEffectTime(time, fn) {
6579
+ this.checkTime(time);
6580
+ const surfaces = await this.liveSurfacesAt(time);
6581
+ const was = this.frameOverride;
6582
+ this.frameOverride = surfaces;
6583
+ try {
6584
+ return await fn();
6585
+ } finally {
6586
+ this.frameOverride = was;
6587
+ }
6588
+ }
6589
+ /** The painting composited `time` ms into its live effects, like `compositePixels`. Nothing in the document changes. */
6590
+ async renderAnimationFrame(time, options = {}) {
6591
+ return this.withEffectTime(time, () => this.compositePixels(options));
6592
+ }
6045
6593
  // --- Stencils --------------------------------------------------------------------
6046
- __init114() {this.stencilCache = null}
6047
- __init115() {this.nextStencilId = 1}
6594
+ __init123() {this.stencilCache = null}
6595
+ __init124() {this.nextStencilId = 1}
6048
6596
  /** The mask stencils' coverage over the canvas (cached), or `null` when none masks. */
6049
6597
  stencilSelection(stencils = this.guides.stencils) {
6050
6598
  const { width, height } = this.doc.canvas;
6051
6599
  const size = `${width}x${height}`;
6052
- if (_optionalChain([this, 'access', _321 => _321.stencilCache, 'optionalAccess', _322 => _322.key]) === stencils && this.stencilCache.size === size) return this.stencilCache.mask;
6600
+ if (_optionalChain([this, 'access', _375 => _375.stencilCache, 'optionalAccess', _376 => _376.key]) === stencils && this.stencilCache.size === size) return this.stencilCache.mask;
6053
6601
  const mask = stencilMask(stencils, width, height);
6054
6602
  this.stencilCache = { key: stencils, size, mask };
6055
6603
  return mask;
@@ -6111,7 +6659,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6111
6659
  makeSwatch(color, name) {
6112
6660
  const p = typeof color === "string" ? { type: "color", color: parseColor(color) } : this.readStrokePaint(color);
6113
6661
  const out = { color: _chunkPQ3H2AANcjs.paintSourceColor.call(void 0, p, { paint: this.color, secondary: this.secondaryColor }) };
6114
- if (_optionalChain([name, 'optionalAccess', _323 => _323.trim, 'call', _324 => _324()])) out.name = name.trim();
6662
+ if (_optionalChain([name, 'optionalAccess', _377 => _377.trim, 'call', _378 => _378()])) out.name = name.trim();
6115
6663
  if (p.type === "gradient") out.paint = p;
6116
6664
  return out;
6117
6665
  }
@@ -6163,7 +6711,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6163
6711
  * mix becomes the paint colour. Returns the mixed colour and the pile's bounds.
6164
6712
  */
6165
6713
  mixOnPad(paints, options = {}) {
6166
- const parsed = (_nullishCoalesce(paints, () => ( []))).map((p, i) => ({ color: parseColor(_optionalChain([p, 'optionalAccess', _325 => _325.color]), `paints[${i}].color`), parts: _optionalChain([p, 'optionalAccess', _326 => _326.parts]) }));
6714
+ const parsed = (_nullishCoalesce(paints, () => ( []))).map((p, i) => ({ color: parseColor(_optionalChain([p, 'optionalAccess', _379 => _379.color]), `paints[${i}].color`), parts: _optionalChain([p, 'optionalAccess', _380 => _380.parts]) }));
6167
6715
  let r;
6168
6716
  try {
6169
6717
  r = this.pad.mix(parsed, options);
@@ -6252,13 +6800,42 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6252
6800
  }
6253
6801
  /** A layer's tile surface (live while stroking), for thumbnails. */
6254
6802
  layerSurface(id) {
6255
- const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.currentLayers(), id), 'optionalAccess', _327 => _327.layer]);
6803
+ const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.currentLayers(), id), 'optionalAccess', _381 => _381.layer]);
6256
6804
  return isPixelLayer(l) ? l.surface : null;
6257
6805
  }
6258
6806
  }, _class10.__initStatic(), _class10);
6259
6807
  var MAX_IMAGE_SIDE = 8192;
6260
6808
  var movePoint = (p, dx, dy) => ({ x: p.x + dx, y: p.y + dy });
6261
6809
  var moveRect = (r, dx, dy) => ({ ...r, x: r.x + dx, y: r.y + dy });
6810
+ function isLiveEffect(s) {
6811
+ return _optionalChain([s, 'access', _382 => _382.paint, 'optionalAccess', _383 => _383.type]) === "effect" && s.paint.live === true;
6812
+ }
6813
+ function framePaint(p, t) {
6814
+ return { ...p, time: p.time + t };
6815
+ }
6816
+ var MAX_LIVE_EFFECT_SOURCES = 8;
6817
+ var defaultEffectClock = {
6818
+ now: () => typeof performance !== "undefined" ? performance.now() : Date.now(),
6819
+ frame: (cb) => {
6820
+ if (typeof requestAnimationFrame === "undefined") {
6821
+ const t = setTimeout(cb, 16);
6822
+ return () => clearTimeout(t);
6823
+ }
6824
+ const id = requestAnimationFrame(cb);
6825
+ return () => cancelAnimationFrame(id);
6826
+ }
6827
+ };
6828
+ function clipRect(r, width, height) {
6829
+ const x0 = Math.max(0, Math.floor(r.x));
6830
+ const y0 = Math.max(0, Math.floor(r.y));
6831
+ const x1 = Math.min(width, Math.ceil(r.x + r.width));
6832
+ const y1 = Math.min(height, Math.ceil(r.y + r.height));
6833
+ return x1 > x0 && y1 > y0 ? { x: x0, y: y0, width: x1 - x0, height: y1 - y0 } : null;
6834
+ }
6835
+ function withLiveStroke(doc, stroke) {
6836
+ const list = _optionalChain([doc, 'access', _384 => _384.liveStrokes, 'optionalAccess', _385 => _385[stroke.layerId]]);
6837
+ return list ? { ...doc, liveStrokes: { ...doc.liveStrokes, [stroke.layerId]: [...list, stroke] } } : doc;
6838
+ }
6262
6839
  function shiftDocument(doc, dx, dy) {
6263
6840
  if (!dx && !dy) return doc;
6264
6841
  let layers = doc.layers;
@@ -6275,11 +6852,18 @@ function shiftDocument(doc, dx, dy) {
6275
6852
  return s.stencils ? { ...moved, stencils: s.stencils.map((x) => translateStencil(x, dx, dy)) } : moved;
6276
6853
  })
6277
6854
  };
6855
+ const moveStroke = (s) => {
6856
+ const moved = varyStroke(s, m, {});
6857
+ return s.stencils ? { ...moved, stencils: s.stencils.map((x) => translateStencil(x, dx, dy)) } : moved;
6858
+ };
6278
6859
  if (doc.stickers) out.stickers = Object.fromEntries(Object.entries(doc.stickers).map(([id, list]) => [id, moveStickers(list)]));
6860
+ if (doc.liveStrokes) out.liveStrokes = Object.fromEntries(Object.entries(doc.liveStrokes).map(([id, list]) => [id, list.map(moveStroke)]));
6279
6861
  if (doc.paintEvents) {
6280
6862
  out.paintEvents = doc.paintEvents.map((e) => {
6281
- const list = e.kind === "stickers" ? _optionalChain([e, 'access', _328 => _328.data, 'optionalAccess', _329 => _329.stickers]) : void 0;
6282
- return list ? { ...e, data: { ...e.data, stickers: moveStickers(list) } } : e;
6863
+ const list = e.kind === "stickers" ? _optionalChain([e, 'access', _386 => _386.data, 'optionalAccess', _387 => _387.stickers]) : void 0;
6864
+ if (list) return { ...e, data: { ...e.data, stickers: moveStickers(list) } };
6865
+ const strokes = e.kind === "live-strokes" ? _optionalChain([e, 'access', _388 => _388.data, 'optionalAccess', _389 => _389.strokes]) : void 0;
6866
+ return strokes ? { ...e, data: { ...e.data, strokes: strokes.map(moveStroke) } } : e;
6283
6867
  });
6284
6868
  }
6285
6869
  if (doc.wetEvents) {
@@ -6360,8 +6944,8 @@ function strokeSchedule(points) {
6360
6944
  return total > 0 ? acc.map((d) => d / total) : points.map(() => 1);
6361
6945
  }
6362
6946
  function strokeDuration(points, max = 1800) {
6363
- const t0 = _optionalChain([points, 'access', _330 => _330[0], 'optionalAccess', _331 => _331.t]);
6364
- const t1 = _optionalChain([points, 'access', _332 => _332[points.length - 1], 'optionalAccess', _333 => _333.t]);
6947
+ const t0 = _optionalChain([points, 'access', _390 => _390[0], 'optionalAccess', _391 => _391.t]);
6948
+ const t1 = _optionalChain([points, 'access', _392 => _392[points.length - 1], 'optionalAccess', _393 => _393.t]);
6365
6949
  let ms;
6366
6950
  if (points.every((p) => Number.isFinite(p.t)) && t1 > t0) ms = t1 - t0;
6367
6951
  else {
@@ -6415,7 +6999,7 @@ var PaintDirector = class {
6415
6999
  }
6416
7000
  where(id) {
6417
7001
  const a = this.presence.get(id);
6418
- return { x: _nullishCoalesce(_optionalChain([a, 'optionalAccess', _334 => _334.x]), () => ( 0)), y: _nullishCoalesce(_optionalChain([a, 'optionalAccess', _335 => _335.y]), () => ( 0)) };
7002
+ return { x: _nullishCoalesce(_optionalChain([a, 'optionalAccess', _394 => _394.x]), () => ( 0)), y: _nullishCoalesce(_optionalChain([a, 'optionalAccess', _395 => _395.y]), () => ( 0)) };
6419
7003
  }
6420
7004
  /** Glides to `to`; an actor that just appeared starts there instead of the corner. */
6421
7005
  async glide(id, to, ms = 380) {
@@ -6462,8 +7046,8 @@ var PaintDirector = class {
6462
7046
  }
6463
7047
  case "replay": {
6464
7048
  await this.say(id, command.note, 500);
6465
- const source = c.getStrokes().filter((s) => _optionalChain([command, 'access', _336 => _336.replay, 'access', _337 => _337.strokeIds, 'optionalAccess', _338 => _338.includes, 'call', _339 => _339(s.id)]));
6466
- const first = _optionalChain([source, 'access', _340 => _340[0], 'optionalAccess', _341 => _341.points, 'access', _342 => _342[0]]);
7049
+ const source = c.getStrokes().filter((s) => _optionalChain([command, 'access', _396 => _396.replay, 'access', _397 => _397.strokeIds, 'optionalAccess', _398 => _398.includes, 'call', _399 => _399(s.id)]));
7050
+ const first = _optionalChain([source, 'access', _400 => _400[0], 'optionalAccess', _401 => _401.points, 'access', _402 => _402[0]]);
6467
7051
  if (first) await this.glide(id, first);
6468
7052
  const result = c.replayStrokes(command.replay);
6469
7053
  if (result.bounds) await this.glide(id, centre(result.bounds), 520);
@@ -6644,7 +7228,7 @@ function createPaintStickerTools(c) {
6644
7228
  items: {
6645
7229
  type: "object",
6646
7230
  properties: {
6647
- stamp: { type: "string", enum: [..._chunkNKTNLJUJcjs.STAMP_KINDS], description: "The stamp." },
7231
+ stamp: { type: "string", enum: [..._chunk5BR7LC24cjs.STAMP_KINDS], description: "The stamp." },
6648
7232
  x: { type: "number", description: "Centre x in document pixels." },
6649
7233
  y: { type: "number", description: "Centre y in document pixels." },
6650
7234
  size: { type: "number", minimum: 2, maximum: 2048, description: "Diameter in pixels (default 48)." },
@@ -6674,7 +7258,7 @@ function createPaintStickerTools(c) {
6674
7258
  properties: {
6675
7259
  layerId: LAYER_ID,
6676
7260
  id: { type: "string", description: "Sticker id from list_stickers." },
6677
- stamp: { type: "string", enum: [..._chunkNKTNLJUJcjs.STAMP_KINDS], description: "Another stamp." },
7261
+ stamp: { type: "string", enum: [..._chunk5BR7LC24cjs.STAMP_KINDS], description: "Another stamp." },
6678
7262
  x: { type: "number", description: "Centre x in document pixels." },
6679
7263
  y: { type: "number", description: "Centre y in document pixels." },
6680
7264
  size: { type: "number", minimum: 2, maximum: 2048, description: "Diameter in pixels." },
@@ -7552,7 +8136,7 @@ var BEHAVIOR = {
7552
8136
  type: "object",
7553
8137
  description: "Multi-coloured brush (the older form of paint, the brush's own gradient: tip is across-tip, dab is distance in dabs or random). Read back from paint when it has a multi-colour equivalent. Missing fields keep their current values.",
7554
8138
  properties: {
7555
- mode: { type: "string", enum: [..._chunkNKTNLJUJcjs.MULTI_COLOR_MODES], description: "off; tip: the colours lie side by side across the tip (stripes along the stroke, like a brush loaded with several paints); dab: the colour changes along the stroke." },
8139
+ mode: { type: "string", enum: [..._chunk5BR7LC24cjs.MULTI_COLOR_MODES], description: "off; tip: the colours lie side by side across the tip (stripes along the stroke, like a brush loaded with several paints); dab: the colour changes along the stroke." },
7556
8140
  colors: { type: "array", maxItems: 8, items: { ...COLOR3 }, description: "2..8 colours #rrggbb; fewer than 2 uses the paint colour and the secondary colour." },
7557
8141
  order: { type: "string", enum: ["sequence", "random"], description: "dab mode: sequence runs through the colours in order, blending between them; random picks one per run of dabs (seeded)." },
7558
8142
  length: { type: "integer", minimum: 1, maximum: 500, description: "dab mode: dabs per colour (default 12)." },
@@ -7593,10 +8177,10 @@ var WET_BRUSH = {
7593
8177
  properties: {
7594
8178
  tool: {
7595
8179
  type: "string",
7596
- enum: [..._chunkNKTNLJUJcjs.WET_TOOLS],
8180
+ enum: [..._chunk5BR7LC24cjs.WET_TOOLS],
7597
8181
  description: "paint: water and pigment (picks up and mixes wet paint by behavior.pickup / mix); water: clear water (with rewet it lifts dried paint); dry: a hair dryer, takes water; blow: pushes the water along the stroke; blend: evens out wet pigment; smudge: drags wet paint; blot: a tissue, takes wet paint up (erase wet)."
7598
8182
  },
7599
- medium: { type: "string", enum: [..._chunkNKTNLJUJcjs.WET_MEDIA], description: "The medium: watercolor, gouache or oil. A layer takes the medium's wet settings the first time it holds wet paint (oil: thick, barely flows, stays wet long and blends into the wet paint it meets)." },
8183
+ medium: { type: "string", enum: [..._chunk5BR7LC24cjs.WET_MEDIA], description: "The medium: watercolor, gouache or oil. A layer takes the medium's wet settings the first time it holds wet paint (oil: thick, barely flows, stays wet long and blends into the wet paint it meets)." },
7600
8184
  load: { type: "number", minimum: 0, maximum: 1, description: "Paint loading: 1 never runs out; lower, the brush runs dry along a long stroke (each stroke reloads)." },
7601
8185
  thinner: { type: "number", minimum: 0, maximum: 1, description: "Thinner (water or solvent): more transparent, runnier paint." },
7602
8186
  water: { type: "number", minimum: 0, maximum: 1, description: "Wetness: water laid per stroke, 0.2 a damp brush .. 1 a dripping one." },
@@ -7613,7 +8197,7 @@ var IMPASTO_BRUSH = {
7613
8197
  properties: {
7614
8198
  tool: {
7615
8199
  type: "string",
7616
- enum: [..._chunkNKTNLJUJcjs.IMPASTO_TOOLS],
8200
+ enum: [..._chunk5BR7LC24cjs.IMPASTO_TOOLS],
7617
8201
  description: "brush: thick paint with bristle ridges along the stroke; tube: blobs squeezed from the tube; knife: a palette knife that scrapes the relief flat and smears the paint; roller: a flat stippled band; gloop: a thick, glossy, rounded bead."
7618
8202
  },
7619
8203
  thickness: { type: "number", minimum: 0, maximum: 1, description: "How far the paint stands up." },
@@ -7629,8 +8213,8 @@ var PARTICLE_BRUSH = {
7629
8213
  type: "object",
7630
8214
  description: "Particle engine settings (procedural brushes). Missing fields keep their current values.",
7631
8215
  properties: {
7632
- kind: { type: "string", enum: [..._chunkNKTNLJUJcjs.PARTICLE_KINDS], description: "string: strands of masses on springs dragged behind the pen (hair, fur, wet string); spring: particles on springs to the pen, overshooting and wobbling; orbit: particles circling the pen (curls); flow: particles thrown off the pen into a swirling flow until they fade." },
7633
- render: { type: "string", enum: [..._chunkNKTNLJUJcjs.PARTICLE_RENDERS], description: "dot: a mark at each step; line: each particle draws a continuous line; sequence: beads spaced along each path." },
8216
+ kind: { type: "string", enum: [..._chunk5BR7LC24cjs.PARTICLE_KINDS], description: "string: strands of masses on springs dragged behind the pen (hair, fur, wet string); spring: particles on springs to the pen, overshooting and wobbling; orbit: particles circling the pen (curls); flow: particles thrown off the pen into a swirling flow until they fade." },
8217
+ render: { type: "string", enum: [..._chunk5BR7LC24cjs.PARTICLE_RENDERS], description: "dot: a mark at each step; line: each particle draws a continuous line; sequence: beads spaced along each path." },
7634
8218
  count: { type: "integer", minimum: 1, maximum: 64, description: "Particles (flow: how many are thrown per tip of travel)." },
7635
8219
  size: { type: "number", minimum: 0, maximum: 1, description: "Mark size as a fraction of the tip size." },
7636
8220
  spread: { type: "number", minimum: 0, maximum: 1, description: "How far apart the particles start, as a fraction of the tip size." },
@@ -7660,7 +8244,7 @@ var STICKER_BRUSH = {
7660
8244
  type: "object",
7661
8245
  description: "Sticker engine settings: stamps sprayed along the stroke as editable stickers. Missing fields keep their current values.",
7662
8246
  properties: {
7663
- stamps: { type: "array", minItems: 1, maxItems: 9, items: { type: "string", enum: [..._chunkNKTNLJUJcjs.STAMP_KINDS], description: "A stamp." }, description: "The stamps to spray (one picked at random per sticker)." },
8247
+ stamps: { type: "array", minItems: 1, maxItems: 9, items: { type: "string", enum: [..._chunk5BR7LC24cjs.STAMP_KINDS], description: "A stamp." }, description: "The stamps to spray (one picked at random per sticker)." },
7664
8248
  density: { type: "number", minimum: 0, maximum: 1, description: "How closely stickers follow each other: about density \xD7 4 stickers per tip diameter of travel (0.25 \u2248 one per diameter, 1 \u2248 four). Default 0.4." },
7665
8249
  sizeJitter: { type: "number", minimum: 0, maximum: 1, description: "Random size change." },
7666
8250
  rotationJitter: { type: "number", minimum: 0, maximum: 1, description: "Random turn as a fraction of \xB1180\xB0." },
@@ -7671,16 +8255,16 @@ var STICKER_BRUSH = {
7671
8255
  };
7672
8256
  var BRUSH_FIELDS = {
7673
8257
  name: { type: "string", minLength: 1, maxLength: 80, description: "Display name." },
7674
- category: { type: "string", enum: [..._chunkNKTNLJUJcjs.PAINT_BRUSH_CATEGORIES], description: "Library category." },
8258
+ category: { type: "string", enum: [..._chunk5BR7LC24cjs.PAINT_BRUSH_CATEGORIES], description: "Library category." },
7675
8259
  engine: {
7676
8260
  type: "string",
7677
- enum: [..._chunkNKTNLJUJcjs.PAINT_BRUSH_ENGINES],
8261
+ enum: [..._chunk5BR7LC24cjs.PAINT_BRUSH_ENGINES],
7678
8262
  description: "dab: paints dabs (with behavior.pickup or gradient paint it paints dab by dab, lifting and mixing paint); eraser: removes paint; mixer: the Mixer Brush (a reservoir plus picked-up paint); blender: mixes the colours under and near the tip; smudge: drags paint along the stroke; wet: watercolour, gouache, oil and the wet tools on a simulated wet layer (see wet); impasto: thick paint with relief (see impasto); particle: procedural particle brushes (see particle); bristle: a tuft of bristles that bend and splay (see bristle); sticker: sprays editable sticker stamps (see sticker)."
7679
8263
  },
7680
8264
  tip: TIP,
7681
8265
  grain: { type: "number", minimum: 0, maximum: 1, description: "Paper grain response: 0 ignores the grain, 1 catches only its peaks at light pressure (pencil, pastel, charcoal). Multiplied by the paper depth." },
7682
8266
  pressureSmoothing: { type: "number", minimum: 0, maximum: 0.95, description: "Pressure smoothing, separate from the tip position smoothing." },
7683
- rotation: { type: "string", enum: [..._chunkNKTNLJUJcjs.BRUSH_ROTATIONS], description: "What turns the tip: fixed (tip.angle), stroke (follows the direction of travel) or barrel (the pen barrel rotation, twist)." },
8267
+ rotation: { type: "string", enum: [..._chunk5BR7LC24cjs.BRUSH_ROTATIONS], description: "What turns the tip: fixed (tip.angle), stroke (follows the direction of travel) or barrel (the pen barrel rotation, twist)." },
7684
8268
  dynamics: DYNAMICS,
7685
8269
  mixer: MIXER,
7686
8270
  loadsColor: { type: "boolean", description: "Mixer engine: load the colour each stroke (false for a mixer that only moves paint)." },
@@ -7694,6 +8278,7 @@ var BRUSH_FIELDS = {
7694
8278
  sticker: STICKER_BRUSH,
7695
8279
  paint: BRUSH_PAINT
7696
8280
  };
8281
+ var TIME = { type: "number", minimum: 0, description: "Draw live effect paint (strokes on stroke layers painted with live effect paint) this many ms into its animation (default: as painted, its own time)." };
7697
8282
  var POINTS = {
7698
8283
  type: "array",
7699
8284
  minItems: 1,
@@ -7730,7 +8315,7 @@ function brushSummary(b) {
7730
8315
  size: b.tip.size,
7731
8316
  grain: b.grain,
7732
8317
  rotation: b.rotation,
7733
- wetCapable: _chunkNKTNLJUJcjs.isWetCapable.call(void 0, b),
8318
+ wetCapable: _chunk5BR7LC24cjs.isWetCapable.call(void 0, b),
7734
8319
  pickup: b.behavior.pickup,
7735
8320
  dirty: b.behavior.dirty,
7736
8321
  multicolor: b.behavior.multicolor.mode,
@@ -7838,8 +8423,8 @@ function createPaintEditorToolProvider(controller, options = {}) {
7838
8423
  inputSchema: EMPTY3,
7839
8424
  annotations: { title: "List papers", ...READ6 },
7840
8425
  handler: () => ({
7841
- papers: _chunkNKTNLJUJcjs.PAPER_LIBRARY.map((p) => ({ id: p.preset, name: p.name, family: p.family, pattern: p.pattern, color: p.color, depth: p.depth, absorption: p.absorption, permeability: p.permeability, roughness: p.roughness })),
7842
- families: [..._chunkNKTNLJUJcjs.PAPER_FAMILIES],
8426
+ papers: _chunk5BR7LC24cjs.PAPER_LIBRARY.map((p) => ({ id: p.preset, name: p.name, family: p.family, pattern: p.pattern, color: p.color, depth: p.depth, absorption: p.absorption, permeability: p.permeability, roughness: p.roughness })),
8427
+ families: [..._chunk5BR7LC24cjs.PAPER_FAMILIES],
7843
8428
  patterns: [..._chunkPQ3H2AANcjs.GRAIN_PATTERNS]
7844
8429
  })
7845
8430
  });
@@ -7899,12 +8484,12 @@ function createPaintEditorToolProvider(controller, options = {}) {
7899
8484
  add({
7900
8485
  name: "add_layer",
7901
8486
  title: "Add a layer",
7902
- description: "Adds an empty pixel layer (or a group) above the active layer, or above aboveId, or at the top of parentId; it becomes the active layer. One undo step. Returns the layer.",
8487
+ description: "Adds an empty pixel layer, a group, or a stroke layer (type strokes: its strokes stay editable with update_stroke and edit_stroke_points) above the active layer, or above aboveId, or at the top of parentId; it becomes the active layer. One undo step. Returns the layer (liveStrokes: its stroke count, for a stroke layer).",
7903
8488
  inputSchema: {
7904
8489
  type: "object",
7905
8490
  properties: {
7906
8491
  name: { type: "string", minLength: 1, description: 'Layer name (default "Layer n").' },
7907
- type: { type: "string", enum: ["pixel", "group"], description: "pixel (default) or group." },
8492
+ type: { type: "string", enum: ["pixel", "group", "strokes"], description: "pixel (default), group, or strokes: a live stroke layer whose pixels are drawn from its strokes, so each stays editable. It takes dab, particle, bristle and Mixer Brush strokes that pick up no paint." },
7908
8493
  aboveId: { type: "string", description: "Put it directly above this layer (default the active layer)." },
7909
8494
  parentId: { type: "string", description: "Put it at the top of this group instead." }
7910
8495
  },
@@ -8059,7 +8644,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
8059
8644
  annotations: { title: "List brushes", ...READ6 },
8060
8645
  handler: () => {
8061
8646
  const s = c.getState();
8062
- return { activeBrushId: s.brush.id, activeEdited: !s.brush.builtin && Boolean(_optionalChain([c, 'access', _343 => _343.getBrush, 'call', _344 => _344(s.brush.id), 'optionalAccess', _345 => _345.builtin])), brushes: s.brushes.map(brushSummary) };
8647
+ return { activeBrushId: s.brush.id, activeEdited: !s.brush.builtin && Boolean(_optionalChain([c, 'access', _403 => _403.getBrush, 'call', _404 => _404(s.brush.id), 'optionalAccess', _405 => _405.builtin])), brushes: s.brushes.map(brushSummary) };
8063
8648
  }
8064
8649
  });
8065
8650
  add({
@@ -8070,10 +8655,10 @@ function createPaintEditorToolProvider(controller, options = {}) {
8070
8655
  annotations: { title: "Get a brush", ...READ6 },
8071
8656
  handler: ({ id }) => {
8072
8657
  const st = c.getState();
8073
- if (id === void 0) return { ..._chunkNKTNLJUJcjs.brushToJSON.call(void 0, st.brush), strokePaint: _nullishCoalesce(st.brush.paint, () => ( st.paint)) };
8658
+ if (id === void 0) return { ..._chunk5BR7LC24cjs.brushToJSON.call(void 0, st.brush), strokePaint: _nullishCoalesce(st.brush.paint, () => ( st.paint)) };
8074
8659
  const b = c.getBrush(id);
8075
8660
  if (!b) throw new Error(`Unknown brush "${id}". Call list_brushes for the ids.`);
8076
- return { ..._chunkNKTNLJUJcjs.brushToJSON.call(void 0, b), builtin: Boolean(b.builtin), strokePaint: _nullishCoalesce(b.paint, () => ( st.paint)) };
8661
+ return { ..._chunk5BR7LC24cjs.brushToJSON.call(void 0, b), builtin: Boolean(b.builtin), strokePaint: _nullishCoalesce(b.paint, () => ( st.paint)) };
8077
8662
  }
8078
8663
  });
8079
8664
  add({
@@ -8082,7 +8667,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
8082
8667
  description: "Chooses the active brush (id) and / or changes its settings; strokes without a brush use it. Editor state, not an undo step. Changes stay on the active brush until save_brush stores them. Returns the active brush.",
8083
8668
  inputSchema: { type: "object", properties: { id: { type: "string", description: "Brush id from list_brushes to start from." }, ...BRUSH_FIELDS }, additionalProperties: false },
8084
8669
  annotations: { title: "Set the brush", ...SET4 },
8085
- handler: (a) => _chunkNKTNLJUJcjs.brushToJSON.call(void 0, c.setBrush(a))
8670
+ handler: (a) => _chunk5BR7LC24cjs.brushToJSON.call(void 0, c.setBrush(a))
8086
8671
  });
8087
8672
  add({
8088
8673
  name: "save_brush",
@@ -8098,7 +8683,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
8098
8683
  additionalProperties: false
8099
8684
  },
8100
8685
  annotations: { title: "Save a brush", ...SET4 },
8101
- handler: ({ id, name }) => _chunkNKTNLJUJcjs.brushToJSON.call(void 0, c.saveBrush({ id, name }))
8686
+ handler: ({ id, name }) => _chunk5BR7LC24cjs.brushToJSON.call(void 0, c.saveBrush({ id, name }))
8102
8687
  });
8103
8688
  add({
8104
8689
  name: "remove_brush",
@@ -8266,44 +8851,156 @@ function createPaintEditorToolProvider(controller, options = {}) {
8266
8851
  add({
8267
8852
  name: "list_strokes",
8268
8853
  title: "List recorded strokes",
8269
- description: "Lists the recorded strokes, oldest first (every stroke is kept as data): id, layer, brush id and name, colour, point count, bounds and seed. With ids, also the points. Use the ids with replay_strokes.",
8854
+ description: "Lists the recorded strokes, oldest first (every stroke is kept as data): id, layer, brush id and name, colour, point count, bounds, seed and whether it is live. With ids, also the points. Use the ids with replay_strokes. With live true, lists the strokes of stroke layers as they are now, bottom first: the ones update_stroke, edit_stroke_points, reorder_stroke and remove_strokes change.",
8270
8855
  inputSchema: {
8271
8856
  type: "object",
8272
8857
  properties: {
8273
8858
  ids: { type: "array", items: { type: "string", description: "Stroke id." }, description: "Only these strokes, with their points." },
8274
- last: { type: "integer", minimum: 1, maximum: 1e3, description: "Only the most recent n strokes (default 100)." }
8859
+ last: { type: "integer", minimum: 1, maximum: 1e3, description: "Only the most recent n strokes (default 100)." },
8860
+ live: { type: "boolean", description: "Only the editable strokes of stroke layers, as they are now, bottom first (default false: the whole recorded history)." },
8861
+ layerId: { type: "string", description: "Only strokes of this layer." }
8275
8862
  },
8276
8863
  additionalProperties: false
8277
8864
  },
8278
8865
  annotations: { title: "List recorded strokes", ...READ6 },
8279
8866
  coordinates: { out: ["strokes[].bounds", "strokes[].points[]"] },
8280
- handler: ({ ids, last }) => {
8281
- const all = c.getStrokes();
8867
+ handler: ({ ids, last, live, layerId }) => {
8868
+ if (layerId !== void 0 && !c.getLayer(layerId)) throw new Error(`Unknown layer "${layerId}". Call list_layers for the ids.`);
8869
+ const liveAll = Object.values(_nullishCoalesce(c.document.liveStrokes, () => ( {}))).flat();
8870
+ const liveIds = new Set(liveAll.map((s) => s.id));
8871
+ let all = live ? liveAll : c.getStrokes();
8872
+ if (layerId !== void 0) all = all.filter((s) => s.layerId === layerId);
8282
8873
  const picked = ids ? all.filter((s) => ids.includes(s.id)) : all.slice(-(_nullishCoalesce(last, () => ( 100))));
8283
8874
  if (ids) {
8284
8875
  for (const id of ids) if (!all.some((s) => s.id === id)) throw new Error(`Unknown stroke "${id}". Call list_strokes without ids for the ids.`);
8285
8876
  }
8286
- return {
8287
- total: all.length,
8288
- strokes: picked.map((s) => {
8289
- const xs = s.points.map((p) => p.x);
8290
- const ys = s.points.map((p) => p.y);
8291
- const pad = s.brush.tip.size / 2;
8292
- return {
8293
- id: s.id,
8294
- layerId: s.layerId,
8295
- brushId: s.brush.id,
8296
- brushName: s.brush.name,
8297
- color: s.color,
8298
- points: ids ? s.points : s.points.length,
8299
- seed: s.seed,
8300
- copies: _nullishCoalesce(_optionalChain([s, 'access', _346 => _346.copies, 'optionalAccess', _347 => _347.length]), () => ( 0)),
8301
- bounds: { x: Math.floor(Math.min(...xs) - pad), y: Math.floor(Math.min(...ys) - pad), width: Math.ceil(Math.max(...xs) - Math.min(...xs) + 2 * pad), height: Math.ceil(Math.max(...ys) - Math.min(...ys) + 2 * pad) }
8302
- };
8303
- })
8304
- };
8877
+ return { total: all.length, strokes: picked.map((s) => strokeSummary(s, Boolean(ids), live ? true : liveIds.has(s.id) && _optionalChain([c, 'access', _406 => _406.document, 'access', _407 => _407.liveStrokes, 'optionalAccess', _408 => _408[s.layerId], 'optionalAccess', _409 => _409.includes, 'call', _410 => _410(s)]) === true)) };
8878
+ }
8879
+ });
8880
+ add({
8881
+ name: "stroke_at",
8882
+ title: "Find the stroke at a point",
8883
+ description: "Returns the top-most stroke of a stroke layer that painted the pixel at a point (document pixels), or stroke null when none did, so you can pick one stroke among overlapping ones before update_stroke, edit_stroke_points or remove_strokes. Changes nothing.",
8884
+ inputSchema: {
8885
+ type: "object",
8886
+ properties: {
8887
+ layerId: { type: "string", description: "Stroke layer id from list_layers (default the active layer)." },
8888
+ x: { type: "number", description: "Point x in document pixels." },
8889
+ y: { type: "number", description: "Point y in document pixels." }
8890
+ },
8891
+ required: ["x", "y"],
8892
+ additionalProperties: false
8893
+ },
8894
+ annotations: { title: "Find the stroke at a point", ...READ6 },
8895
+ coordinates: { in: [""], out: ["stroke.bounds"] },
8896
+ handler: ({ layerId, x, y }) => {
8897
+ const s = c.liveStrokeAt(layerId, x, y);
8898
+ return { stroke: s ? strokeSummary(s, false, true) : null };
8899
+ }
8900
+ });
8901
+ add({
8902
+ name: "update_stroke",
8903
+ title: "Change a stroke",
8904
+ description: "Changes a stroke on a stroke layer after it was painted: another brush preset (brushId, keeping the stroke size), brush settings (as set_brush takes), colour (replaces gradient paint), paint, or seed, then redraws the layer exactly. Ids come from list_strokes {live: true}. Refuses brushes a stroke layer cannot redraw (wet, impasto, sticker, eraser, smudge, blender, pick-up). One undo step. Returns the stroke and the area that changed.",
8905
+ inputSchema: {
8906
+ type: "object",
8907
+ properties: {
8908
+ id: { type: "string", description: "Stroke id from list_strokes {live: true}." },
8909
+ brushId: { type: "string", description: "Brush preset id from list_brushes; the stroke keeps its size." },
8910
+ brush: { type: "object", properties: BRUSH_FIELDS, additionalProperties: false, description: "Brush settings to change on top of brushId or the stroke\u2019s brush; missing fields keep their values." },
8911
+ color: { type: "string", pattern: "^#[0-9a-fA-F]{6}$", description: "New colour #rrggbb (replaces gradient paint)." },
8912
+ paint: { anyOf: [_chunkPQ3H2AANcjs.PAINT_SOURCE_JSON_SCHEMA, { type: "null", description: "Back to the stroke\u2019s plain colour." }], description: "A colour or gradient paint for the stroke; null drops its gradient." },
8913
+ seed: { type: "integer", minimum: 0, description: "Another jitter seed: particles, scatter and texture fall differently." }
8914
+ },
8915
+ required: ["id"],
8916
+ additionalProperties: false
8917
+ },
8918
+ annotations: { title: "Change a stroke", ...WRITE5 },
8919
+ coordinates: { out: ["bounds", "stroke.bounds"] },
8920
+ handler: ({ id, ...update }) => {
8921
+ if (!Object.keys(update).length) throw new Error("Pass at least one change: brushId, brush, color, paint or seed.");
8922
+ const r = c.updateStroke(id, update);
8923
+ return { stroke: strokeSummary(r.stroke, false, true), bounds: r.bounds };
8924
+ }
8925
+ });
8926
+ add({
8927
+ name: "edit_stroke_points",
8928
+ title: "Move or reshape a stroke",
8929
+ description: "Replaces a stroke\u2019s points (points, document pixels), or moves, scales, rotates or flips it (transform, about its centre unless origin is given; the brush size scales with it), then redraws its stroke layer exactly. Pass one of points or transform. One undo step. Returns the stroke with its points and the area that changed.",
8930
+ inputSchema: {
8931
+ type: "object",
8932
+ properties: {
8933
+ id: { type: "string", description: "Stroke id from list_strokes {live: true}." },
8934
+ points: POINTS,
8935
+ transform: {
8936
+ type: "object",
8937
+ description: "A move, scale, rotation or flip of the whole stroke.",
8938
+ properties: {
8939
+ dx: { type: "number", description: "Move right by this many pixels (default 0)." },
8940
+ dy: { type: "number", description: "Move down by this many pixels (default 0)." },
8941
+ scale: { type: "number", exclusiveMinimum: 0, maximum: 20, description: "Scale about the origin; brush size scales too (default 1)." },
8942
+ rotate: { type: "number", minimum: -360, maximum: 360, description: "Rotate about the origin, degrees clockwise (default 0)." },
8943
+ flipX: { type: "boolean", description: "Mirror left to right about the origin (default false)." },
8944
+ origin: { type: "object", description: "Centre for scale, rotate and flip (default the centre of the stroke).", properties: { x: { type: "number", description: "Document x." }, y: { type: "number", description: "Document y." } }, required: ["x", "y"], additionalProperties: false }
8945
+ },
8946
+ additionalProperties: false
8947
+ }
8948
+ },
8949
+ required: ["id"],
8950
+ additionalProperties: false
8951
+ },
8952
+ annotations: { title: "Move or reshape a stroke", ...WRITE5 },
8953
+ coordinates: { in: ["points[]", "transform.origin"], out: ["bounds", "stroke.bounds", "stroke.points[]"] },
8954
+ handler: ({ id, points, transform }) => {
8955
+ const r = c.editStrokePoints(id, { points, transform });
8956
+ return { stroke: strokeSummary(r.stroke, true, true), bounds: r.bounds };
8305
8957
  }
8306
8958
  });
8959
+ add({
8960
+ name: "reorder_stroke",
8961
+ title: "Move a stroke up or down",
8962
+ description: "Moves a stroke to index in its stroke layer (0 = bottom, drawn first) and redraws the layer. One undo step. Returns the new order of ids, bottom first.",
8963
+ inputSchema: {
8964
+ type: "object",
8965
+ properties: { id: { type: "string", description: "Stroke id from list_strokes {live: true}." }, index: { type: "integer", minimum: 0, description: "New position, 0 = bottom." } },
8966
+ required: ["id", "index"],
8967
+ additionalProperties: false
8968
+ },
8969
+ annotations: { title: "Move a stroke up or down", ...WRITE5 },
8970
+ handler: ({ id, index }) => ({ order: c.reorderStroke(id, index) })
8971
+ });
8972
+ add({
8973
+ name: "remove_strokes",
8974
+ title: "Delete strokes",
8975
+ description: "Deletes strokes from their stroke layers (the eraser of a stroke layer) and redraws them. All checked first; one undo step. Returns the ids removed and the area that changed.",
8976
+ inputSchema: {
8977
+ type: "object",
8978
+ properties: { ids: { type: "array", minItems: 1, items: { type: "string", description: "Stroke id." }, description: "Stroke ids from list_strokes {live: true}." } },
8979
+ required: ["ids"],
8980
+ additionalProperties: false
8981
+ },
8982
+ annotations: { title: "Delete strokes", ...DESTRUCTIVE3 },
8983
+ coordinates: { out: ["bounds"] },
8984
+ handler: ({ ids }) => c.removeStrokes(ids)
8985
+ });
8986
+ add({
8987
+ name: "convert_to_stroke_layer",
8988
+ title: "Make a layer a stroke layer",
8989
+ description: 'Makes an empty pixel layer a stroke layer, so strokes painted on it stay editable. Refuses a layer with paint (add_layer {type: "strokes"} instead). One undo step. Returns the layer.',
8990
+ inputSchema: { type: "object", properties: { layerId: { type: "string", description: "Layer id from list_layers (default the active layer)." } }, additionalProperties: false },
8991
+ annotations: { title: "Make a layer a stroke layer", ...WRITE5 },
8992
+ coordinates: { out: ["bounds"] },
8993
+ handler: ({ layerId }) => c.convertToLiveStrokes(layerId)
8994
+ });
8995
+ add({
8996
+ name: "rasterize_stroke_layer",
8997
+ title: "Rasterize a stroke layer",
8998
+ description: "Turns a stroke layer into a plain pixel layer with the same pixels: its strokes stop being editable, and erasers, smudges and fills work on it. One undo step. Returns the layer.",
8999
+ inputSchema: { type: "object", properties: { layerId: { type: "string", description: "Stroke layer id from list_layers (default the active layer)." } }, additionalProperties: false },
9000
+ annotations: { title: "Rasterize a stroke layer", ...DESTRUCTIVE3 },
9001
+ coordinates: { out: ["bounds"] },
9002
+ handler: ({ layerId }) => c.rasterizeLiveStrokes(layerId)
9003
+ });
8307
9004
  add({
8308
9005
  name: "replay_strokes",
8309
9006
  title: "Replay strokes, varied",
@@ -8418,20 +9115,22 @@ function createPaintEditorToolProvider(controller, options = {}) {
8418
9115
  add({
8419
9116
  name: "export_image",
8420
9117
  title: "Export the painting",
8421
- description: "Exports the painting as PNG, JPEG or WebP (base64). JPEG always includes the paper; JPEG and WebP need a browser canvas. Changes nothing. Returns the data, its MIME type and size.",
9118
+ description: "Exports the painting as PNG, JPEG or WebP (base64). JPEG always includes the paper; JPEG and WebP need a browser canvas. With time, live effect paint is drawn at that moment (get_effect_playback / set_effect_playback). Changes nothing. Returns the data, its MIME type and size.",
8422
9119
  inputSchema: {
8423
9120
  type: "object",
8424
9121
  properties: {
8425
9122
  format: { type: "string", enum: ["image/png", "image/jpeg", "image/webp"], description: "File type (default image/png)." },
8426
9123
  quality: { type: "number", minimum: 0, maximum: 1, description: "JPEG / WebP quality 0..1 (default 0.92)." },
8427
9124
  scale: { type: "number", exclusiveMinimum: 0, maximum: 1, description: "Shrink by this factor (default 1)." },
8428
- paper: { type: "boolean", description: "Include the paper (default true)." }
9125
+ paper: { type: "boolean", description: "Include the paper (default true)." },
9126
+ time: TIME
8429
9127
  },
8430
9128
  additionalProperties: false
8431
9129
  },
8432
9130
  annotations: { title: "Export the painting", ...READ6 },
8433
- handler: async ({ format, quality, scale, paper }) => {
8434
- const bytes = await c.exportImage({ format, quality, scale, paper });
9131
+ handler: async ({ format, quality, scale, paper, time }) => {
9132
+ const run = () => c.exportImage({ format, quality, scale, paper });
9133
+ const bytes = time === void 0 ? await run() : await c.withEffectTime(time, run);
8435
9134
  return { mimeType: _nullishCoalesce(format, () => ( "image/png")), bytes: bytes.length, data: _chunkOLNIPTB3cjs.bytesToBase64.call(void 0, bytes) };
8436
9135
  }
8437
9136
  });
@@ -8439,10 +9138,10 @@ function createPaintEditorToolProvider(controller, options = {}) {
8439
9138
  name: "export_psd",
8440
9139
  title: "Export as PSD",
8441
9140
  description: 'Writes the painting as an 8-bit RGB PSD (PSB above 30000 px) through the shared PSD writer: every layer and group with its opacity, blend, mask and locks, the paper as a locked "Paper" pixel layer at the bottom, and the composite as the merged image. Changes nothing. Returns the file as base64, its size, whether it is a PSB, and warnings (pigment and painterly blend modes have no PSD equivalent: they are written as the nearest standard mode, see list_blend_modes).',
8442
- inputSchema: EMPTY3,
9141
+ inputSchema: { type: "object", properties: { time: TIME }, additionalProperties: false },
8443
9142
  annotations: { title: "Export as PSD", ...READ6 },
8444
- handler: () => {
8445
- const r = c.exportPsd();
9143
+ handler: async ({ time }) => {
9144
+ const r = time === void 0 ? c.exportPsd() : await c.withEffectTime(time, () => c.exportPsd());
8446
9145
  return { data: _chunkOLNIPTB3cjs.bytesToBase64.call(void 0, r.bytes), bytes: r.bytes.length, psb: r.psb, warnings: r.warnings };
8447
9146
  }
8448
9147
  });
@@ -8465,44 +9164,62 @@ function createPaintEditorToolProvider(controller, options = {}) {
8465
9164
  }
8466
9165
  function hostTool({ coordinates, ...tool }, { origin, onChange }) {
8467
9166
  if (!origin && !onChange) return tool;
8468
- const write = !_optionalChain([tool, 'access', _348 => _348.annotations, 'optionalAccess', _349 => _349.readOnlyHint]);
9167
+ const write = !_optionalChain([tool, 'access', _411 => _411.annotations, 'optionalAccess', _412 => _412.readOnlyHint]);
8469
9168
  const done = (result) => {
8470
- if (write) _optionalChain([onChange, 'optionalCall', _350 => _350({ tool: tool.name })]);
8471
- return origin && _optionalChain([coordinates, 'optionalAccess', _351 => _351.out]) ? fromDocument(result, coordinates.out, origin) : result;
9169
+ if (write) _optionalChain([onChange, 'optionalCall', _413 => _413({ tool: tool.name })]);
9170
+ return origin && _optionalChain([coordinates, 'optionalAccess', _414 => _414.out]) ? fromDocument(result, coordinates.out, origin) : result;
8472
9171
  };
8473
9172
  return {
8474
9173
  ...tool,
8475
9174
  ...origin ? { description: hostedText(_nullishCoalesce(tool.description, () => ( ""))), inputSchema: hostedSchema(tool.inputSchema) } : {},
8476
9175
  handler: (args, context) => {
8477
- const r = tool.handler(origin && _optionalChain([coordinates, 'optionalAccess', _352 => _352.in]) ? toDocument(args, coordinates.in, origin) : args, context);
9176
+ const r = tool.handler(origin && _optionalChain([coordinates, 'optionalAccess', _415 => _415.in]) ? toDocument(args, coordinates.in, origin) : args, context);
8478
9177
  return r instanceof Promise ? r.then(done) : done(r);
8479
9178
  }
8480
9179
  };
8481
9180
  }
9181
+ function strokeSummary(s, withPoints, live) {
9182
+ const xs = s.points.map((p) => p.x);
9183
+ const ys = s.points.map((p) => p.y);
9184
+ const pad = s.brush.tip.size / 2;
9185
+ return {
9186
+ id: s.id,
9187
+ layerId: s.layerId,
9188
+ brushId: s.brush.id,
9189
+ brushName: s.brush.name,
9190
+ color: s.color,
9191
+ points: withPoints ? s.points : s.points.length,
9192
+ seed: s.seed,
9193
+ copies: _nullishCoalesce(_optionalChain([s, 'access', _416 => _416.copies, 'optionalAccess', _417 => _417.length]), () => ( 0)),
9194
+ live,
9195
+ bounds: { x: Math.floor(Math.min(...xs) - pad), y: Math.floor(Math.min(...ys) - pad), width: Math.ceil(Math.max(...xs) - Math.min(...xs) + 2 * pad), height: Math.ceil(Math.max(...ys) - Math.min(...ys) + 2 * pad) }
9196
+ };
9197
+ }
8482
9198
 
8483
9199
  // src/paint-editor/ui.ts
8484
9200
  var PAINT_TOOLS = [
8485
9201
  { id: "brush", label: "Brush", key: "b" },
8486
9202
  { id: "eyedropper", label: "Eyedropper", key: "i" },
8487
9203
  { id: "sticker", label: "Stickers", key: "s" },
9204
+ { id: "stroke", label: "Select stroke", key: "v" },
8488
9205
  { id: "pan", label: "Pan", key: "h" },
8489
9206
  { id: "rotate", label: "Rotate view", key: "r" }
8490
9207
  ];
8491
9208
  var PaintGesture = (_class11 = class {
8492
- constructor(controller, box) {;_class11.prototype.__init116.call(this);_class11.prototype.__init117.call(this);_class11.prototype.__init118.call(this);_class11.prototype.__init119.call(this);_class11.prototype.__init120.call(this);_class11.prototype.__init121.call(this);
9209
+ constructor(controller, box) {;_class11.prototype.__init125.call(this);_class11.prototype.__init126.call(this);_class11.prototype.__init127.call(this);_class11.prototype.__init128.call(this);_class11.prototype.__init129.call(this);_class11.prototype.__init130.call(this);
8493
9210
  this.controller = controller;
8494
9211
  this.box = box;
8495
9212
  }
8496
9213
 
8497
9214
 
8498
- __init116() {this.tool = "brush"}
9215
+ __init125() {this.tool = "brush"}
8499
9216
  /** The last refusal (a hidden or locked layer), for the editor to show. */
8500
- __init117() {this.error = null}
9217
+ __init126() {this.error = null}
8501
9218
  /** The predicted tail of the current stroke in document pixels (draw it as a thin preview). */
8502
- __init118() {this.predicted = []}
8503
- __init119() {this.mode = null}
8504
- __init120() {this.penAt = null}
8505
- __init121() {this.penDown = false}
9219
+ __init127() {this.predicted = []}
9220
+ __init128() {this.mode = null}
9221
+ __init129() {this.penAt = null}
9222
+ __init130() {this.penDown = false}
8506
9223
  doc(e) {
8507
9224
  const b = this.box();
8508
9225
  const s = this.controller.getState();
@@ -8533,6 +9250,7 @@ var PaintGesture = (_class11 = class {
8533
9250
  this.mode = { kind: "rotate", pointerId: e.pointerId, angle: Math.atan2(e.clientY - b.top - b.height / 2, e.clientX - b.left - b.width / 2), rotation: this.controller.getState().view.rotation };
8534
9251
  return true;
8535
9252
  }
9253
+ if (tool === "stroke") return this.strokeDown(e);
8536
9254
  if (tool === "eyedropper" || e.altKey) {
8537
9255
  this.pick(e);
8538
9256
  return false;
@@ -8549,6 +9267,34 @@ var PaintGesture = (_class11 = class {
8549
9267
  return false;
8550
9268
  }
8551
9269
  }
9270
+ /**
9271
+ * The Select stroke tool, on a stroke layer: pressing a stroke picks it and dragging moves it
9272
+ * (one undo step at the drop); pressing where no stroke painted clears the pick.
9273
+ */
9274
+ strokeDown(e) {
9275
+ const p = this.doc(e);
9276
+ const s = this.controller.getState();
9277
+ const layerId = s.document.activeLayerId;
9278
+ if (!layerId || !_optionalChain([s, 'access', _418 => _418.document, 'access', _419 => _419.liveStrokes, 'optionalAccess', _420 => _420[layerId]])) {
9279
+ this.error = "Pick a stroke layer in the Layers panel (or add one) to select its strokes.";
9280
+ return false;
9281
+ }
9282
+ try {
9283
+ const hit = this.controller.liveStrokeAt(layerId, p.x, p.y);
9284
+ this.controller.selectStroke(_nullishCoalesce(_optionalChain([hit, 'optionalAccess', _421 => _421.id]), () => ( null)));
9285
+ if (!hit) return false;
9286
+ this.mode = { kind: "move-stroke", pointerId: e.pointerId, id: hit.id, from: p, to: p };
9287
+ return true;
9288
+ } catch (err) {
9289
+ this.error = err instanceof Error ? err.message : String(err);
9290
+ return false;
9291
+ }
9292
+ }
9293
+ /** While a stroke is dragged: how far it has moved (document pixels), to draw its outline there. */
9294
+ get strokeDrag() {
9295
+ const m = this.mode;
9296
+ return _optionalChain([m, 'optionalAccess', _422 => _422.kind]) === "move-stroke" ? { id: m.id, dx: m.to.x - m.from.x, dy: m.to.y - m.from.y } : null;
9297
+ }
8552
9298
  /**
8553
9299
  * The Stickers tool: pressing a sticker picks it and dragging moves it (one undo step at the
8554
9300
  * drop); pressing anywhere else places the active brush's first stamp there, at its tip size and
@@ -8594,6 +9340,10 @@ var PaintGesture = (_class11 = class {
8594
9340
  this.controller.setView({ rotation: r });
8595
9341
  return;
8596
9342
  }
9343
+ if (m.kind === "move-stroke") {
9344
+ m.to = this.doc(e);
9345
+ return;
9346
+ }
8597
9347
  if (m.kind === "sticker") {
8598
9348
  const p = this.doc(e);
8599
9349
  m.at = { x: round2(p.x + m.dx), y: round2(p.y + m.dy) };
@@ -8617,7 +9367,18 @@ var PaintGesture = (_class11 = class {
8617
9367
  if (!m || m.pointerId !== e.pointerId) return;
8618
9368
  this.mode = null;
8619
9369
  this.predicted = [];
8620
- if (m.kind === "stroke") _optionalChain([this, 'access', _353 => _353.controller, 'access', _354 => _354.activeStroke, 'optionalAccess', _355 => _355.end, 'call', _356 => _356()]);
9370
+ if (m.kind === "stroke") _optionalChain([this, 'access', _423 => _423.controller, 'access', _424 => _424.activeStroke, 'optionalAccess', _425 => _425.end, 'call', _426 => _426()]);
9371
+ if (m.kind === "move-stroke") {
9372
+ const dx = Math.round((this.doc(e).x - m.from.x) * 100) / 100;
9373
+ const dy = Math.round((this.doc(e).y - m.from.y) * 100) / 100;
9374
+ if (dx || dy) {
9375
+ try {
9376
+ this.controller.editStrokePoints(m.id, { transform: { dx, dy } });
9377
+ } catch (err) {
9378
+ this.error = err instanceof Error ? err.message : String(err);
9379
+ }
9380
+ }
9381
+ }
8621
9382
  if (m.kind === "sticker" && m.at) {
8622
9383
  try {
8623
9384
  this.controller.updateSticker(void 0, m.id, m.at);
@@ -8630,8 +9391,8 @@ var PaintGesture = (_class11 = class {
8630
9391
  cancel() {
8631
9392
  this.penDown = false;
8632
9393
  this.predicted = [];
8633
- if (_optionalChain([this, 'access', _357 => _357.mode, 'optionalAccess', _358 => _358.kind]) === "stroke") _optionalChain([this, 'access', _359 => _359.controller, 'access', _360 => _360.activeStroke, 'optionalAccess', _361 => _361.cancel, 'call', _362 => _362()]);
8634
- if (_optionalChain([this, 'access', _363 => _363.mode, 'optionalAccess', _364 => _364.kind]) === "sticker") this.controller.cancelStickerPreview();
9394
+ if (_optionalChain([this, 'access', _427 => _427.mode, 'optionalAccess', _428 => _428.kind]) === "stroke") _optionalChain([this, 'access', _429 => _429.controller, 'access', _430 => _430.activeStroke, 'optionalAccess', _431 => _431.cancel, 'call', _432 => _432()]);
9395
+ if (_optionalChain([this, 'access', _433 => _433.mode, 'optionalAccess', _434 => _434.kind]) === "sticker") this.controller.cancelStickerPreview();
8635
9396
  this.mode = null;
8636
9397
  }
8637
9398
  get active() {
@@ -8693,13 +9454,13 @@ function handlePaintShortcut(controller, gesture, e) {
8693
9454
  return false;
8694
9455
  }
8695
9456
  var PaintCanvasPresenter = (_class12 = class {
8696
- constructor(controller) {;_class12.prototype.__init122.call(this);_class12.prototype.__init123.call(this);_class12.prototype.__init124.call(this);
9457
+ constructor(controller) {;_class12.prototype.__init131.call(this);_class12.prototype.__init132.call(this);_class12.prototype.__init133.call(this);
8697
9458
  this.controller = controller;
8698
9459
  }
8699
9460
 
8700
- __init122() {this.uploaded = /* @__PURE__ */ new Map()}
8701
- __init123() {this.scratch = null}
8702
- __init124() {this.size = ""}
9461
+ __init131() {this.uploaded = /* @__PURE__ */ new Map()}
9462
+ __init132() {this.scratch = null}
9463
+ __init133() {this.size = ""}
8703
9464
  /** Draws what changed into `ctx` (a canvas the size of the document); returns the tiles drawn. */
8704
9465
  render(ctx) {
8705
9466
  const { width, height } = this.controller.document.canvas;
@@ -8726,12 +9487,25 @@ var PaintCanvasPresenter = (_class12 = class {
8726
9487
  return drawn;
8727
9488
  }
8728
9489
  }, _class12);
8729
- function guideOverlay(controller) {
9490
+ function guideOverlay(controller, drag) {
8730
9491
  const s = controller.getState();
8731
9492
  const size = s.document.canvas;
8732
9493
  const out = [];
8733
9494
  const layerId = s.document.activeLayerId;
8734
- const picked = layerId && s.selectedSticker ? _optionalChain([s, 'access', _365 => _365.document, 'access', _366 => _366.stickers, 'optionalAccess', _367 => _367[layerId], 'optionalAccess', _368 => _368.find, 'call', _369 => _369((x) => x.id === s.selectedSticker)]) : void 0;
9495
+ const strokePicked = layerId && s.selectedStroke ? _optionalChain([s, 'access', _435 => _435.document, 'access', _436 => _436.liveStrokes, 'optionalAccess', _437 => _437[layerId], 'optionalAccess', _438 => _438.some, 'call', _439 => _439((x) => x.id === s.selectedStroke)]) : false;
9496
+ const area = strokePicked ? controller.liveStrokeArea(s.selectedStroke) : null;
9497
+ if (area) {
9498
+ const dx = drag && drag.id === s.selectedStroke ? drag.dx : 0;
9499
+ const dy = drag && drag.id === s.selectedStroke ? drag.dy : 0;
9500
+ const box = [
9501
+ { x: area.x + dx, y: area.y + dy },
9502
+ { x: area.x + area.width + dx, y: area.y + dy },
9503
+ { x: area.x + area.width + dx, y: area.y + area.height + dy },
9504
+ { x: area.x + dx, y: area.y + area.height + dy }
9505
+ ];
9506
+ for (let i = 0; i < 4; i++) out.push({ from: box[i], to: box[(i + 1) % 4], kind: "stroke" });
9507
+ }
9508
+ const picked = layerId && s.selectedSticker ? _optionalChain([s, 'access', _440 => _440.document, 'access', _441 => _441.stickers, 'optionalAccess', _442 => _442[layerId], 'optionalAccess', _443 => _443.find, 'call', _444 => _444((x) => x.id === s.selectedSticker)]) : void 0;
8735
9509
  if (picked) {
8736
9510
  const r = picked.size / 2 + 2;
8737
9511
  const a = picked.rotation * Math.PI / 180;
@@ -8775,7 +9549,7 @@ var PAPER_SLIDERS = [
8775
9549
  ];
8776
9550
  function readPath(obj, path) {
8777
9551
  let v = obj;
8778
- for (const k of path.split(".")) v = _optionalChain([v, 'optionalAccess', _370 => _370[k]]);
9552
+ for (const k of path.split(".")) v = _optionalChain([v, 'optionalAccess', _445 => _445[k]]);
8779
9553
  return typeof v === "number" ? v : 0;
8780
9554
  }
8781
9555
  function patchPath(obj, path, value) {
@@ -8897,7 +9671,7 @@ function brushEngineControls(brush) {
8897
9671
  impastoTool: true
8898
9672
  };
8899
9673
  case "wet": {
8900
- const tool = _nullishCoalesce(_optionalChain([brush, 'access', _371 => _371.wet, 'optionalAccess', _372 => _372.tool]), () => ( "paint"));
9674
+ const tool = _nullishCoalesce(_optionalChain([brush, 'access', _446 => _446.wet, 'optionalAccess', _447 => _447.tool]), () => ( "paint"));
8901
9675
  const sliders = tool === "paint" ? [
8902
9676
  { path: "wet.water", label: "Water", min: 0, max: 1, step: 0.01 },
8903
9677
  { path: "wet.pigment", label: "Pigment", min: 0, max: 1, step: 0.01 },
@@ -9030,7 +9804,7 @@ function startWetClock(controller, frame, cancel) {
9030
9804
  id = raf(loop);
9031
9805
  return () => {
9032
9806
  stopped = true;
9033
- _optionalChain([caf, 'optionalCall', _373 => _373(id)]);
9807
+ _optionalChain([caf, 'optionalCall', _448 => _448(id)]);
9034
9808
  };
9035
9809
  }
9036
9810
  function drawWaterOverlay(controller, ctx) {
@@ -9060,6 +9834,11 @@ function toggleStamp(stamps, stamp) {
9060
9834
  const next = stamps.includes(stamp) ? stamps.filter((s) => s !== stamp) : [...stamps, stamp];
9061
9835
  return next.length ? next : [stamp];
9062
9836
  }
9837
+ var STROKE_SLIDERS = [
9838
+ { path: "tip.size", label: "Size", min: 1, max: 400, step: 1 },
9839
+ { path: "tip.opacity", label: "Opacity", min: 0, max: 1, step: 0.01 },
9840
+ { path: "tip.flow", label: "Flow", min: 0, max: 1, step: 0.01 }
9841
+ ];
9063
9842
  var STICKER_SLIDERS = [
9064
9843
  { path: "size", label: "Size", min: 2, max: 400, step: 1 },
9065
9844
  { path: "rotation", label: "Rotation", min: -180, max: 180, step: 1 },
@@ -9094,7 +9873,7 @@ function createPaintEffectsToolProvider(controller, options = {}) {
9094
9873
  };
9095
9874
  const paintInfo = () => {
9096
9875
  const p = controller.getEffectPaint();
9097
- return p ? [{ id: PAINT_TARGET, effect: p.effect, provider: p.provider, role: controller.getState().effectPaintRendering ? "effect paint (rendering)" : "effect paint", enabled: true, values: p.values, ...p.time ? { time: p.time } : {} }] : [];
9876
+ return p ? [{ id: PAINT_TARGET, effect: p.effect, provider: p.provider, role: controller.getState().effectPaintRendering ? "effect paint (rendering)" : "effect paint", enabled: true, values: p.values, ...p.time ? { time: p.time } : {}, ...p.map ? { map: p.map } : {}, ...p.live ? { live: true } : {} }] : [];
9098
9877
  };
9099
9878
  const layerInfo = (layerId) => {
9100
9879
  const s = controller.getEffectLayer(layerId);
@@ -9163,7 +9942,7 @@ function createPaintEffectsToolProvider(controller, options = {}) {
9163
9942
  if (target === PAINT_TARGET) {
9164
9943
  if (changes.opacity !== void 0) throw new Error("opacity does not apply to the effect paint: change the brush opacity (set_brush tip.opacity).");
9165
9944
  const p = controller.getEffectPaint();
9166
- await controller.setEffectPaint({ effect: p.effect, values: { ...p.values, ..._nullishCoalesce(changes.values, () => ( {})) }, time: p.time });
9945
+ await controller.setEffectPaint({ effect: p.effect, values: { ...p.values, ..._nullishCoalesce(changes.values, () => ( {})) }, time: p.time, map: p.map, live: p.live });
9167
9946
  const effects2 = paintInfo();
9168
9947
  return { target, effect: effects2[0], effects: effects2 };
9169
9948
  }
@@ -9222,13 +10001,25 @@ function createPaintEffectsToolProvider(controller, options = {}) {
9222
10001
  {
9223
10002
  name: "set_effect_paint",
9224
10003
  title: "Paint with an effect",
9225
- description: `Makes brushes paint with a provider generator, overlay or title effect (search_effects with use generator, overlay or title): it is rendered once at the canvas size, and each stroke then takes every pixel's colour from it, fixed to the canvas, as if painting a hidden picture into view (the effect's transparency paints nothing). Dab, particle and bristle brushes take the colour per pixel; mixer brushes and dab brushes that pick up paint take it per dab, at its centre. Wet, impasto and sticker brushes refuse it (paint the shape and use fill_layer_with_effect with lockTransparency instead); erasers, blenders and smudges ignore it. It replaces the paint colour or gradient until set_color or set_paint; a brush with its own gradient paints that instead. Strokes record it and replay exactly. Editor state, not an undo step. Returns the effect paint: { effect, provider, values (every control), time } (list_applied_effects target "paint" shows it; update_effect / remove_effect with target and effect "paint" change or clear it).`,
10004
+ description: `Makes brushes paint with a provider generator, overlay or title effect (search_effects with use generator, overlay or title): it is rendered once at the canvas size, and each stroke then takes every pixel's colour from it, fixed to the canvas, as if painting a hidden picture into view (the effect's transparency paints nothing). Dab, particle and bristle brushes take the colour per pixel; mixer brushes and dab brushes that pick up paint take it per dab, at its centre. Watercolour, gouache, oil and impasto brushes take it per pixel too, shifted by paint they pick up (in wet paint the pattern then flows with the water; impasto keeps its relief), and sprayed stickers take the colour at their centre. Erasers, blenders and smudges ignore it. It replaces the paint colour or gradient until set_color or set_paint; a brush with its own gradient paints that instead. Strokes record it and replay exactly. Editor state, not an undo step. Returns the effect paint: { effect, provider, values (every control), time } (list_applied_effects target "paint" shows it; update_effect / remove_effect with target and effect "paint" change or clear it).`,
9226
10005
  inputSchema: {
9227
10006
  type: "object",
9228
10007
  properties: {
9229
10008
  effect: ctx.effectIdSchema(["generator", "overlay", "title"]),
9230
10009
  values: ctx.valuesSchema("Control values over the effect's defaults"),
9231
- time: { type: "number", minimum: 0, description: "Which moment of an animated effect to paint with, in ms (default 0)." }
10010
+ time: { type: "number", minimum: 0, description: "Which moment of an animated effect to paint with, in ms (default 0)." },
10011
+ map: {
10012
+ type: "object",
10013
+ description: "How the effect lies on the paint: by canvas (default: fixed to the canvas, painting reveals a hidden picture) or by stroke (along each stroke: the effect is rendered as a strip length px long, and each pixel takes the colour at its distance along the stroke and its place across the tip).",
10014
+ properties: {
10015
+ by: { type: "string", enum: ["canvas", "stroke"], description: "canvas (default) or stroke." },
10016
+ length: { type: "number", minimum: 16, maximum: 4096, description: "by stroke: px of stroke per repeat of the effect (default 300)." },
10017
+ repeat: { type: "string", enum: ["repeat", "mirror", "clamp"], description: "by stroke: past the strip end start again (default), run back, or hold the last colour." }
10018
+ },
10019
+ required: ["by"],
10020
+ additionalProperties: false
10021
+ },
10022
+ live: { type: "boolean", description: "Live effect paint (default false): strokes on a stroke layer (add_layer type strokes) keep playing the effect, so the paint moves (set_effect_playback); elsewhere strokes bake the frame at time." }
9232
10023
  },
9233
10024
  required: ["effect"],
9234
10025
  additionalProperties: false
@@ -9236,9 +10027,24 @@ function createPaintEffectsToolProvider(controller, options = {}) {
9236
10027
  annotations: { title: "Paint with an effect", ...ctx.annotations.writeIdempotent },
9237
10028
  handler: async (args) => {
9238
10029
  ctx.requireProvider();
9239
- return { effectPaint: await controller.setEffectPaint({ effect: args.effect, values: values(args.values), time: args.time }) };
10030
+ return { effectPaint: await controller.setEffectPaint({ effect: args.effect, values: values(args.values), time: args.time, map: args.map, live: args.live }) };
9240
10031
  }
9241
10032
  },
10033
+ {
10034
+ name: "set_effect_playback",
10035
+ title: "Play or pause live effect paint",
10036
+ description: "Plays or pauses live effect paint (strokes painted on stroke layers with set_effect_paint live true) in the editor view, and caps its frame rate. Exports draw it at a chosen time with export_image / export_psd time. Editor state, not an undo step. Returns playing, fps, the time the view shows (ms) and what plays (animation: layer ids, live strokes, sources; null when nothing does).",
10037
+ inputSchema: {
10038
+ type: "object",
10039
+ properties: {
10040
+ playing: { type: "boolean", description: "Play (true, the default state) or pause at the current frame." },
10041
+ fps: { type: "integer", minimum: 1, maximum: 60, description: "Frame rate cap, frames a second (default 30); lower it on large canvases." }
10042
+ },
10043
+ additionalProperties: false
10044
+ },
10045
+ annotations: { title: "Play or pause live effect paint", ...ctx.annotations.writeIdempotent },
10046
+ handler: async (args) => ({ ...controller.setEffectPlayback({ playing: args.playing, fps: args.fps }), animation: controller.getAnimation() })
10047
+ },
9242
10048
  {
9243
10049
  name: "add_effect_layer",
9244
10050
  title: "Add an effect layer",
@@ -9634,4 +10440,14 @@ function createPaintEditorDocument(controller, options = {}) {
9634
10440
 
9635
10441
 
9636
10442
 
9637
- exports.BLEND_MENU_GROUPS = BLEND_MENU_GROUPS; exports.BLEND_MODES = _chunkOLNIPTB3cjs.BLEND_MODES; exports.BRUSH_ID = _chunkNKTNLJUJcjs.BRUSH_ID; exports.BRUSH_ROTATIONS = _chunkNKTNLJUJcjs.BRUSH_ROTATIONS; exports.BRUSH_SLIDERS = BRUSH_SLIDERS; exports.BUILTIN_BRUSHES = _chunkNKTNLJUJcjs.BUILTIN_BRUSHES; exports.BristleStroke = _chunkNKTNLJUJcjs.BristleStroke; exports.DEFAULT_BEHAVIOR = _chunkNKTNLJUJcjs.DEFAULT_BEHAVIOR; exports.DEFAULT_BRISTLE = _chunkNKTNLJUJcjs.DEFAULT_BRISTLE; exports.DEFAULT_BRUSH_ID = _chunkNKTNLJUJcjs.DEFAULT_BRUSH_ID; exports.DEFAULT_DPI = DEFAULT_DPI; exports.DEFAULT_DYNAMICS = _chunkNKTNLJUJcjs.DEFAULT_DYNAMICS; exports.DEFAULT_FLOW = _chunkXFYLA3ZOcjs.DEFAULT_FLOW; exports.DEFAULT_IMPASTO = _chunkNKTNLJUJcjs.DEFAULT_IMPASTO; exports.DEFAULT_LIGHTING = DEFAULT_LIGHTING; exports.DEFAULT_MULTI_COLOR = _chunkNKTNLJUJcjs.DEFAULT_MULTI_COLOR; exports.DEFAULT_PAINT_GRADIENT = _chunkPQ3H2AANcjs.DEFAULT_PAINT_GRADIENT; exports.DEFAULT_PAINT_GRADIENT_MAPPING = _chunkPQ3H2AANcjs.DEFAULT_PAINT_GRADIENT_MAPPING; exports.DEFAULT_PAPER = _chunkNKTNLJUJcjs.DEFAULT_PAPER; exports.DEFAULT_PARTICLE = _chunkNKTNLJUJcjs.DEFAULT_PARTICLE; exports.DEFAULT_STICKER = _chunkNKTNLJUJcjs.DEFAULT_STICKER; exports.DEFAULT_SWATCHES = DEFAULT_SWATCHES; exports.DEFAULT_VIEW = DEFAULT_VIEW; exports.DEFAULT_WET_BRUSH = _chunkNKTNLJUJcjs.DEFAULT_WET_BRUSH; exports.DEFAULT_WET_PARAMS = _chunkXFYLA3ZOcjs.DEFAULT_WET_PARAMS; exports.DryStroke = _chunkNKTNLJUJcjs.DryStroke; exports.GPU_LEAD = GPU_LEAD; exports.GRAIN_PATTERNS = _chunkPQ3H2AANcjs.GRAIN_PATTERNS; exports.GRAIN_TILE = _chunkPQ3H2AANcjs.GRAIN_TILE; exports.HARMONY_KINDS = _chunk7PT4RJL5cjs.HARMONY_KINDS; exports.IDENTITY_AFFINE = IDENTITY_AFFINE; exports.IDENTITY_COPY = _chunkNKTNLJUJcjs.IDENTITY_COPY; exports.IMPASTO_TOOLS = _chunkNKTNLJUJcjs.IMPASTO_TOOLS; exports.IMPASTO_TOOL_LABELS = IMPASTO_TOOL_LABELS; exports.LIGHT_SLIDERS = LIGHT_SLIDERS; exports.LitCache = LitCache; exports.MAX_BRUSH_SIZE = _chunkPQ3H2AANcjs.MAX_BRUSH_SIZE; exports.MAX_CANVAS_SIDE = MAX_CANVAS_SIDE; exports.MAX_COORDINATE = MAX_COORDINATE; exports.MAX_IMAGE_SIDE = MAX_IMAGE_SIDE; exports.MAX_ZOOM = MAX_ZOOM; exports.MIN_CANVAS_SIDE = MIN_CANVAS_SIDE; exports.MIN_ZOOM = MIN_ZOOM; exports.MIXING_PAD_HEIGHT = MIXING_PAD_HEIGHT; exports.MIXING_PAD_WIDTH = MIXING_PAD_WIDTH; exports.MULTI_COLOR_MODES = _chunkNKTNLJUJcjs.MULTI_COLOR_MODES; exports.MixingPad = MixingPad; exports.PAD_SAMPLE_SIZES = PAD_SAMPLE_SIZES; exports.PAINTERLY_BLEND_MODES = _chunkOLNIPTB3cjs.PAINTERLY_BLEND_MODES; exports.PAINT_BRUSH_CATEGORIES = _chunkNKTNLJUJcjs.PAINT_BRUSH_CATEGORIES; exports.PAINT_BRUSH_ENGINES = _chunkNKTNLJUJcjs.PAINT_BRUSH_ENGINES; exports.PAINT_COLOR_JSON_SCHEMA = _chunkPQ3H2AANcjs.PAINT_COLOR_JSON_SCHEMA; exports.PAINT_EFFECT_PLACES = PAINT_EFFECT_PLACES; exports.PAINT_FORMAT = PAINT_FORMAT; exports.PAINT_GRADIENT_FULL_SPEED = _chunkPQ3H2AANcjs.PAINT_GRADIENT_FULL_SPEED; exports.PAINT_GRADIENT_JSON_SCHEMA = _chunkPQ3H2AANcjs.PAINT_GRADIENT_JSON_SCHEMA; exports.PAINT_GRADIENT_KIT = _chunkPQ3H2AANcjs.PAINT_GRADIENT_KIT; exports.PAINT_GRADIENT_MAPPINGS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_MAPPINGS; exports.PAINT_GRADIENT_MAPPING_LABELS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_MAPPING_LABELS; exports.PAINT_GRADIENT_PRESETS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_PRESETS; exports.PAINT_GRADIENT_REPEATS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_REPEATS; exports.PAINT_GRADIENT_REPEAT_LABELS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_REPEAT_LABELS; exports.PAINT_GRADIENT_SPACES = _chunkPQ3H2AANcjs.PAINT_GRADIENT_SPACES; exports.PAINT_GRADIENT_SPACE_LABELS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_SPACE_LABELS; exports.PAINT_SCHEMA_VERSION = PAINT_SCHEMA_VERSION; exports.PAINT_SOURCE_JSON_SCHEMA = _chunkPQ3H2AANcjs.PAINT_SOURCE_JSON_SCHEMA; exports.PAINT_TARGET = PAINT_TARGET; exports.PAINT_TOOLS = PAINT_TOOLS; exports.PAPER_EFFECT_BLENDS = PAPER_EFFECT_BLENDS; exports.PAPER_EFFECT_NODE_ID = PAPER_EFFECT_NODE_ID; exports.PAPER_FAMILIES = _chunkNKTNLJUJcjs.PAPER_FAMILIES; exports.PAPER_LIBRARY = _chunkNKTNLJUJcjs.PAPER_LIBRARY; exports.PAPER_NODE_ID = PAPER_NODE_ID; exports.PAPER_SLIDERS = PAPER_SLIDERS; exports.PAPER_TARGET = PAPER_TARGET; exports.PARTICLE_KINDS = _chunkNKTNLJUJcjs.PARTICLE_KINDS; exports.PARTICLE_KIND_LABELS = PARTICLE_KIND_LABELS; exports.PARTICLE_RENDERS = _chunkNKTNLJUJcjs.PARTICLE_RENDERS; exports.PARTICLE_RENDER_LABELS = PARTICLE_RENDER_LABELS; exports.PaintCanvasPresenter = PaintCanvasPresenter; exports.PaintDabStroke = PaintDabStroke; exports.PaintDirector = PaintDirector; exports.PaintDocumentError = PaintDocumentError; exports.PaintEditorController = PaintEditorController; exports.PaintEditorError = PaintEditorError; exports.PaintEffectsRenderer = PaintEffectsRenderer; exports.PaintGesture = PaintGesture; exports.PaintGradientSampler = _chunkPQ3H2AANcjs.PaintGradientSampler; exports.PaintPresence = PaintPresence; exports.PaintSourceError = _chunkPQ3H2AANcjs.PaintSourceError; exports.PaintStrokeSession = PaintStrokeSession; exports.PaintWetLayers = PaintWetLayers; exports.PaperGrain = _chunkPQ3H2AANcjs.PaperGrain; exports.ParticleStroke = _chunkNKTNLJUJcjs.ParticleStroke; exports.PenDabWalker = _chunkNKTNLJUJcjs.PenDabWalker; exports.PenSmoother = _chunkNKTNLJUJcjs.PenSmoother; exports.PerspectiveSnapper = PerspectiveSnapper; exports.SNAP_DECIDE_DISTANCE = SNAP_DECIDE_DISTANCE; exports.STAMP_KINDS = _chunkNKTNLJUJcjs.STAMP_KINDS; exports.STAMP_LABELS = STAMP_LABELS; exports.STENCIL_KINDS = STENCIL_KINDS; exports.STENCIL_SHAPES = STENCIL_SHAPES; exports.STICKER_SLIDERS = STICKER_SLIDERS; exports.SYMMETRY_MODES = SYMMETRY_MODES; exports.StencilSnapper = StencilSnapper; exports.StickerSpray = _chunkNKTNLJUJcjs.StickerSpray; exports.TRACING_NODE_ID = TRACING_NODE_ID; exports.ThickStrokeDriver = ThickStrokeDriver; exports.TileableNoise = _chunkPQ3H2AANcjs.TileableNoise; exports.WET_FLOW_KINDS = _chunkXFYLA3ZOcjs.WET_FLOW_KINDS; exports.WET_GPU_MODES = WET_GPU_MODES; exports.WET_GPU_OPTIONS = WET_GPU_OPTIONS; exports.WET_MEDIA = _chunkNKTNLJUJcjs.WET_MEDIA; exports.WET_MEDIUM_LABELS = WET_MEDIUM_LABELS; exports.WET_MEDIUM_PARAMS = WET_MEDIUM_PARAMS; exports.WET_PARAM_KEYS = _chunkXFYLA3ZOcjs.WET_PARAM_KEYS; exports.WET_PARAM_SCHEMA = WET_PARAM_SCHEMA; exports.WET_PARAM_SLIDERS = WET_PARAM_SLIDERS; exports.WET_SPEED_SLIDER = WET_SPEED_SLIDER; exports.WET_TILT_SLIDERS = WET_TILT_SLIDERS; exports.WET_TOOLS = _chunkNKTNLJUJcjs.WET_TOOLS; exports.WET_TOOL_LABELS = WET_TOOL_LABELS; exports.WetStrokeDriver = WetStrokeDriver; exports.addPaintGradientStop = _chunkPQ3H2AANcjs.addPaintGradientStop; exports.affineCopy = affineCopy; exports.applyAffine = applyAffine; exports.brushEngineControls = brushEngineControls; exports.brushLoadColor = brushLoadColor; exports.brushLoadForStroke = brushLoadForStroke; exports.brushTakesPaint = _chunkNKTNLJUJcjs.brushTakesPaint; exports.brushToJSON = _chunkNKTNLJUJcjs.brushToJSON; exports.buildTimeLapse = buildTimeLapse; exports.checkPaintEffectPlace = checkPaintEffectPlace; exports.clonePaper = _chunkNKTNLJUJcjs.clonePaper; exports.colorHarmony = colorHarmony2; exports.composeAffine = composeAffine; exports.contactSheet = contactSheet; exports.createPaintCompositor = createPaintCompositor; exports.createPaintDocument = createPaintDocument; exports.createPaintEditorDocument = createPaintEditorDocument; exports.createPaintEditorToolProvider = createPaintEditorToolProvider; exports.createPaintEffectsToolProvider = createPaintEffectsToolProvider; exports.createPaintLayer = createPaintLayer; exports.createPaintReplayTools = createPaintReplayTools; exports.createPaintStickerTools = createPaintStickerTools; exports.createPaintThickTools = createPaintThickTools; exports.createPaintWetTools = createPaintWetTools; exports.decodeImageBlob = decodeImageBlob; exports.defaultGuides = defaultGuides; exports.defaultImageEncoder = defaultImageEncoder; exports.deserializePaintDocument = deserializePaintDocument; exports.deserializePaintDocumentAsync = deserializePaintDocumentAsync; exports.documentExtensions = documentExtensions; exports.documentToScreen = documentToScreen; exports.downloadBytes = downloadBytes; exports.downsampleRgba = downsampleRgba; exports.drawSticker = _chunkNKTNLJUJcjs.drawSticker; exports.drawWaterOverlay = drawWaterOverlay; exports.effectLayersToJSON = effectLayersToJSON; exports.effectPaintKey = _chunkNKTNLJUJcjs.effectPaintKey; exports.emptiedLayers = emptiedLayers; exports.emptyThickChannels = emptyThickChannels; exports.fitZoom = fitZoom; exports.grainCatch = _chunkPQ3H2AANcjs.grainCatch; exports.grainHeights = _chunkPQ3H2AANcjs.grainHeights; exports.guideOverlay = guideOverlay; exports.handlePaintShortcut = handlePaintShortcut; exports.hash2 = _chunkPQ3H2AANcjs.hash2; exports.hexToPaintRgb = _chunkPQ3H2AANcjs.hexToPaintRgb; exports.hexToRgb8 = _chunkNKTNLJUJcjs.hexToRgb8; exports.hslToRgb = hslToRgb; exports.hsvToRgb = hsvToRgb; exports.hueToWheel = _chunk7PT4RJL5cjs.hueToWheel; exports.invertAffine = invertAffine; exports.isBlendMode = _chunkOLNIPTB3cjs.isBlendMode; exports.isGradientPaint = _chunkPQ3H2AANcjs.isGradientPaint; exports.isPaintEffectPaint = _chunkNKTNLJUJcjs.isPaintEffectPaint; exports.isPainterlyBlendMode = _chunkOLNIPTB3cjs.isPainterlyBlendMode; exports.isPalmTouch = isPalmTouch; exports.isWetBrush = _chunkNKTNLJUJcjs.isWetBrush; exports.isWetCapable = _chunkNKTNLJUJcjs.isWetCapable; exports.lightTile = lightTile; exports.lightingKey = lightingKey; exports.mapPenPoint = mapPenPoint; exports.mix32 = _chunkPQ3H2AANcjs.mix32; exports.mixPixels = mixPixels; exports.mixerLoadForStroke = mixerLoadForStroke; exports.mixerTip = _chunkNKTNLJUJcjs.mixerTip; exports.multiColorForDab = multiColorForDab; exports.multiColorGradient = multiColorGradient; exports.multiColorPalette = multiColorPalette; exports.multiColorSliders = multiColorSliders; exports.multiColorToPaint = _chunkNKTNLJUJcjs.multiColorToPaint; exports.newPaintLayerId = newPaintLayerId; exports.nextPaperEffectVersion = nextPaperEffectVersion; exports.normalizeBehavior = _chunkNKTNLJUJcjs.normalizeBehavior; exports.normalizeBristle = _chunkNKTNLJUJcjs.normalizeBristle; exports.normalizeBrushStroke = normalizeBrushStroke; exports.normalizeCanvas = normalizeCanvas; exports.normalizeDynamics = _chunkNKTNLJUJcjs.normalizeDynamics; exports.normalizeFlow = _chunkXFYLA3ZOcjs.normalizeFlow; exports.normalizeHexColor = _chunkPQ3H2AANcjs.normalizeHexColor; exports.normalizeImpasto = _chunkNKTNLJUJcjs.normalizeImpasto; exports.normalizeLighting = normalizeLighting; exports.normalizeMultiColor = _chunkNKTNLJUJcjs.normalizeMultiColor; exports.normalizePaintBrush = _chunkNKTNLJUJcjs.normalizePaintBrush; exports.normalizePaper = _chunkNKTNLJUJcjs.normalizePaper; exports.normalizeParticle = _chunkNKTNLJUJcjs.normalizeParticle; exports.normalizePerspective = normalizePerspective; exports.normalizeRotation = normalizeRotation; exports.normalizeStencil = normalizeStencil; exports.normalizeStickerSettings = _chunkNKTNLJUJcjs.normalizeStickerSettings; exports.normalizeStickerUpdate = _chunkNKTNLJUJcjs.normalizeStickerUpdate; exports.normalizeSymmetry = normalizeSymmetry; exports.normalizeView = normalizeView; exports.normalizeWetBrush = _chunkNKTNLJUJcjs.normalizeWetBrush; exports.normalizeWetParams = _chunkXFYLA3ZOcjs.normalizeWetParams; exports.overPixels = overPixels; exports.padSampleRadius = padSampleRadius; exports.paintGradientMappingFor = _chunkPQ3H2AANcjs.paintGradientMappingFor; exports.paintPlaybackPanel = paintPlaybackPanel; exports.paintSourceColor = _chunkPQ3H2AANcjs.paintSourceColor; exports.paintSourceCss = _chunkPQ3H2AANcjs.paintSourceCss; exports.paintToMultiColor = _chunkNKTNLJUJcjs.paintToMultiColor; exports.paperEffectFromJSON = paperEffectFromJSON; exports.paperEffectToJSON = paperEffectToJSON; exports.paperGrain = paperGrain; exports.paperKey = paperKey; exports.paperNode = paperNode; exports.paperPreset = _chunkNKTNLJUJcjs.paperPreset; exports.parseBrushLibrary = _chunkNKTNLJUJcjs.parseBrushLibrary; exports.parseHexColor = parseHexColor; exports.parsePaintSource = _chunkPQ3H2AANcjs.parsePaintSource; exports.patchBrushValue = patchBrushValue; exports.patchPath = patchPath; exports.penOrientation = _chunkNKTNLJUJcjs.penOrientation; exports.perspectiveDirections = perspectiveDirections; exports.perspectiveGuideLines = perspectiveGuideLines; exports.planPaintToolPlayback = planPaintToolPlayback; exports.pointerToPenPoint = pointerToPenPoint; exports.quickStencil = quickStencil; exports.rand01 = _chunkPQ3H2AANcjs.rand01; exports.randSigned = _chunkPQ3H2AANcjs.randSigned; exports.readPaintEffectPaint = _chunkNKTNLJUJcjs.readPaintEffectPaint; exports.readPaintEffectSource = readPaintEffectSource; exports.readPaintSource = _chunkPQ3H2AANcjs.readPaintSource; exports.readPath = readPath; exports.readSwatch = readSwatch; exports.reliefInfo = reliefInfo; exports.removePaintGradientStop = _chunkPQ3H2AANcjs.removePaintGradientStop; exports.renderBrushThumbnail = renderBrushThumbnail; exports.renderColorWheel = renderColorWheel; exports.renderPaper = _chunkNKTNLJUJcjs.renderPaper; exports.renderStickers = _chunkNKTNLJUJcjs.renderStickers; exports.replayFrame = replayFrame; exports.replayItems = replayItems; exports.replayPainting = replayPainting; exports.resolvePaintGradient = _chunkPQ3H2AANcjs.resolvePaintGradient; exports.reversePaintGradient = _chunkPQ3H2AANcjs.reversePaintGradient; exports.rgbToHexColor = rgbToHexColor; exports.rgbToHsl = rgbToHsl; exports.rgbToHsv = rgbToHsv; exports.sampleEffectAt = _chunkNKTNLJUJcjs.sampleEffectAt; exports.sampleEffectField = _chunkNKTNLJUJcjs.sampleEffectField; exports.samplePressure = samplePressure; exports.scalePaintGradient = _chunkPQ3H2AANcjs.scalePaintGradient; exports.scaleStencil = scaleStencil; exports.screenToDocument = screenToDocument; exports.seedFromString = _chunkPQ3H2AANcjs.seedFromString; exports.serializePaintDocument = serializePaintDocument; exports.serializePaintDocumentExternal = serializePaintDocumentExternal; exports.solidPaint = _chunkPQ3H2AANcjs.solidPaint; exports.stampCoverage = _chunkNKTNLJUJcjs.stampCoverage; exports.standardBlendMode = _chunkOLNIPTB3cjs.standardBlendMode; exports.startWetClock = startWetClock; exports.stencilMask = stencilMask; exports.stencilOutline = stencilOutline; exports.stickerAt = _chunkNKTNLJUJcjs.stickerAt; exports.stickerRect = _chunkNKTNLJUJcjs.stickerRect; exports.stickerSurface = _chunkNKTNLJUJcjs.stickerSurface; exports.strokeBounds = strokeBounds; exports.strokeDuration = strokeDuration; exports.strokeSchedule = strokeSchedule; exports.sweepPath = sweepPath; exports.symmetryAxisAngles = symmetryAxisAngles; exports.symmetryAxisSegments = symmetryAxisSegments; exports.symmetryCopies = symmetryCopies; exports.thickFromJSON = thickFromJSON; exports.thickToJSON = thickToJSON; exports.thumbnailStroke = thumbnailStroke; exports.tiltToOrientation = _chunkNKTNLJUJcjs.tiltToOrientation; exports.timeLapseVideo = timeLapseVideo; exports.timeLapseZip = timeLapseZip; exports.toStrokePoint = _chunkNKTNLJUJcjs.toStrokePoint; exports.toggleStamp = toggleStamp; exports.translateStencil = translateStencil; exports.updatePaintGradientStop = _chunkPQ3H2AANcjs.updatePaintGradientStop; exports.usesPaintExchange = _chunkNKTNLJUJcjs.usesPaintExchange; exports.variationAffine = variationAffine; exports.varyStroke = varyStroke; exports.viewCssTransform = viewCssTransform; exports.viewMatrix = viewMatrix; exports.viewPenToDocument = viewPenToDocument; exports.wetEngineSummary = wetEngineSummary; exports.wetPaperInput = wetPaperInput; exports.wetPigmentOf = wetPigmentOf; exports.wetSupport = _chunkXFYLA3ZOcjs.wetSupport; exports.wheelAngle = wheelAngle; exports.wheelPick = wheelPick; exports.wheelPoint = wheelPoint; exports.wheelToHue = _chunk7PT4RJL5cjs.wheelToHue; exports.withPaintPlayback = withPaintPlayback;
10443
+
10444
+
10445
+
10446
+
10447
+
10448
+
10449
+
10450
+
10451
+
10452
+
10453
+ exports.BLEND_MENU_GROUPS = BLEND_MENU_GROUPS; exports.BLEND_MODES = _chunkOLNIPTB3cjs.BLEND_MODES; exports.BRUSH_ID = _chunk5BR7LC24cjs.BRUSH_ID; exports.BRUSH_ROTATIONS = _chunk5BR7LC24cjs.BRUSH_ROTATIONS; exports.BRUSH_SLIDERS = BRUSH_SLIDERS; exports.BUILTIN_BRUSHES = _chunk5BR7LC24cjs.BUILTIN_BRUSHES; exports.BristleStroke = _chunk5BR7LC24cjs.BristleStroke; exports.DEFAULT_BEHAVIOR = _chunk5BR7LC24cjs.DEFAULT_BEHAVIOR; exports.DEFAULT_BRISTLE = _chunk5BR7LC24cjs.DEFAULT_BRISTLE; exports.DEFAULT_BRUSH_ID = _chunk5BR7LC24cjs.DEFAULT_BRUSH_ID; exports.DEFAULT_DPI = DEFAULT_DPI; exports.DEFAULT_DYNAMICS = _chunk5BR7LC24cjs.DEFAULT_DYNAMICS; exports.DEFAULT_FLOW = _chunkXFYLA3ZOcjs.DEFAULT_FLOW; exports.DEFAULT_IMPASTO = _chunk5BR7LC24cjs.DEFAULT_IMPASTO; exports.DEFAULT_LIGHTING = DEFAULT_LIGHTING; exports.DEFAULT_MULTI_COLOR = _chunk5BR7LC24cjs.DEFAULT_MULTI_COLOR; exports.DEFAULT_PAINT_GRADIENT = _chunkPQ3H2AANcjs.DEFAULT_PAINT_GRADIENT; exports.DEFAULT_PAINT_GRADIENT_MAPPING = _chunkPQ3H2AANcjs.DEFAULT_PAINT_GRADIENT_MAPPING; exports.DEFAULT_PAPER = _chunk5BR7LC24cjs.DEFAULT_PAPER; exports.DEFAULT_PARTICLE = _chunk5BR7LC24cjs.DEFAULT_PARTICLE; exports.DEFAULT_STICKER = _chunk5BR7LC24cjs.DEFAULT_STICKER; exports.DEFAULT_SWATCHES = DEFAULT_SWATCHES; exports.DEFAULT_VIEW = DEFAULT_VIEW; exports.DEFAULT_WET_BRUSH = _chunk5BR7LC24cjs.DEFAULT_WET_BRUSH; exports.DEFAULT_WET_PARAMS = _chunkXFYLA3ZOcjs.DEFAULT_WET_PARAMS; exports.DryStroke = _chunk5BR7LC24cjs.DryStroke; exports.EFFECT_MAP_REPEATS = _chunk5BR7LC24cjs.EFFECT_MAP_REPEATS; exports.EFFECT_STRIP_HEIGHT = _chunk5BR7LC24cjs.EFFECT_STRIP_HEIGHT; exports.GPU_LEAD = GPU_LEAD; exports.GRAIN_PATTERNS = _chunkPQ3H2AANcjs.GRAIN_PATTERNS; exports.GRAIN_TILE = _chunkPQ3H2AANcjs.GRAIN_TILE; exports.HARMONY_KINDS = _chunk7PT4RJL5cjs.HARMONY_KINDS; exports.IDENTITY_AFFINE = IDENTITY_AFFINE; exports.IDENTITY_COPY = _chunk5BR7LC24cjs.IDENTITY_COPY; exports.IMPASTO_TOOLS = _chunk5BR7LC24cjs.IMPASTO_TOOLS; exports.IMPASTO_TOOL_LABELS = IMPASTO_TOOL_LABELS; exports.LIGHT_SLIDERS = LIGHT_SLIDERS; exports.LIVE_STROKE_ENGINES = LIVE_STROKE_ENGINES; exports.LitCache = LitCache; exports.MAX_BRUSH_SIZE = _chunkPQ3H2AANcjs.MAX_BRUSH_SIZE; exports.MAX_CANVAS_SIDE = MAX_CANVAS_SIDE; exports.MAX_COORDINATE = MAX_COORDINATE; exports.MAX_IMAGE_SIDE = MAX_IMAGE_SIDE; exports.MAX_LIVE_EFFECT_SOURCES = MAX_LIVE_EFFECT_SOURCES; exports.MAX_ZOOM = MAX_ZOOM; exports.MIN_CANVAS_SIDE = MIN_CANVAS_SIDE; exports.MIN_ZOOM = MIN_ZOOM; exports.MIXING_PAD_HEIGHT = MIXING_PAD_HEIGHT; exports.MIXING_PAD_WIDTH = MIXING_PAD_WIDTH; exports.MULTI_COLOR_MODES = _chunk5BR7LC24cjs.MULTI_COLOR_MODES; exports.MixingPad = MixingPad; exports.PAD_SAMPLE_SIZES = PAD_SAMPLE_SIZES; exports.PAINTERLY_BLEND_MODES = _chunkOLNIPTB3cjs.PAINTERLY_BLEND_MODES; exports.PAINT_BRUSH_CATEGORIES = _chunk5BR7LC24cjs.PAINT_BRUSH_CATEGORIES; exports.PAINT_BRUSH_ENGINES = _chunk5BR7LC24cjs.PAINT_BRUSH_ENGINES; exports.PAINT_COLOR_JSON_SCHEMA = _chunkPQ3H2AANcjs.PAINT_COLOR_JSON_SCHEMA; exports.PAINT_EFFECT_PLACES = PAINT_EFFECT_PLACES; exports.PAINT_FORMAT = PAINT_FORMAT; exports.PAINT_GRADIENT_FULL_SPEED = _chunkPQ3H2AANcjs.PAINT_GRADIENT_FULL_SPEED; exports.PAINT_GRADIENT_JSON_SCHEMA = _chunkPQ3H2AANcjs.PAINT_GRADIENT_JSON_SCHEMA; exports.PAINT_GRADIENT_KIT = _chunkPQ3H2AANcjs.PAINT_GRADIENT_KIT; exports.PAINT_GRADIENT_MAPPINGS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_MAPPINGS; exports.PAINT_GRADIENT_MAPPING_LABELS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_MAPPING_LABELS; exports.PAINT_GRADIENT_PRESETS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_PRESETS; exports.PAINT_GRADIENT_REPEATS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_REPEATS; exports.PAINT_GRADIENT_REPEAT_LABELS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_REPEAT_LABELS; exports.PAINT_GRADIENT_SPACES = _chunkPQ3H2AANcjs.PAINT_GRADIENT_SPACES; exports.PAINT_GRADIENT_SPACE_LABELS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_SPACE_LABELS; exports.PAINT_SCHEMA_VERSION = PAINT_SCHEMA_VERSION; exports.PAINT_SOURCE_JSON_SCHEMA = _chunkPQ3H2AANcjs.PAINT_SOURCE_JSON_SCHEMA; exports.PAINT_TARGET = PAINT_TARGET; exports.PAINT_TOOLS = PAINT_TOOLS; exports.PAPER_EFFECT_BLENDS = PAPER_EFFECT_BLENDS; exports.PAPER_EFFECT_NODE_ID = PAPER_EFFECT_NODE_ID; exports.PAPER_FAMILIES = _chunk5BR7LC24cjs.PAPER_FAMILIES; exports.PAPER_LIBRARY = _chunk5BR7LC24cjs.PAPER_LIBRARY; exports.PAPER_NODE_ID = PAPER_NODE_ID; exports.PAPER_SLIDERS = PAPER_SLIDERS; exports.PAPER_TARGET = PAPER_TARGET; exports.PARTICLE_KINDS = _chunk5BR7LC24cjs.PARTICLE_KINDS; exports.PARTICLE_KIND_LABELS = PARTICLE_KIND_LABELS; exports.PARTICLE_RENDERS = _chunk5BR7LC24cjs.PARTICLE_RENDERS; exports.PARTICLE_RENDER_LABELS = PARTICLE_RENDER_LABELS; exports.PaintCanvasPresenter = PaintCanvasPresenter; exports.PaintDabStroke = PaintDabStroke; exports.PaintDirector = PaintDirector; exports.PaintDocumentError = PaintDocumentError; exports.PaintEditorController = PaintEditorController; exports.PaintEditorError = PaintEditorError; exports.PaintEffectsRenderer = PaintEffectsRenderer; exports.PaintGesture = PaintGesture; exports.PaintGradientSampler = _chunkPQ3H2AANcjs.PaintGradientSampler; exports.PaintPresence = PaintPresence; exports.PaintSourceError = _chunkPQ3H2AANcjs.PaintSourceError; exports.PaintStrokeSession = PaintStrokeSession; exports.PaintWetLayers = PaintWetLayers; exports.PaperGrain = _chunkPQ3H2AANcjs.PaperGrain; exports.ParticleStroke = _chunk5BR7LC24cjs.ParticleStroke; exports.PenDabWalker = _chunk5BR7LC24cjs.PenDabWalker; exports.PenSmoother = _chunk5BR7LC24cjs.PenSmoother; exports.PerspectiveSnapper = PerspectiveSnapper; exports.SNAP_DECIDE_DISTANCE = SNAP_DECIDE_DISTANCE; exports.STAMP_KINDS = _chunk5BR7LC24cjs.STAMP_KINDS; exports.STAMP_LABELS = STAMP_LABELS; exports.STENCIL_KINDS = STENCIL_KINDS; exports.STENCIL_SHAPES = STENCIL_SHAPES; exports.STICKER_SLIDERS = STICKER_SLIDERS; exports.STROKE_SLIDERS = STROKE_SLIDERS; exports.SYMMETRY_MODES = SYMMETRY_MODES; exports.StencilSnapper = StencilSnapper; exports.StickerSpray = _chunk5BR7LC24cjs.StickerSpray; exports.TRACING_NODE_ID = TRACING_NODE_ID; exports.ThickStrokeDriver = ThickStrokeDriver; exports.TileableNoise = _chunkPQ3H2AANcjs.TileableNoise; exports.WET_FLOW_KINDS = _chunkXFYLA3ZOcjs.WET_FLOW_KINDS; exports.WET_GPU_MODES = WET_GPU_MODES; exports.WET_GPU_OPTIONS = WET_GPU_OPTIONS; exports.WET_MEDIA = _chunk5BR7LC24cjs.WET_MEDIA; exports.WET_MEDIUM_LABELS = WET_MEDIUM_LABELS; exports.WET_MEDIUM_PARAMS = WET_MEDIUM_PARAMS; exports.WET_PARAM_KEYS = _chunkXFYLA3ZOcjs.WET_PARAM_KEYS; exports.WET_PARAM_SCHEMA = WET_PARAM_SCHEMA; exports.WET_PARAM_SLIDERS = WET_PARAM_SLIDERS; exports.WET_SPEED_SLIDER = WET_SPEED_SLIDER; exports.WET_TILT_SLIDERS = WET_TILT_SLIDERS; exports.WET_TOOLS = _chunk5BR7LC24cjs.WET_TOOLS; exports.WET_TOOL_LABELS = WET_TOOL_LABELS; exports.WetStrokeDriver = WetStrokeDriver; exports.addPaintGradientStop = _chunkPQ3H2AANcjs.addPaintGradientStop; exports.affineCopy = affineCopy; exports.applyAffine = applyAffine; exports.brushEngineControls = brushEngineControls; exports.brushLoadColor = brushLoadColor; exports.brushLoadForStroke = brushLoadForStroke; exports.brushTakesPaint = _chunk5BR7LC24cjs.brushTakesPaint; exports.brushToJSON = _chunk5BR7LC24cjs.brushToJSON; exports.buildTimeLapse = buildTimeLapse; exports.checkPaintEffectPlace = checkPaintEffectPlace; exports.clonePaper = _chunk5BR7LC24cjs.clonePaper; exports.colorHarmony = colorHarmony2; exports.composeAffine = composeAffine; exports.contactSheet = contactSheet; exports.createPaintCompositor = createPaintCompositor; exports.createPaintDocument = createPaintDocument; exports.createPaintEditorDocument = createPaintEditorDocument; exports.createPaintEditorToolProvider = createPaintEditorToolProvider; exports.createPaintEffectsToolProvider = createPaintEffectsToolProvider; exports.createPaintLayer = createPaintLayer; exports.createPaintReplayTools = createPaintReplayTools; exports.createPaintStickerTools = createPaintStickerTools; exports.createPaintThickTools = createPaintThickTools; exports.createPaintWetTools = createPaintWetTools; exports.decodeImageBlob = decodeImageBlob; exports.defaultGuides = defaultGuides; exports.defaultImageEncoder = defaultImageEncoder; exports.deserializePaintDocument = deserializePaintDocument; exports.deserializePaintDocumentAsync = deserializePaintDocumentAsync; exports.documentExtensions = documentExtensions; exports.documentToScreen = documentToScreen; exports.downloadBytes = downloadBytes; exports.downsampleRgba = downsampleRgba; exports.drawSticker = _chunk5BR7LC24cjs.drawSticker; exports.drawWaterOverlay = drawWaterOverlay; exports.effectDabField = _chunk5BR7LC24cjs.effectDabField; exports.effectLayersToJSON = effectLayersToJSON; exports.effectPaintKey = _chunk5BR7LC24cjs.effectPaintKey; exports.effectPaintSize = _chunk5BR7LC24cjs.effectPaintSize; exports.emptiedLayers = emptiedLayers; exports.emptyThickChannels = emptyThickChannels; exports.fitZoom = fitZoom; exports.grainCatch = _chunkPQ3H2AANcjs.grainCatch; exports.grainHeights = _chunkPQ3H2AANcjs.grainHeights; exports.guideOverlay = guideOverlay; exports.handlePaintShortcut = handlePaintShortcut; exports.hash2 = _chunkPQ3H2AANcjs.hash2; exports.hexToPaintRgb = _chunkPQ3H2AANcjs.hexToPaintRgb; exports.hexToRgb8 = _chunk5BR7LC24cjs.hexToRgb8; exports.hslToRgb = hslToRgb; exports.hsvToRgb = hsvToRgb; exports.hueToWheel = _chunk7PT4RJL5cjs.hueToWheel; exports.invertAffine = invertAffine; exports.isBlendMode = _chunkOLNIPTB3cjs.isBlendMode; exports.isGradientPaint = _chunkPQ3H2AANcjs.isGradientPaint; exports.isPaintEffectPaint = _chunk5BR7LC24cjs.isPaintEffectPaint; exports.isPainterlyBlendMode = _chunkOLNIPTB3cjs.isPainterlyBlendMode; exports.isPalmTouch = isPalmTouch; exports.isWetBrush = _chunk5BR7LC24cjs.isWetBrush; exports.isWetCapable = _chunk5BR7LC24cjs.isWetCapable; exports.lightTile = lightTile; exports.lightingKey = lightingKey; exports.liveStrokeBounds = liveStrokeBounds; exports.liveStrokeRefusal = liveStrokeRefusal; exports.mapPenPoint = mapPenPoint; exports.mix32 = _chunkPQ3H2AANcjs.mix32; exports.mixPixels = mixPixels; exports.mixerLoadForStroke = mixerLoadForStroke; exports.mixerTip = _chunk5BR7LC24cjs.mixerTip; exports.multiColorForDab = multiColorForDab; exports.multiColorGradient = multiColorGradient; exports.multiColorPalette = multiColorPalette; exports.multiColorSliders = multiColorSliders; exports.multiColorToPaint = _chunk5BR7LC24cjs.multiColorToPaint; exports.newPaintLayerId = newPaintLayerId; exports.nextPaperEffectVersion = nextPaperEffectVersion; exports.normalizeBehavior = _chunk5BR7LC24cjs.normalizeBehavior; exports.normalizeBristle = _chunk5BR7LC24cjs.normalizeBristle; exports.normalizeBrushStroke = normalizeBrushStroke; exports.normalizeCanvas = normalizeCanvas; exports.normalizeDynamics = _chunk5BR7LC24cjs.normalizeDynamics; exports.normalizeFlow = _chunkXFYLA3ZOcjs.normalizeFlow; exports.normalizeHexColor = _chunkPQ3H2AANcjs.normalizeHexColor; exports.normalizeImpasto = _chunk5BR7LC24cjs.normalizeImpasto; exports.normalizeLighting = normalizeLighting; exports.normalizeMultiColor = _chunk5BR7LC24cjs.normalizeMultiColor; exports.normalizePaintBrush = _chunk5BR7LC24cjs.normalizePaintBrush; exports.normalizePaper = _chunk5BR7LC24cjs.normalizePaper; exports.normalizeParticle = _chunk5BR7LC24cjs.normalizeParticle; exports.normalizePerspective = normalizePerspective; exports.normalizeRotation = normalizeRotation; exports.normalizeStencil = normalizeStencil; exports.normalizeStickerSettings = _chunk5BR7LC24cjs.normalizeStickerSettings; exports.normalizeStickerUpdate = _chunk5BR7LC24cjs.normalizeStickerUpdate; exports.normalizeSymmetry = normalizeSymmetry; exports.normalizeView = normalizeView; exports.normalizeWetBrush = _chunk5BR7LC24cjs.normalizeWetBrush; exports.normalizeWetParams = _chunkXFYLA3ZOcjs.normalizeWetParams; exports.overPixels = overPixels; exports.padSampleRadius = padSampleRadius; exports.paintGradientMappingFor = _chunkPQ3H2AANcjs.paintGradientMappingFor; exports.paintPlaybackPanel = paintPlaybackPanel; exports.paintSourceColor = _chunkPQ3H2AANcjs.paintSourceColor; exports.paintSourceCss = _chunkPQ3H2AANcjs.paintSourceCss; exports.paintToMultiColor = _chunk5BR7LC24cjs.paintToMultiColor; exports.paperEffectFromJSON = paperEffectFromJSON; exports.paperEffectToJSON = paperEffectToJSON; exports.paperGrain = paperGrain; exports.paperKey = paperKey; exports.paperNode = paperNode; exports.paperPreset = _chunk5BR7LC24cjs.paperPreset; exports.parseBrushLibrary = _chunk5BR7LC24cjs.parseBrushLibrary; exports.parseHexColor = parseHexColor; exports.parsePaintSource = _chunkPQ3H2AANcjs.parsePaintSource; exports.patchBrushValue = patchBrushValue; exports.patchPath = patchPath; exports.penOrientation = _chunk5BR7LC24cjs.penOrientation; exports.perspectiveDirections = perspectiveDirections; exports.perspectiveGuideLines = perspectiveGuideLines; exports.planPaintToolPlayback = planPaintToolPlayback; exports.pointerToPenPoint = pointerToPenPoint; exports.quickStencil = quickStencil; exports.rand01 = _chunkPQ3H2AANcjs.rand01; exports.randSigned = _chunkPQ3H2AANcjs.randSigned; exports.readEffectStrokeMap = _chunk5BR7LC24cjs.readEffectStrokeMap; exports.readPaintEffectPaint = _chunk5BR7LC24cjs.readPaintEffectPaint; exports.readPaintEffectSource = readPaintEffectSource; exports.readPaintSource = _chunkPQ3H2AANcjs.readPaintSource; exports.readPath = readPath; exports.readSwatch = readSwatch; exports.reliefInfo = reliefInfo; exports.removePaintGradientStop = _chunkPQ3H2AANcjs.removePaintGradientStop; exports.renderBrushThumbnail = renderBrushThumbnail; exports.renderColorWheel = renderColorWheel; exports.renderPaper = _chunk5BR7LC24cjs.renderPaper; exports.renderStickers = _chunk5BR7LC24cjs.renderStickers; exports.replayFrame = replayFrame; exports.replayItems = replayItems; exports.replayPainting = replayPainting; exports.resolvePaintGradient = _chunkPQ3H2AANcjs.resolvePaintGradient; exports.reversePaintGradient = _chunkPQ3H2AANcjs.reversePaintGradient; exports.rgbToHexColor = rgbToHexColor; exports.rgbToHsl = rgbToHsl; exports.rgbToHsv = rgbToHsv; exports.sampleEffectAt = _chunk5BR7LC24cjs.sampleEffectAt; exports.sampleEffectField = _chunk5BR7LC24cjs.sampleEffectField; exports.samplePressure = samplePressure; exports.scalePaintGradient = _chunkPQ3H2AANcjs.scalePaintGradient; exports.scaleStencil = scaleStencil; exports.screenToDocument = screenToDocument; exports.seedFromString = _chunkPQ3H2AANcjs.seedFromString; exports.serializePaintDocument = serializePaintDocument; exports.serializePaintDocumentExternal = serializePaintDocumentExternal; exports.solidPaint = _chunkPQ3H2AANcjs.solidPaint; exports.stampCoverage = _chunk5BR7LC24cjs.stampCoverage; exports.standardBlendMode = _chunkOLNIPTB3cjs.standardBlendMode; exports.startWetClock = startWetClock; exports.stencilMask = stencilMask; exports.stencilOutline = stencilOutline; exports.stickerAt = _chunk5BR7LC24cjs.stickerAt; exports.stickerRect = _chunk5BR7LC24cjs.stickerRect; exports.stickerSurface = _chunk5BR7LC24cjs.stickerSurface; exports.strokeBounds = strokeBounds; exports.strokeDuration = strokeDuration; exports.strokeSchedule = strokeSchedule; exports.sweepPath = sweepPath; exports.symmetryAxisAngles = symmetryAxisAngles; exports.symmetryAxisSegments = symmetryAxisSegments; exports.symmetryCopies = symmetryCopies; exports.thickFromJSON = thickFromJSON; exports.thickToJSON = thickToJSON; exports.thumbnailStroke = thumbnailStroke; exports.tiltToOrientation = _chunk5BR7LC24cjs.tiltToOrientation; exports.timeLapseVideo = timeLapseVideo; exports.timeLapseZip = timeLapseZip; exports.toStrokePoint = _chunk5BR7LC24cjs.toStrokePoint; exports.toggleStamp = toggleStamp; exports.translateStencil = translateStencil; exports.updatePaintGradientStop = _chunkPQ3H2AANcjs.updatePaintGradientStop; exports.usesPaintExchange = _chunk5BR7LC24cjs.usesPaintExchange; exports.variationAffine = variationAffine; exports.varyStroke = varyStroke; exports.viewCssTransform = viewCssTransform; exports.viewMatrix = viewMatrix; exports.viewPenToDocument = viewPenToDocument; exports.wetEngineSummary = wetEngineSummary; exports.wetPaperInput = wetPaperInput; exports.wetPigmentOf = wetPigmentOf; exports.wetSupport = _chunkXFYLA3ZOcjs.wetSupport; exports.wheelAngle = wheelAngle; exports.wheelPick = wheelPick; exports.wheelPoint = wheelPoint; exports.wheelToHue = _chunk7PT4RJL5cjs.wheelToHue; exports.withPaintPlayback = withPaintPlayback;