@pixerate/editor 0.241.0 → 0.243.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 (75) hide show
  1. package/dist/animation-editor/controller.d.cts +10 -0
  2. package/dist/animation-editor/controller.d.ts +10 -0
  3. package/dist/animation-editor/effects.d.cts +1 -1
  4. package/dist/animation-editor/effects.d.ts +1 -1
  5. package/dist/animation-editor/index.cjs +195 -146
  6. package/dist/animation-editor/index.js +63 -14
  7. package/dist/{brushstrokeRender-MOYGEXOC.js → brushstrokeRender-274HGNZR.js} +3 -3
  8. package/dist/{brushstrokeRender-UKADOV5D.cjs → brushstrokeRender-C7RJZPMC.cjs} +5 -5
  9. package/dist/canvas/index.cjs +15 -15
  10. package/dist/canvas/index.js +2 -2
  11. package/dist/{chunk-KCMV2534.cjs → chunk-3WOZEBXZ.cjs} +2 -2
  12. package/dist/{chunk-ZDPLZDTT.js → chunk-4ZHXLFXS.js} +1 -1
  13. package/dist/{chunk-5BR7LC24.cjs → chunk-5FZS7J7C.cjs} +50 -4
  14. package/dist/{chunk-ILM6GYAX.js → chunk-6L3WWIDW.js} +1 -1
  15. package/dist/{chunk-A7PQNIBX.cjs → chunk-7V7QMFTI.cjs} +14 -14
  16. package/dist/{chunk-JAOSL334.cjs → chunk-GHCSC2WP.cjs} +38 -16
  17. package/dist/{chunk-UDBDYZET.js → chunk-H4Z72Z4Z.js} +1 -1
  18. package/dist/{chunk-XBZK47LV.js → chunk-HQZHOPNE.js} +49 -3
  19. package/dist/{chunk-QP2B3MID.js → chunk-IXI5KATU.js} +1 -1
  20. package/dist/{chunk-JWCKJNDR.cjs → chunk-K5DS2JTA.cjs} +6 -6
  21. package/dist/{chunk-HGGJ36HR.cjs → chunk-KK2FWMA2.cjs} +1 -1
  22. package/dist/{chunk-WDDUPWVG.js → chunk-L3JFOTBQ.js} +1 -1
  23. package/dist/{chunk-SIK3LT6M.js → chunk-OFH6Q67U.js} +28 -6
  24. package/dist/{chunk-AFLMXITL.cjs → chunk-OONFIFEU.cjs} +23 -23
  25. package/dist/{chunk-HW72ABGR.cjs → chunk-R2FVAKFH.cjs} +86 -86
  26. package/dist/{chunk-G3RUKJ5C.js → chunk-RXZL4NG5.js} +1 -1
  27. package/dist/{chunk-NE6OOKWJ.js → chunk-SRXWDOUI.js} +1 -1
  28. package/dist/{chunk-7F4G74UQ.cjs → chunk-UV7OOZJQ.cjs} +35 -35
  29. package/dist/{chunk-TEFQISBY.js → chunk-X3ZINLIP.js} +1 -1
  30. package/dist/{chunk-ZQIWBJGH.cjs → chunk-XNZFVYC2.cjs} +7 -7
  31. package/dist/effects/fake.d.cts +4 -0
  32. package/dist/effects/fake.d.ts +4 -0
  33. package/dist/effects/helpers.d.cts +6 -0
  34. package/dist/effects/helpers.d.ts +6 -0
  35. package/dist/effects/index.cjs +222 -94
  36. package/dist/effects/index.js +131 -3
  37. package/dist/effects/types.d.cts +31 -0
  38. package/dist/effects/types.d.ts +31 -0
  39. package/dist/image-editor/index.cjs +788 -788
  40. package/dist/image-editor/index.js +5 -5
  41. package/dist/paint-editor/brushes.d.cts +18 -2
  42. package/dist/paint-editor/brushes.d.ts +18 -2
  43. package/dist/paint-editor/controller.d.cts +28 -0
  44. package/dist/paint-editor/controller.d.ts +28 -0
  45. package/dist/paint-editor/document.d.cts +3 -0
  46. package/dist/paint-editor/document.d.ts +3 -0
  47. package/dist/paint-editor/index.cjs +1017 -499
  48. package/dist/paint-editor/index.js +562 -44
  49. package/dist/paint-editor/light.d.cts +74 -0
  50. package/dist/paint-editor/light.d.ts +74 -0
  51. package/dist/paint-editor/stroke.d.cts +14 -0
  52. package/dist/paint-editor/stroke.d.ts +14 -0
  53. package/dist/paint-editor/thick-tools.d.cts +2 -2
  54. package/dist/paint-editor/thick-tools.d.ts +2 -2
  55. package/dist/paint-editor/thick.d.cts +5 -1
  56. package/dist/paint-editor/thick.d.ts +5 -1
  57. package/dist/paint-editor/ui.d.cts +2 -0
  58. package/dist/paint-editor/ui.d.ts +2 -0
  59. package/dist/vector/index.cjs +968 -968
  60. package/dist/vector/index.js +5 -5
  61. package/dist/video-editor/controller.d.cts +49 -1
  62. package/dist/video-editor/controller.d.ts +49 -1
  63. package/dist/video-editor/effect-path.d.cts +66 -0
  64. package/dist/video-editor/effect-path.d.ts +66 -0
  65. package/dist/video-editor/index.cjs +616 -427
  66. package/dist/video-editor/index.d.cts +1 -0
  67. package/dist/video-editor/index.d.ts +1 -0
  68. package/dist/video-editor/index.js +193 -4
  69. package/package.json +1 -1
  70. package/skills/pixerate-animation-editor/SKILL.md +1 -1
  71. package/skills/pixerate-paint-editor/SKILL.md +9 -9
  72. package/skills/pixerate-paint-editor/references/canvas-hosted.md +8 -0
  73. package/skills/pixerate-paint-editor/references/light-paint.md +31 -0
  74. package/skills/pixerate-paint-oil-impasto/SKILL.md +1 -0
  75. package/skills/pixerate-video-editor/SKILL.md +9 -2
@@ -1,13 +1,13 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7; var _class8; var _class9; var _class10; var _class11; var _class12;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7; var _class8; var _class9; var _class10; var _class11; var _class12; var _class13;
2
2
 
3
- var _chunkZQIWBJGHcjs = require('../chunk-ZQIWBJGH.cjs');
3
+ var _chunkXNZFVYC2cjs = require('../chunk-XNZFVYC2.cjs');
4
4
 
5
5
 
6
6
 
7
7
 
8
8
 
9
9
 
10
- var _chunkHW72ABGRcjs = require('../chunk-HW72ABGR.cjs');
10
+ var _chunkR2FVAKFHcjs = require('../chunk-R2FVAKFH.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-KCMV2534.cjs');
58
+ require('../chunk-3WOZEBXZ.cjs');
59
59
 
60
60
 
61
61
 
@@ -131,7 +131,9 @@ require('../chunk-KCMV2534.cjs');
131
131
 
132
132
 
133
133
 
134
- var _chunk5BR7LC24cjs = require('../chunk-5BR7LC24.cjs');
134
+
135
+
136
+ var _chunk5FZS7J7Ccjs = require('../chunk-5FZS7J7C.cjs');
135
137
  require('../chunk-CVFAAZXE.cjs');
136
138
  require('../chunk-GF2RBSDM.cjs');
137
139
  require('../chunk-NM3G4VQT.cjs');
@@ -143,7 +145,7 @@ var _chunkPY4GWSE2cjs = require('../chunk-PY4GWSE2.cjs');
143
145
 
144
146
 
145
147
 
146
- var _chunkJAOSL334cjs = require('../chunk-JAOSL334.cjs');
148
+ var _chunkGHCSC2WPcjs = require('../chunk-GHCSC2WP.cjs');
147
149
  require('../chunk-WOFXZ3XU.cjs');
148
150
 
149
151
 
@@ -256,7 +258,10 @@ require('../chunk-A6QWKBJP.cjs');
256
258
  var _chunk7PT4RJL5cjs = require('../chunk-7PT4RJL5.cjs');
257
259
  require('../chunk-PPFJHTW5.cjs');
258
260
  require('../chunk-RD7RX52G.cjs');
259
- require('../chunk-7AHBW2CQ.cjs');
261
+
262
+
263
+
264
+ var _chunk7AHBW2CQcjs = require('../chunk-7AHBW2CQ.cjs');
260
265
  require('../chunk-IPJ3TEYR.cjs');
261
266
 
262
267
  // src/paint-editor/document.ts
@@ -290,7 +295,7 @@ function normalizeCanvas(raw) {
290
295
  function createPaintDocument(options = {}) {
291
296
  const canvas = normalizeCanvas({ width: _nullishCoalesce(options.width, () => ( 2048)), height: _nullishCoalesce(options.height, () => ( 1536)), dpi: options.dpi });
292
297
  const layer = createPaintLayer("Layer 1");
293
- return { canvas, paper: _chunk5BR7LC24cjs.normalizePaper.call(void 0, _nullishCoalesce(options.paper, () => ( {}))), layers: [layer], activeLayerId: layer.id, strokes: [] };
298
+ return { canvas, paper: _chunk5FZS7J7Ccjs.normalizePaper.call(void 0, _nullishCoalesce(options.paper, () => ( {}))), layers: [layer], activeLayerId: layer.id, strokes: [] };
294
299
  }
295
300
 
296
301
  // src/paint-editor/dab-stroke.ts
@@ -344,13 +349,13 @@ var PaintDabStroke = (_class = class {
344
349
  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);
345
350
  this.brush = brush;
346
351
  this.options = options;
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];
352
+ this.smoother = new (0, _chunk5FZS7J7Ccjs.PenSmoother)(brush.tip.smoothing, brush.pressureSmoothing);
353
+ this.copies = _optionalChain([options, 'access', _2 => _2.copies, 'optionalAccess', _3 => _3.length]) ? options.copies : [_chunk5FZS7J7Ccjs.IDENTITY_COPY];
349
354
  const exchanging = brush.engine === "mixer" || brush.engine === "dab";
350
355
  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);
351
356
  this.mixing = brush.engine === "mixer" ? brush.mixer.mixing : brush.behavior.mixing;
352
357
  this.states = this.copies.map((c, i) => ({
353
- walker: new (0, _chunk5BR7LC24cjs.PenDabWalker)(brush, (_nullishCoalesce(options.seed, () => ( 0))) + i * 7919 | 0, c.linear),
358
+ walker: new (0, _chunk5FZS7J7Ccjs.PenDabWalker)(brush, (_nullishCoalesce(options.seed, () => ( 0))) + i * 7919 | 0, c.linear),
354
359
  exchange: exchanging ? new (0, _chunkPQ3H2AANcjs.PaintExchange)(rates, options.load) : null,
355
360
  smudge: brush.engine === "smudge" ? new (0, _chunkPQ3H2AANcjs.SmudgeTip)(brush.smudge, brush.tip.size * (1 + brush.dynamics.tiltSize * 1.5)) : null
356
361
  }));
@@ -463,7 +468,7 @@ var PaintDabStroke = (_class = class {
463
468
  let flow = 1;
464
469
  if (this.options.effect) {
465
470
  const rgb = [0, 0, 0];
466
- flow = _chunk5BR7LC24cjs.sampleEffectAt.call(void 0, this.options.effect, d.x, d.y, rgb, d);
471
+ flow = _chunk5FZS7J7Ccjs.sampleEffectAt.call(void 0, this.options.effect, d.x, d.y, rgb, d);
467
472
  ex.setReservoirColor(rgb[0], rgb[1], rgb[2]);
468
473
  } else if (this.sampler && !this.gradient) {
469
474
  const g = this.sampler.dab(d, this.sample);
@@ -694,12 +699,12 @@ var ThickStrokeDriver = (_class3 = class {
694
699
  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);
695
700
  this.init = init;
696
701
  const b = init.brush;
697
- this.smoother = new (0, _chunk5BR7LC24cjs.PenSmoother)(b.tip.smoothing, b.pressureSmoothing);
698
- const copies = init.copies.length ? init.copies : [_chunk5BR7LC24cjs.IDENTITY_COPY];
702
+ this.smoother = new (0, _chunk5FZS7J7Ccjs.PenSmoother)(b.tip.smoothing, b.pressureSmoothing);
703
+ const copies = init.copies.length ? init.copies : [_chunk5FZS7J7Ccjs.IDENTITY_COPY];
699
704
  this.copies = copies;
700
705
  const rates = _chunkPQ3H2AANcjs.exchangeRates.call(void 0, b.behavior.pickup, b.behavior.mix, 1, b.behavior.mixing);
701
706
  this.states = copies.map((c, i) => ({
702
- walker: new (0, _chunk5BR7LC24cjs.PenDabWalker)(b, init.seed + i * 7919 | 0, c.linear),
707
+ walker: new (0, _chunk5FZS7J7Ccjs.PenDabWalker)(b, init.seed + i * 7919 | 0, c.linear),
703
708
  exchange: new (0, _chunkPQ3H2AANcjs.PaintExchange)(rates, init.load),
704
709
  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,
705
710
  carried: null,
@@ -838,14 +843,14 @@ var ThickStrokeDriver = (_class3 = class {
838
843
  if (im.tool === "knife") {
839
844
  st.smudge.dab(this.medium, { x: d.x, y: d.y, radius: d.radius, opacity: k }, rect, tip);
840
845
  this.knifeHeight(st, d, rect, tip, k, im.drag);
841
- this.paintMaterial(rect, tip, k * 0.6, im.gloss, im.metallic);
846
+ this.paintMaterial(rect, tip, k * 0.6, im.gloss, im.metallic, im.rim, im.clarity);
842
847
  return;
843
848
  }
844
849
  const ex = st.exchange;
845
850
  let flow = 1;
846
851
  const effect = this.init.effect;
847
852
  if (effect) {
848
- _chunk5BR7LC24cjs.sampleEffectAt.call(void 0, effect, d.x, d.y, this.centre, d);
853
+ _chunk5FZS7J7Ccjs.sampleEffectAt.call(void 0, effect, d.x, d.y, this.centre, d);
849
854
  ex.setReservoirColor(this.centre[0], this.centre[1], this.centre[2]);
850
855
  } else if (this.sampler) {
851
856
  const g = this.sampler.dab(d, this.sample);
@@ -862,7 +867,7 @@ var ThickStrokeDriver = (_class3 = class {
862
867
  let field = null;
863
868
  if (effect) {
864
869
  field = new Float32Array(n * 3);
865
- _chunk5BR7LC24cjs.effectDabField.call(void 0, effect, rect, cov, this.centre, this.paint, field, 0, d);
870
+ _chunk5FZS7J7Ccjs.effectDabField.call(void 0, effect, rect, cov, this.centre, this.paint, field, 0, d);
866
871
  }
867
872
  this.medium.deposit(rect, cov, dep, this.paint, ex.paintPigment, field);
868
873
  }
@@ -878,34 +883,34 @@ var ThickStrokeDriver = (_class3 = class {
878
883
  const u = ((dx + 0.5 - d.x) * nxs + (dy + 0.5 - d.y) * nys) / r;
879
884
  const h0 = this.getH(lx, ly);
880
885
  let h = h0;
881
- const full = 255 * thick * k;
886
+ const full2 = 255 * thick * k;
882
887
  switch (im.tool) {
883
888
  case "brush": {
884
889
  const ridge = 0.6 + 0.4 * Math.sin(u * 9 + _chunkPQ3H2AANcjs.hash2.call(void 0, Math.round(u * 6), 3, seed) * 6.28) + 0.15 * (_chunkPQ3H2AANcjs.hash2.call(void 0, lx, ly, seed) - 0.5);
885
- const target = full * c * ridge;
890
+ const target = full2 * c * ridge;
886
891
  h = Math.max(h0 * (1 - 0.25 * c * im.drag), Math.min(255, h0 * 0.35 + target));
887
892
  break;
888
893
  }
889
894
  case "tube": {
890
895
  const dome = Math.sqrt(Math.max(0, c));
891
- h = Math.max(h0, Math.min(255, full * dome * 1.1));
896
+ h = Math.max(h0, Math.min(255, full2 * dome * 1.1));
892
897
  break;
893
898
  }
894
899
  case "gloop": {
895
900
  const dome = Math.sqrt(Math.max(0, 1 - Math.min(1, Math.abs(u)) ** 2));
896
- h = Math.max(h0, Math.min(255, full * dome * (c > 0.2 ? 1 : c * 5)));
901
+ h = Math.max(h0, Math.min(255, full2 * dome * (c > 0.2 ? 1 : c * 5)));
897
902
  break;
898
903
  }
899
904
  case "roller": {
900
905
  const stipple = 0.75 + 0.5 * _chunkPQ3H2AANcjs.hash2.call(void 0, lx >> 1, ly >> 1, seed);
901
- h = h0 + (Math.min(255, full * 0.6 * stipple) - h0) * Math.min(1, c * 1.5);
906
+ h = h0 + (Math.min(255, full2 * 0.6 * stipple) - h0) * Math.min(1, c * 1.5);
902
907
  break;
903
908
  }
904
909
  }
905
910
  this.setH(lx, ly, Math.round(Math.max(0, Math.min(255, h))));
906
911
  }
907
912
  }
908
- this.paintMaterial(rect, tip, k, im.gloss * (1 - 0.3 * im.thinner), im.metallic);
913
+ this.paintMaterial(rect, tip, k, im.gloss * (1 - 0.3 * im.thinner), im.metallic, im.rim, im.clarity);
909
914
  }
910
915
  /** The knife: flattens the relief under it and carries some of it along (a patch, like a smudge). */
911
916
  knifeHeight(st, d, rect, tip, k, drag) {
@@ -934,7 +939,12 @@ var ThickStrokeDriver = (_class3 = class {
934
939
  for (let p = 0; p < n; p++) pick[p] = Math.max(0, cur[p] - next[p]) + (carried ? carried[p] * (1 - drag) * 0.5 : 0);
935
940
  st.carried = pick;
936
941
  }
937
- paintMaterial(rect, tip, k, gloss, metal) {
942
+ /**
943
+ * Material: R gloss, G metallic, B rim (light paint, 0..255), A opacity for light paint's clarity
944
+ * (255 solid .. 1 clearest; 0 is no material, read as solid).
945
+ */
946
+ paintMaterial(rect, tip, k, gloss, metal, rim = 0, clarity = 0) {
947
+ const opacity = 255 - clarity * 254;
938
948
  const off = this.init.target.offset;
939
949
  for (let yy = 0, p = 0; yy < rect.height; yy++) {
940
950
  for (let xx = 0; xx < rect.width; xx++, p++) {
@@ -946,7 +956,9 @@ var ThickStrokeDriver = (_class3 = class {
946
956
  const i = ((ly & 255) * _chunk474IQG74cjs.TILE + (lx & 255)) * 4;
947
957
  t[i] = Math.round(t[i] + (gloss * 255 - t[i]) * c);
948
958
  t[i + 1] = Math.round(t[i + 1] + (metal * 255 - t[i + 1]) * c);
949
- t[i + 3] = 255;
959
+ t[i + 2] = Math.round(t[i + 2] + (rim * 255 - t[i + 2]) * c);
960
+ const a = t[i + 3] || 255;
961
+ t[i + 3] = Math.max(1, Math.round(a + (opacity - a) * c));
950
962
  }
951
963
  }
952
964
  }
@@ -981,6 +993,261 @@ function thickFromJSON(raw) {
981
993
  };
982
994
  }
983
995
 
996
+ // src/paint-editor/light.ts
997
+ var GLOW = 1.5;
998
+ var DEFAULT_LIGHT_SPILL = { radius: 24, strength: 1 };
999
+ function normalizeLightSpill(raw, base = DEFAULT_LIGHT_SPILL) {
1000
+ const radius = _nullishCoalesce(raw.radius, () => ( base.radius));
1001
+ const strength = _nullishCoalesce(raw.strength, () => ( base.strength));
1002
+ if (!(Number.isFinite(radius) && radius >= 0 && radius <= 512)) throw new Error(`radius ${radius} is out of range; use 0..512 px (0 turns spill off).`);
1003
+ if (!(Number.isFinite(strength) && strength >= 0 && strength <= 2)) throw new Error(`strength ${strength} is out of range; use 0..2.`);
1004
+ return { radius, strength };
1005
+ }
1006
+ function emptyLight() {
1007
+ return new (0, _chunk474IQG74cjs.TiledSurface)(1, /* @__PURE__ */ new Map(), 0);
1008
+ }
1009
+ var full = (t, channels) => {
1010
+ if (t.data.length === _chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE * channels) return t.data;
1011
+ const out = new Uint8ClampedArray(_chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE * channels);
1012
+ const u = _nullishCoalesce(t.uniform, () => ( 0));
1013
+ if (channels === 1) out.fill(u & 255);
1014
+ else {
1015
+ const bytes = [u >>> 24 & 255, u >>> 16 & 255, u >>> 8 & 255, u & 255];
1016
+ for (let i = 0; i < out.length; i += 4) out.set(bytes, i);
1017
+ }
1018
+ return out;
1019
+ };
1020
+ function lightToShadow(light) {
1021
+ const tiles = /* @__PURE__ */ new Map();
1022
+ for (const [key, tile] of light.tiles) {
1023
+ const l = full(tile, 1);
1024
+ const d = new Uint8ClampedArray(_chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE * 4);
1025
+ for (let i = 0, j = 0; i < l.length; i++, j += 4) {
1026
+ if (!l[i]) continue;
1027
+ d[j] = d[j + 1] = d[j + 2] = 255;
1028
+ d[j + 3] = l[i];
1029
+ }
1030
+ const t = _chunk474IQG74cjs.finishTile.call(void 0, d, 4, 0);
1031
+ if (t) tiles.set(key, t);
1032
+ }
1033
+ return new (0, _chunk474IQG74cjs.TiledSurface)(4, tiles, 0);
1034
+ }
1035
+ function shadowToLight(shadow) {
1036
+ const tiles = /* @__PURE__ */ new Map();
1037
+ for (const [key, tile] of shadow.tiles) {
1038
+ const s = full(tile, 4);
1039
+ const d = new Uint8ClampedArray(_chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE);
1040
+ for (let i = 0, j = 0; i < d.length; i++, j += 4) if (s[j + 3]) d[i] = Math.round((s[j] + s[j + 1] + s[j + 2]) / 3 * s[j + 3] / 255);
1041
+ const t = _chunk474IQG74cjs.finishTile.call(void 0, d, 1, 0);
1042
+ if (t) tiles.set(key, t);
1043
+ }
1044
+ return new (0, _chunk474IQG74cjs.TiledSurface)(1, tiles, 0);
1045
+ }
1046
+ function lightTwinBrush(brush) {
1047
+ return _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0,
1048
+ {
1049
+ tip: { ...brush.tip, blend: "normal", pressureOpacity: brush.tip.pressureOpacity || brush.light.pressure },
1050
+ behavior: { ...brush.behavior, mixing: "rgb" },
1051
+ paint: null,
1052
+ ...brush.engine === "mixer" ? { mixer: { ...brush.mixer, mixing: "rgb" } } : {},
1053
+ ...brush.engine === "blender" ? { blender: { ...brush.blender, mixing: "rgb" } } : {},
1054
+ ...brush.engine === "smudge" ? { smudge: { ...brush.smudge, mixing: "rgb" } } : {}
1055
+ },
1056
+ brush
1057
+ );
1058
+ }
1059
+ function lightInfo(light) {
1060
+ if (!light) return { bounds: null, peak: 0 };
1061
+ let peak = 0;
1062
+ for (const t of light.tiles.values()) {
1063
+ if (t.uniform !== null) peak = Math.max(peak, t.uniform & 255);
1064
+ else for (let i = 0; i < t.data.length; i++) if (t.data[i] > peak) peak = t.data[i];
1065
+ }
1066
+ return { bounds: peak ? light.bounds : null, peak };
1067
+ }
1068
+ function scaleLight(light, k) {
1069
+ if (k >= 1) return light;
1070
+ const tiles = /* @__PURE__ */ new Map();
1071
+ if (k > 0) {
1072
+ for (const [key, tile] of light.tiles) {
1073
+ const d = Uint8ClampedArray.from(full(tile, 1), (v) => Math.round(v * k));
1074
+ const t = _chunk474IQG74cjs.finishTile.call(void 0, d, 1, 0);
1075
+ if (t) tiles.set(key, t);
1076
+ }
1077
+ }
1078
+ return new (0, _chunk474IQG74cjs.TiledSurface)(1, tiles, 0);
1079
+ }
1080
+ function mergeLight(lower, upper, upperColor) {
1081
+ if (!_optionalChain([upper, 'optionalAccess', _15 => _15.tiles, 'access', _16 => _16.size])) return lower;
1082
+ const keys = /* @__PURE__ */ new Set([..._nullishCoalesce(_optionalChain([lower, 'optionalAccess', _17 => _17.tiles, 'access', _18 => _18.keys, 'call', _19 => _19()]), () => ( [])), ...upper.tiles.keys()]);
1083
+ const tiles = /* @__PURE__ */ new Map();
1084
+ for (const key of keys) {
1085
+ const lo = _optionalChain([lower, 'optionalAccess', _20 => _20.tiles, 'access', _21 => _21.get, 'call', _22 => _22(key)]);
1086
+ const up = upper.tiles.get(key);
1087
+ const col = upperColor.tiles.get(key);
1088
+ const l = lo ? full(lo, 1) : null;
1089
+ const u = up ? full(up, 1) : null;
1090
+ const c = col ? full(col, 4) : null;
1091
+ const d = new Uint8ClampedArray(_chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE);
1092
+ for (let i = 0; i < d.length; i++) {
1093
+ const a = c ? c[i * 4 + 3] / 255 : 0;
1094
+ d[i] = Math.round((u ? u[i] : 0) + (l ? l[i] : 0) * (1 - a));
1095
+ }
1096
+ const t = _chunk474IQG74cjs.finishTile.call(void 0, d, 1, 0);
1097
+ if (t) tiles.set(key, t);
1098
+ }
1099
+ return new (0, _chunk474IQG74cjs.TiledSurface)(1, tiles, 0);
1100
+ }
1101
+ function facing(h, x, y) {
1102
+ const at = (xx, yy) => h[Math.min(_chunk474IQG74cjs.TILE - 1, Math.max(0, yy)) * _chunk474IQG74cjs.TILE + Math.min(_chunk474IQG74cjs.TILE - 1, Math.max(0, xx))];
1103
+ const k = 7 / 255;
1104
+ const dx = (at(x + 1, y) - at(x - 1, y)) / 2 * k;
1105
+ const dy = (at(x, y + 1) - at(x, y - 1)) / 2 * k;
1106
+ return 1 / Math.hypot(dx, dy, 1);
1107
+ }
1108
+ var LightCache = (_class4 = class {constructor() { _class4.prototype.__init22.call(this);_class4.prototype.__init23.call(this); }
1109
+ __init22() {this.tiles = /* @__PURE__ */ new Map()}
1110
+ glow(color, light, thick) {
1111
+ const clear = (m) => !!m && (m.uniform === null ? m.data.some((v, i) => i % 4 === 3 && v > 0 && v < 255) : (m.uniform & 255) > 0 && (m.uniform & 255) < 255);
1112
+ if (!_optionalChain([light, 'optionalAccess', _23 => _23.tiles, 'access', _24 => _24.size]) && !(thick && [...thick.material.tiles.values()].some(clear))) return color;
1113
+ const out = new Map(color.tiles);
1114
+ const seen = /* @__PURE__ */ new Set();
1115
+ for (const [key, tile] of color.tiles) {
1116
+ const l = _optionalChain([light, 'optionalAccess', _25 => _25.tiles, 'access', _26 => _26.get, 'call', _27 => _27(key)]);
1117
+ const m = _optionalChain([thick, 'optionalAccess', _28 => _28.material, 'access', _29 => _29.tiles, 'access', _30 => _30.get, 'call', _31 => _31(key)]);
1118
+ const h = _optionalChain([thick, 'optionalAccess', _32 => _32.height, 'access', _33 => _33.tiles, 'access', _34 => _34.get, 'call', _35 => _35(key)]);
1119
+ if (!l && !clear(m)) continue;
1120
+ seen.add(key);
1121
+ const inputs = [tile, _nullishCoalesce(l, () => ( null)), _nullishCoalesce(m, () => ( null)), _nullishCoalesce(h, () => ( null))];
1122
+ const cached = this.tiles.get(key);
1123
+ if (cached && cached.inputs.every((v, i) => v === inputs[i])) {
1124
+ out.set(key, cached.tile);
1125
+ continue;
1126
+ }
1127
+ const c = full(tile, 4);
1128
+ const ld = l ? full(l, 1) : null;
1129
+ const md = m ? full(m, 4) : null;
1130
+ const hd = h ? full(h, 1) : null;
1131
+ const d = new Uint8ClampedArray(c);
1132
+ for (let y = 0, i = 0; y < _chunk474IQG74cjs.TILE; y++) {
1133
+ for (let x = 0; x < _chunk474IQG74cjs.TILE; x++, i++) {
1134
+ const j = i * 4;
1135
+ if (!d[j + 3]) continue;
1136
+ const rim = md ? md[j + 2] / 255 : 0;
1137
+ const opacity = md && md[j + 3] ? md[j + 3] : 255;
1138
+ const f = (rim > 0 || opacity < 255) && hd ? facing(hd, x, y) : 1;
1139
+ const g = ld && ld[i] ? ld[i] / 255 * (1 - rim + rim * 2 * (1 - f) ** 3) : 0;
1140
+ if (g > 0) {
1141
+ for (let ch = 0; ch < 3; ch++) d[j + ch] = Math.round(_chunk7AHBW2CQcjs.linearToSrgb.call(void 0, Math.min(1, _chunk7AHBW2CQcjs.srgbToLinear.call(void 0, d[j + ch] / 255) * (1 + GLOW * g))) * 255);
1142
+ }
1143
+ if (opacity < 255) d[j + 3] = Math.round(d[j + 3] * (1 - (255 - opacity) / 254 * f));
1144
+ }
1145
+ }
1146
+ const lit = { data: d, uniform: null };
1147
+ this.tiles.set(key, { inputs, tile: lit });
1148
+ out.set(key, lit);
1149
+ }
1150
+ for (const key of [...this.tiles.keys()]) if (!seen.has(key)) this.tiles.delete(key);
1151
+ return new (0, _chunk474IQG74cjs.TiledSurface)(4, out, color.defaultValue);
1152
+ }
1153
+ __init23() {this.spillCache = null}
1154
+ /**
1155
+ * The layer's spill (layer space, RGBA for Linear Dodge (Add)): `light × colour` blurred by
1156
+ * `radius` px at a reduced resolution, times `strength`; `null` without light or spill.
1157
+ */
1158
+ spill(color, light, spill) {
1159
+ const inputs = [color, light, spill.radius, spill.strength];
1160
+ if (this.spillCache && this.spillCache.inputs.every((v, i) => v === inputs[i])) return this.spillCache.surface;
1161
+ const surface = computeSpill(color, light, spill);
1162
+ this.spillCache = { inputs, surface };
1163
+ return surface;
1164
+ }
1165
+ }, _class4);
1166
+ function computeSpill(color, light, spill) {
1167
+ const b = light ? lightInfo(light).bounds : null;
1168
+ if (!light || !b || spill.radius <= 0 || spill.strength <= 0) return null;
1169
+ const r = Math.ceil(spill.radius);
1170
+ const region = { x: b.x - r, y: b.y - r, width: b.width + 2 * r, height: b.height + 2 * r };
1171
+ const col = _chunk474IQG74cjs.readRegion.call(void 0, color, region);
1172
+ const lit = _chunk474IQG74cjs.readRegion.call(void 0, light, region);
1173
+ const f = Math.max(1, Math.floor(spill.radius / 6));
1174
+ const w = Math.ceil(region.width / f);
1175
+ const h = Math.ceil(region.height / f);
1176
+ const e = [new Float32Array(w * h), new Float32Array(w * h), new Float32Array(w * h)];
1177
+ for (let y = 0; y < region.height; y++) {
1178
+ for (let x = 0; x < region.width; x++) {
1179
+ const i = y * region.width + x;
1180
+ const l = lit[i];
1181
+ if (!l) continue;
1182
+ const j = i * 4;
1183
+ const k = l / 255 * (col[j + 3] / 255) / (f * f);
1184
+ const cell = Math.floor(y / f) * w + Math.floor(x / f);
1185
+ for (let ch = 0; ch < 3; ch++) e[ch][cell] += _chunk7AHBW2CQcjs.srgbToLinear.call(void 0, col[j + ch] / 255) * k;
1186
+ }
1187
+ }
1188
+ const box = Math.max(1, Math.round(spill.radius / f / 2.5));
1189
+ for (const plane of e) for (let pass = 0; pass < 3; pass++) boxBlur(plane, w, h, box);
1190
+ const out = new Uint8ClampedArray(region.width * region.height * 4);
1191
+ for (let y = 0; y < region.height; y++) {
1192
+ for (let x = 0; x < region.width; x++) {
1193
+ const fx = Math.min(w - 1, Math.max(0, (x + 0.5) / f - 0.5));
1194
+ const fy = Math.min(h - 1, Math.max(0, (y + 0.5) / f - 0.5));
1195
+ const x0 = Math.floor(fx);
1196
+ const y0 = Math.floor(fy);
1197
+ const x1 = Math.min(w - 1, x0 + 1);
1198
+ const y1 = Math.min(h - 1, y0 + 1);
1199
+ const tx = fx - x0;
1200
+ const ty = fy - y0;
1201
+ const rgb = e.map((p) => (p[y0 * w + x0] * (1 - tx) + p[y0 * w + x1] * tx) * (1 - ty) + (p[y1 * w + x0] * (1 - tx) + p[y1 * w + x1] * tx) * ty);
1202
+ const add = rgb.map((v) => Math.min(1, _chunk7AHBW2CQcjs.linearToSrgb.call(void 0, Math.min(1, v * spill.strength))));
1203
+ const a = Math.max(add[0], add[1], add[2]);
1204
+ if (a <= 1 / 255) continue;
1205
+ const o = (y * region.width + x) * 4;
1206
+ out[o] = Math.round(add[0] / a * 255);
1207
+ out[o + 1] = Math.round(add[1] / a * 255);
1208
+ out[o + 2] = Math.round(add[2] / a * 255);
1209
+ out[o + 3] = Math.round(a * 255);
1210
+ }
1211
+ }
1212
+ return _chunk474IQG74cjs.surfaceFromPixels.call(void 0, { width: region.width, height: region.height, data: out }, 4, region.x, region.y);
1213
+ }
1214
+ function boxBlur(p, w, h, r) {
1215
+ const tmp = new Float32Array(Math.max(w, h));
1216
+ const n = 2 * r + 1;
1217
+ for (let y = 0; y < h; y++) {
1218
+ let sum = 0;
1219
+ for (let k = -r; k <= r; k++) sum += p[y * w + Math.min(w - 1, Math.max(0, k))];
1220
+ for (let x = 0; x < w; x++) {
1221
+ tmp[x] = sum / n;
1222
+ sum += p[y * w + Math.min(w - 1, x + r + 1)] - p[y * w + Math.max(0, x - r)];
1223
+ }
1224
+ for (let x = 0; x < w; x++) p[y * w + x] = tmp[x];
1225
+ }
1226
+ for (let x = 0; x < w; x++) {
1227
+ let sum = 0;
1228
+ for (let k = -r; k <= r; k++) sum += p[Math.min(h - 1, Math.max(0, k)) * w + x];
1229
+ for (let y = 0; y < h; y++) {
1230
+ tmp[y] = sum / n;
1231
+ sum += p[Math.min(h - 1, y + r + 1) * w + x] - p[Math.max(0, y - r) * w + x];
1232
+ }
1233
+ for (let y = 0; y < h; y++) p[y * w + x] = tmp[y];
1234
+ }
1235
+ }
1236
+ function lightToJSON(light) {
1237
+ const b = lightInfo(light).bounds;
1238
+ if (!b) return null;
1239
+ return { rect: { ...b }, light: _chunkOLNIPTB3cjs.pngDataUrl.call(void 0, b.width, b.height, _chunk474IQG74cjs.readRegion.call(void 0, light, b), 1) };
1240
+ }
1241
+ function lightFromJSON(raw) {
1242
+ const r = raw;
1243
+ if (!r || typeof r !== "object" || !r.rect || typeof r.light !== "string") throw new Error("not a saved light channel");
1244
+ const parsed = _chunkOLNIPTB3cjs.parseDataUrl.call(void 0, r.light);
1245
+ if (!parsed) throw new Error("the light is not a PNG data URL");
1246
+ const img = _chunkOLNIPTB3cjs.decodePng.call(void 0, parsed.bytes, { keepGray: true });
1247
+ const data = img.channels === 1 ? img.data : Uint8ClampedArray.from({ length: img.width * img.height }, (_, i) => img.data[i * 4]);
1248
+ return _chunk474IQG74cjs.surfaceFromPixels.call(void 0, { width: img.width, height: img.height, data }, 1, r.rect.x, r.rect.y);
1249
+ }
1250
+
984
1251
  // src/paint-editor/stroke.ts
985
1252
  function defaultTile(surface) {
986
1253
  const data = new Uint8ClampedArray(_chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE * surface.channels);
@@ -1000,17 +1267,18 @@ function union(a, b) {
1000
1267
  const y = Math.min(a.y, b.y);
1001
1268
  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 };
1002
1269
  }
1003
- var PaintStrokeSession = (_class4 = class {
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);
1270
+ var PaintStrokeSession = (_class5 = class _PaintStrokeSession {
1271
+ constructor(host, init) {;_class5.prototype.__init24.call(this);_class5.prototype.__init25.call(this);_class5.prototype.__init26.call(this);_class5.prototype.__init27.call(this);_class5.prototype.__init28.call(this);_class5.prototype.__init29.call(this);_class5.prototype.__init30.call(this);_class5.prototype.__init31.call(this);_class5.prototype.__init32.call(this);_class5.prototype.__init33.call(this);_class5.prototype.__init34.call(this);_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);
1005
1272
  this.host = host;
1006
1273
  this.init = init;
1007
1274
  this.layerId = init.layerId;
1008
1275
  this.base = init.surface;
1009
1276
  this.live = new Map(this.base.tiles);
1010
- this.copies = init.copies.length ? init.copies : [_chunk5BR7LC24cjs.IDENTITY_COPY];
1277
+ this.copies = init.copies.length ? init.copies : [_chunk5FZS7J7Ccjs.IDENTITY_COPY];
1011
1278
  const brush = init.brush;
1012
1279
  if (init.wet) this.wetDriver = init.wet.driver;
1013
- const effect = _nullishCoalesce(_optionalChain([init, 'access', _15 => _15.effect, 'optionalAccess', _16 => _16.pixels]), () => ( null));
1280
+ if (init.light && !init.wet && !init.sticker) this.lightTwin = this.makeLightTwin(init.light.surface);
1281
+ const effect = _nullishCoalesce(_optionalChain([init, 'access', _36 => _36.effect, 'optionalAccess', _37 => _37.pixels]), () => ( null));
1014
1282
  this.apply = {
1015
1283
  mode: brush.engine === "eraser" ? "erase" : "paint",
1016
1284
  color: init.color,
@@ -1023,11 +1291,11 @@ var PaintStrokeSession = (_class4 = class {
1023
1291
  };
1024
1292
  if (this.wetDriver) {
1025
1293
  } else if (init.sticker) {
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 });
1294
+ this.stickerSpray = new (0, _chunk5FZS7J7Ccjs.StickerSpray)({ brush, target: this.tileTarget(), color: init.colorHex, seed: init.seed, copies: this.copies, nextId: init.sticker.nextId, paint: _nullishCoalesce(init.paint, () => ( null)), effect });
1027
1295
  } else if (init.thick) {
1028
1296
  const rgb = (c) => [c[0] / 255, c[1] / 255, c[2] / 255];
1029
1297
  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))) {
1298
+ } else if (brush.engine === "mixer" || brush.engine === "blender" || brush.engine === "smudge" || _chunk5FZS7J7Ccjs.usesPaintExchange.call(void 0, brush, Boolean(init.paint))) {
1031
1299
  const rgb = (c) => [c[0] / 255, c[1] / 255, c[2] / 255];
1032
1300
  this.wet = new PaintDabStroke(brush, {
1033
1301
  target: this.tileTarget(),
@@ -1043,43 +1311,81 @@ var PaintStrokeSession = (_class4 = class {
1043
1311
  });
1044
1312
  } else {
1045
1313
  const opts = { grain: init.grain, paperDepth: init.paperDepth, seed: init.seed, copies: this.copies, paint: _nullishCoalesce(init.paint, () => ( null)), effect };
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);
1314
+ this.dry = brush.engine === "particle" ? new (0, _chunk5FZS7J7Ccjs.ParticleStroke)(brush, opts) : brush.engine === "bristle" ? new (0, _chunk5FZS7J7Ccjs.BristleStroke)(brush, opts) : new (0, _chunk5FZS7J7Ccjs.DryStroke)(brush, opts);
1047
1315
  }
1048
1316
  }
1049
1317
 
1050
1318
 
1051
1319
 
1052
1320
  /** The raw input so far (what a replay feeds back in). */
1053
- __init22() {this.points = []}
1321
+ __init24() {this.points = []}
1054
1322
 
1055
1323
 
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}
1324
+ __init25() {this.touched = /* @__PURE__ */ new Set()}
1325
+ __init26() {this.fresh = /* @__PURE__ */ new Set()}
1326
+ __init27() {this.dry = null}
1327
+ __init28() {this.wet = null}
1328
+ __init29() {this.wetDriver = null}
1329
+ __init30() {this.thickDriver = null}
1330
+ __init31() {this.stickerSpray = null}
1331
+ /** Light paint: the stroke's twin over the light channel, and what it left there. */
1332
+ __init32() {this.lightTwin = null}
1333
+ __init33() {this.lightResult = null}
1063
1334
  /** A wet stroke that ended while the simulation was mid-batch: it commits when the driver catches up. */
1064
- __init30() {this.wetEndPending = false}
1335
+ __init34() {this.wetEndPending = false}
1065
1336
 
1066
1337
 
1067
- __init31() {this.bounds = null}
1068
- __init32() {this.lastRaw = null}
1069
- __init33() {this.closed = false}
1070
- __init34() {this.result = null}
1338
+ __init35() {this.bounds = null}
1339
+ __init36() {this.lastRaw = null}
1340
+ __init37() {this.closed = false}
1341
+ __init38() {this.result = null}
1071
1342
  /** The mask stencils this stroke was painted with when they are not the editor's (replays). */
1072
- __init35() {this.maskStencils = null}
1343
+ __init39() {this.maskStencils = null}
1344
+ /** The stroke's twin over the light channel: the same marks in grey at the brush's light amount (see `light.ts`). */
1345
+ makeLightTwin(light) {
1346
+ const init = this.init;
1347
+ const grey = Math.round(init.brush.light.amount * 255);
1348
+ const g = grey / 255;
1349
+ const hex = "#" + grey.toString(16).padStart(2, "0").repeat(3);
1350
+ const host = {
1351
+ update: () => {
1352
+ },
1353
+ commit: (_s, surface) => {
1354
+ this.lightResult = surface ? shadowToLight(surface) : null;
1355
+ }
1356
+ };
1357
+ const effect = init.effect ? { source: init.effect.source, pixels: { ...init.effect.pixels, data: greyPixels(init.effect.pixels.data, grey) } } : null;
1358
+ const paint = init.paint ? { ...init.paint, stops: init.paint.stops.map((st) => ({ ...st, color: hex })) } : null;
1359
+ return new _PaintStrokeSession(host, {
1360
+ ...init,
1361
+ surface: lightToShadow(light),
1362
+ lockTransparency: false,
1363
+ brush: lightTwinBrush(init.brush),
1364
+ color: [grey, grey, grey, 255],
1365
+ colorHex: hex,
1366
+ secondary: [grey, grey, grey, 255],
1367
+ inputFilter: void 0,
1368
+ mixerLoad: { reservoir: init.mixerLoad.reservoir ? [g, g, g] : null, amount: init.mixerLoad.amount, pickup: null },
1369
+ thick: init.thick,
1370
+ paint,
1371
+ effect,
1372
+ light: null
1373
+ });
1374
+ }
1375
+ /** Light paint: the layer's light channel after the stroke, or `null` when the stroke has no twin or left it unchanged. */
1376
+ lightChannel() {
1377
+ return this.lightResult;
1378
+ }
1073
1379
  tileTarget() {
1074
1380
  return {
1075
1381
  offset: this.init.offset,
1076
1382
  lockTransparency: this.init.lockTransparency,
1077
1383
  selection: _nullishCoalesce(this.init.selection, () => ( null)),
1078
- readTile: (tx, ty) => _nullishCoalesce(_optionalChain([this, 'access', _17 => _17.live, 'access', _18 => _18.get, 'call', _19 => _19(_chunk474IQG74cjs.tileKey.call(void 0, tx, ty)), 'optionalAccess', _20 => _20.data]), () => ( null)),
1384
+ readTile: (tx, ty) => _nullishCoalesce(_optionalChain([this, 'access', _38 => _38.live, 'access', _39 => _39.get, 'call', _40 => _40(_chunk474IQG74cjs.tileKey.call(void 0, tx, ty)), 'optionalAccess', _41 => _41.data]), () => ( null)),
1079
1385
  writeTile: (tx, ty) => {
1080
1386
  const key = _chunk474IQG74cjs.tileKey.call(void 0, tx, ty);
1081
1387
  if (!this.touched.has(key)) {
1082
- const src = _nullishCoalesce(_optionalChain([this, 'access', _21 => _21.base, 'access', _22 => _22.tiles, 'access', _23 => _23.get, 'call', _24 => _24(key), 'optionalAccess', _25 => _25.data]), () => ( defaultTile(this.base)));
1388
+ const src = _nullishCoalesce(_optionalChain([this, 'access', _42 => _42.base, 'access', _43 => _43.tiles, 'access', _44 => _44.get, 'call', _45 => _45(key), 'optionalAccess', _46 => _46.data]), () => ( defaultTile(this.base)));
1083
1389
  this.live.set(key, { data: new Uint8ClampedArray(src), uniform: null });
1084
1390
  this.touched.add(key);
1085
1391
  }
@@ -1098,6 +1404,7 @@ var PaintStrokeSession = (_class4 = class {
1098
1404
  if (filter) points = points.map(filter);
1099
1405
  for (const p of points) this.points.push(p);
1100
1406
  this.lastRaw = points[points.length - 1];
1407
+ _optionalChain([this, 'access', _47 => _47.lightTwin, 'optionalAccess', _48 => _48.push, 'call', _49 => _49(points)]);
1101
1408
  if (this.wetDriver) {
1102
1409
  this.wetUpdate(this.wetDriver.push(points));
1103
1410
  return;
@@ -1110,7 +1417,7 @@ var PaintStrokeSession = (_class4 = class {
1110
1417
  }
1111
1418
  /** Impasto strokes: the layer's thick-paint channels with the stroke so far. */
1112
1419
  thickChannels() {
1113
- return _nullishCoalesce(_optionalChain([this, 'access', _26 => _26.thickDriver, 'optionalAccess', _27 => _27.channels, 'call', _28 => _28()]), () => ( null));
1420
+ return _nullishCoalesce(_optionalChain([this, 'access', _50 => _50.thickDriver, 'optionalAccess', _51 => _51.channels, 'call', _52 => _52()]), () => ( null));
1114
1421
  }
1115
1422
  /** Shows a wet stroke's progress (the wet layer's display). */
1116
1423
  wetUpdate(rects) {
@@ -1156,8 +1463,8 @@ var PaintStrokeSession = (_class4 = class {
1156
1463
  }
1157
1464
  }
1158
1465
  for (const [key, { tx, ty, r }] of regions) {
1159
- const src = _nullishCoalesce(_optionalChain([this, 'access', _29 => _29.base, 'access', _30 => _30.tiles, 'access', _31 => _31.get, 'call', _32 => _32(key), 'optionalAccess', _33 => _33.data]), () => ( defaultTile(this.base)));
1160
- const prev = this.touched.has(key) ? _optionalChain([this, 'access', _34 => _34.live, 'access', _35 => _35.get, 'call', _36 => _36(key), 'optionalAccess', _37 => _37.data]) : void 0;
1466
+ const src = _nullishCoalesce(_optionalChain([this, 'access', _53 => _53.base, 'access', _54 => _54.tiles, 'access', _55 => _55.get, 'call', _56 => _56(key), 'optionalAccess', _57 => _57.data]), () => ( defaultTile(this.base)));
1467
+ const prev = this.touched.has(key) ? _optionalChain([this, 'access', _58 => _58.live, 'access', _59 => _59.get, 'call', _60 => _60(key), 'optionalAccess', _61 => _61.data]) : void 0;
1161
1468
  this.touched.add(key);
1162
1469
  const data = _chunkPQ3H2AANcjs.applyStrokeToTile.call(void 0, this.dry.coverage, src, ch, tx, ty, this.apply, prev ? { into: prev, region: r } : {});
1163
1470
  this.live.set(key, { data, uniform: null });
@@ -1179,6 +1486,7 @@ var PaintStrokeSession = (_class4 = class {
1179
1486
  }
1180
1487
  this.update(this.dry ? this.dry.finish() : this.thickDriver ? this.thickDriver.finish() : this.stickerSpray ? this.stickerSpray.finish() : this.wet.finish());
1181
1488
  this.closed = true;
1489
+ if (_optionalChain([this, 'access', _62 => _62.lightTwin, 'optionalAccess', _63 => _63.open])) this.lightTwin.end();
1182
1490
  const ch = this.base.channels;
1183
1491
  const tiles = new Map(this.base.tiles);
1184
1492
  let changed = false;
@@ -1193,7 +1501,7 @@ var PaintStrokeSession = (_class4 = class {
1193
1501
  const surface = changed ? new (0, _chunk474IQG74cjs.TiledSurface)(ch, tiles, this.base.defaultValue) : null;
1194
1502
  const dabs = this.dry ? this.dry.dabCount : this.thickDriver ? this.thickDriver.dabCount : this.stickerSpray ? this.stickerSpray.dabCount : this.wet.dabCount;
1195
1503
  this.result = { layerId: this.layerId, bounds: changed ? this.bounds : null, dabs, strokeId: null };
1196
- const carried = _nullishCoalesce(_optionalChain([this, 'access', _38 => _38.wet, 'optionalAccess', _39 => _39.load]), () => ( _optionalChain([this, 'access', _40 => _40.thickDriver, 'optionalAccess', _41 => _41.load])));
1504
+ const carried = _nullishCoalesce(_optionalChain([this, 'access', _64 => _64.wet, 'optionalAccess', _65 => _65.load]), () => ( _optionalChain([this, 'access', _66 => _66.thickDriver, 'optionalAccess', _67 => _67.load])));
1197
1505
  if (carried) this.result.mixerLoad = this.init.brush.behavior.dirty ? carried : _chunkPQ3H2AANcjs.cleanMixer.call(void 0, carried);
1198
1506
  this.host.commit(this, surface, false);
1199
1507
  return this.result;
@@ -1202,9 +1510,19 @@ var PaintStrokeSession = (_class4 = class {
1202
1510
  cancel() {
1203
1511
  if (this.closed) return;
1204
1512
  this.closed = true;
1513
+ if (_optionalChain([this, 'access', _68 => _68.lightTwin, 'optionalAccess', _69 => _69.open])) this.lightTwin.cancel();
1514
+ this.lightResult = null;
1205
1515
  this.host.commit(this, null, true);
1206
1516
  }
1207
- }, _class4);
1517
+ }, _class5);
1518
+ function greyPixels(data, grey) {
1519
+ const out = new Uint8ClampedArray(data.length);
1520
+ for (let i = 0; i < data.length; i += 4) {
1521
+ out[i] = out[i + 1] = out[i + 2] = grey;
1522
+ out[i + 3] = data[i + 3];
1523
+ }
1524
+ return out;
1525
+ }
1208
1526
  function mixerLoadForStroke(brush, current, color) {
1209
1527
  if (!brush.loadsColor) return { reservoir: null, amount: 0, pickup: brush.mixer.cleanEachStroke ? null : current.pickup };
1210
1528
  if (brush.mixer.loadEachStroke || !current.reservoir) return _chunkPQ3H2AANcjs.loadMixer.call(void 0, current, color);
@@ -1214,7 +1532,7 @@ function brushLoadForStroke(brush, current, color) {
1214
1532
  if (brush.engine === "mixer") return mixerLoadForStroke(brush, current, color);
1215
1533
  if (brush.engine === "impasto") return { reservoir: [color[0], color[1], color[2]], amount: 1, pickup: brush.behavior.dirty ? current.pickup : null };
1216
1534
  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;
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 };
1535
+ if (_chunk5FZS7J7Ccjs.usesPaintExchange.call(void 0, brush)) return { reservoir: [color[0], color[1], color[2]], amount: 1, pickup: brush.behavior.dirty ? current.pickup : null };
1218
1536
  return _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD;
1219
1537
  }
1220
1538
 
@@ -1224,36 +1542,36 @@ var STENCIL_SHAPES = ["rect", "ellipse", "polygon"];
1224
1542
  var finite = (v) => typeof v === "number" && Number.isFinite(v);
1225
1543
  var pt = (p) => p && finite(p.x) && finite(p.y) ? { x: p.x, y: p.y } : void 0;
1226
1544
  function normalizeStencil(raw, id, base) {
1227
- const kind = STENCIL_KINDS.includes(raw.kind) ? raw.kind : _optionalChain([base, 'optionalAccess', _42 => _42.kind]);
1545
+ const kind = STENCIL_KINDS.includes(raw.kind) ? raw.kind : _optionalChain([base, 'optionalAccess', _70 => _70.kind]);
1228
1546
  if (!kind) throw new Error(`kind ${JSON.stringify(raw.kind)} is not a stencil; use ${STENCIL_KINDS.join(", ")}.`);
1229
1547
  const s = {
1230
1548
  id,
1231
1549
  kind,
1232
- mode: raw.mode === "guide" || raw.mode === "mask" ? raw.mode : _nullishCoalesce(_optionalChain([base, 'optionalAccess', _43 => _43.mode]), () => ( (kind === "ruler" || kind === "arc" ? "guide" : "mask"))),
1233
- enabled: typeof raw.enabled === "boolean" ? raw.enabled : _nullishCoalesce(_optionalChain([base, 'optionalAccess', _44 => _44.enabled]), () => ( true)),
1234
- invert: typeof raw.invert === "boolean" ? raw.invert : _nullishCoalesce(_optionalChain([base, 'optionalAccess', _45 => _45.invert]), () => ( false)),
1235
- snap: finite(raw.snap) ? Math.min(400, Math.max(1, raw.snap)) : _nullishCoalesce(_optionalChain([base, 'optionalAccess', _46 => _46.snap]), () => ( 40)),
1236
- feather: finite(raw.feather) ? Math.min(100, Math.max(0, raw.feather)) : _nullishCoalesce(_optionalChain([base, 'optionalAccess', _47 => _47.feather]), () => ( 1))
1550
+ mode: raw.mode === "guide" || raw.mode === "mask" ? raw.mode : _nullishCoalesce(_optionalChain([base, 'optionalAccess', _71 => _71.mode]), () => ( (kind === "ruler" || kind === "arc" ? "guide" : "mask"))),
1551
+ enabled: typeof raw.enabled === "boolean" ? raw.enabled : _nullishCoalesce(_optionalChain([base, 'optionalAccess', _72 => _72.enabled]), () => ( true)),
1552
+ invert: typeof raw.invert === "boolean" ? raw.invert : _nullishCoalesce(_optionalChain([base, 'optionalAccess', _73 => _73.invert]), () => ( false)),
1553
+ snap: finite(raw.snap) ? Math.min(400, Math.max(1, raw.snap)) : _nullishCoalesce(_optionalChain([base, 'optionalAccess', _74 => _74.snap]), () => ( 40)),
1554
+ feather: finite(raw.feather) ? Math.min(100, Math.max(0, raw.feather)) : _nullishCoalesce(_optionalChain([base, 'optionalAccess', _75 => _75.feather]), () => ( 1))
1237
1555
  };
1238
1556
  if (kind === "ruler" || kind === "line") {
1239
- s.from = _nullishCoalesce(pt(raw.from), () => ( _optionalChain([base, 'optionalAccess', _48 => _48.from])));
1240
- s.to = _nullishCoalesce(pt(raw.to), () => ( _optionalChain([base, 'optionalAccess', _49 => _49.to])));
1557
+ s.from = _nullishCoalesce(pt(raw.from), () => ( _optionalChain([base, 'optionalAccess', _76 => _76.from])));
1558
+ s.to = _nullishCoalesce(pt(raw.to), () => ( _optionalChain([base, 'optionalAccess', _77 => _77.to])));
1241
1559
  if (!s.from || !s.to) throw new Error(`A ${kind} needs from and to points ({x, y} in document pixels).`);
1242
1560
  if (Math.hypot(s.to.x - s.from.x, s.to.y - s.from.y) < 1) throw new Error(`A ${kind}'s from and to must be at least 1 px apart.`);
1243
1561
  } else if (kind === "arc") {
1244
- s.center = _nullishCoalesce(pt(raw.center), () => ( _optionalChain([base, 'optionalAccess', _50 => _50.center])));
1245
- s.radius = finite(raw.radius) ? raw.radius : _optionalChain([base, 'optionalAccess', _51 => _51.radius]);
1562
+ s.center = _nullishCoalesce(pt(raw.center), () => ( _optionalChain([base, 'optionalAccess', _78 => _78.center])));
1563
+ s.radius = finite(raw.radius) ? raw.radius : _optionalChain([base, 'optionalAccess', _79 => _79.radius]);
1246
1564
  if (!s.center || !(s.radius > 0)) throw new Error("An arc needs a center ({x, y}) and a radius above 0.");
1247
- s.startAngle = finite(raw.startAngle) ? raw.startAngle : _nullishCoalesce(_optionalChain([base, 'optionalAccess', _52 => _52.startAngle]), () => ( 0));
1248
- s.endAngle = finite(raw.endAngle) ? raw.endAngle : _nullishCoalesce(_optionalChain([base, 'optionalAccess', _53 => _53.endAngle]), () => ( 360));
1565
+ s.startAngle = finite(raw.startAngle) ? raw.startAngle : _nullishCoalesce(_optionalChain([base, 'optionalAccess', _80 => _80.startAngle]), () => ( 0));
1566
+ s.endAngle = finite(raw.endAngle) ? raw.endAngle : _nullishCoalesce(_optionalChain([base, 'optionalAccess', _81 => _81.endAngle]), () => ( 360));
1249
1567
  } else {
1250
- s.shape = STENCIL_SHAPES.includes(raw.shape) ? raw.shape : _nullishCoalesce(_optionalChain([base, 'optionalAccess', _54 => _54.shape]), () => ( "rect"));
1568
+ s.shape = STENCIL_SHAPES.includes(raw.shape) ? raw.shape : _nullishCoalesce(_optionalChain([base, 'optionalAccess', _82 => _82.shape]), () => ( "rect"));
1251
1569
  if (s.shape === "polygon") {
1252
- const pts = Array.isArray(raw.points) ? raw.points.map(pt).filter((p) => Boolean(p)) : _optionalChain([base, 'optionalAccess', _55 => _55.points]);
1570
+ const pts = Array.isArray(raw.points) ? raw.points.map(pt).filter((p) => Boolean(p)) : _optionalChain([base, 'optionalAccess', _83 => _83.points]);
1253
1571
  if (!pts || pts.length < 3) throw new Error("A polygon stencil needs at least 3 points.");
1254
1572
  s.points = pts;
1255
1573
  } else {
1256
- const r = _nullishCoalesce(raw.rect, () => ( _optionalChain([base, 'optionalAccess', _56 => _56.rect])));
1574
+ const r = _nullishCoalesce(raw.rect, () => ( _optionalChain([base, 'optionalAccess', _84 => _84.rect])));
1257
1575
  if (!r || !finite(r.x) || !finite(r.y) || !(r.width > 0) || !(r.height > 0)) throw new Error(`A ${s.shape} stencil needs rect {x, y, width, height} with a width and height above 0.`);
1258
1576
  s.rect = { x: r.x, y: r.y, width: r.width, height: r.height };
1259
1577
  }
@@ -1308,8 +1626,8 @@ function nearestOnStencil(s, p) {
1308
1626
  if (s.kind === "arc") {
1309
1627
  const c = s.center;
1310
1628
  const ang = Math.atan2(p.y - c.y, p.x - c.x);
1311
- const full = Math.abs(s.endAngle - s.startAngle) >= 360;
1312
- if (full) {
1629
+ const full2 = Math.abs(s.endAngle - s.startAngle) >= 360;
1630
+ if (full2) {
1313
1631
  const q = { x: c.x + Math.cos(ang) * s.radius, y: c.y + Math.sin(ang) * s.radius };
1314
1632
  return { point: q, distance: Math.hypot(p.x - q.x, p.y - q.y) };
1315
1633
  }
@@ -1335,7 +1653,7 @@ var StencilSnapper = class {
1335
1653
  const { distance } = nearestOnStencil(s, p);
1336
1654
  if (distance <= s.snap && (!best || distance < best.d)) best = { s, d: distance };
1337
1655
  }
1338
- this.guide = _nullishCoalesce(_optionalChain([best, 'optionalAccess', _57 => _57.s]), () => ( null));
1656
+ this.guide = _nullishCoalesce(_optionalChain([best, 'optionalAccess', _85 => _85.s]), () => ( null));
1339
1657
  }
1340
1658
  if (!this.guide) return p;
1341
1659
  const q = nearestOnStencil(this.guide, p).point;
@@ -1482,7 +1800,7 @@ function normalizeBrushStroke(raw) {
1482
1800
  const out = {
1483
1801
  id: r.id,
1484
1802
  layerId: r.layerId,
1485
- brush: _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, _nullishCoalesce(r.brush, () => ( {}))),
1803
+ brush: _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, _nullishCoalesce(r.brush, () => ( {}))),
1486
1804
  color: typeof r.color === "string" && /^#[0-9a-f]{6}$/i.test(r.color) ? r.color.toLowerCase() : "#000000",
1487
1805
  points,
1488
1806
  seed: finite2(r.seed) ? r.seed | 0 : 0
@@ -1495,13 +1813,13 @@ function normalizeBrushStroke(raw) {
1495
1813
  const st = [];
1496
1814
  for (const raw2 of r.stencils) {
1497
1815
  try {
1498
- st.push(normalizeStencil(raw2, typeof _optionalChain([raw2, 'optionalAccess', _58 => _58.id]) === "string" ? raw2.id : "stencil"));
1816
+ st.push(normalizeStencil(raw2, typeof _optionalChain([raw2, 'optionalAccess', _86 => _86.id]) === "string" ? raw2.id : "stencil"));
1499
1817
  } catch (e2) {
1500
1818
  }
1501
1819
  }
1502
1820
  if (st.length) out.stencils = st;
1503
1821
  }
1504
- const effect = _chunk5BR7LC24cjs.readPaintEffectPaint.call(void 0, r.paint);
1822
+ const effect = _chunk5FZS7J7Ccjs.readPaintEffectPaint.call(void 0, r.paint);
1505
1823
  const paint = effect ? null : _chunkPQ3H2AANcjs.readPaintSource.call(void 0, r.paint);
1506
1824
  if (effect) out.paint = effect;
1507
1825
  else if (paint && paint.type === "gradient") out.paint = paint;
@@ -1541,11 +1859,11 @@ function varyStroke(stroke, m, v) {
1541
1859
  if (ps !== 1) q.pressure = Math.min(1, Math.max(0, (_nullishCoalesce(p.pressure, () => ( 1))) * ps));
1542
1860
  return q;
1543
1861
  });
1544
- const brush = s !== 1 ? _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { ...stroke.brush, tip: { ...stroke.brush.tip, size: stroke.brush.tip.size * s } }) : stroke.brush;
1862
+ const brush = s !== 1 ? _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, { ...stroke.brush, tip: { ...stroke.brush.tip, size: stroke.brush.tip.size * s } }) : stroke.brush;
1545
1863
  const out = { ...stroke, brush, points };
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);
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])) {
1864
+ if (_optionalChain([stroke, 'access', _87 => _87.paint, 'optionalAccess', _88 => _88.type]) === "gradient" && s !== 1) out.paint = _chunkPQ3H2AANcjs.scalePaintGradient.call(void 0, stroke.paint, s);
1865
+ if (_optionalChain([stroke, 'access', _89 => _89.paint, 'optionalAccess', _90 => _90.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))) } };
1866
+ if (_optionalChain([stroke, 'access', _91 => _91.copies, 'optionalAccess', _92 => _92.length])) {
1549
1867
  const inv = invertAffine(m);
1550
1868
  out.copies = stroke.copies.map((c) => composeAffine(m, composeAffine(c, inv)));
1551
1869
  }
@@ -1564,7 +1882,7 @@ var paperNodes = /* @__PURE__ */ new WeakMap();
1564
1882
  function paperNode(paper, light) {
1565
1883
  let n = paperNodes.get(paper);
1566
1884
  const metal = (_nullishCoalesce(paper.metallic, () => ( 0))) > 0;
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]))) {
1885
+ if (!n || metal && (_optionalChain([n, 'access', _93 => _93.light, 'optionalAccess', _94 => _94.azimuth]) !== _optionalChain([light, 'optionalAccess', _95 => _95.azimuth]) || _optionalChain([n, 'access', _96 => _96.light, 'optionalAccess', _97 => _97.elevation]) !== _optionalChain([light, 'optionalAccess', _98 => _98.elevation]))) {
1568
1886
  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 } } : {} };
1569
1887
  paperNodes.set(paper, n);
1570
1888
  }
@@ -1586,7 +1904,7 @@ function paperTile(paper, r, light) {
1586
1904
  const key = `${paperKey(paper, light)}|${r.x},${r.y},${r.width},${r.height}`;
1587
1905
  let t = tileCache.get(key);
1588
1906
  if (!t) {
1589
- t = _chunk5BR7LC24cjs.renderPaper.call(void 0, paper, r, paperGrain(paper), light);
1907
+ t = _chunk5FZS7J7Ccjs.renderPaper.call(void 0, paper, r, paperGrain(paper), light);
1590
1908
  if (tileCache.size > 512) tileCache.delete(tileCache.keys().next().value);
1591
1909
  tileCache.set(key, t);
1592
1910
  }
@@ -1685,17 +2003,17 @@ function header(state, options) {
1685
2003
  if (options.swatches !== void 0) out.swatches = JSON.parse(JSON.stringify(options.swatches));
1686
2004
  if (options.mixingPad !== void 0) out.mixingPad = JSON.parse(JSON.stringify(options.mixingPad));
1687
2005
  if (options.wet !== void 0) out.wet = JSON.parse(JSON.stringify(options.wet));
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));
2006
+ if (_optionalChain([state, 'access', _99 => _99.wetEvents, 'optionalAccess', _100 => _100.length])) out.wetEvents = JSON.parse(JSON.stringify(state.wetEvents));
2007
+ if (_optionalChain([state, 'access', _101 => _101.paintEvents, 'optionalAccess', _102 => _102.length])) out.paintEvents = JSON.parse(JSON.stringify(state.paintEvents));
1690
2008
  if (state.origin && (state.origin.x || state.origin.y)) out.origin = { ...state.origin };
1691
2009
  return out;
1692
2010
  }
1693
2011
  function serializePaintDocument(state, options = {}) {
1694
- const { layers, assets } = _chunkHW72ABGRcjs.serializeLayers.call(void 0, state.layers);
2012
+ const { layers, assets } = _chunkR2FVAKFHcjs.serializeLayers.call(void 0, state.layers);
1695
2013
  return { ...header(state, options), layers, assets };
1696
2014
  }
1697
2015
  async function serializePaintDocumentExternal(state, store, options = {}) {
1698
- const { layers, assets } = await _chunkHW72ABGRcjs.serializeLayersExternal.call(void 0, state.layers, store);
2016
+ const { layers, assets } = await _chunkR2FVAKFHcjs.serializeLayersExternal.call(void 0, state.layers, store);
1699
2017
  return { ...header(state, options), layers, assets };
1700
2018
  }
1701
2019
  function paintLayersOnly(layers, problems) {
@@ -1703,7 +2021,7 @@ function paintLayersOnly(layers, problems) {
1703
2021
  for (const l of layers) {
1704
2022
  if (l.type === "pixel") {
1705
2023
  const { filters, source, ...rest } = l;
1706
- if (_optionalChain([filters, 'optionalAccess', _75 => _75.length])) problems.push(`Layer "${l.name}": its filters are an image editor feature and were dropped.`);
2024
+ if (_optionalChain([filters, 'optionalAccess', _103 => _103.length])) problems.push(`Layer "${l.name}": its filters are an image editor feature and were dropped.`);
1707
2025
  if (source) problems.push(`Layer "${l.name}" refers to an image editor source image; it loads empty.`);
1708
2026
  out.push(rest);
1709
2027
  } else if (l.type === "group") out.push({ ...l, children: paintLayersOnly(l.children, problems) });
@@ -1730,11 +2048,11 @@ function deserializePaintDocument(raw) {
1730
2048
  }
1731
2049
  const problems = [];
1732
2050
  const canvas = normalizeCanvas({ width: r.width, height: r.height, dpi: _nullishCoalesce(r.dpi, () => ( void 0)) });
1733
- const paper = _chunk5BR7LC24cjs.normalizePaper.call(void 0, _nullishCoalesce(r.paper, () => ( {})));
2051
+ const paper = _chunk5FZS7J7Ccjs.normalizePaper.call(void 0, _nullishCoalesce(r.paper, () => ( {})));
1734
2052
  const assets = r.assets && typeof r.assets === "object" ? r.assets : {};
1735
- const layers = paintLayersOnly(_chunkHW72ABGRcjs.deserializeLayers.call(void 0, r.layers, { assets, problems }), problems);
2053
+ const layers = paintLayersOnly(_chunkR2FVAKFHcjs.deserializeLayers.call(void 0, r.layers, { assets, problems }), problems);
1736
2054
  const wanted = typeof r.activeLayerId === "string" ? r.activeLayerId : 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));
2055
+ const activeLayerId = wanted && _chunkHB4ICNNAcjs.findLayer.call(void 0, layers, wanted) ? wanted : _nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.flattenLayers.call(void 0, layers), 'access', _104 => _104.filter, 'call', _105 => _105((l) => l.type === "pixel"), 'access', _106 => _106.pop, 'call', _107 => _107(), 'optionalAccess', _108 => _108.id]), () => ( null));
1738
2056
  const wetEvents = Array.isArray(r.wetEvents) ? r.wetEvents.filter((e) => e && typeof e === "object" && Number.isInteger(e.clock) && typeof e.kind === "string") : [];
1739
2057
  const state = { canvas, paper, layers, activeLayerId, strokes: readStrokes(r.strokes, problems) };
1740
2058
  if (wetEvents.length) state.wetEvents = wetEvents;
@@ -1748,7 +2066,7 @@ async function deserializePaintDocumentAsync(raw, store) {
1748
2066
  if (!raw || typeof raw !== "object") throw new Error("Not a paint document: expected a JSON object.");
1749
2067
  const r = raw;
1750
2068
  const problems = [];
1751
- const assets = await _chunkHW72ABGRcjs.resolveExternalAssets.call(void 0, _nullishCoalesce(r.assets, () => ( {})), store, problems);
2069
+ const assets = await _chunkR2FVAKFHcjs.resolveExternalAssets.call(void 0, _nullishCoalesce(r.assets, () => ( {})), store, problems);
1752
2070
  const loaded = deserializePaintDocument({ ...r, assets });
1753
2071
  return { ...loaded, problems: [...problems, ...loaded.problems] };
1754
2072
  }
@@ -1831,7 +2149,7 @@ function pointerToPenPoint(e, x, y, view) {
1831
2149
  const p = { x, y, pressure: samplePressure(e) };
1832
2150
  if (typeof e.timeStamp === "number" && Number.isFinite(e.timeStamp)) p.t = e.timeStamp;
1833
2151
  if (e.pointerType !== "pen") return p;
1834
- const o = _chunk5BR7LC24cjs.penOrientation.call(void 0, {
2152
+ const o = _chunk5FZS7J7Ccjs.penOrientation.call(void 0, {
1835
2153
  x,
1836
2154
  y,
1837
2155
  altitude: typeof e.altitudeAngle === "number" ? e.altitudeAngle : void 0,
@@ -1879,7 +2197,7 @@ async function decodeImageBlob(blob) {
1879
2197
  if (!ctx) throw new Error("No 2D context to decode with.");
1880
2198
  ctx.drawImage(bitmap, 0, 0);
1881
2199
  const data = new Uint8ClampedArray(ctx.getImageData(0, 0, bitmap.width, bitmap.height).data);
1882
- _optionalChain([bitmap, 'access', _81 => _81.close, 'optionalCall', _82 => _82()]);
2200
+ _optionalChain([bitmap, 'access', _109 => _109.close, 'optionalCall', _110 => _110()]);
1883
2201
  return { width: bitmap.width, height: bitmap.height, data };
1884
2202
  }
1885
2203
  function downloadBytes(bytes, fileName, mimeType) {
@@ -1910,16 +2228,16 @@ function wetPaperInput(paper) {
1910
2228
  absorption: paper.absorption,
1911
2229
  permeability: paper.permeability,
1912
2230
  roughness: flat ? 0 : paper.roughness,
1913
- color: _chunk5BR7LC24cjs.hexToRgb8.call(void 0, paper.color),
2231
+ color: _chunk5FZS7J7Ccjs.hexToRgb8.call(void 0, paper.color),
1914
2232
  heightAt: flat ? () => 0.5 : (x, y) => grain.at(x, y)
1915
2233
  };
1916
2234
  }
1917
- var WetStrokeDriver = (_class5 = class {
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);
2235
+ var WetStrokeDriver = (_class6 = class {
2236
+ constructor(init) {;_class6.prototype.__init40.call(this);_class6.prototype.__init41.call(this);_class6.prototype.__init42.call(this);_class6.prototype.__init43.call(this);_class6.prototype.__init44.call(this);_class6.prototype.__init45.call(this);_class6.prototype.__init46.call(this);_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);
1919
2237
  this.init = init;
1920
2238
  const brush = init.brush;
1921
- this.smoother = new (0, _chunk5BR7LC24cjs.PenSmoother)(brush.tip.smoothing, brush.pressureSmoothing);
1922
- const copies = init.copies.length ? init.copies : [_chunk5BR7LC24cjs.IDENTITY_COPY];
2239
+ this.smoother = new (0, _chunk5FZS7J7Ccjs.PenSmoother)(brush.tip.smoothing, brush.pressureSmoothing);
2240
+ const copies = init.copies.length ? init.copies : [_chunk5FZS7J7Ccjs.IDENTITY_COPY];
1923
2241
  const wet = brush.wet;
1924
2242
  const spacing = Math.max(0.02, brush.tip.spacing);
1925
2243
  const perDab = Math.min(1, spacing * 1.2);
@@ -1931,7 +2249,7 @@ var WetStrokeDriver = (_class5 = class {
1931
2249
  });
1932
2250
  const rates = _chunkPQ3H2AANcjs.exchangeRates.call(void 0, brush.behavior.pickup, brush.behavior.mix, wet.load, brush.behavior.mixing);
1933
2251
  this.states = copies.map((c, i) => ({
1934
- walker: new (0, _chunk5BR7LC24cjs.PenDabWalker)(brush, init.seed + i * 7919 | 0, c.linear),
2252
+ walker: new (0, _chunk5FZS7J7Ccjs.PenDabWalker)(brush, init.seed + i * 7919 | 0, c.linear),
1935
2253
  exchange: wet.tool === "paint" ? new (0, _chunkPQ3H2AANcjs.PaintExchange)(rates, init.load) : null,
1936
2254
  last: null
1937
2255
  }));
@@ -1941,29 +2259,29 @@ var WetStrokeDriver = (_class5 = class {
1941
2259
  this.sampler = g ? new (0, _chunkPQ3H2AANcjs.PaintGradientSampler)(g.map.by === "across-tip" ? { ...g, map: { by: "distance", length: 300, repeat: "mirror" } } : g, init.seed) : null;
1942
2260
  }
1943
2261
 
1944
- __init36() {this.dabCount = 0}
1945
- __init37() {this.bounds = null}
2262
+ __init40() {this.dabCount = 0}
2263
+ __init41() {this.bounds = null}
1946
2264
  /** The world's clock at which each raw point was applied (recorded with the stroke). */
1947
- __init38() {this.steps = []}
2265
+ __init42() {this.steps = []}
1948
2266
 
1949
2267
 
1950
2268
 
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]}
2269
+ __init43() {this.queue = []}
2270
+ __init44() {this.finishing = false}
2271
+ __init45() {this.finished = false}
2272
+ __init46() {this.lastRaw = null}
2273
+ __init47() {this.lastFed = null}
2274
+ __init48() {this.tip = new Float32Array(0)}
2275
+ __init49() {this.cov = new Float32Array(0)}
2276
+ __init50() {this.paint = [0, 0, 0]}
2277
+ __init51() {this.centre = [0, 0, 0]}
1960
2278
 
1961
2279
 
1962
- __init48() {this.sample = { rgb: [0, 0, 0], alpha: 1 }}
2280
+ __init52() {this.sample = { rgb: [0, 0, 0], alpha: 1 }}
1963
2281
 
1964
2282
  /** What copy 0's brush carries after the dabs so far. */
1965
2283
  get load() {
1966
- return _nullishCoalesce(_optionalChain([this, 'access', _83 => _83.states, 'access', _84 => _84[0], 'access', _85 => _85.exchange, 'optionalAccess', _86 => _86.load]), () => ( null));
2284
+ return _nullishCoalesce(_optionalChain([this, 'access', _111 => _111.states, 'access', _112 => _112[0], 'access', _113 => _113.exchange, 'optionalAccess', _114 => _114.load]), () => ( null));
1967
2285
  }
1968
2286
  /** Whether every point (and the end) has been applied. */
1969
2287
  get done() {
@@ -2059,7 +2377,7 @@ var WetStrokeDriver = (_class5 = class {
2059
2377
  let flow = 1;
2060
2378
  const effect = this.init.effect;
2061
2379
  if (effect) {
2062
- _chunk5BR7LC24cjs.sampleEffectAt.call(void 0, effect, d.x, d.y, this.centre, d);
2380
+ _chunk5FZS7J7Ccjs.sampleEffectAt.call(void 0, effect, d.x, d.y, this.centre, d);
2063
2381
  ex.setReservoirColor(this.centre[0], this.centre[1], this.centre[2]);
2064
2382
  } else if (this.sampler) {
2065
2383
  const g = this.sampler.dab(d, this.sample);
@@ -2071,7 +2389,7 @@ var WetStrokeDriver = (_class5 = class {
2071
2389
  if (total > 0 && flow > 0 && effect) {
2072
2390
  const ec = cov.slice();
2073
2391
  const field = new Float32Array(ec.length * 3);
2074
- _chunk5BR7LC24cjs.effectDabField.call(void 0, effect, rect, ec, this.centre, this.paint, field, COVERAGE_STEPS, d);
2392
+ _chunk5FZS7J7Ccjs.effectDabField.call(void 0, effect, rect, ec, this.centre, this.paint, field, COVERAGE_STEPS, d);
2075
2393
  this.medium.deposit(rect, ec, Math.min(1, total) * k, this.paint, null, field);
2076
2394
  } else if (total > 0 && flow > 0) this.medium.deposit(rect, cov, Math.min(1, total) * k * flow, this.paint, null);
2077
2395
  else world.paint(laneId, rect, cov, this.medium.options.water * k, null);
@@ -2105,7 +2423,7 @@ var WetStrokeDriver = (_class5 = class {
2105
2423
  }
2106
2424
  st.last = d;
2107
2425
  }
2108
- }, _class5);
2426
+ }, _class6);
2109
2427
  var WET_MEDIUM_PARAMS = {
2110
2428
  watercolor: {},
2111
2429
  gouache: { viscosity: 0.45, evaporation: 0.1, edgeDarkening: 0.15, granulation: 0.1, bloom: 0.1, bleed: 0.15, pickup: 0.5, pigmentWeight: 0.5 },
@@ -2114,8 +2432,8 @@ var WET_MEDIUM_PARAMS = {
2114
2432
  var WET_GPU_MODES = ["auto", "off"];
2115
2433
  var GPU_LEAD = 16;
2116
2434
  var DISPLAY_TILES_PER_TICK = 24;
2117
- var PaintWetLayers = (_class6 = class {
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);
2435
+ var PaintWetLayers = (_class7 = class {
2436
+ constructor(host, options = {}) {;_class7.prototype.__init53.call(this);_class7.prototype.__init54.call(this);_class7.prototype.__init55.call(this);_class7.prototype.__init56.call(this);_class7.prototype.__init57.call(this);_class7.prototype.__init58.call(this);_class7.prototype.__init59.call(this);_class7.prototype.__init60.call(this);_class7.prototype.__init61.call(this);_class7.prototype.__init62.call(this);_class7.prototype.__init63.call(this);_class7.prototype.__init64.call(this);_class7.prototype.__init65.call(this);_class7.prototype.__init66.call(this);_class7.prototype.__init67.call(this);_class7.prototype.__init68.call(this);
2119
2437
  this.host = host;
2120
2438
  this.options = options;
2121
2439
  this.speed = Math.max(1, Math.min(2e3, _nullishCoalesce(options.speed, () => ( 120))));
@@ -2125,27 +2443,27 @@ var PaintWetLayers = (_class6 = class {
2125
2443
  }
2126
2444
 
2127
2445
 
2128
- __init49() {this.world = null}
2129
- __init50() {this.tilt = { ..._chunkXFYLA3ZOcjs.FLAT }}
2130
- __init51() {this.settings = /* @__PURE__ */ new Map()}
2131
- __init52() {this.paused = false}
2446
+ __init53() {this.world = null}
2447
+ __init54() {this.tilt = { ..._chunkXFYLA3ZOcjs.FLAT }}
2448
+ __init55() {this.settings = /* @__PURE__ */ new Map()}
2449
+ __init56() {this.paused = false}
2132
2450
  /** Wet strokes dry as soon as they end (one undo step with the stroke). */
2133
- __init53() {this.instantDry = false}
2451
+ __init57() {this.instantDry = false}
2134
2452
 
2135
- __init54() {this.overlay = false}
2453
+ __init58() {this.overlay = false}
2136
2454
 
2137
2455
  /** The live wet stroke, if any. */
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}
2456
+ __init59() {this.driver = null}
2457
+ __init60() {this.lastTick = null}
2458
+ __init61() {this.ticking = null}
2459
+ __init62() {this.stepsOwed = 0}
2460
+ __init63() {this.displaying = null}
2461
+ __init64() {this.leadAt = 0}
2144
2462
 
2145
2463
 
2146
- __init61() {this.gpu = null}
2147
- __init62() {this.gpuSupport = null}
2148
- __init63() {this.gpuStarting = null}
2464
+ __init65() {this.gpu = null}
2465
+ __init66() {this.gpuSupport = null}
2466
+ __init67() {this.gpuStarting = null}
2149
2467
  // --- Engine B ------------------------------------------------------------------
2150
2468
  /** Starts the GPU engine for the world (once; it may turn out unavailable). */
2151
2469
  startGpu() {
@@ -2181,8 +2499,8 @@ var PaintWetLayers = (_class6 = class {
2181
2499
  if (mode === this.gpuMode) return this.engineInfo();
2182
2500
  this.gpuMode = mode;
2183
2501
  if (mode === "off") {
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()]);
2502
+ _optionalChain([this, 'access', _115 => _115.world, 'optionalAccess', _116 => _116.attachGpu, 'call', _117 => _117(null)]);
2503
+ _optionalChain([this, 'access', _118 => _118.gpu, 'optionalAccess', _119 => _119.destroy, 'call', _120 => _120()]);
2186
2504
  this.gpu = null;
2187
2505
  this.gpuSupport = null;
2188
2506
  } else if (this.world) this.startGpu();
@@ -2190,13 +2508,13 @@ var PaintWetLayers = (_class6 = class {
2190
2508
  }
2191
2509
  engineInfo() {
2192
2510
  const w = this.world;
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() };
2511
+ const base = { mode: this.gpuMode, adapter: _nullishCoalesce(_optionalChain([this, 'access', _121 => _121.gpuSupport, 'optionalAccess', _122 => _122.adapter]), () => ( null)), maxTiles: _nullishCoalesce(_optionalChain([this, 'access', _123 => _123.gpuSupport, 'optionalAccess', _124 => _124.maxSlots]), () => ( 0)), minTiles: this.gpuMinTiles, activeTiles: _nullishCoalesce(_optionalChain([w, 'optionalAccess', _125 => _125.activeTiles, 'call', _126 => _126()]), () => ( 0)), cpu: this.backend() };
2194
2512
  if (this.gpuMode === "off") return { ...base, state: "off", reason: "Forced to the CPU." };
2195
2513
  if (this.gpuStarting) return { ...base, state: "starting", reason: null };
2196
- if (_optionalChain([w, 'optionalAccess', _99 => _99.gpuError])) return { ...base, state: "failed", reason: w.gpuError };
2514
+ if (_optionalChain([w, 'optionalAccess', _127 => _127.gpuError])) return { ...base, state: "failed", reason: w.gpuError };
2197
2515
  if (this.gpuSupport && !this.gpuSupport.supported) return { ...base, state: "unavailable", reason: this.gpuSupport.reason };
2198
2516
  if (!this.gpu) return { ...base, state: "not-started", reason: "Starts with the first wet stroke." };
2199
- if (_optionalChain([w, 'optionalAccess', _100 => _100.gpuActive])) return { ...base, state: "active", reason: null };
2517
+ if (_optionalChain([w, 'optionalAccess', _128 => _128.gpuActive])) return { ...base, state: "active", reason: null };
2200
2518
  return { ...base, state: "standby", reason: `The GPU takes over at ${this.gpuMinTiles} wet tiles (256 px).` };
2201
2519
  }
2202
2520
  /** The world (made on first use, at the canvas size). */
@@ -2235,13 +2553,13 @@ var PaintWetLayers = (_class6 = class {
2235
2553
  if (!this.canExtend(left, top)) throw new Error("Wet paint only moves by whole 256 px tiles, between strokes.");
2236
2554
  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 } } });
2237
2555
  const old = this.world;
2238
- const snap = _optionalChain([old, 'optionalAccess', _101 => _101.laneIds, 'call', _102 => _102(), 'access', _103 => _103.length]) ? old.snapshot() : null;
2556
+ const snap = _optionalChain([old, 'optionalAccess', _129 => _129.laneIds, 'call', _130 => _130(), 'access', _131 => _131.length]) ? old.snapshot() : null;
2239
2557
  this.dropWorld();
2240
2558
  this.lastTick = null;
2241
2559
  this.stepsOwed = 0;
2242
2560
  if (!old || !snap) return;
2243
2561
  const moved = _chunkXFYLA3ZOcjs.moveWetSnapshot.call(void 0, snap, old, { width, height }, left, top, (l) => {
2244
- const flow = _optionalChain([this, 'access', _104 => _104.settings, 'access', _105 => _105.get, 'call', _106 => _106(l.id), 'optionalAccess', _107 => _107.flow]);
2562
+ const flow = _optionalChain([this, 'access', _132 => _132.settings, 'access', _133 => _133.get, 'call', _134 => _134(l.id), 'optionalAccess', _135 => _135.flow]);
2245
2563
  return {
2246
2564
  base: _chunk474IQG74cjs.translateSurface.call(void 0, l.base, left, top),
2247
2565
  flow: l.flow && flow && flow.kind !== "none" ? _chunkXFYLA3ZOcjs.flowField.call(void 0, flow, width, height) : l.flow
@@ -2259,11 +2577,11 @@ var PaintWetLayers = (_class6 = class {
2259
2577
  }
2260
2578
  /** Layers holding a wet layer (wet or drying). */
2261
2579
  wetLayerIds() {
2262
- return _nullishCoalesce(_optionalChain([this, 'access', _108 => _108.world, 'optionalAccess', _109 => _109.laneIds, 'call', _110 => _110()]), () => ( []));
2580
+ return _nullishCoalesce(_optionalChain([this, 'access', _136 => _136.world, 'optionalAccess', _137 => _137.laneIds, 'call', _138 => _138()]), () => ( []));
2263
2581
  }
2264
2582
  /** Whether a batch is running (edits wait). */
2265
2583
  get busy() {
2266
- return Boolean(_optionalChain([this, 'access', _111 => _111.world, 'optionalAccess', _112 => _112.busy]));
2584
+ return Boolean(_optionalChain([this, 'access', _139 => _139.world, 'optionalAccess', _140 => _140.busy]));
2267
2585
  }
2268
2586
  /** Resolves when no batch is running. */
2269
2587
  async idle() {
@@ -2292,13 +2610,13 @@ var PaintWetLayers = (_class6 = class {
2292
2610
  setParams(layerId, update) {
2293
2611
  const s = this.settingsOf(layerId);
2294
2612
  s.params = _chunkXFYLA3ZOcjs.normalizeWetParams.call(void 0, update, s.params);
2295
- if (_optionalChain([this, 'access', _113 => _113.world, 'optionalAccess', _114 => _114.hasLane, 'call', _115 => _115(layerId)])) this.world.setParams(layerId, s.params);
2613
+ if (_optionalChain([this, 'access', _141 => _141.world, 'optionalAccess', _142 => _142.hasLane, 'call', _143 => _143(layerId)])) this.world.setParams(layerId, s.params);
2296
2614
  return { ...s.params };
2297
2615
  }
2298
2616
  setFlow(layerId, update) {
2299
2617
  const s = this.settingsOf(layerId);
2300
2618
  s.flow = _chunkXFYLA3ZOcjs.normalizeFlow.call(void 0, update, s.flow);
2301
- if (_optionalChain([this, 'access', _116 => _116.world, 'optionalAccess', _117 => _117.hasLane, 'call', _118 => _118(layerId)])) {
2619
+ if (_optionalChain([this, 'access', _144 => _144.world, 'optionalAccess', _145 => _145.hasLane, 'call', _146 => _146(layerId)])) {
2302
2620
  const { width, height } = this.host.canvas();
2303
2621
  this.world.setFlow(layerId, _chunkXFYLA3ZOcjs.flowField.call(void 0, s.flow, width, height), s.flow.strength);
2304
2622
  }
@@ -2306,29 +2624,29 @@ var PaintWetLayers = (_class6 = class {
2306
2624
  }
2307
2625
  setTilt(update) {
2308
2626
  this.tilt = _chunkXFYLA3ZOcjs.normalizeTilt.call(void 0, update, this.tilt);
2309
- _optionalChain([this, 'access', _119 => _119.world, 'optionalAccess', _120 => _120.setTilt, 'call', _121 => _121(this.tilt)]);
2627
+ _optionalChain([this, 'access', _147 => _147.world, 'optionalAccess', _148 => _148.setTilt, 'call', _149 => _149(this.tilt)]);
2310
2628
  return { ...this.tilt };
2311
2629
  }
2312
2630
  /** The paper changed. */
2313
2631
  paperChanged() {
2314
- _optionalChain([this, 'access', _122 => _122.world, 'optionalAccess', _123 => _123.setPaper, 'call', _124 => _124(wetPaperInput(this.host.paper()))]);
2632
+ _optionalChain([this, 'access', _150 => _150.world, 'optionalAccess', _151 => _151.setPaper, 'call', _152 => _152(wetPaperInput(this.host.paper()))]);
2315
2633
  }
2316
2634
  /** A layer as tools describe it. */
2317
2635
  info(layerId) {
2318
2636
  const s = this.settingsOf(layerId);
2319
- const st = _optionalChain([this, 'access', _125 => _125.world, 'optionalAccess', _126 => _126.hasLane, 'call', _127 => _127(layerId)]) ? this.world.stats(layerId) : null;
2637
+ const st = _optionalChain([this, 'access', _153 => _153.world, 'optionalAccess', _154 => _154.hasLane, 'call', _155 => _155(layerId)]) ? this.world.stats(layerId) : null;
2320
2638
  return {
2321
2639
  layerId,
2322
2640
  wet: Boolean(st && st.wetCells > 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)),
2641
+ wetCells: _nullishCoalesce(_optionalChain([st, 'optionalAccess', _156 => _156.wetCells]), () => ( 0)),
2642
+ wetBounds: _nullishCoalesce(_optionalChain([st, 'optionalAccess', _157 => _157.wetBounds]), () => ( null)),
2643
+ activeTiles: _nullishCoalesce(_optionalChain([st, 'optionalAccess', _158 => _158.activeTiles]), () => ( 0)),
2326
2644
  params: { ...s.params },
2327
2645
  flow: { ...s.flow }
2328
2646
  };
2329
2647
  }
2330
2648
  backend() {
2331
- return _nullishCoalesce(_optionalChain([this, 'access', _131 => _131.world, 'optionalAccess', _132 => _132.backend, 'call', _133 => _133()]), () => ( null));
2649
+ return _nullishCoalesce(_optionalChain([this, 'access', _159 => _159.world, 'optionalAccess', _160 => _160.backend, 'call', _161 => _161()]), () => ( null));
2332
2650
  }
2333
2651
  /** Shows the current wet pixels (at most `maxTiles` reshaded per layer). */
2334
2652
  syncDisplays(maxTiles = Infinity) {
@@ -2364,7 +2682,7 @@ var PaintWetLayers = (_class6 = class {
2364
2682
  const w = this.world;
2365
2683
  if (!w) return;
2366
2684
  for (const id of w.laneIds()) {
2367
- if (w.isActive(id) || _optionalChain([this, 'access', _134 => _134.driver, 'optionalAccess', _135 => _135.init, 'access', _136 => _136.laneId]) === id) continue;
2685
+ if (w.isActive(id) || _optionalChain([this, 'access', _162 => _162.driver, 'optionalAccess', _163 => _163.init, 'access', _164 => _164.laneId]) === id) continue;
2368
2686
  const base = w.getBase(id);
2369
2687
  w.removeLane(id);
2370
2688
  this.host.updateSurfaces(/* @__PURE__ */ new Map([[id, base]]), []);
@@ -2444,10 +2762,10 @@ var PaintWetLayers = (_class6 = class {
2444
2762
  if (!d || !this.world) return;
2445
2763
  const rects = d.flush();
2446
2764
  if (rects.length) this.syncDisplays(DISPLAY_TILES_PER_TICK);
2447
- if (d.done) _optionalChain([this, 'access', _137 => _137.onDriverDone, 'optionalCall', _138 => _138(d)]);
2765
+ if (d.done) _optionalChain([this, 'access', _165 => _165.onDriverDone, 'optionalCall', _166 => _166(d)]);
2448
2766
  }
2449
2767
  /** Called when a live stroke that ended mid-batch has finished applying. */
2450
- __init64() {this.onDriverDone = null}
2768
+ __init68() {this.onDriverDone = null}
2451
2769
  /** Steps `steps` times now (agents, tests). Returns the steps run. */
2452
2770
  async advance(steps) {
2453
2771
  await this.idle();
@@ -2488,7 +2806,7 @@ var PaintWetLayers = (_class6 = class {
2488
2806
  /** The layer's pixels with all its wet paint dried (deterministic), without changing anything. */
2489
2807
  bakedSurface(layerId) {
2490
2808
  const w = this.world;
2491
- if (!_optionalChain([w, 'optionalAccess', _139 => _139.hasLane, 'call', _140 => _140(layerId)])) return null;
2809
+ if (!_optionalChain([w, 'optionalAccess', _167 => _167.hasLane, 'call', _168 => _168(layerId)])) return null;
2492
2810
  return w.bakedSurface(layerId);
2493
2811
  }
2494
2812
  /** A snapshot of the world for undo (`null` when there is none). */
@@ -2509,13 +2827,13 @@ var PaintWetLayers = (_class6 = class {
2509
2827
  this.syncDisplays();
2510
2828
  }
2511
2829
  dropWorld() {
2512
- _optionalChain([this, 'access', _141 => _141.world, 'optionalAccess', _142 => _142.destroy, 'call', _143 => _143()]);
2830
+ _optionalChain([this, 'access', _169 => _169.world, 'optionalAccess', _170 => _170.destroy, 'call', _171 => _171()]);
2513
2831
  this.world = null;
2514
2832
  this.driver = null;
2515
2833
  }
2516
2834
  /** Forgets everything wet (a new canvas, a resize, opening a document). */
2517
2835
  reset() {
2518
- _optionalChain([this, 'access', _144 => _144.world, 'optionalAccess', _145 => _145.destroy, 'call', _146 => _146()]);
2836
+ _optionalChain([this, 'access', _172 => _172.world, 'optionalAccess', _173 => _173.destroy, 'call', _174 => _174()]);
2519
2837
  this.world = null;
2520
2838
  this.driver = null;
2521
2839
  this.settings.clear();
@@ -2545,14 +2863,14 @@ var PaintWetLayers = (_class6 = class {
2545
2863
  return out;
2546
2864
  }
2547
2865
  destroy() {
2548
- _optionalChain([this, 'access', _147 => _147.world, 'optionalAccess', _148 => _148.destroy, 'call', _149 => _149()]);
2866
+ _optionalChain([this, 'access', _175 => _175.world, 'optionalAccess', _176 => _176.destroy, 'call', _177 => _177()]);
2549
2867
  this.world = null;
2550
- _optionalChain([this, 'access', _150 => _150.gpu, 'optionalAccess', _151 => _151.destroy, 'call', _152 => _152()]);
2868
+ _optionalChain([this, 'access', _178 => _178.gpu, 'optionalAccess', _179 => _179.destroy, 'call', _180 => _180()]);
2551
2869
  this.gpu = null;
2552
2870
  }
2553
- }, _class6);
2871
+ }, _class7);
2554
2872
  function wetPigmentOf(hex, body) {
2555
- const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, hex);
2873
+ const [r, g, b] = _chunk5FZS7J7Ccjs.hexToRgb8.call(void 0, hex);
2556
2874
  return Array.from(_chunkXFYLA3ZOcjs.pigmentOf.call(void 0, [r / 255, g / 255, b / 255], body));
2557
2875
  }
2558
2876
 
@@ -2560,13 +2878,13 @@ function wetPigmentOf(hex, body) {
2560
2878
  var thumbWorld = null;
2561
2879
  function paintWetTestStroke(brush, paper, color, width, height) {
2562
2880
  if (!thumbWorld || thumbWorld.width !== width || thumbWorld.height !== height) {
2563
- _optionalChain([thumbWorld, 'optionalAccess', _153 => _153.destroy, 'call', _154 => _154()]);
2881
+ _optionalChain([thumbWorld, 'optionalAccess', _181 => _181.destroy, 'call', _182 => _182()]);
2564
2882
  thumbWorld = new (0, _chunkXFYLA3ZOcjs.WetWorld)(width, height, wetPaperInput(paper), { kernel: "typescript", threads: 0 });
2565
2883
  } else thumbWorld.setPaper(wetPaperInput(paper));
2566
2884
  const w = thumbWorld;
2567
2885
  if (w.hasLane("thumb")) w.removeLane("thumb");
2568
2886
  w.ensureLane("thumb");
2569
- const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, color);
2887
+ const [r, g, b] = _chunk5FZS7J7Ccjs.hexToRgb8.call(void 0, color);
2570
2888
  if (brush.wet.tool !== "paint" && brush.wet.tool !== "water") {
2571
2889
  const half = Math.ceil(height / 2);
2572
2890
  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));
@@ -2580,9 +2898,9 @@ function paintWetTestStroke(brush, paper, color, width, height) {
2580
2898
  load: brushLoadForStroke(brush, _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD, [r / 255, g / 255, b / 255]),
2581
2899
  grain: paperGrain(paper),
2582
2900
  paperDepth: paper.depth,
2583
- paperColor: _chunk5BR7LC24cjs.hexToRgb8.call(void 0, paper.color),
2901
+ paperColor: _chunk5FZS7J7Ccjs.hexToRgb8.call(void 0, paper.color),
2584
2902
  seed: 7,
2585
- copies: [_chunk5BR7LC24cjs.IDENTITY_COPY]
2903
+ copies: [_chunk5FZS7J7Ccjs.IDENTITY_COPY]
2586
2904
  });
2587
2905
  driver.push(thumbnailStroke(width, height));
2588
2906
  driver.finish();
@@ -2602,9 +2920,10 @@ function thumbnailStroke(width, height) {
2602
2920
  return pts;
2603
2921
  }
2604
2922
  function paintTestStroke(surface, brush, paper, color, width, height) {
2605
- const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, color);
2923
+ const [r, g, b] = _chunk5FZS7J7Ccjs.hexToRgb8.call(void 0, color);
2606
2924
  let result = surface;
2607
2925
  let thick = null;
2926
+ let light = null;
2608
2927
  const session = new PaintStrokeSession(
2609
2928
  {
2610
2929
  update: () => {
@@ -2612,6 +2931,7 @@ function paintTestStroke(surface, brush, paper, color, width, height) {
2612
2931
  commit: (sess, s) => {
2613
2932
  result = _nullishCoalesce(s, () => ( result));
2614
2933
  thick = sess.thickChannels();
2934
+ light = sess.lightChannel();
2615
2935
  }
2616
2936
  },
2617
2937
  {
@@ -2621,28 +2941,30 @@ function paintTestStroke(surface, brush, paper, color, width, height) {
2621
2941
  lockTransparency: false,
2622
2942
  brush,
2623
2943
  color: [r, g, b, 255],
2624
- background: [..._chunk5BR7LC24cjs.hexToRgb8.call(void 0, paper.color), 255],
2944
+ background: [..._chunk5FZS7J7Ccjs.hexToRgb8.call(void 0, paper.color), 255],
2625
2945
  grain: paperGrain(paper),
2626
2946
  paperDepth: paper.depth,
2627
2947
  seed: 7,
2628
2948
  colorHex: color,
2629
2949
  affineCopies: [],
2630
- copies: [_chunk5BR7LC24cjs.IDENTITY_COPY],
2950
+ copies: [_chunk5FZS7J7Ccjs.IDENTITY_COPY],
2631
2951
  mixerLoad: brushLoadForStroke(brush, _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD, [r / 255, g / 255, b / 255]),
2632
2952
  thick: brush.engine === "impasto" ? { channels: emptyThickChannels() } : void 0,
2633
- sticker: brush.engine === "sticker" ? { nextId: 1 } : void 0
2953
+ sticker: brush.engine === "sticker" ? { nextId: 1 } : void 0,
2954
+ light: brush.light.amount > 0 && brush.engine !== "wet" && brush.engine !== "sticker" ? { surface: emptyLight() } : null
2634
2955
  }
2635
2956
  );
2636
2957
  session.push(thumbnailStroke(width, height));
2637
2958
  session.end();
2638
- return thick ? new LitCache().lit(result, thick, DEFAULT_LIGHTING) : result;
2959
+ const lit = thick ? new LitCache().lit(result, thick, DEFAULT_LIGHTING) : result;
2960
+ return { surface: light || thick ? new LightCache().glow(lit, _nullishCoalesce(light, () => ( void 0)), _nullishCoalesce(thick, () => ( void 0))) : lit, light };
2639
2961
  }
2640
2962
  function renderBrushThumbnail(brush, options) {
2641
2963
  const width = Math.max(16, Math.min(512, Math.round(_nullishCoalesce(options.width, () => ( 160)))));
2642
2964
  const height = Math.max(16, Math.min(512, Math.round(_nullishCoalesce(options.height, () => ( 48)))));
2643
2965
  const maxSize = height * 0.42;
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];
2966
+ const b = brush.tip.size > maxSize ? _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, { tip: { ...brush.tip, size: maxSize } }, brush) : brush;
2967
+ const rgba = [..._chunk5FZS7J7Ccjs.hexToRgb8.call(void 0, options.color), 255];
2646
2968
  let surface = _chunk474IQG74cjs.emptySurface.call(void 0, 4);
2647
2969
  if (b.engine === "eraser") {
2648
2970
  surface = _chunk474IQG74cjs.fillRect.call(void 0, surface, { x: 0, y: 0, width, height }, rgba);
@@ -2652,9 +2974,15 @@ function renderBrushThumbnail(brush, options) {
2652
2974
  surface = _chunk474IQG74cjs.fillRect.call(void 0, surface, { x: 0, y: 0, width, height: half }, rgba);
2653
2975
  surface = _chunk474IQG74cjs.fillRect.call(void 0, surface, { x: 0, y: half, width, height: height - half }, [232, 196, 64, 255]);
2654
2976
  }
2655
- surface = b.engine === "wet" ? paintWetTestStroke(b, options.paper, options.color, width, height) : paintTestStroke(surface, b, options.paper, options.color, width, height);
2977
+ let light = null;
2978
+ if (b.engine === "wet") surface = paintWetTestStroke(b, options.paper, options.color, width, height);
2979
+ else ({ surface, light } = paintTestStroke(surface, b, options.paper, options.color, width, height));
2656
2980
  const layer = { type: "pixel", id: "thumbnail", name: "thumbnail", visible: true, opacity: 1, blend: "normal", surface, offset: { x: 0, y: 0 } };
2657
- const data = createPaintCompositor().composite([paperNode(options.paper), layer], { x: 0, y: 0, width, height }, { width, height });
2981
+ const paper = light ? { ...options.paper, color: "#141414" } : options.paper;
2982
+ const spill = light ? computeSpill(surface, light, { radius: Math.max(4, height / 6), strength: 1 }) : null;
2983
+ const layers = [paperNode(paper), layer];
2984
+ if (spill) layers.push({ type: "pixel", id: "thumbnail-light", name: "light", visible: true, opacity: 1, blend: "linear-dodge", surface: spill, offset: { x: 0, y: 0 } });
2985
+ const data = createPaintCompositor().composite(layers, { x: 0, y: 0, width, height }, { width, height });
2658
2986
  return { width, height, data };
2659
2987
  }
2660
2988
 
@@ -2783,13 +3111,13 @@ function perspectiveDirections(g, p) {
2783
3111
  return out;
2784
3112
  }
2785
3113
  var SNAP_DECIDE_DISTANCE = 6;
2786
- var PerspectiveSnapper = (_class7 = class {
2787
- constructor(guide) {;_class7.prototype.__init65.call(this);_class7.prototype.__init66.call(this);
3114
+ var PerspectiveSnapper = (_class8 = class {
3115
+ constructor(guide) {;_class8.prototype.__init69.call(this);_class8.prototype.__init70.call(this);
2788
3116
  this.guide = guide;
2789
3117
  }
2790
3118
 
2791
- __init65() {this.start = null}
2792
- __init66() {this.dir = null}
3119
+ __init69() {this.start = null}
3120
+ __init70() {this.dir = null}
2793
3121
  /** The line the stroke follows, once chosen. */
2794
3122
  get line() {
2795
3123
  return this.start && this.dir ? { from: this.start, direction: this.dir } : null;
@@ -2822,7 +3150,7 @@ var PerspectiveSnapper = (_class7 = class {
2822
3150
  const t = (p.x - s.x) * d.x + (p.y - s.y) * d.y;
2823
3151
  return { ...p, x: s.x + d.x * t, y: s.y + d.y * t };
2824
3152
  }
2825
- }, _class7);
3153
+ }, _class8);
2826
3154
  function perspectiveGuideLines(g, size, count = 12) {
2827
3155
  const out = [];
2828
3156
  const far = Math.hypot(size.width, size.height) * 4;
@@ -2895,11 +3223,11 @@ function readSwatch(raw) {
2895
3223
  if (!raw || typeof raw !== "object") return null;
2896
3224
  const r = raw;
2897
3225
  const g = _chunkPQ3H2AANcjs.readPaintSource.call(void 0, r.paint);
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;
3226
+ const color = typeof r.color === "string" && /^#[0-9a-f]{6}$/i.test(r.color) ? r.color.toLowerCase() : _optionalChain([g, 'optionalAccess', _183 => _183.type]) === "gradient" ? _chunkPQ3H2AANcjs.paintSourceColor.call(void 0, g) : null;
2899
3227
  if (!color) return null;
2900
3228
  const out = { color };
2901
3229
  if (typeof r.name === "string" && r.name.trim()) out.name = r.name.trim().slice(0, 80);
2902
- if (_optionalChain([g, 'optionalAccess', _156 => _156.type]) === "gradient") out.paint = g;
3230
+ if (_optionalChain([g, 'optionalAccess', _184 => _184.type]) === "gradient") out.paint = g;
2903
3231
  return out;
2904
3232
  }
2905
3233
  var DEFAULT_SWATCHES = [
@@ -2918,7 +3246,7 @@ var DEFAULT_SWATCHES = [
2918
3246
  // src/paint-editor/mixing-pad.ts
2919
3247
  var MIXING_PAD_WIDTH = 360;
2920
3248
  var MIXING_PAD_HEIGHT = 220;
2921
- var PAD_PAINT = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, {
3249
+ var PAD_PAINT = _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, {
2922
3250
  id: "pad-paint",
2923
3251
  name: "Pad paint",
2924
3252
  category: "mixer",
@@ -2926,7 +3254,7 @@ var PAD_PAINT = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, {
2926
3254
  tip: { size: 28, hardness: 0.75, spacing: 0.1, opacity: 1, flow: 0.7, pressureSize: false, pressureOpacity: false },
2927
3255
  mixer: { wet: 0.45, load: 1, mix: 0.4, loadEachStroke: true, cleanEachStroke: true, mixing: "pigment" }
2928
3256
  });
2929
- var PAD_KNIFE = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, {
3257
+ var PAD_KNIFE = _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, {
2930
3258
  id: "pad-knife",
2931
3259
  name: "Palette knife",
2932
3260
  category: "smudge",
@@ -2936,19 +3264,19 @@ var PAD_KNIFE = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, {
2936
3264
  // A knife drags streaks of paint and folds them into each other, mixing them as pigment.
2937
3265
  smudge: { strength: 0.6, length: 0.8, mixing: "pigment" }
2938
3266
  });
2939
- var PAD_BLOB = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, {
3267
+ var PAD_BLOB = _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, {
2940
3268
  id: "pad-blob",
2941
3269
  name: "Pad blob",
2942
3270
  category: "custom",
2943
3271
  engine: "dab",
2944
3272
  tip: { size: 20, hardness: 1, spacing: 0.1, opacity: 1, flow: 1, pressureSize: false, pressureOpacity: false }
2945
3273
  });
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)}
3274
+ var MixingPad = (_class9 = class {constructor() { _class9.prototype.__init71.call(this);_class9.prototype.__init72.call(this);_class9.prototype.__init73.call(this);_class9.prototype.__init74.call(this); }
3275
+ __init71() {this.width = MIXING_PAD_WIDTH}
3276
+ __init72() {this.height = MIXING_PAD_HEIGHT}
3277
+ __init73() {this.surface = _chunk474IQG74cjs.emptySurface.call(void 0, 4)}
2950
3278
  /** Increments on every change (for redrawing). */
2951
- __init70() {this.version = 0}
3279
+ __init74() {this.version = 0}
2952
3280
  /** Paints (`paint`, with `color`) or smears (`knife`) through `points` in pad pixels. */
2953
3281
  stroke(points, options) {
2954
3282
  if (!points.length) throw new Error("points is empty; give at least one {x, y} in pad pixels.");
@@ -2958,12 +3286,12 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init67.cal
2958
3286
  }
2959
3287
  }
2960
3288
  let brush = options.tool === "knife" ? PAD_KNIFE : PAD_PAINT;
2961
- if (options.size !== void 0) brush = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { tip: { ...brush.tip, size: options.size } }, brush);
3289
+ if (options.size !== void 0) brush = _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, { tip: { ...brush.tip, size: options.size } }, brush);
2962
3290
  return this.run(brush, points, options.color);
2963
3291
  }
2964
3292
  /** Paints `points` with `brush` in `color` on the pad. */
2965
3293
  run(brush, points, color) {
2966
- const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, color);
3294
+ const [r, g, b] = _chunk5FZS7J7Ccjs.hexToRgb8.call(void 0, color);
2967
3295
  let result = null;
2968
3296
  const session = new PaintStrokeSession(
2969
3297
  { update: () => {
@@ -2981,7 +3309,7 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init67.cal
2981
3309
  seed: 1,
2982
3310
  colorHex: color,
2983
3311
  affineCopies: [],
2984
- copies: [_chunk5BR7LC24cjs.IDENTITY_COPY],
3312
+ copies: [_chunk5FZS7J7Ccjs.IDENTITY_COPY],
2985
3313
  mixerLoad: brushLoadForStroke(brush, _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD, [r / 255, g / 255, b / 255])
2986
3314
  }
2987
3315
  );
@@ -3057,10 +3385,10 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init67.cal
3057
3385
  const off = used.length === 1 ? 0 : 0.45 * R;
3058
3386
  const x = Math.min(cx + R - r, Math.max(cx - R + r, cx + Math.cos(a) * off));
3059
3387
  const y = Math.min(cy + R - r, Math.max(cy - R + r, cy + Math.sin(a) * off));
3060
- const blob = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_BLOB.tip, size: Math.max(2, 2 * r) } }, PAD_BLOB);
3388
+ const blob = _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_BLOB.tip, size: Math.max(2, 2 * r) } }, PAD_BLOB);
3061
3389
  this.run(blob, [{ x, y }], p.color.toLowerCase());
3062
3390
  });
3063
- const knife = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_KNIFE.tip, size: Math.max(12, R * 0.5) } }, PAD_KNIFE);
3391
+ const knife = _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_KNIFE.tip, size: Math.max(12, R * 0.5) } }, PAD_KNIFE);
3064
3392
  for (let k = 0; k < passes; k++) {
3065
3393
  const stir = [];
3066
3394
  const turns = 1.6;
@@ -3072,10 +3400,10 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init67.cal
3072
3400
  this.run(knife, stir, "#000000");
3073
3401
  }
3074
3402
  const mixed = _chunkOLNIPTB3cjs.mixPigments.call(void 0,
3075
- paints.map((p) => _chunk5BR7LC24cjs.hexToRgb8.call(void 0, p.color).map((v) => v / 255)),
3403
+ paints.map((p) => _chunk5FZS7J7Ccjs.hexToRgb8.call(void 0, p.color).map((v) => v / 255)),
3076
3404
  paints.map((p) => p.parts)
3077
3405
  );
3078
- const core = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_BLOB.tip, size: R * 1.1, hardness: 0.55 } }, PAD_BLOB);
3406
+ const core = _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_BLOB.tip, size: R * 1.1, hardness: 0.55 } }, PAD_BLOB);
3079
3407
  const hex = "#" + mixed.map((v) => Math.round(v * 255).toString(16).padStart(2, "0")).join("");
3080
3408
  this.run(core, [{ x: cx, y: cy }], hex);
3081
3409
  return { color: this.pick(cx, cy, 2), bounds };
@@ -3086,7 +3414,7 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init67.cal
3086
3414
  }
3087
3415
  /** Replaces the pad with a PNG (data URL or base64) of the pad's size, as `toPng` writes. */
3088
3416
  fromPng(png) {
3089
- const bytes = png.startsWith("data:") ? _optionalChain([_chunkOLNIPTB3cjs.parseDataUrl.call(void 0, png), 'optionalAccess', _157 => _157.bytes]) : _chunkOLNIPTB3cjs.base64ToBytes.call(void 0, png);
3417
+ const bytes = png.startsWith("data:") ? _optionalChain([_chunkOLNIPTB3cjs.parseDataUrl.call(void 0, png), 'optionalAccess', _185 => _185.bytes]) : _chunkOLNIPTB3cjs.base64ToBytes.call(void 0, png);
3090
3418
  if (!bytes || !_chunkOLNIPTB3cjs.isPng.call(void 0, bytes)) throw new Error("The pad image is not a PNG data URL or base64 PNG.");
3091
3419
  const img = _chunkOLNIPTB3cjs.decodePng.call(void 0, bytes);
3092
3420
  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}.`);
@@ -3109,7 +3437,7 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init67.cal
3109
3437
  }
3110
3438
  return px;
3111
3439
  }
3112
- }, _class8);
3440
+ }, _class9);
3113
3441
 
3114
3442
  // src/paint-editor/live-strokes.ts
3115
3443
  var LIVE_STROKE_ENGINES = ["dab", "particle", "bristle", "mixer"];
@@ -3127,6 +3455,7 @@ function liveStrokeRefusal(brush) {
3127
3455
  case "smudge":
3128
3456
  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
3457
  }
3458
+ if (brush.light.amount > 0) return `The ${brush.name} brush lays light, which a stroke layer doesn't redraw yet; paint light on a pixel layer (add_layer), or set light.amount to 0 (set_brush).`;
3130
3459
  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
3460
  return null;
3132
3461
  }
@@ -3170,7 +3499,7 @@ async function replayPainting(source, target, options = {}) {
3170
3499
  const maxStrokes = _nullishCoalesce(options.strokes, () => ( source.strokes.length));
3171
3500
  const chunk = Math.max(1, _nullishCoalesce(options.wetChunk, () => ( 0))) || Infinity;
3172
3501
  let strokes = 0;
3173
- await target.prepareEffectPaints(source.strokes.slice(0, maxStrokes).flatMap((st) => _optionalChain([st, 'access', _158 => _158.paint, 'optionalAccess', _159 => _159.type]) === "effect" ? [st.paint] : []));
3502
+ await target.prepareEffectPaints(source.strokes.slice(0, maxStrokes).flatMap((st) => _optionalChain([st, 'access', _186 => _186.paint, 'optionalAccess', _187 => _187.type]) === "effect" ? [st.paint] : []));
3174
3503
  const advanceTo = async (clock) => {
3175
3504
  const now = target.getState().wet.clock;
3176
3505
  let left = clock - now;
@@ -3178,7 +3507,7 @@ async function replayPainting(source, target, options = {}) {
3178
3507
  const n = Math.min(left, chunk);
3179
3508
  await target.advanceWet({ steps: n });
3180
3509
  left -= n;
3181
- if (Number.isFinite(chunk)) await _optionalChain([options, 'access', _160 => _160.onWetSteps, 'optionalCall', _161 => _161(target.getState().wet.clock)]);
3510
+ if (Number.isFinite(chunk)) await _optionalChain([options, 'access', _188 => _188.onWetSteps, 'optionalCall', _189 => _189(target.getState().wet.clock)]);
3182
3511
  }
3183
3512
  };
3184
3513
  const sr = (r) => ({ x: r.x * s, y: r.y * s, width: r.width * s, height: r.height * s });
@@ -3191,14 +3520,14 @@ async function replayPainting(source, target, options = {}) {
3191
3520
  else if (e.kind === "clear") target.clearLayer(e.layerId);
3192
3521
  else if (e.kind === "live-strokes") {
3193
3522
  const d = e.data;
3194
- if (_optionalChain([d, 'optionalAccess', _162 => _162.rasterized])) target.replaceLiveStrokes(e.layerId, null);
3523
+ if (_optionalChain([d, 'optionalAccess', _190 => _190.rasterized])) target.replaceLiveStrokes(e.layerId, null);
3195
3524
  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] : []));
3525
+ const list = (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _191 => _191.strokes]), () => ( []))).map((st2) => s === 1 ? st2 : { ...varyStroke(st2, scaleM, { scale: s }), ...st2.stencils ? { stencils: st2.stencils.map((x) => scaleStencil(x, s)) } : {} });
3526
+ await target.prepareEffectPaints(list.flatMap((st2) => _optionalChain([st2, 'access', _192 => _192.paint, 'optionalAccess', _193 => _193.type]) === "effect" ? [st2.paint] : []));
3198
3527
  target.replaceLiveStrokes(e.layerId, list);
3199
3528
  }
3200
3529
  } 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 });
3530
+ const list = (_nullishCoalesce(_optionalChain([e, 'access', _194 => _194.data, 'optionalAccess', _195 => _195.stickers]), () => ( []))).map((x) => s === 1 ? x : { ...x, x: x.x * s, y: x.y * s, size: x.size * s });
3202
3531
  target.replaceStickers(e.layerId, list);
3203
3532
  } else target.flattenRelief(e.layerId);
3204
3533
  continue;
@@ -3211,8 +3540,8 @@ async function replayPainting(source, target, options = {}) {
3211
3540
  switch (e.kind) {
3212
3541
  case "dry": {
3213
3542
  const rect = e.data;
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 });
3543
+ const ids = _optionalChain([e, 'access', _196 => _196.layerId, 'optionalAccess', _197 => _197.split, 'call', _198 => _198(","), 'access', _199 => _199.filter, 'call', _200 => _200(Boolean)]);
3544
+ if (_optionalChain([ids, 'optionalAccess', _201 => _201.length])) for (const id of ids) target.dryPaint({ layerId: id, rect: rect ? sr(rect) : void 0 });
3216
3545
  else target.dryPaint({ rect: rect ? sr(rect) : void 0 });
3217
3546
  break;
3218
3547
  }
@@ -3243,7 +3572,7 @@ async function replayPainting(source, target, options = {}) {
3243
3572
  strokes++;
3244
3573
  if (!target.getLayer(st.layerId)) continue;
3245
3574
  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)) };
3246
- if (st.brush.engine === "wet" && _optionalChain([st, 'access', _174 => _174.steps, 'optionalAccess', _175 => _175.length]) === st.points.length) {
3575
+ if (st.brush.engine === "wet" && _optionalChain([st, 'access', _202 => _202.steps, 'optionalAccess', _203 => _203.length]) === st.points.length) {
3247
3576
  await target.wetIdle();
3248
3577
  await advanceTo(st.steps[0]);
3249
3578
  const session = target.beginStroke({ ...init, start: st.points[0] });
@@ -3263,7 +3592,7 @@ async function replayPainting(source, target, options = {}) {
3263
3592
  await target.wetIdle();
3264
3593
  target.paintStroke({ ...init, points: st.points });
3265
3594
  }
3266
- await _optionalChain([options, 'access', _176 => _176.onStroke, 'optionalCall', _177 => _177(strokes)]);
3595
+ await _optionalChain([options, 'access', _204 => _204.onStroke, 'optionalCall', _205 => _205(strokes)]);
3267
3596
  }
3268
3597
  void IDENTITY_AFFINE;
3269
3598
  }
@@ -3286,7 +3615,7 @@ function timeLapseVideo(frameCount, width, height, fps, holdMs, name = "Time-lap
3286
3615
  }
3287
3616
  const video = { version: 1, name, width: w, height: h, fps, background: "#000000", assets, tracks: [{ id: "v1", kind: "video", clips }] };
3288
3617
  const durationMs = frameCount ? clips[clips.length - 1].start + clips[clips.length - 1].duration : 0;
3289
- return { video, ffmpeg: _chunkZQIWBJGHcjs.toVideoFfmpeg.call(void 0, video, { output: "timelapse.mp4" }), durationMs };
3618
+ return { video, ffmpeg: _chunkXNZFVYC2cjs.toVideoFfmpeg.call(void 0, video, { output: "timelapse.mp4" }), durationMs };
3290
3619
  }
3291
3620
  async function buildTimeLapse(source, target, options = {}) {
3292
3621
  const maxSide = Math.max(32, Math.min(4096, Math.round(_nullishCoalesce(options.maxSide, () => ( 720)))));
@@ -3308,7 +3637,7 @@ async function buildTimeLapse(source, target, options = {}) {
3308
3637
  wetChunk: wetEvery > 0 ? wetEvery : 0,
3309
3638
  onStroke: (i) => {
3310
3639
  if (i % perFrame === 0 || i === total) shoot(i);
3311
- _optionalChain([options, 'access', _178 => _178.onProgress, 'optionalCall', _179 => _179(total ? i / total : 1)]);
3640
+ _optionalChain([options, 'access', _206 => _206.onProgress, 'optionalCall', _207 => _207(total ? i / total : 1)]);
3312
3641
  },
3313
3642
  onWetSteps: wetEvery > 0 ? () => shoot(-1) : void 0
3314
3643
  });
@@ -3369,7 +3698,7 @@ function checkPaintEffectPlace(d, place) {
3369
3698
  else if (d.uses.includes("filter") && place !== "light") instead = "; run it on a layer's dried paint with apply_effect instead";
3370
3699
  else if (place === "light" && d.uses.some((u) => PAINT_EFFECT_PLACES.layer.includes(u))) instead = "; add it as an effect layer (add_effect_layer) or fill a layer with it (fill_layer_with_effect) instead";
3371
3700
  else if (place === "paper" && d.uses.some((u) => u === "overlay" || u === "title")) instead = "; add it as an effect layer with add_effect_layer instead";
3372
- throw new (0, _chunkJAOSL334cjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) is a ${d.uses.join(" / ") || d.kind} effect, and ${PLACE_WORDS[place]}${instead}.`, d.id);
3701
+ throw new (0, _chunkGHCSC2WPcjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) is a ${d.uses.join(" / ") || d.kind} effect, and ${PLACE_WORDS[place]}${instead}.`, d.id);
3373
3702
  }
3374
3703
  var PAPER_EFFECT_BLENDS = ["multiply", "normal", "overlay", "soft-light", "screen", "color"];
3375
3704
  var paperVersion = 1;
@@ -3395,10 +3724,10 @@ function mixPixels(before, after, amount) {
3395
3724
  }
3396
3725
  return out;
3397
3726
  }
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}
3727
+ var PaintEffectsRenderer = (_class10 = class {constructor() { _class10.prototype.__init75.call(this);_class10.prototype.__init76.call(this);_class10.prototype.__init77.call(this); }
3728
+ __init75() {this.pool = new (0, _chunkPY4GWSE2cjs.EffectRendererPool)({ maxRenderers: 3 })}
3729
+ __init76() {this.descriptions = /* @__PURE__ */ new Map()}
3730
+ __init77() {this.provider = null}
3402
3731
  /** Forgets cached descriptions and renderers when the provider changes. */
3403
3732
  follow(provider) {
3404
3733
  if (provider === this.provider) return;
@@ -3428,7 +3757,7 @@ var PaintEffectsRenderer = (_class9 = class {constructor() { _class9.prototype._
3428
3757
  this.pool.clear();
3429
3758
  this.descriptions.clear();
3430
3759
  }
3431
- }, _class9);
3760
+ }, _class10);
3432
3761
  var sourceOut = (s) => ({
3433
3762
  effect: s.effect,
3434
3763
  ...s.provider ? { provider: s.provider } : {},
@@ -3483,7 +3812,7 @@ function parseColor(value, what = "color") {
3483
3812
  return value.trim().toLowerCase();
3484
3813
  }
3485
3814
  function rgbaOf(hex) {
3486
- const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, hex);
3815
+ const [r, g, b] = _chunk5FZS7J7Ccjs.hexToRgb8.call(void 0, hex);
3487
3816
  return [r, g, b, 255];
3488
3817
  }
3489
3818
  function rgbToHex(r, g, b) {
@@ -3496,87 +3825,90 @@ function unionRect(a, b) {
3496
3825
  const y = Math.min(a.y, b.y);
3497
3826
  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 };
3498
3827
  }
3499
- var isPixelLayer = (l) => _optionalChain([l, 'optionalAccess', _180 => _180.type]) === "pixel";
3500
- var PaintEditorController = (_class10 = class _PaintEditorController {
3828
+ var isPixelLayer = (l) => _optionalChain([l, 'optionalAccess', _208 => _208.type]) === "pixel";
3829
+ var PaintEditorController = (_class11 = class _PaintEditorController {
3501
3830
 
3502
- __init74() {this.extensions = {}}
3503
- __init75() {this.undoStack = []}
3504
- __init76() {this.redoStack = []}
3831
+ __init78() {this.extensions = {}}
3832
+ __init79() {this.undoStack = []}
3833
+ __init80() {this.redoStack = []}
3505
3834
 
3506
3835
 
3507
3836
 
3508
3837
  /** The library list in the state (the same array until the user brushes change, so thumbnails are not redrawn). */
3509
- __init77() {this.library = null}
3838
+ __init81() {this.library = null}
3510
3839
 
3511
- __init78() {this.secondaryColor = "#ffffff"}
3840
+ __init82() {this.secondaryColor = "#ffffff"}
3512
3841
  /** Gradient paint chosen with `setPaint` (`null`: the paint colour). */
3513
- __init79() {this.gradient = null}
3842
+ __init83() {this.gradient = null}
3514
3843
  /** Effect paint chosen with `setEffectPaint` (rendered), and one still rendering. */
3515
- __init80() {this.effectPaint = null}
3516
- __init81() {this.effectPaintPending = null}
3844
+ __init84() {this.effectPaint = null}
3845
+ __init85() {this.effectPaintPending = null}
3517
3846
  /** Changes whenever the paint is chosen, so an effect paint that finishes rendering late does not take over. */
3518
- __init82() {this.paintEpoch = 0}
3847
+ __init86() {this.paintEpoch = 0}
3519
3848
  /** Rendered effect paints by `effectPaintKey` (most recently used last). */
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 } }}
3849
+ __init87() {this.effectPaintPixels = /* @__PURE__ */ new Map()}
3850
+ __init88() {this.mixerLoad = _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD}
3851
+ __init89() {this.view = { ...DEFAULT_VIEW, pan: { ...DEFAULT_VIEW.pan } }}
3523
3852
 
3524
- __init86() {this.selectedStroke = null}
3525
- __init87() {this.selectedSticker = null}
3853
+ __init90() {this.selectedStroke = null}
3854
+ __init91() {this.selectedSticker = null}
3526
3855
  /** A sticker drag in progress (`previewSticker`): the document before it, so the drop undoes in one step, and the one it showed last. */
3527
- __init88() {this.stickerPreview = null}
3856
+ __init92() {this.stickerPreview = null}
3528
3857
 
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}
3858
+ __init93() {this.listeners = /* @__PURE__ */ new Set()}
3859
+ __init94() {this.version = 0}
3860
+ __init95() {this.cached = null}
3861
+ __init96() {this.session = null}
3862
+ __init97() {this.live = null}
3863
+ __init98() {this.liveLayers = null}
3864
+ __init99() {this.dirty = "all"}
3865
+ __init100() {this.seedCounter = 1}
3866
+ __init101() {this.strokeCounter = 1}
3867
+ /** Light paint: each layer's spill settings (saved with the document, not undo steps; missing = the defaults). */
3868
+ __init102() {this.lightSpills = {}}
3869
+ __init103() {this.lightCaches = /* @__PURE__ */ new Map()}
3538
3870
  /** 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}
3871
+ __init104() {this.effectPlaybackState = { playing: true, fps: 30 }}
3872
+ __init105() {this.effectTime = 0}
3541
3873
  /** While a frame is drawn: the time live effect strokes are drawn at (else their own time). */
3542
- __init100() {this.liveEffectAt = null}
3874
+ __init106() {this.liveEffectAt = null}
3543
3875
  /** 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()}
3876
+ __init107() {this.animatedLayers = /* @__PURE__ */ new Map()}
3545
3877
  /** `renderAnimationFrame` / `withEffectTime`: layer surfaces at another time, for one render. */
3546
- __init102() {this.frameOverride = null}
3878
+ __init108() {this.frameOverride = null}
3547
3879
  /** 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()}
3880
+ __init109() {this.paintedBounds = /* @__PURE__ */ new WeakMap()}
3549
3881
 
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()}
3882
+ __init110() {this.swatches = DEFAULT_SWATCHES.map((s) => ({ ...s }))}
3883
+ __init111() {this.pad = new MixingPad()}
3884
+ __init112() {this.savedPads = []}
3885
+ __init113() {this.nextPadId = 1}
3886
+ __init114() {this.tracingVersion = 1}
3887
+ __init115() {this.tracingCache = null}
3888
+ __init116() {this.imagePixels = /* @__PURE__ */ new Map()}
3889
+ __init117() {this.nextReferenceId = 1}
3890
+ __init118() {this.viewCompositor = createPaintCompositor()}
3891
+ __init119() {this.exportCompositor = createPaintCompositor()}
3560
3892
  /** Wet media: the simulation, its settings and time. */
3561
3893
 
3562
3894
 
3563
3895
  /** Where the light comes from for thick paint (saved with the document, not undo steps). */
3564
- __init114() {this.lighting = { ...DEFAULT_LIGHTING }}
3565
- __init115() {this.litCaches = /* @__PURE__ */ new Map()}
3896
+ __init120() {this.lighting = { ...DEFAULT_LIGHTING }}
3897
+ __init121() {this.litCaches = /* @__PURE__ */ new Map()}
3566
3898
  /** The live impasto stroke's channels. */
3567
- __init116() {this.liveThick = null}
3899
+ __init122() {this.liveThick = null}
3568
3900
  /** Set while an operation that changes wet paint runs: the simulation before it (for undo). */
3569
- __init117() {this.wetOp = null}
3901
+ __init123() {this.wetOp = null}
3570
3902
  /** The effects provider slot (see `setEffectsProvider`). */
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);
3903
+ __init124() {this.effects = new (0, _chunkGHCSC2WPcjs.EffectsSlot)()}
3904
+ __init125() {this.effectsRenderer = new PaintEffectsRenderer()}
3905
+ __init126() {this.paperEffectNodeCache = null}
3906
+ constructor(options = {}) {;_class11.prototype.__init78.call(this);_class11.prototype.__init79.call(this);_class11.prototype.__init80.call(this);_class11.prototype.__init81.call(this);_class11.prototype.__init82.call(this);_class11.prototype.__init83.call(this);_class11.prototype.__init84.call(this);_class11.prototype.__init85.call(this);_class11.prototype.__init86.call(this);_class11.prototype.__init87.call(this);_class11.prototype.__init88.call(this);_class11.prototype.__init89.call(this);_class11.prototype.__init90.call(this);_class11.prototype.__init91.call(this);_class11.prototype.__init92.call(this);_class11.prototype.__init93.call(this);_class11.prototype.__init94.call(this);_class11.prototype.__init95.call(this);_class11.prototype.__init96.call(this);_class11.prototype.__init97.call(this);_class11.prototype.__init98.call(this);_class11.prototype.__init99.call(this);_class11.prototype.__init100.call(this);_class11.prototype.__init101.call(this);_class11.prototype.__init102.call(this);_class11.prototype.__init103.call(this);_class11.prototype.__init104.call(this);_class11.prototype.__init105.call(this);_class11.prototype.__init106.call(this);_class11.prototype.__init107.call(this);_class11.prototype.__init108.call(this);_class11.prototype.__init109.call(this);_class11.prototype.__init110.call(this);_class11.prototype.__init111.call(this);_class11.prototype.__init112.call(this);_class11.prototype.__init113.call(this);_class11.prototype.__init114.call(this);_class11.prototype.__init115.call(this);_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);_class11.prototype.__init122.call(this);_class11.prototype.__init123.call(this);_class11.prototype.__init124.call(this);_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);
3575
3907
  this.historyLimit = Math.max(1, _nullishCoalesce(options.historyLimit, () => ( 200)));
3576
3908
  this.doc = createPaintDocument({ width: options.width, height: options.height, dpi: options.dpi, paper: this.resolvePaper(options.paper) });
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);
3909
+ this.userBrushes = (_nullishCoalesce(options.brushes, () => ( []))).map((b) => _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, { ...b, builtin: false }));
3910
+ const start = _nullishCoalesce(this.findBrush(_nullishCoalesce(options.brushId, () => ( _chunk5FZS7J7Ccjs.DEFAULT_BRUSH_ID))), () => ( this.findBrush(_chunk5FZS7J7Ccjs.DEFAULT_BRUSH_ID)));
3911
+ this.brush = _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, start);
3580
3912
  this.color = options.color ? parseColor(options.color) : "#1f2937";
3581
3913
  this.hand = options.hand === "left" ? "left" : "right";
3582
3914
  this.encodeImage = options.encodeImage;
@@ -3587,7 +3919,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3587
3919
  canvas: () => this.doc.canvas,
3588
3920
  paper: () => this.doc.paper,
3589
3921
  layerSurface: (id) => {
3590
- const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _181 => _181.layer]);
3922
+ const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _209 => _209.layer]);
3591
3923
  return isPixelLayer(l) ? l.surface : null;
3592
3924
  },
3593
3925
  updateSurfaces: (surfaces, rects) => {
@@ -3601,7 +3933,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3601
3933
  },
3602
3934
  options.wet
3603
3935
  );
3604
- this.wet.onDriverDone = () => _optionalChain([this, 'access', _182 => _182.session, 'optionalAccess', _183 => _183.completeWetEnd, 'call', _184 => _184()]);
3936
+ this.wet.onDriverDone = () => _optionalChain([this, 'access', _210 => _210.session, 'optionalAccess', _211 => _211.completeWetEnd, 'call', _212 => _212()]);
3605
3937
  }
3606
3938
  // --- State and events ----------------------------------------------------------
3607
3939
  subscribe(listener) {
@@ -3623,7 +3955,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3623
3955
  this.cached = {
3624
3956
  document: this.doc,
3625
3957
  brush: this.brush,
3626
- brushes: this.library ??= [..._chunk5BR7LC24cjs.BUILTIN_BRUSHES, ...this.userBrushes],
3958
+ brushes: this.library ??= [..._chunk5FZS7J7Ccjs.BUILTIN_BRUSHES, ...this.userBrushes],
3627
3959
  color: this.color,
3628
3960
  secondaryColor: this.secondaryColor,
3629
3961
  paint: _nullishCoalesce(this.gradient, () => ( { type: "color", color: this.color })),
@@ -3637,8 +3969,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3637
3969
  selectedSticker: this.selectedSticker,
3638
3970
  canUndo: this.undoStack.length > 0 && !this.session,
3639
3971
  canRedo: this.redoStack.length > 0 && !this.session,
3640
- undoLabel: _nullishCoalesce(_optionalChain([last, 'optionalAccess', _185 => _185.label]), () => ( null)),
3641
- redoLabel: _nullishCoalesce(_optionalChain([next, 'optionalAccess', _186 => _186.label]), () => ( null)),
3972
+ undoLabel: _nullishCoalesce(_optionalChain([last, 'optionalAccess', _213 => _213.label]), () => ( null)),
3973
+ redoLabel: _nullishCoalesce(_optionalChain([next, 'optionalAccess', _214 => _214.label]), () => ( null)),
3642
3974
  guides: this.guides,
3643
3975
  swatches: this.swatches,
3644
3976
  mixingPadVersion: this.pad.version,
@@ -3651,10 +3983,10 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3651
3983
  overlay: this.wet.overlay,
3652
3984
  instantDry: this.wet.instantDry,
3653
3985
  speed: this.wet.speed,
3654
- clock: _nullishCoalesce(_optionalChain([this, 'access', _187 => _187.wet, 'access', _188 => _188.world, 'optionalAccess', _189 => _189.clock]), () => ( 0)),
3986
+ clock: _nullishCoalesce(_optionalChain([this, 'access', _215 => _215.wet, 'access', _216 => _216.world, 'optionalAccess', _217 => _217.clock]), () => ( 0)),
3655
3987
  tilt: { ...this.wet.tilt },
3656
3988
  gpu: this.wet.gpuMode,
3657
- gpuActive: Boolean(_optionalChain([this, 'access', _190 => _190.wet, 'access', _191 => _191.world, 'optionalAccess', _192 => _192.gpuActive]))
3989
+ gpuActive: Boolean(_optionalChain([this, 'access', _218 => _218.wet, 'access', _219 => _219.world, 'optionalAccess', _220 => _220.gpuActive]))
3658
3990
  },
3659
3991
  version: this.version
3660
3992
  };
@@ -3673,7 +4005,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3673
4005
  return d;
3674
4006
  }
3675
4007
  // --- History -------------------------------------------------------------------
3676
- __init121() {this.docRevision = 0}
4008
+ __init127() {this.docRevision = 0}
3677
4009
  /**
3678
4010
  * Bumped on every change to the painting (a committed or amended step, undo, redo, opening a
3679
4011
  * document), not on wet-paint frames or view changes. `createPaintEditorDocument` uses it.
@@ -3756,9 +4088,9 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3756
4088
  return wetIds;
3757
4089
  }
3758
4090
  /** Records a wet event (for replays) in the document being built. */
3759
- __init122() {this.pendingWetEvents = []}
4091
+ __init128() {this.pendingWetEvents = []}
3760
4092
  logWet(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 });
4093
+ this.pendingWetEvents.push({ clock: _nullishCoalesce(_optionalChain([this, 'access', _221 => _221.wet, 'access', _222 => _222.world, 'optionalAccess', _223 => _223.clock]), () => ( 0)), at: this.doc.strokes.length, ...e });
3762
4094
  }
3763
4095
  /** The document with a paint event appended (for replays). */
3764
4096
  withPaintEvent(doc, e) {
@@ -3819,7 +4151,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3819
4151
  // --- Canvas and paper ----------------------------------------------------------
3820
4152
  resolvePaper(paper) {
3821
4153
  if (typeof paper === "string") {
3822
- const p = _chunk5BR7LC24cjs.paperPreset.call(void 0, paper);
4154
+ const p = _chunk5FZS7J7Ccjs.paperPreset.call(void 0, paper);
3823
4155
  if (!p) throw new PaintEditorError(`Unknown paper "${paper}". Call list_papers for the ids.`);
3824
4156
  return p;
3825
4157
  }
@@ -3902,7 +4234,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3902
4234
  }
3903
4235
  let next = { ...shiftDocument(this.withWetEvents(this.doc), left, top), canvas: { ...was, width, height } };
3904
4236
  for (const id of keptWet) {
3905
- const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, next.layers, id), 'optionalAccess', _196 => _196.layer]);
4237
+ const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, next.layers, id), 'optionalAccess', _224 => _224.layer]);
3906
4238
  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 } })) };
3907
4239
  }
3908
4240
  if (this.wet.world) {
@@ -3930,23 +4262,23 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3930
4262
  this.requireIdle("changing the paper");
3931
4263
  let base = this.doc.paper;
3932
4264
  if (update.preset !== void 0 && update.preset !== this.doc.paper.preset) {
3933
- const p = _chunk5BR7LC24cjs.paperPreset.call(void 0, update.preset);
4265
+ const p = _chunk5FZS7J7Ccjs.paperPreset.call(void 0, update.preset);
3934
4266
  if (!p) throw new PaintEditorError(`Unknown paper "${update.preset}". Call list_papers for the ids.`);
3935
4267
  base = p;
3936
4268
  }
3937
4269
  const { preset: _p, ...rest } = update;
3938
4270
  if (rest.color !== void 0) parseColor(rest.color, "paper color");
3939
- const paper = _chunk5BR7LC24cjs.normalizePaper.call(void 0, { ...rest, preset: base.preset, offset: _nullishCoalesce(rest.offset, () => ( this.doc.paper.offset)) }, base);
4271
+ const paper = _chunk5FZS7J7Ccjs.normalizePaper.call(void 0, { ...rest, preset: base.preset, offset: _nullishCoalesce(rest.offset, () => ( this.doc.paper.offset)) }, base);
3940
4272
  const changedKeys = Object.keys(rest).filter((k) => k !== "name");
3941
4273
  if (changedKeys.length && update.preset === void 0) paper.preset = base.preset;
3942
4274
  if (this.wet.world) this.beginWetOp();
3943
4275
  this.commit("Paper", { ...this.doc, paper });
3944
4276
  this.wet.paperChanged();
3945
- return _chunk5BR7LC24cjs.clonePaper.call(void 0, paper);
4277
+ return _chunk5FZS7J7Ccjs.clonePaper.call(void 0, paper);
3946
4278
  }
3947
4279
  // --- Layers --------------------------------------------------------------------
3948
4280
  getLayer(id) {
3949
- return _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _197 => _197.layer]);
4281
+ return _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _225 => _225.layer]);
3950
4282
  }
3951
4283
  /** The active pixel layer (or `undefined`). */
3952
4284
  getActiveLayer() {
@@ -3965,7 +4297,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3965
4297
  walk(this.doc.layers, null, 0);
3966
4298
  return out;
3967
4299
  }
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))) {
4300
+ describeLayer(l, parentId = _nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, l.id), 'optionalAccess', _226 => _226.parent, 'optionalAccess', _227 => _227.id]), () => ( null)), depth = _nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, l.id), 'optionalAccess', _228 => _228.path, 'access', _229 => _229.length]), () => ( 0))) {
3969
4301
  let bounds = null;
3970
4302
  if (l.type === "pixel") {
3971
4303
  const b = _chunk474IQG74cjs.contentBounds.call(void 0, l.surface);
@@ -3982,7 +4314,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3982
4314
  locks: { ..._nullishCoalesce(l.locks, () => ( {})) },
3983
4315
  active: l.id === this.doc.activeLayerId,
3984
4316
  bounds,
3985
- ..._optionalChain([this, 'access', _202 => _202.doc, 'access', _203 => _203.liveStrokes, 'optionalAccess', _204 => _204[l.id]]) ? { liveStrokes: this.doc.liveStrokes[l.id].length } : {},
4317
+ ..._optionalChain([this, 'access', _230 => _230.doc, 'access', _231 => _231.liveStrokes, 'optionalAccess', _232 => _232[l.id]]) ? { liveStrokes: this.doc.liveStrokes[l.id].length } : {},
3986
4318
  parentId,
3987
4319
  depth
3988
4320
  };
@@ -4014,7 +4346,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4014
4346
  this.requireIdle("adding a layer");
4015
4347
  const count = _chunkHB4ICNNAcjs.flattenLayers.call(void 0, this.doc.layers).length;
4016
4348
  const type = _nullishCoalesce(options.type, () => ( "pixel"));
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}`);
4349
+ const name = _optionalChain([options, 'access', _233 => _233.name, 'optionalAccess', _234 => _234.trim, 'call', _235 => _235()]) || (type === "group" ? `Group ${count + 1}` : type === "strokes" ? `Strokes ${count + 1}` : `Layer ${count + 1}`);
4018
4350
  const layer = type === "group" ? { type: "group", id: newPaintLayerId(), name, visible: true, opacity: 1, blend: "pass-through", children: [] } : createPaintLayer(name);
4019
4351
  let parentId = null;
4020
4352
  let index;
@@ -4026,7 +4358,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4026
4358
  } else if (above) {
4027
4359
  const loc = _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, above);
4028
4360
  if (!loc) throw new PaintEditorError(`Unknown layer "${above}". Call list_layers for the ids.`);
4029
- parentId = _nullishCoalesce(_optionalChain([loc, 'access', _208 => _208.parent, 'optionalAccess', _209 => _209.id]), () => ( null));
4361
+ parentId = _nullishCoalesce(_optionalChain([loc, 'access', _236 => _236.parent, 'optionalAccess', _237 => _237.id]), () => ( null));
4030
4362
  index = loc.index + 1;
4031
4363
  }
4032
4364
  const layers = _chunkHB4ICNNAcjs.insertLayer.call(void 0, this.doc.layers, layer, parentId, index);
@@ -4067,8 +4399,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4067
4399
  }
4068
4400
  const layers = _chunkHB4ICNNAcjs.mapLayer.call(void 0, this.doc.layers, id, () => null);
4069
4401
  let active = this.doc.activeLayerId;
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)) });
4402
+ if (active && !_chunkHB4ICNNAcjs.findLayer.call(void 0, layers, active)) active = _nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.flattenLayers.call(void 0, layers), 'access', _238 => _238.filter, 'call', _239 => _239((x) => x.type === "pixel"), 'access', _240 => _240.pop, 'call', _241 => _241(), 'optionalAccess', _242 => _242.id]), () => ( null));
4403
+ 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)), ...this.lightWithout(_chunkHB4ICNNAcjs.flattenLayers.call(void 0, [l]).map((x) => x.id)) });
4072
4404
  return { removed: id, activeLayerId: active };
4073
4405
  }
4074
4406
  duplicateLayer(id) {
@@ -4082,16 +4414,19 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4082
4414
  const thick = { ..._nullishCoalesce(this.doc.thick, () => ( {})) };
4083
4415
  const stickers = { ..._nullishCoalesce(this.doc.stickers, () => ( {})) };
4084
4416
  const liveStrokes = { ..._nullishCoalesce(this.doc.liveStrokes, () => ( {})) };
4417
+ const light = { ..._nullishCoalesce(this.doc.light, () => ( {})) };
4085
4418
  const copy = (l) => {
4086
4419
  const id2 = newPaintLayerId();
4420
+ if (light[l.id]) light[id2] = light[l.id];
4421
+ if (this.lightSpills[l.id]) this.lightSpills[id2] = { ...this.lightSpills[l.id] };
4087
4422
  if (thick[l.id]) thick[id2] = thick[l.id];
4088
4423
  if (stickers[l.id]) stickers[id2] = stickers[l.id].map((x) => ({ ...x }));
4089
4424
  if (liveStrokes[l.id]) liveStrokes[id2] = liveStrokes[l.id].map((x) => ({ ...x, id: `s${this.strokeCounter++}`, layerId: id2 }));
4090
4425
  return l.type === "group" ? { ...l, id: id2, children: l.children.map(copy) } : { ...l, id: id2 };
4091
4426
  };
4092
4427
  const dup = { ...copy(loc.layer), name: `${loc.layer.name} copy` };
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 } : {} }));
4428
+ const layers = _chunkHB4ICNNAcjs.insertLayer.call(void 0, this.doc.layers, dup, _nullishCoalesce(_optionalChain([loc, 'access', _243 => _243.parent, 'optionalAccess', _244 => _244.id]), () => ( null)), loc.index + 1);
4429
+ this.commit("Duplicate layer", this.withWetEvents({ ...this.doc, layers, activeLayerId: dup.id, ...this.doc.thick ? { thick } : {}, ...this.doc.stickers ? { stickers } : {}, ...this.doc.liveStrokes ? { liveStrokes } : {}, ...this.doc.light ? { light } : {} }));
4095
4430
  return this.describeLayer(dup);
4096
4431
  }
4097
4432
  /** Moves a layer to `index` (0 = bottom) within `parentId` (default its own parent; `null` = top level). */
@@ -4099,7 +4434,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4099
4434
  this.requireIdle("moving a layer");
4100
4435
  const loc = _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id);
4101
4436
  if (!loc) throw new PaintEditorError(`Unknown layer "${id}". Call list_layers for the ids.`);
4102
- const target = parentId === void 0 ? _nullishCoalesce(_optionalChain([loc, 'access', _217 => _217.parent, 'optionalAccess', _218 => _218.id]), () => ( null)) : parentId;
4437
+ const target = parentId === void 0 ? _nullishCoalesce(_optionalChain([loc, 'access', _245 => _245.parent, 'optionalAccess', _246 => _246.id]), () => ( null)) : parentId;
4103
4438
  if (target) {
4104
4439
  const g = this.requireLayer(target);
4105
4440
  if (g.type !== "group") throw new PaintEditorError(`Layer "${g.name}" is not a group; pass a group id as parentId.`);
@@ -4118,7 +4453,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4118
4453
  const pre = _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id);
4119
4454
  if (pre) {
4120
4455
  const sib = pre.parent ? pre.parent.children : this.doc.layers;
4121
- this.dryLayersFirst([id, _optionalChain([sib, 'access', _219 => _219[pre.index - 1], 'optionalAccess', _220 => _220.id])].filter((x) => Boolean(x)));
4456
+ this.dryLayersFirst([id, _optionalChain([sib, 'access', _247 => _247[pre.index - 1], 'optionalAccess', _248 => _248.id])].filter((x) => Boolean(x)));
4122
4457
  }
4123
4458
  const loc = _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id);
4124
4459
  if (!loc) throw new PaintEditorError(`Unknown layer "${id}". Call list_layers for the ids.`);
@@ -4137,7 +4472,11 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4137
4472
  }
4138
4473
  const merged = { ...below, surface, offset: { x: 0, y: 0 } };
4139
4474
  const layers = _chunkHB4ICNNAcjs.mapLayer.call(void 0, _chunkHB4ICNNAcjs.mapLayer.call(void 0, this.doc.layers, upper.id, () => null), below.id, () => merged);
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]) }));
4475
+ const toDoc = (t, l) => t ? _chunk474IQG74cjs.translateSurface.call(void 0, t, l.offset.x, l.offset.y) : void 0;
4476
+ const mergedLight = _optionalChain([this, 'access', _249 => _249.doc, 'access', _250 => _250.light, 'optionalAccess', _251 => _251[upper.id]]) || _optionalChain([this, 'access', _252 => _252.doc, 'access', _253 => _253.light, 'optionalAccess', _254 => _254[below.id]]) ? mergeLight(toDoc(_optionalChain([this, 'access', _255 => _255.doc, 'access', _256 => _256.light, 'optionalAccess', _257 => _257[below.id]]), below), upper.visible ? toDoc(_optionalChain([this, 'access', _258 => _258.doc, 'access', _259 => _259.light, 'optionalAccess', _260 => _260[upper.id]]), upper) : void 0, _chunk474IQG74cjs.translateSurface.call(void 0, upper.surface, upper.offset.x, upper.offset.y)) : void 0;
4477
+ const lightAfter = _nullishCoalesce(this.lightWithout([upper.id, below.id]).light, () => ( (this.doc.light ? { ...this.doc.light } : void 0)));
4478
+ const light = mergedLight ? { ..._nullishCoalesce(lightAfter, () => ( {})), [below.id]: mergedLight } : lightAfter;
4479
+ 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]), ...light ? { light } : {} }));
4141
4480
  return this.describeLayer(merged);
4142
4481
  }
4143
4482
  /** Removes every pixel of a layer. */
@@ -4149,7 +4488,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4149
4488
  this.wet.world.removeLane(l.id);
4150
4489
  }
4151
4490
  const bounds = this.describeLayer(l).bounds;
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 }));
4491
+ 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', _261 => _261.doc, 'access', _262 => _262.liveStrokes, 'optionalAccess', _263 => _263[l.id]]) ? { liveStrokes: { ...this.doc.liveStrokes, [l.id]: [] } } : {}, ...this.lightWithout([l.id]) }, { kind: "clear", layerId: l.id }));
4153
4492
  return { layerId: l.id, bounds };
4154
4493
  }
4155
4494
  /**
@@ -4159,8 +4498,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4159
4498
  fillLayer(options = {}) {
4160
4499
  this.requireIdle("filling");
4161
4500
  const fillTarget = this.paintTarget(options.layerId, "fill");
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).`);
4501
+ if (_optionalChain([this, 'access', _264 => _264.doc, 'access', _265 => _265.stickers, 'optionalAccess', _266 => _266[fillTarget.id], 'optionalAccess', _267 => _267.length])) throw new PaintEditorError(`Layer "${fillTarget.name}" is a sticker layer; fill another layer (add_layer).`);
4502
+ if (_optionalChain([this, 'access', _268 => _268.doc, 'access', _269 => _269.liveStrokes, 'optionalAccess', _270 => _270[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).`);
4164
4503
  this.dryLayersFirst([_nullishCoalesce(_nullishCoalesce(options.layerId, () => ( this.doc.activeLayerId)), () => ( ""))]);
4165
4504
  const l = this.paintTarget(options.layerId, "fill");
4166
4505
  const color = options.color === void 0 ? this.color : parseColor(options.color);
@@ -4193,7 +4532,9 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4193
4532
  offset: l.offset
4194
4533
  };
4195
4534
  const surface = this.applyCoverage(l, coverage, rect, apply);
4196
- this.commit("Fill layer", this.withPaintEvent(this.withWetEvents({ ...this.doc, layers: _chunkHB4ICNNAcjs.mapLayer.call(void 0, this.doc.layers, l.id, () => ({ ...l, surface })) }), { kind: "fill", layerId: l.id, data: { color, opacity, grain: grainAmount, pressure, blend } }));
4535
+ const lit = _optionalChain([this, 'access', _271 => _271.doc, 'access', _272 => _272.light, 'optionalAccess', _273 => _273[l.id]]);
4536
+ const light = lit ? withLayerLight(this.doc.light, l.id, scaleLight(lit, 1 - opacity)) : void 0;
4537
+ this.commit("Fill layer", this.withPaintEvent(this.withWetEvents({ ...this.doc, layers: _chunkHB4ICNNAcjs.mapLayer.call(void 0, this.doc.layers, l.id, () => ({ ...l, surface })), ...light ? { light } : {} }), { kind: "fill", layerId: l.id, data: { color, opacity, grain: grainAmount, pressure, blend } }));
4197
4538
  return { layerId: l.id, bounds: rect };
4198
4539
  }
4199
4540
  applyCoverage(l, coverage, rect, apply) {
@@ -4201,7 +4542,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4201
4542
  const tiles = new Map(base.tiles);
4202
4543
  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 })) {
4203
4544
  const key = _chunk474IQG74cjs.tileKey.call(void 0, tx, ty);
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)));
4545
+ const src = _nullishCoalesce(_optionalChain([base, 'access', _274 => _274.tiles, 'access', _275 => _275.get, 'call', _276 => _276(key), 'optionalAccess', _277 => _277.data]), () => ( new Uint8ClampedArray(_chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE * 4)));
4205
4546
  const tile = _chunk474IQG74cjs.finishTile.call(void 0, _chunkPQ3H2AANcjs.applyStrokeToTile.call(void 0, coverage, src, 4, tx, ty, apply), 4, 0);
4206
4547
  if (tile) tiles.set(key, tile);
4207
4548
  else tiles.delete(key);
@@ -4234,7 +4575,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4234
4575
  }
4235
4576
  /** The effect a layer is drawn from, or `undefined` for other layers. */
4236
4577
  getEffectLayer(layerId) {
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;
4578
+ return _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, layerId) ? _optionalChain([this, 'access', _278 => _278.doc, 'access', _279 => _279.effectLayers, 'optionalAccess', _280 => _280[layerId]]) : void 0;
4238
4579
  }
4239
4580
  /** The effect paper (without its pixels), or `null`. */
4240
4581
  getPaperEffect() {
@@ -4252,7 +4593,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4252
4593
  if (v === null) merged[k] = d.defaults[k];
4253
4594
  else given[k] = v;
4254
4595
  }
4255
- return { ...merged, ..._chunkJAOSL334cjs.resolveEffectValues.call(void 0, { ...d, defaults: merged }, given) };
4596
+ return { ...merged, ..._chunkGHCSC2WPcjs.resolveEffectValues.call(void 0, { ...d, defaults: merged }, given) };
4256
4597
  }
4257
4598
  /** Renders a generator, overlay or title effect at the canvas size. */
4258
4599
  renderEffectCanvas(d, values, time = 0) {
@@ -4271,8 +4612,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4271
4612
  /** A pixel layer that takes effect pixels: unlocked, visible, no sticker layer, no wet paint. */
4272
4613
  effectTarget(layerId, what) {
4273
4614
  const l = this.paintTarget(layerId, what);
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).`);
4615
+ if (_optionalChain([this, 'access', _281 => _281.doc, 'access', _282 => _282.stickers, 'optionalAccess', _283 => _283[l.id], 'optionalAccess', _284 => _284.length])) throw new PaintEditorError(`Layer "${l.name}" is a sticker layer; ${what} another layer (add_layer).`);
4616
+ if (_optionalChain([this, 'access', _285 => _285.doc, 'access', _286 => _286.liveStrokes, 'optionalAccess', _287 => _287[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).`);
4276
4617
  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.`);
4277
4618
  return l;
4278
4619
  }
@@ -4311,8 +4652,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4311
4652
  const was = this.doc.paperEffect;
4312
4653
  const paperEffect = {
4313
4654
  ...this.sourceOf(d, values, options.time),
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")),
4655
+ opacity: _nullishCoalesce(_nullishCoalesce(options.opacity, () => ( _optionalChain([was, 'optionalAccess', _288 => _288.opacity]))), () => ( 1)),
4656
+ blend: _nullishCoalesce(_nullishCoalesce(options.blend, () => ( _optionalChain([was, 'optionalAccess', _289 => _289.blend]))), () => ( "multiply")),
4316
4657
  image: { width: pixels.width, height: pixels.height, data: new Uint8ClampedArray(pixels.data) },
4317
4658
  version: nextPaperEffectVersion()
4318
4659
  };
@@ -4365,8 +4706,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4365
4706
  const above = _nullishCoalesce(options.aboveId, () => ( this.doc.activeLayerId));
4366
4707
  const loc = above ? _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, above) : null;
4367
4708
  if (options.aboveId !== void 0 && !loc) throw new PaintEditorError(`Layer "${options.aboveId}" was removed while the effect rendered.`);
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);
4709
+ const layer = { ...createPaintLayer(_optionalChain([options, 'access', _290 => _290.name, 'optionalAccess', _291 => _291.trim, 'call', _292 => _292()]) || d.name), surface: _chunk474IQG74cjs.surfaceFromPixels.call(void 0, pixels, 4), opacity: _nullishCoalesce(options.opacity, () => ( 1)), blend: _nullishCoalesce(options.blend, () => ( "normal")) };
4710
+ const layers = _chunkHB4ICNNAcjs.insertLayer.call(void 0, this.doc.layers, layer, _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _293 => _293.parent, 'optionalAccess', _294 => _294.id]), () => ( null)), loc ? loc.index + 1 : void 0);
4370
4711
  const source = this.sourceOf(d, values, options.time);
4371
4712
  this.commit("Add effect layer", { ...this.doc, layers, activeLayerId: layer.id, effectLayers: { ..._nullishCoalesce(this.doc.effectLayers, () => ( {})), [layer.id]: source } });
4372
4713
  return { layer: this.describeLayer(layer), source };
@@ -4444,7 +4785,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4444
4785
  this.checkTime(source.time);
4445
4786
  let map;
4446
4787
  try {
4447
- map = _chunk5BR7LC24cjs.readEffectStrokeMap.call(void 0, source.map);
4788
+ map = _chunk5FZS7J7Ccjs.readEffectStrokeMap.call(void 0, source.map);
4448
4789
  } catch (e) {
4449
4790
  throw new PaintEditorError(e.message);
4450
4791
  }
@@ -4475,9 +4816,9 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4475
4816
  }
4476
4817
  /** The rendered pixels of an effect paint at the current canvas size, or `null` when it has not been rendered. */
4477
4818
  effectPaintImage(p) {
4478
- const key = _chunk5BR7LC24cjs.effectPaintKey.call(void 0, p);
4819
+ const key = _chunk5FZS7J7Ccjs.effectPaintKey.call(void 0, p);
4479
4820
  const img = this.effectPaintPixels.get(key);
4480
- const size = _chunk5BR7LC24cjs.effectPaintSize.call(void 0, p, this.doc.canvas);
4821
+ const size = _chunk5FZS7J7Ccjs.effectPaintSize.call(void 0, p, this.doc.canvas);
4481
4822
  if (!img || img.width !== size.width || img.height !== size.height) return null;
4482
4823
  this.effectPaintPixels.delete(key);
4483
4824
  this.effectPaintPixels.set(key, img);
@@ -4491,7 +4832,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4491
4832
  const what = `the effect "${p.effect}"${p.provider ? ` from provider "${p.provider}"` : ""}`;
4492
4833
  if (!provider) throw new PaintEditorError(`Effect paint needs ${what}, but no effects provider is registered; register it (setEffectsProvider) and try again.`);
4493
4834
  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.`);
4494
- const { width, height } = _chunk5BR7LC24cjs.effectPaintSize.call(void 0, p, this.doc.canvas);
4835
+ const { width, height } = _chunk5FZS7J7Ccjs.effectPaintSize.call(void 0, p, this.doc.canvas);
4495
4836
  let pixels;
4496
4837
  try {
4497
4838
  pixels = await this.effectsRenderer.render(provider, p.effect, { width, height }, { time: p.time, values: p.values });
@@ -4499,7 +4840,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4499
4840
  throw new PaintEditorError(`Effect paint could not render ${what}: ${e instanceof Error ? e.message : String(e)}`);
4500
4841
  }
4501
4842
  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);
4843
+ this.effectPaintPixels.set(_chunk5FZS7J7Ccjs.effectPaintKey.call(void 0, p), img);
4503
4844
  while (this.effectPaintPixels.size > _PaintEditorController.EFFECT_PAINT_CACHE) this.effectPaintPixels.delete(this.effectPaintPixels.keys().next().value);
4504
4845
  return img;
4505
4846
  }
@@ -4510,7 +4851,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4510
4851
  async prepareEffectPaints(paints) {
4511
4852
  const seen = /* @__PURE__ */ new Set();
4512
4853
  for (const p of paints) {
4513
- const key = _chunk5BR7LC24cjs.effectPaintKey.call(void 0, p);
4854
+ const key = _chunk5FZS7J7Ccjs.effectPaintKey.call(void 0, p);
4514
4855
  if (seen.has(key)) continue;
4515
4856
  seen.add(key);
4516
4857
  await this.renderEffectPaint(p);
@@ -4519,7 +4860,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4519
4860
  /** Renders the effect paints of recorded strokes (default all of them) before a replay. */
4520
4861
  async prepareStrokeEffects(strokeIds) {
4521
4862
  const ids = strokeIds ? new Set(strokeIds) : null;
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);
4863
+ const paints = this.doc.strokes.filter((s) => (!ids || ids.has(s.id)) && _optionalChain([s, 'access', _295 => _295.paint, 'optionalAccess', _296 => _296.type]) === "effect").map((s) => s.paint);
4523
4864
  await this.prepareEffectPaints(paints);
4524
4865
  }
4525
4866
  /** A small preview of the effect paint (a PNG data URL `size` px wide), or `null` without a rendered one. */
@@ -4548,11 +4889,11 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4548
4889
  * its pixels are not rendered.
4549
4890
  */
4550
4891
  strokeEffect(init, brush) {
4551
- if (!_chunk5BR7LC24cjs.brushTakesPaint.call(void 0, brush)) return null;
4892
+ if (!_chunk5FZS7J7Ccjs.brushTakesPaint.call(void 0, brush)) return null;
4552
4893
  let source = null;
4553
4894
  if (init.paint !== void 0) {
4554
- if (!_chunk5BR7LC24cjs.isPaintEffectPaint.call(void 0, init.paint)) return null;
4555
- source = _chunk5BR7LC24cjs.readPaintEffectPaint.call(void 0, init.paint);
4895
+ if (!_chunk5FZS7J7Ccjs.isPaintEffectPaint.call(void 0, init.paint)) return null;
4896
+ source = _chunk5FZS7J7Ccjs.readPaintEffectPaint.call(void 0, init.paint);
4556
4897
  if (!source) throw new PaintEditorError("The stroke's effect paint needs an effect id and values (getEffectPaint returns one).");
4557
4898
  } else if (init.color === void 0 && !brush.paint) {
4558
4899
  if (this.effectPaintPending) throw new PaintEditorError(`The effect paint "${this.effectPaintPending.effect}" is still rendering; wait for set_effect_paint to finish, then paint.`);
@@ -4582,14 +4923,14 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4582
4923
  const before = _chunk474IQG74cjs.readRegion.call(void 0, l.surface, rect);
4583
4924
  const inputs = { source: { width: rect.width, height: rect.height, data: before } };
4584
4925
  const heightInput = d.inputs.find((i) => i.kind === "image" && /^(height|depth|relief)$/i.test(i.name));
4585
- const relief = _optionalChain([this, 'access', _254 => _254.doc, 'access', _255 => _255.thick, 'optionalAccess', _256 => _256[l.id]]);
4926
+ const relief = _optionalChain([this, 'access', _297 => _297.doc, 'access', _298 => _298.thick, 'optionalAccess', _299 => _299[l.id]]);
4586
4927
  if (heightInput && relief) {
4587
4928
  const h = _chunk474IQG74cjs.readRegion.call(void 0, relief.height, rect);
4588
4929
  const rgba = new Uint8ClampedArray(rect.width * rect.height * 4);
4589
4930
  for (let i = 0; i < h.length; i++) rgba.set([h[i], h[i], h[i], 255], i * 4);
4590
4931
  inputs[heightInput.name] = { width: rect.width, height: rect.height, data: rgba };
4591
4932
  }
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"));
4933
+ const sourceInput = _nullishCoalesce(_optionalChain([d, 'access', _300 => _300.inputs, 'access', _301 => _301.find, 'call', _302 => _302((i) => i.kind === "image" && i.name !== _optionalChain([heightInput, 'optionalAccess', _303 => _303.name])), 'optionalAccess', _304 => _304.name]), () => ( "source"));
4593
4934
  if (sourceInput !== "source") {
4594
4935
  inputs[sourceInput] = inputs.source;
4595
4936
  delete inputs.source;
@@ -4602,12 +4943,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4602
4943
  }
4603
4944
  // --- Brushes -------------------------------------------------------------------
4604
4945
  findBrush(id) {
4605
- return _nullishCoalesce(_chunk5BR7LC24cjs.BUILTIN_BRUSHES.find((b) => b.id === id), () => ( this.userBrushes.find((b) => b.id === id)));
4946
+ return _nullishCoalesce(_chunk5FZS7J7Ccjs.BUILTIN_BRUSHES.find((b) => b.id === id), () => ( this.userBrushes.find((b) => b.id === id)));
4606
4947
  }
4607
4948
  /** A library brush by id (`undefined` when unknown). */
4608
4949
  getBrush(id) {
4609
4950
  const b = this.findBrush(id);
4610
- return b ? _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, b, b) : void 0;
4951
+ return b ? _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, b, b) : void 0;
4611
4952
  }
4612
4953
  requireBrush(id) {
4613
4954
  const b = this.findBrush(id);
@@ -4622,7 +4963,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4622
4963
  const { id, ...changes } = update;
4623
4964
  const base = id !== void 0 ? this.requireBrush(id) : this.brush;
4624
4965
  if (changes.paint !== void 0 && changes.paint !== null) this.readStrokePaint(changes.paint);
4625
- const next = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { ...changes, id: base.id }, base);
4966
+ const next = _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, { ...changes, id: base.id }, base);
4626
4967
  if (base.builtin && !Object.keys(changes).length) next.builtin = true;
4627
4968
  else delete next.builtin;
4628
4969
  this.brush = next;
@@ -4631,10 +4972,10 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4631
4972
  }
4632
4973
  /** Saves the active brush (or `brush`) into the user library under `id` / `name`. */
4633
4974
  saveBrush(options) {
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)) });
4975
+ if (!_chunk5FZS7J7Ccjs.BRUSH_ID.test(options.id)) throw new PaintEditorError(`Brush id "${options.id}" is invalid; use lowercase letters, digits and dashes (e.g. "my-pencil").`);
4976
+ if (_chunk5FZS7J7Ccjs.BUILTIN_BRUSHES.some((b) => b.id === options.id)) throw new PaintEditorError(`"${options.id}" is a built-in brush; save under another id.`);
4977
+ const source = options.brush ? _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, options.brush, this.brush) : this.brush;
4978
+ const saved = _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, { ...source, id: options.id, name: _nullishCoalesce(options.name, () => ( source.name)) });
4638
4979
  delete saved.builtin;
4639
4980
  const i = this.userBrushes.findIndex((b) => b.id === options.id);
4640
4981
  if (i >= 0) this.userBrushes[i] = saved;
@@ -4644,7 +4985,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4644
4985
  return saved;
4645
4986
  }
4646
4987
  removeBrush(id) {
4647
- if (_chunk5BR7LC24cjs.BUILTIN_BRUSHES.some((b) => b.id === id)) throw new PaintEditorError(`"${id}" is a built-in brush and cannot be removed.`);
4988
+ if (_chunk5FZS7J7Ccjs.BUILTIN_BRUSHES.some((b) => b.id === id)) throw new PaintEditorError(`"${id}" is a built-in brush and cannot be removed.`);
4648
4989
  const i = this.userBrushes.findIndex((b) => b.id === id);
4649
4990
  if (i < 0) throw new PaintEditorError(`Unknown brush "${id}". Call list_brushes for the ids.`);
4650
4991
  this.userBrushes.splice(i, 1);
@@ -4653,12 +4994,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4653
4994
  }
4654
4995
  /** The user's saved brushes. */
4655
4996
  getUserBrushes() {
4656
- return this.userBrushes.map((b) => _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, b, b));
4997
+ return this.userBrushes.map((b) => _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, b, b));
4657
4998
  }
4658
4999
  /** A brush library file of the given brushes (default: the user's brushes). */
4659
5000
  exportBrushes(ids) {
4660
5001
  const list = ids ? ids.map((id) => this.requireBrush(id)) : this.userBrushes;
4661
- return { format: "pixerate-brushes", version: 1, brushes: list.map(_chunk5BR7LC24cjs.brushToJSON) };
5002
+ return { format: "pixerate-brushes", version: 1, brushes: list.map(_chunk5FZS7J7Ccjs.brushToJSON) };
4662
5003
  }
4663
5004
  /**
4664
5005
  * Adds the brushes of a library file to the user library. A brush whose id is taken is skipped
@@ -4668,14 +5009,14 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4668
5009
  const problems = [];
4669
5010
  let list;
4670
5011
  try {
4671
- list = _chunk5BR7LC24cjs.parseBrushLibrary.call(void 0, file, problems);
5012
+ list = _chunk5FZS7J7Ccjs.parseBrushLibrary.call(void 0, file, problems);
4672
5013
  } catch (e) {
4673
5014
  throw new PaintEditorError(e.message);
4674
5015
  }
4675
5016
  const added = [];
4676
5017
  const skipped = [];
4677
5018
  for (const b of list) {
4678
- if (_chunk5BR7LC24cjs.BUILTIN_BRUSHES.some((x) => x.id === b.id)) {
5019
+ if (_chunk5FZS7J7Ccjs.BUILTIN_BRUSHES.some((x) => x.id === b.id)) {
4679
5020
  skipped.push(b.id);
4680
5021
  problems.push(`"${b.id}" is a built-in brush id; rename it to import it.`);
4681
5022
  continue;
@@ -4740,10 +5081,10 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4740
5081
  * brush's gradient, else the editor's gradient paint. Erasers, blenders and smudges take none.
4741
5082
  */
4742
5083
  strokeGradient(init, brush, color) {
4743
- if (!_chunk5BR7LC24cjs.brushTakesPaint.call(void 0, brush)) return null;
5084
+ if (!_chunk5FZS7J7Ccjs.brushTakesPaint.call(void 0, brush)) return null;
4744
5085
  let g = null;
4745
5086
  if (init.paint !== void 0) {
4746
- if (_chunk5BR7LC24cjs.isPaintEffectPaint.call(void 0, init.paint)) return null;
5087
+ if (_chunk5FZS7J7Ccjs.isPaintEffectPaint.call(void 0, init.paint)) return null;
4747
5088
  const p = this.readStrokePaint(init.paint);
4748
5089
  g = p.type === "gradient" ? p : null;
4749
5090
  } else g = _nullishCoalesce(brush.paint, () => ( (init.color === void 0 ? this.gradient : null)));
@@ -4787,7 +5128,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4787
5128
  // --- Mixer Brush ---------------------------------------------------------------
4788
5129
  /** Fills the Mixer Brush reservoir with a colour (default the editor colour). */
4789
5130
  loadMixerBrush(color) {
4790
- const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, color === void 0 ? this.color : parseColor(color));
5131
+ const [r, g, b] = _chunk5FZS7J7Ccjs.hexToRgb8.call(void 0, color === void 0 ? this.color : parseColor(color));
4791
5132
  this.mixerLoad = _chunkPQ3H2AANcjs.loadMixer.call(void 0, this.mixerLoad, [r / 255, g / 255, b / 255]);
4792
5133
  this.emit([]);
4793
5134
  return this.mixerLoad;
@@ -4817,7 +5158,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4817
5158
  // --- Strokes -------------------------------------------------------------------
4818
5159
  strokeBrush(init) {
4819
5160
  let brush = init.brushId !== void 0 ? this.requireBrush(init.brushId) : this.brush;
4820
- if (init.brush) brush = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { ...init.brush, id: brush.id }, brush);
5161
+ if (init.brush) brush = _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, { ...init.brush, id: brush.id }, brush);
4821
5162
  return brush;
4822
5163
  }
4823
5164
  /**
@@ -4833,7 +5174,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4833
5174
  return symmetryCopies(this.guides.symmetry);
4834
5175
  }
4835
5176
  sessionFor(doc, init, host) {
4836
- if (init.paint !== void 0 && !_chunk5BR7LC24cjs.isPaintEffectPaint.call(void 0, init.paint)) {
5177
+ if (init.paint !== void 0 && !_chunk5FZS7J7Ccjs.isPaintEffectPaint.call(void 0, init.paint)) {
4837
5178
  const p = this.readStrokePaint(init.paint);
4838
5179
  if (p.type === "color") init = { ...init, color: p.color, paint: void 0 };
4839
5180
  else init = { ...init, paint: p };
@@ -4851,25 +5192,29 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4851
5192
  })();
4852
5193
  const brush = this.strokeBrush(init);
4853
5194
  const effect = this.strokeEffect(init, brush);
4854
- if (_optionalChain([doc, 'access', _262 => _262.liveStrokes, 'optionalAccess', _263 => _263[layer.id]])) {
5195
+ if (brush.light.amount > 0) {
5196
+ if (brush.engine === "wet") throw new PaintEditorError(`The ${brush.name} brush paints wet media, which doesn't take light yet (glowing watercolour is planned in #494); paint light with a dab or impasto light brush (list_brushes category light), or set light.amount to 0 (set_brush).`);
5197
+ if (brush.engine === "sticker") throw new PaintEditorError(`The ${brush.name} brush sprays stickers, which don't take light yet; paint light with a dab or particle light brush (list_brushes category light), or set light.amount to 0 (set_brush).`);
5198
+ }
5199
+ if (_optionalChain([doc, 'access', _305 => _305.liveStrokes, 'optionalAccess', _306 => _306[layer.id]])) {
4855
5200
  const why = liveStrokeRefusal(brush);
4856
5201
  if (why) throw new PaintEditorError(`Layer "${layer.name}" is a stroke layer. ${why}`);
4857
5202
  }
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);
5203
+ if (_optionalChain([doc, 'access', _307 => _307.liveStrokes, 'optionalAccess', _308 => _308[layer.id]]) && _optionalChain([effect, 'optionalAccess', _309 => _309.source, 'access', _310 => _310.live])) {
5204
+ const key = _chunk5FZS7J7Ccjs.effectPaintKey.call(void 0, effect.source);
4860
5205
  const { sources } = this.liveEffects(doc);
4861
- if (!sources.some((p) => _chunk5BR7LC24cjs.effectPaintKey.call(void 0, p) === key) && sources.length >= MAX_LIVE_EFFECT_SOURCES) {
5206
+ if (!sources.some((p) => _chunk5FZS7J7Ccjs.effectPaintKey.call(void 0, p) === key) && sources.length >= MAX_LIVE_EFFECT_SOURCES) {
4862
5207
  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
5208
  }
4864
5209
  }
4865
- const stickerLayer = Boolean(_optionalChain([doc, 'access', _268 => _268.stickers, 'optionalAccess', _269 => _269[layer.id], 'optionalAccess', _270 => _270.length]));
5210
+ const stickerLayer = Boolean(_optionalChain([doc, 'access', _311 => _311.stickers, 'optionalAccess', _312 => _312[layer.id], 'optionalAccess', _313 => _313.length]));
4866
5211
  if (brush.engine === "sticker") {
4867
5212
  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.`);
4868
5213
  } else if (stickerLayer) {
4869
5214
  throw new PaintEditorError(`Layer "${layer.name}" is a sticker layer (its pixels are drawn from its stickers); paint on another layer (add_layer).`);
4870
5215
  }
4871
5216
  let wet;
4872
- if (_chunk5BR7LC24cjs.isWetBrush.call(void 0, brush)) {
5217
+ if (_chunk5FZS7J7Ccjs.isWetBrush.call(void 0, brush)) {
4873
5218
  this.requireWetIdle("painting");
4874
5219
  if (layer.offset.x || layer.offset.y) {
4875
5220
  const moved = { ...layer, surface: _chunk474IQG74cjs.translateSurface.call(void 0, layer.surface, layer.offset.x, layer.offset.y), offset: { x: 0, y: 0 } };
@@ -4884,7 +5229,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4884
5229
  const world = this.wet.ensureLane(layer.id, brush.wet.medium);
4885
5230
  const id = layer.id;
4886
5231
  const wetColor = init.color === void 0 ? this.color : parseColor(init.color);
4887
- const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, wetColor);
5232
+ const [r, g, b] = _chunk5FZS7J7Ccjs.hexToRgb8.call(void 0, wetColor);
4888
5233
  const wetGradient = this.strokeGradient(init, brush, wetColor);
4889
5234
  const load = _nullishCoalesce(init.load, () => ( brushLoadForStroke(brush, this.mixerLoad, [r / 255, g / 255, b / 255])));
4890
5235
  const seedW = _nullishCoalesce(init.seed, () => ( _chunkPQ3H2AANcjs.mix32.call(void 0, this.seedCounter * 2654435761 >>> 0) & 2147483647));
@@ -4897,12 +5242,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4897
5242
  load,
4898
5243
  grain: paperGrain(doc.paper),
4899
5244
  paperDepth: doc.paper.depth,
4900
- paperColor: _chunk5BR7LC24cjs.hexToRgb8.call(void 0, doc.paper.color),
5245
+ paperColor: _chunk5FZS7J7Ccjs.hexToRgb8.call(void 0, doc.paper.color),
4901
5246
  seed: seedW,
4902
5247
  copies: [IDENTITY_AFFINE, ...copiesW].map(affineCopy),
4903
5248
  mask: init.maskStencils ? this.stencilSelection(init.maskStencils) : init.stencils !== false ? this.stencilSelection() : null,
4904
5249
  paint: effect ? null : wetGradient,
4905
- effect: _nullishCoalesce(_optionalChain([effect, 'optionalAccess', _271 => _271.pixels]), () => ( null))
5250
+ effect: _nullishCoalesce(_optionalChain([effect, 'optionalAccess', _314 => _314.pixels]), () => ( null))
4906
5251
  });
4907
5252
  init = { ...init, seed: seedW, load };
4908
5253
  wet = { driver, surface: () => world.display(id).surface };
@@ -4913,7 +5258,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4913
5258
  }
4914
5259
  let color = init.color === void 0 ? this.color : parseColor(init.color);
4915
5260
  const tracing = this.guides.tracing;
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));
5261
+ if (init.color === void 0 && init.start && _optionalChain([tracing, 'optionalAccess', _315 => _315.visible]) && tracing.sampleColors) color = _nullishCoalesce(this.sampleTracingColor(init.start.x, init.start.y), () => ( color));
4917
5262
  const perspective = this.guides.perspective;
4918
5263
  const snapper = init.snapToPerspective && perspective.enabled && perspective.snap ? new PerspectiveSnapper(perspective) : null;
4919
5264
  const useStencils = init.stencils !== false;
@@ -4946,7 +5291,9 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4946
5291
  mixerLoad: _nullishCoalesce(init.load, () => ( brushLoadForStroke(brush, this.mixerLoad, [rgba[0] / 255, rgba[1] / 255, rgba[2] / 255]))),
4947
5292
  secondary: rgbaOf(this.secondaryColor),
4948
5293
  wet,
4949
- thick: brush.engine === "impasto" ? { channels: _nullishCoalesce(_optionalChain([doc, 'access', _273 => _273.thick, 'optionalAccess', _274 => _274[layer.id]]), () => ( emptyThickChannels())) } : void 0,
5294
+ thick: brush.engine === "impasto" ? { channels: _nullishCoalesce(_optionalChain([doc, 'access', _316 => _316.thick, 'optionalAccess', _317 => _317[layer.id]]), () => ( emptyThickChannels())) } : void 0,
5295
+ // Light paint: a twin of the stroke paints the light channel (laying, dimming, moving light).
5296
+ light: !wet && brush.engine !== "sticker" && (brush.light.amount > 0 || _optionalChain([doc, 'access', _318 => _318.light, 'optionalAccess', _319 => _319[layer.id]])) ? { surface: _nullishCoalesce(_optionalChain([doc, 'access', _320 => _320.light, 'optionalAccess', _321 => _321[layer.id]]), () => ( emptyLight())) } : null,
4950
5297
  sticker: brush.engine === "sticker" ? { nextId: this.nextStickerNumber(doc) } : void 0,
4951
5298
  paint: effect ? null : this.strokeGradient(init, brush, color),
4952
5299
  effect
@@ -4960,7 +5307,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4960
5307
  const stroke = {
4961
5308
  id: `s${this.strokeCounter++}`,
4962
5309
  layerId: session.layerId,
4963
- brush: _chunk5BR7LC24cjs.brushToJSON.call(void 0, init.brush),
5310
+ brush: _chunk5FZS7J7Ccjs.brushToJSON.call(void 0, init.brush),
4964
5311
  color: init.colorHex,
4965
5312
  points: session.points.map((p) => ({ ...p })),
4966
5313
  seed: init.seed
@@ -4985,12 +5332,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4985
5332
  const before = this.doc;
4986
5333
  const host = {
4987
5334
  update: (layerId, surface, rects) => {
4988
- if (_optionalChain([this, 'access', _275 => _275.session, 'optionalAccess', _276 => _276.init, 'access', _277 => _277.wet])) {
5335
+ if (_optionalChain([this, 'access', _322 => _322.session, 'optionalAccess', _323 => _323.init, 'access', _324 => _324.wet])) {
4989
5336
  this.wet.syncDisplays();
4990
5337
  return;
4991
5338
  }
4992
5339
  this.live = { layerId, surface };
4993
- const ch = _optionalChain([this, 'access', _278 => _278.session, 'optionalAccess', _279 => _279.thickChannels, 'call', _280 => _280()]);
5340
+ const ch = _optionalChain([this, 'access', _325 => _325.session, 'optionalAccess', _326 => _326.thickChannels, 'call', _327 => _327()]);
4994
5341
  this.liveThick = ch ? { layerId, channels: ch } : null;
4995
5342
  this.emit(rects);
4996
5343
  },
@@ -5002,7 +5349,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5002
5349
  const wetStroke = Boolean(s.init.wet);
5003
5350
  if (wetStroke) this.wet.driver = null;
5004
5351
  const brush = s.init.brush;
5005
- if (!cancelled && _optionalChain([s, 'access', _281 => _281.result, 'optionalAccess', _282 => _282.mixerLoad])) this.keepLoad(brush, s.result.mixerLoad);
5352
+ if (!cancelled && _optionalChain([s, 'access', _328 => _328.result, 'optionalAccess', _329 => _329.mixerLoad])) this.keepLoad(brush, s.result.mixerLoad);
5006
5353
  if (cancelled || !surface) {
5007
5354
  if (this.wetOp) {
5008
5355
  const snap = this.wetOp.before;
@@ -5017,7 +5364,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5017
5364
  }
5018
5365
  const stroke = this.record(s);
5019
5366
  s.result.strokeId = stroke.id;
5020
- this.paintedBounds.set(stroke, _nullishCoalesce(_optionalChain([s, 'access', _283 => _283.result, 'optionalAccess', _284 => _284.bounds]), () => ( null)));
5367
+ this.paintedBounds.set(stroke, _nullishCoalesce(_optionalChain([s, 'access', _330 => _330.result, 'optionalAccess', _331 => _331.bounds]), () => ( null)));
5021
5368
  if (wetStroke && this.wet.instantDry) {
5022
5369
  this.wet.dry(s.layerId);
5023
5370
  this.logWet({ kind: "dry", layerId: s.layerId, at: this.doc.strokes.length + 1 });
@@ -5028,8 +5375,9 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5028
5375
  const ch = s.thickChannels();
5029
5376
  const thick = ch ? { ..._nullishCoalesce(this.doc.thick, () => ( {})), [l.id]: ch } : this.doc.thick;
5030
5377
  const sprayed = s.stickers();
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);
5378
+ const stickers = sprayed ? { ..._nullishCoalesce(this.doc.stickers, () => ( {})), [l.id]: [..._nullishCoalesce(_optionalChain([this, 'access', _332 => _332.doc, 'access', _333 => _333.stickers, 'optionalAccess', _334 => _334[l.id]]), () => ( [])), ...sprayed] } : this.doc.stickers;
5379
+ const light = withLayerLight(this.doc.light, l.id, s.lightChannel());
5380
+ 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 } : {}, ...light ? { light } : {} }, stroke)), wetStroke ? before : this.doc);
5033
5381
  }
5034
5382
  };
5035
5383
  let session;
@@ -5057,7 +5405,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5057
5405
  if (cancelled || !surface) return;
5058
5406
  const stroke = this.record(s);
5059
5407
  s.result.strokeId = stroke.id;
5060
- this.paintedBounds.set(stroke, _nullishCoalesce(_optionalChain([s, 'access', _288 => _288.result, 'optionalAccess', _289 => _289.bounds]), () => ( null)));
5408
+ this.paintedBounds.set(stroke, _nullishCoalesce(_optionalChain([s, 'access', _335 => _335.result, 'optionalAccess', _336 => _336.bounds]), () => ( null)));
5061
5409
  const l = _chunkHB4ICNNAcjs.findLayer.call(void 0, out.layers, s.layerId).layer;
5062
5410
  const offset = s.init.wet ? { x: 0, y: 0 } : l.offset;
5063
5411
  const ch = s.thickChannels();
@@ -5067,7 +5415,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5067
5415
  layers: _chunkHB4ICNNAcjs.mapLayer.call(void 0, out.layers, l.id, () => ({ ...l, surface, offset })),
5068
5416
  strokes: [...out.strokes, stroke],
5069
5417
  ...ch ? { thick: { ..._nullishCoalesce(out.thick, () => ( {})), [l.id]: ch } } : {},
5070
- ...sprayed ? { stickers: { ..._nullishCoalesce(out.stickers, () => ( {})), [l.id]: [..._nullishCoalesce(_optionalChain([out, 'access', _290 => _290.stickers, 'optionalAccess', _291 => _291[l.id]]), () => ( [])), ...sprayed] } } : {}
5418
+ ...sprayed ? { stickers: { ..._nullishCoalesce(out.stickers, () => ( {})), [l.id]: [..._nullishCoalesce(_optionalChain([out, 'access', _337 => _337.stickers, 'optionalAccess', _338 => _338[l.id]]), () => ( [])), ...sprayed] } } : {},
5419
+ ...s.lightChannel() ? { light: withLayerLight(out.light, l.id, s.lightChannel()) } : {}
5071
5420
  }, stroke);
5072
5421
  }
5073
5422
  };
@@ -5125,7 +5474,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5125
5474
  */
5126
5475
  replayStrokes(options) {
5127
5476
  this.requireIdle("replaying");
5128
- if (!_optionalChain([options, 'access', _292 => _292.strokeIds, 'optionalAccess', _293 => _293.length])) throw new PaintEditorError("strokeIds is empty; pass ids from list_strokes.");
5477
+ if (!_optionalChain([options, 'access', _339 => _339.strokeIds, 'optionalAccess', _340 => _340.length])) throw new PaintEditorError("strokeIds is empty; pass ids from list_strokes.");
5129
5478
  const strokes = options.strokeIds.map((id) => {
5130
5479
  const s = this.doc.strokes.find((x) => x.id === id);
5131
5480
  if (!s) throw new PaintEditorError(`Unknown stroke "${id}". Call list_strokes for the ids.`);
@@ -5139,7 +5488,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5139
5488
  if (options.layerId !== void 0) this.paintTarget(options.layerId, "replay onto");
5140
5489
  if (options.color === void 0) {
5141
5490
  for (const s of strokes) {
5142
- if (_optionalChain([s, 'access', _294 => _294.paint, 'optionalAccess', _295 => _295.type]) === "effect" && !this.effectPaintImage(s.paint)) {
5491
+ if (_optionalChain([s, 'access', _341 => _341.paint, 'optionalAccess', _342 => _342.type]) === "effect" && !this.effectPaintImage(s.paint)) {
5143
5492
  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.`);
5144
5493
  }
5145
5494
  }
@@ -5162,13 +5511,13 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5162
5511
  let brush = v.brush;
5163
5512
  if (options.brushId !== void 0) {
5164
5513
  const b = this.requireBrush(options.brushId);
5165
- brush = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { tip: { ...b.tip, size: b.tip.size * (v.brush.tip.size / s.brush.tip.size) } }, b);
5514
+ brush = _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, { tip: { ...b.tip, size: b.tip.size * (v.brush.tip.size / s.brush.tip.size) } }, b);
5166
5515
  }
5167
5516
  const layerId = _nullishCoalesce(options.layerId, () => ( s.layerId));
5168
5517
  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.`);
5169
5518
  let load = s.load;
5170
5519
  if (load && options.color !== void 0 && load.reservoir) {
5171
- const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, options.color);
5520
+ const [r, g, b] = _chunk5FZS7J7Ccjs.hexToRgb8.call(void 0, options.color);
5172
5521
  load = { ...load, reservoir: [r / 255, g / 255, b / 255] };
5173
5522
  }
5174
5523
  const paint = v.paint && options.color === void 0 ? { paint: v.paint } : {};
@@ -5182,6 +5531,45 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5182
5531
  if (doc !== this.doc) this.commit(strokes.length === 1 && repeat === 1 ? "Replay stroke" : "Replay strokes", doc);
5183
5532
  return { strokeIds: ids, bounds, layerIds: [...layerIds] };
5184
5533
  }
5534
+ // --- Light paint --------------------------------------------------------------------
5535
+ /** The document's `light` without these layers (as a spread: `{}` when unchanged). */
5536
+ lightWithout(ids) {
5537
+ const t = this.doc.light;
5538
+ if (!t || !ids.some((id) => t[id])) return {};
5539
+ const out = { ...t };
5540
+ for (const id of ids) delete out[id];
5541
+ return { light: out };
5542
+ }
5543
+ lightSpillOf(layerId) {
5544
+ return _nullishCoalesce(this.lightSpills[layerId], () => ( DEFAULT_LIGHT_SPILL));
5545
+ }
5546
+ /** A layer's light paint: whether it has any, where (document pixels), its brightest value 0..255, and its spill. */
5547
+ lightOf(layerId) {
5548
+ const l = this.wetLayer(layerId, "read the light of");
5549
+ const info = lightInfo(_optionalChain([this, 'access', _343 => _343.doc, 'access', _344 => _344.light, 'optionalAccess', _345 => _345[l.id]]));
5550
+ const bounds = info.bounds ? { ...info.bounds, x: info.bounds.x + l.offset.x, y: info.bounds.y + l.offset.y } : null;
5551
+ return { layerId: l.id, hasLight: info.peak > 0, bounds, peak: info.peak, spill: { ...this.lightSpillOf(l.id) } };
5552
+ }
5553
+ /** How far a layer's light spills (px; 0 turns it off) and how strongly (0..2). Saved with the document, not an undo step; nothing is repainted. */
5554
+ setLightSpill(layerId, update) {
5555
+ const l = this.wetLayer(layerId, "set the light spill of");
5556
+ try {
5557
+ this.lightSpills[l.id] = normalizeLightSpill(update, this.lightSpillOf(l.id));
5558
+ } catch (e) {
5559
+ throw new PaintEditorError(e.message);
5560
+ }
5561
+ this.emit("all");
5562
+ return { ...this.lightSpills[l.id] };
5563
+ }
5564
+ /** Takes a layer's light away; its paint keeps its colour. One undo step. Returns whether there was any. */
5565
+ clearLight(layerId) {
5566
+ this.requireIdle("clearing light");
5567
+ const l = this.paintTarget(layerId, "clear the light of");
5568
+ const t = this.lightWithout([l.id]);
5569
+ if (!t.light) return { layerId: l.id, cleared: false };
5570
+ this.commit("Clear light", { ...this.doc, light: t.light });
5571
+ return { layerId: l.id, cleared: true };
5572
+ }
5185
5573
  // --- Thick paint and light -------------------------------------------------------
5186
5574
  /** The document's `thick` without these layers (as a spread: `{}` when unchanged). */
5187
5575
  thickWithout(ids) {
@@ -5208,7 +5596,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5208
5596
  const id = _nullishCoalesce(layerId, () => ( this.doc.activeLayerId));
5209
5597
  if (!id) return null;
5210
5598
  this.requireLayer(id);
5211
- const ch = _optionalChain([this, 'access', _296 => _296.doc, 'access', _297 => _297.thick, 'optionalAccess', _298 => _298[id]]);
5599
+ const ch = _optionalChain([this, 'access', _346 => _346.doc, 'access', _347 => _347.thick, 'optionalAccess', _348 => _348[id]]);
5212
5600
  if (!ch) return null;
5213
5601
  const r = reliefInfo(ch);
5214
5602
  return { layerId: id, bounds: r.bounds, maxHeight: r.maxHeight };
@@ -5316,7 +5704,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5316
5704
  wetState() {
5317
5705
  const st = this.getState().wet;
5318
5706
  const backend = this.wet.backend();
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() };
5707
+ return { ...st, layers: this.wet.wetLayerIds().map((id) => this.wet.info(id)), backend, message: _nullishCoalesce(_optionalChain([backend, 'optionalAccess', _349 => _349.message]), () => ( _chunkXFYLA3ZOcjs.wetSupport.call(void 0, ).message)), engine: this.wet.engineInfo() };
5320
5708
  }
5321
5709
  /**
5322
5710
  * Engine B, the WebGPU accelerator for large wet areas: whether it may run, its state (`off`,
@@ -5401,7 +5789,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5401
5789
  }
5402
5790
  /** Records a settings change as a wet event (not an undo step). */
5403
5791
  recordWetSetting(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 }] };
5792
+ this.doc = { ...this.doc, wetEvents: [..._nullishCoalesce(this.doc.wetEvents, () => ( [])), { clock: _nullishCoalesce(_optionalChain([this, 'access', _350 => _350.wet, 'access', _351 => _351.world, 'optionalAccess', _352 => _352.clock]), () => ( 0)), at: this.doc.strokes.length, ...e }] };
5405
5793
  }
5406
5794
  /** Changes a layer's wet settings (viscosity, evaporation, granulation…). Editor settings saved with the document, not an undo step. */
5407
5795
  setWetParams(layerId, update) {
@@ -5470,7 +5858,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5470
5858
  if (override) return Array.from(override).reduce((ls, [id, surface]) => _chunkHB4ICNNAcjs.mapLayer.call(void 0, ls, id, (l) => ({ ...l, surface })), this.doc.layers);
5471
5859
  const live = this.live;
5472
5860
  if (!live) return this.animatedView();
5473
- if (_optionalChain([this, 'access', _303 => _303.liveLayers, 'optionalAccess', _304 => _304.key]) === live.surface) return this.liveLayers.layers;
5861
+ if (_optionalChain([this, 'access', _353 => _353.liveLayers, 'optionalAccess', _354 => _354.key]) === live.surface) return this.liveLayers.layers;
5474
5862
  const layers = _chunkHB4ICNNAcjs.mapLayer.call(void 0, this.doc.layers, live.layerId, (l) => ({ ...l, surface: live.surface }));
5475
5863
  this.liveLayers = { key: live.surface, layers };
5476
5864
  return layers;
@@ -5493,7 +5881,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5493
5881
  tracingNode(t) {
5494
5882
  const { width, height } = this.doc.canvas;
5495
5883
  const key = this.tracingVersion * 100003 + width * 7 + height;
5496
- if (_optionalChain([this, 'access', _305 => _305.tracingCache, 'optionalAccess', _306 => _306.key]) === key && this.tracingCache.node.opacity === t.opacity) return this.tracingCache.node;
5884
+ if (_optionalChain([this, 'access', _355 => _355.tracingCache, 'optionalAccess', _356 => _356.key]) === key && this.tracingCache.node.opacity === t.opacity) return this.tracingCache.node;
5497
5885
  const src = _nullishCoalesce(this.imagePixels.get(t.src), () => ( this.decodeImage(t.src)));
5498
5886
  const image = resampleInto(src, t.rect, width, height);
5499
5887
  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 };
@@ -5520,8 +5908,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5520
5908
  let out = layers;
5521
5909
  const ids = /* @__PURE__ */ new Set([...Object.keys(_nullishCoalesce(thick, () => ( {}))), ...live ? [live.layerId] : []]);
5522
5910
  for (const id of ids) {
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]);
5911
+ const ch = _optionalChain([live, 'optionalAccess', _357 => _357.layerId]) === id ? live.channels : _optionalChain([thick, 'optionalAccess', _358 => _358[id]]);
5912
+ const l = ch && _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, out, id), 'optionalAccess', _359 => _359.layer]);
5525
5913
  if (!ch || !isPixelLayer(l)) continue;
5526
5914
  let cache = this.litCaches.get(id);
5527
5915
  if (!cache) this.litCaches.set(id, cache = new LitCache());
@@ -5530,11 +5918,34 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5530
5918
  }
5531
5919
  return out;
5532
5920
  }
5921
+ /**
5922
+ * Light paint over the (lit) layers: glow, rim and clarity in each layer's pixels, and its spill
5923
+ * as a Linear Dodge (Add) layer directly above it (from the painting as committed, so a stroke in
5924
+ * progress does not redraw the blur every frame).
5925
+ */
5926
+ lightLayers(layers) {
5927
+ const light = _nullishCoalesce(this.doc.light, () => ( {}));
5928
+ const ids = /* @__PURE__ */ new Set([...Object.keys(light), ...Object.keys(_nullishCoalesce(this.doc.thick, () => ( {})))]);
5929
+ if (!ids.size) return layers;
5930
+ let out = layers;
5931
+ for (const id of ids) {
5932
+ const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, out, id), 'optionalAccess', _360 => _360.layer]);
5933
+ if (!isPixelLayer(l)) continue;
5934
+ let cache = this.lightCaches.get(id);
5935
+ if (!cache) this.lightCaches.set(id, cache = new LightCache());
5936
+ const glowed = cache.glow(l.surface, light[id], _optionalChain([this, 'access', _361 => _361.liveThick, 'optionalAccess', _362 => _362.layerId]) === id ? this.liveThick.channels : _optionalChain([this, 'access', _363 => _363.doc, 'access', _364 => _364.thick, 'optionalAccess', _365 => _365[id]]));
5937
+ if (glowed !== l.surface) out = _chunkHB4ICNNAcjs.mapLayer.call(void 0, out, id, () => ({ ...l, surface: glowed }));
5938
+ const committed = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _366 => _366.layer]);
5939
+ const spill = light[id] && isPixelLayer(committed) ? cache.spill(committed.surface, light[id], this.lightSpillOf(id)) : null;
5940
+ if (spill) out = insertAfter(out, id, { type: "pixel", id: `${id}${SPILL_SUFFIX}`, name: `${l.name} light`, visible: l.visible, opacity: l.opacity, blend: "linear-dodge", surface: spill, offset: { ...l.offset } });
5941
+ }
5942
+ return out;
5943
+ }
5533
5944
  renderLayers(options, view = false) {
5534
- let layers = this.litLayers((_nullishCoalesce(options.wet, () => ( "display"))) === "dried" ? this.driedLayers() : this.currentLayers());
5945
+ let layers = this.lightLayers(this.litLayers((_nullishCoalesce(options.wet, () => ( "display"))) === "dried" ? this.driedLayers() : this.currentLayers()));
5535
5946
  if (options.layerIds) {
5536
5947
  const picked = [];
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))));
5948
+ for (const id of options.layerIds) picked.push(_nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, layers, id), 'optionalAccess', _367 => _367.layer]), () => ( this.requireLayer(id))));
5538
5949
  layers = picked.map((l) => ({ ...l, visible: true }));
5539
5950
  }
5540
5951
  const under = options.paper === false ? [] : [paperNode(this.doc.paper, this.lighting)];
@@ -5550,7 +5961,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5550
5961
  */
5551
5962
  compositeTiles(rect, onTile, options = {}) {
5552
5963
  const dirty = options.dirty === "all" ? void 0 : options.dirty;
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)) });
5964
+ this.viewCompositor.compositeTiles(this.renderLayers({ paper: options.paper }, true), rect, this.sources(), onTile, { dirty, checkpointLayerId: _nullishCoalesce(_optionalChain([this, 'access', _368 => _368.live, 'optionalAccess', _369 => _369.layerId]), () => ( this.doc.activeLayerId)) });
5554
5965
  }
5555
5966
  /** The canvas as canvas-sized RGBA (paper included by default). */
5556
5967
  compositePixels(options = {}) {
@@ -5560,13 +5971,13 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5560
5971
  }
5561
5972
  /** The canvas (or some layers), scaled so the longest side is at most `maxSide`. */
5562
5973
  renderPreview(options = {}) {
5563
- const full = this.compositePixels({ ...options, wet: _nullishCoalesce(options.wet, () => ( "display")) });
5974
+ const full2 = this.compositePixels({ ...options, wet: _nullishCoalesce(options.wet, () => ( "display")) });
5564
5975
  const side = Math.max(1, Math.round(_nullishCoalesce(options.maxSide, () => ( 512))));
5565
- const k = Math.min(1, side / Math.max(full.width, full.height));
5566
- if (k >= 1) return full;
5567
- const w = Math.max(1, Math.round(full.width * k));
5568
- const h = Math.max(1, Math.round(full.height * k));
5569
- return { width: w, height: h, data: downsampleRgba(full.data, full.width, full.height, w, h) };
5976
+ const k = Math.min(1, side / Math.max(full2.width, full2.height));
5977
+ if (k >= 1) return full2;
5978
+ const w = Math.max(1, Math.round(full2.width * k));
5979
+ const h = Math.max(1, Math.round(full2.height * k));
5980
+ return { width: w, height: h, data: downsampleRgba(full2.data, full2.width, full2.height, w, h) };
5570
5981
  }
5571
5982
  // --- Save, load and export -------------------------------------------------------
5572
5983
  /** The document as JSON with inline PNG assets. */
@@ -5618,7 +6029,18 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5618
6029
  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
6030
  if (Object.keys(liveStrokes).length) ext.liveStrokes = liveStrokes;
5620
6031
  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])));
6032
+ const light = {};
6033
+ for (const [id, t] of Object.entries(_nullishCoalesce(this.doc.light, () => ( {})))) {
6034
+ if (!_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id)) continue;
6035
+ const saved = lightToJSON(t);
6036
+ if (saved) light[id] = saved;
6037
+ }
6038
+ if (Object.keys(light).length) ext.light = light;
6039
+ else delete ext.light;
6040
+ const spills = Object.fromEntries(Object.entries(this.lightSpills).filter(([id]) => _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id)));
6041
+ if (Object.keys(spills).length) ext.lightSpill = spills;
6042
+ else delete ext.lightSpill;
6043
+ const effectLayers = effectLayersToJSON(_nullishCoalesce(this.doc.effectLayers, () => ( {})), (id) => isPixelLayer(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _370 => _370.layer])));
5622
6044
  if (effectLayers) ext.effectLayers = effectLayers;
5623
6045
  else delete ext.effectLayers;
5624
6046
  if (this.doc.paperEffect) ext.paperEffect = paperEffectToJSON(this.doc.paperEffect);
@@ -5635,7 +6057,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5635
6057
  const stickers = {};
5636
6058
  for (const [id, list] of Object.entries(ext.stickers)) {
5637
6059
  if (!_chunkHB4ICNNAcjs.findLayer.call(void 0, state.layers, id) || !Array.isArray(list)) continue;
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);
6060
+ const ok = list.filter((x) => x && typeof x.id === "string" && _chunk5FZS7J7Ccjs.STAMP_KINDS.includes(x.stamp) && Number.isFinite(x.x) && Number.isFinite(x.y) && x.size > 0);
5639
6061
  if (ok.length !== list.length) problems.push(`Some stickers of layer "${id}" could not be read; they were dropped.`);
5640
6062
  stickers[id] = ok.map((x) => ({ ...x }));
5641
6063
  }
@@ -5644,7 +6066,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5644
6066
  if (ext.liveStrokes && typeof ext.liveStrokes === "object") {
5645
6067
  const liveStrokes = {};
5646
6068
  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;
6069
+ if (!isPixelLayer(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, state.layers, id), 'optionalAccess', _371 => _371.layer])) || !Array.isArray(list)) continue;
5648
6070
  const ok = list.map(normalizeBrushStroke).filter((x) => x !== null).map((x) => ({ ...x, layerId: id }));
5649
6071
  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
6072
  liveStrokes[id] = ok;
@@ -5654,7 +6076,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5654
6076
  if (ext.effectLayers && typeof ext.effectLayers === "object") {
5655
6077
  const effectLayers = {};
5656
6078
  for (const [id, v] of Object.entries(ext.effectLayers)) {
5657
- if (!isPixelLayer(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, state.layers, id), 'optionalAccess', _315 => _315.layer]))) continue;
6079
+ if (!isPixelLayer(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, state.layers, id), 'optionalAccess', _372 => _372.layer]))) continue;
5658
6080
  const source = readPaintEffectSource(v);
5659
6081
  if (source) effectLayers[id] = source;
5660
6082
  else problems.push(`The effect of layer "${id}" could not be read; it loads as a plain layer.`);
@@ -5668,6 +6090,29 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5668
6090
  problems.push(`The effect paper could not be read (${e.message}); the plain paper shows.`);
5669
6091
  }
5670
6092
  }
6093
+ this.lightSpills = {};
6094
+ this.lightCaches.clear();
6095
+ if (ext.lightSpill && typeof ext.lightSpill === "object") {
6096
+ for (const [id, v] of Object.entries(ext.lightSpill)) {
6097
+ try {
6098
+ if (_chunkHB4ICNNAcjs.findLayer.call(void 0, state.layers, id)) this.lightSpills[id] = normalizeLightSpill(_nullishCoalesce(v, () => ( {})));
6099
+ } catch (e4) {
6100
+ problems.push(`The light spill of layer "${id}" could not be read; it takes the defaults.`);
6101
+ }
6102
+ }
6103
+ }
6104
+ if (ext.light && typeof ext.light === "object") {
6105
+ const light = {};
6106
+ for (const [id, v] of Object.entries(ext.light)) {
6107
+ if (!isPixelLayer(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, state.layers, id), 'optionalAccess', _373 => _373.layer]))) continue;
6108
+ try {
6109
+ light[id] = lightFromJSON(v);
6110
+ } catch (e) {
6111
+ problems.push(`The light paint of layer "${id}" could not be read (${e.message}); it loads without light.`);
6112
+ }
6113
+ }
6114
+ if (Object.keys(light).length) state = { ...state, light };
6115
+ }
5671
6116
  const raw = ext.layerChannels;
5672
6117
  if (!raw || typeof raw !== "object") return state;
5673
6118
  const thick = {};
@@ -5695,7 +6140,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5695
6140
  this.readGuides(loaded.guides, loaded.state, loaded.problems);
5696
6141
  if (Array.isArray(loaded.swatches)) this.swatches = loaded.swatches.map(readSwatch).filter((x) => x !== null);
5697
6142
  for (const s of loaded.state.strokes) {
5698
- const n = Number(_nullishCoalesce(_optionalChain([/^s(\d+)$/, 'access', _316 => _316.exec, 'call', _317 => _317(s.id), 'optionalAccess', _318 => _318[1]]), () => ( 0)));
6143
+ const n = Number(_nullishCoalesce(_optionalChain([/^s(\d+)$/, 'access', _374 => _374.exec, 'call', _375 => _375(s.id), 'optionalAccess', _376 => _376[1]]), () => ( 0)));
5699
6144
  if (n >= this.strokeCounter) this.strokeCounter = n + 1;
5700
6145
  }
5701
6146
  this.undoStack = [];
@@ -5703,12 +6148,14 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5703
6148
  this.doc = this.readThick(loaded.state, loaded.extensions, loaded.problems);
5704
6149
  for (const list of Object.values(_nullishCoalesce(this.doc.liveStrokes, () => ( {})))) {
5705
6150
  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)));
6151
+ const n = Number(_nullishCoalesce(_optionalChain([/^s(\d+)$/, 'access', _377 => _377.exec, 'call', _378 => _378(s.id), 'optionalAccess', _379 => _379[1]]), () => ( 0)));
5707
6152
  if (n >= this.strokeCounter) this.strokeCounter = n + 1;
5708
6153
  }
5709
6154
  }
5710
6155
  this.docRevision++;
5711
6156
  delete this.extensions.layerChannels;
6157
+ delete this.extensions.light;
6158
+ delete this.extensions.lightSpill;
5712
6159
  delete this.extensions.lighting;
5713
6160
  delete this.extensions.stickers;
5714
6161
  delete this.extensions.liveStrokes;
@@ -5742,13 +6189,13 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5742
6189
  return _chunkOLNIPTB3cjs.encodePng.call(void 0, img.width, img.height, img.data);
5743
6190
  }
5744
6191
  scaled(options) {
5745
- const full = this.compositePixels(options);
6192
+ const full2 = this.compositePixels(options);
5746
6193
  const s = _nullishCoalesce(options.scale, () => ( 1));
5747
6194
  if (!(s > 0 && s <= 1)) throw new PaintEditorError(`scale ${s} is out of range; use above 0 up to 1 (exports never upscale pixels).`);
5748
- if (s === 1) return full;
5749
- const w = Math.max(1, Math.round(full.width * s));
5750
- const h = Math.max(1, Math.round(full.height * s));
5751
- return { width: w, height: h, data: downsampleRgba(full.data, full.width, full.height, w, h) };
6195
+ if (s === 1) return full2;
6196
+ const w = Math.max(1, Math.round(full2.width * s));
6197
+ const h = Math.max(1, Math.round(full2.height * s));
6198
+ return { width: w, height: h, data: downsampleRgba(full2.data, full2.width, full2.height, w, h) };
5752
6199
  }
5753
6200
  /**
5754
6201
  * The canvas as PNG, JPEG or WebP bytes. JPEG has no alpha, so it always includes the paper.
@@ -5771,7 +6218,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5771
6218
  * pixels), and the composite as the merged image.
5772
6219
  */
5773
6220
  exportPsd() {
5774
- const dried = this.litLayers(this.driedLayers(this.doc.layers));
6221
+ const dried = this.lightLayers(this.litLayers(this.driedLayers(this.doc.layers)));
5775
6222
  const { width, height, dpi } = this.doc.canvas;
5776
6223
  const paperPixels = this.compositePixels({ layerIds: [], paper: true });
5777
6224
  const paperLayer = {
@@ -5794,14 +6241,17 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5794
6241
  return next.type === "group" ? { ...next, children: toStandard(next.children) } : next;
5795
6242
  });
5796
6243
  const layers = toStandard(dried);
5797
- const result = _chunkHW72ABGRcjs.exportPsd.call(void 0, { width, height, layers: [paperLayer, ...layers], merged, resolution: dpi });
6244
+ const result = _chunkR2FVAKFHcjs.exportPsd.call(void 0, { width, height, layers: [paperLayer, ...layers], merged, resolution: dpi });
5798
6245
  for (const [mode, names] of changed) {
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)));
6246
+ const label = _nullishCoalesce(_optionalChain([_chunkOLNIPTB3cjs.BLEND_MODES, 'access', _380 => _380.find, 'call', _381 => _381((m) => m.id === mode), 'optionalAccess', _382 => _382.label]), () => ( mode));
6247
+ const std = _nullishCoalesce(_optionalChain([_chunkOLNIPTB3cjs.BLEND_MODES, 'access', _383 => _383.find, 'call', _384 => _384((m) => m.id === _chunkOLNIPTB3cjs.standardBlendMode.call(void 0, mode)), 'optionalAccess', _385 => _385.label]), () => ( _chunkOLNIPTB3cjs.standardBlendMode.call(void 0, mode)));
5801
6248
  const list = names.map((n) => `"${n}"`).join(", ");
5802
6249
  const one = names.length === 1;
5803
6250
  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).`);
5804
6251
  }
6252
+ if (Object.values(_nullishCoalesce(this.doc.light, () => ( {}))).some((l) => l.tiles.size)) {
6253
+ result.warnings.push('Light paint is written as pixels: each layer with its glow, and its spill as a "\u2039layer\u203A light" Linear Dodge (Add) layer above it. Opening the PSD again gives plain pixel layers, not editable light.');
6254
+ }
5805
6255
  return result;
5806
6256
  }
5807
6257
  /** `render_preview` as a base64 PNG. */
@@ -5913,7 +6363,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5913
6363
  const { src, pixels } = this.importImage(options, "The reference image");
5914
6364
  const ref2 = {
5915
6365
  id: `ref${this.nextReferenceId++}`,
5916
- name: _optionalChain([options, 'access', _328 => _328.name, 'optionalAccess', _329 => _329.trim, 'call', _330 => _330()]) || `Reference ${this.guides.references.length + 1}`,
6366
+ name: _optionalChain([options, 'access', _386 => _386.name, 'optionalAccess', _387 => _387.trim, 'call', _388 => _388()]) || `Reference ${this.guides.references.length + 1}`,
5917
6367
  src,
5918
6368
  width: pixels.width,
5919
6369
  height: pixels.height,
@@ -5992,7 +6442,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5992
6442
  try {
5993
6443
  const px = this.decodeImage(ref2.src, `Reference "${ref2.name}"`);
5994
6444
  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 });
5995
- const n = Number(_nullishCoalesce(_optionalChain([/^ref(\d+)$/, 'access', _331 => _331.exec, 'call', _332 => _332(ref2.id), 'optionalAccess', _333 => _333[1]]), () => ( 0)));
6445
+ const n = Number(_nullishCoalesce(_optionalChain([/^ref(\d+)$/, 'access', _389 => _389.exec, 'call', _390 => _390(ref2.id), 'optionalAccess', _391 => _391[1]]), () => ( 0)));
5996
6446
  if (n >= this.nextReferenceId) this.nextReferenceId = n + 1;
5997
6447
  } catch (e) {
5998
6448
  problems.push(e.message);
@@ -6003,7 +6453,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6003
6453
  guides.stencils = [];
6004
6454
  r.stencils.forEach((raw2, i) => {
6005
6455
  try {
6006
- const id = typeof _optionalChain([raw2, 'optionalAccess', _334 => _334.id]) === "string" && /^stencil\d+$/.test(raw2.id) ? raw2.id : `stencil${this.nextStencilId}`;
6456
+ const id = typeof _optionalChain([raw2, 'optionalAccess', _392 => _392.id]) === "string" && /^stencil\d+$/.test(raw2.id) ? raw2.id : `stencil${this.nextStencilId}`;
6007
6457
  guides.stencils.push(normalizeStencil(raw2, id));
6008
6458
  const n = Number(id.slice(7));
6009
6459
  if (n >= this.nextStencilId) this.nextStencilId = n + 1;
@@ -6026,12 +6476,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6026
6476
  /** The next free sticker number (ids `st<n>` are unique across the document). */
6027
6477
  nextStickerNumber(doc = this.doc) {
6028
6478
  let n = 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))));
6479
+ for (const list of Object.values(_nullishCoalesce(doc.stickers, () => ( {})))) for (const st of list) n = Math.max(n, Number(_nullishCoalesce(_optionalChain([/^st(\d+)$/, 'access', _393 => _393.exec, 'call', _394 => _394(st.id), 'optionalAccess', _395 => _395[1]]), () => ( 0))));
6030
6480
  return n + 1;
6031
6481
  }
6032
6482
  stickerLayer(layerId) {
6033
6483
  const l = this.wetLayer(layerId, "edit the stickers of");
6034
- const stickers = _optionalChain([this, 'access', _338 => _338.doc, 'access', _339 => _339.stickers, 'optionalAccess', _340 => _340[l.id]]);
6484
+ const stickers = _optionalChain([this, 'access', _396 => _396.doc, 'access', _397 => _397.stickers, 'optionalAccess', _398 => _398[l.id]]);
6035
6485
  if (!stickers) throw new PaintEditorError(`Layer "${l.name}" has no stickers; spray some with a sticker brush (list_brushes category sticker).`);
6036
6486
  return { layer: l, stickers };
6037
6487
  }
@@ -6040,7 +6490,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6040
6490
  const id = _nullishCoalesce(layerId, () => ( this.doc.activeLayerId));
6041
6491
  if (!id) return [];
6042
6492
  this.requireLayer(id);
6043
- return (_nullishCoalesce(_optionalChain([this, 'access', _341 => _341.doc, 'access', _342 => _342.stickers, 'optionalAccess', _343 => _343[id]]), () => ( []))).map((s) => ({ ...s }));
6493
+ return (_nullishCoalesce(_optionalChain([this, 'access', _399 => _399.doc, 'access', _400 => _400.stickers, 'optionalAccess', _401 => _401[id]]), () => ( []))).map((s) => ({ ...s }));
6044
6494
  }
6045
6495
  /**
6046
6496
  * Replaces a layer's stickers and redraws its pixels from them (one undo step). With `dirty`,
@@ -6056,15 +6506,15 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6056
6506
  }
6057
6507
  redrawStickers(layer, stickers, dirty) {
6058
6508
  const partial = dirty && !layer.offset.x && !layer.offset.y;
6059
- return partial ? _chunk5BR7LC24cjs.stickerSurface.call(void 0, stickers, layer.surface, dirty) : _chunk5BR7LC24cjs.stickerSurface.call(void 0, stickers);
6509
+ return partial ? _chunk5FZS7J7Ccjs.stickerSurface.call(void 0, stickers, layer.surface, dirty) : _chunk5FZS7J7Ccjs.stickerSurface.call(void 0, stickers);
6060
6510
  }
6061
6511
  /** The document from before the sticker preview on screen, or `null` when none is (anything else changed the document since). */
6062
6512
  previewBase() {
6063
- return _optionalChain([this, 'access', _344 => _344.stickerPreview, 'optionalAccess', _345 => _345.shown]) === this.doc ? this.stickerPreview.base : null;
6513
+ return _optionalChain([this, 'access', _402 => _402.stickerPreview, 'optionalAccess', _403 => _403.shown]) === this.doc ? this.stickerPreview.base : null;
6064
6514
  }
6065
6515
  readStickerUpdate(update) {
6066
6516
  try {
6067
- return _chunk5BR7LC24cjs.normalizeStickerUpdate.call(void 0, update);
6517
+ return _chunk5FZS7J7Ccjs.normalizeStickerUpdate.call(void 0, update);
6068
6518
  } catch (e) {
6069
6519
  throw new PaintEditorError(e.message);
6070
6520
  }
@@ -6075,7 +6525,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6075
6525
  const id = _nullishCoalesce(layerId, () => ( this.doc.activeLayerId));
6076
6526
  if (!id) return null;
6077
6527
  this.requireLayer(id);
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);
6528
+ const hit = _chunk5FZS7J7Ccjs.stickerAt.call(void 0, _nullishCoalesce(_optionalChain([this, 'access', _404 => _404.doc, 'access', _405 => _405.stickers, 'optionalAccess', _406 => _406[id]]), () => ( [])), x, y);
6079
6529
  return hit ? { ...hit } : null;
6080
6530
  }
6081
6531
  /** Picks a sticker for the panel and canvas (`null` for none). Editor state, not an undo step. */
@@ -6096,7 +6546,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6096
6546
  if (i < 0) throw new PaintEditorError(`Layer "${layer.name}" has no sticker "${id}". Call list_stickers for the ids.`);
6097
6547
  const next = { ...stickers[i], ...this.readStickerUpdate(update) };
6098
6548
  const list = stickers.map((s, j) => j === i ? next : s);
6099
- const dirty = [_chunk5BR7LC24cjs.stickerRect.call(void 0, stickers[i]), _chunk5BR7LC24cjs.stickerRect.call(void 0, next)];
6549
+ const dirty = [_chunk5FZS7J7Ccjs.stickerRect.call(void 0, stickers[i]), _chunk5FZS7J7Ccjs.stickerRect.call(void 0, next)];
6100
6550
  const base = _nullishCoalesce(this.previewBase(), () => ( this.doc));
6101
6551
  const surface = this.redrawStickers(layer, list, dirty);
6102
6552
  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 } };
@@ -6119,7 +6569,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6119
6569
  if (i < 0) throw new PaintEditorError(`Layer "${layer.name}" has no sticker "${id}". Call list_stickers for the ids.`);
6120
6570
  const u = this.readStickerUpdate(update);
6121
6571
  const next = { ...stickers[i], ...u };
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)]);
6572
+ this.commitStickers("Edit sticker", layer, stickers.map((s, j) => j === i ? next : s), [_chunk5FZS7J7Ccjs.stickerRect.call(void 0, stickers[i]), _chunk5FZS7J7Ccjs.stickerRect.call(void 0, next)]);
6123
6573
  return { ...next };
6124
6574
  }
6125
6575
  /**
@@ -6131,8 +6581,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6131
6581
  const layer = this.wetLayer(layerId, "add stickers to");
6132
6582
  if (!input.length) throw new PaintEditorError("Pass at least one sticker (stamp, x, y).");
6133
6583
  if (input.length > MAX_ADD_STICKERS) throw new PaintEditorError(`At most ${MAX_ADD_STICKERS} stickers per call; split them over several calls.`);
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])) {
6584
+ const existing = _optionalChain([this, 'access', _407 => _407.doc, 'access', _408 => _408.stickers, 'optionalAccess', _409 => _409[layer.id]]);
6585
+ if (!_optionalChain([existing, 'optionalAccess', _410 => _410.length])) {
6136
6586
  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.`);
6137
6587
  }
6138
6588
  let n = this.nextStickerNumber();
@@ -6148,7 +6598,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6148
6598
  const id = n++;
6149
6599
  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 };
6150
6600
  });
6151
- this.commitStickers("Add stickers", layer, [..._nullishCoalesce(existing, () => ( [])), ...added], _optionalChain([existing, 'optionalAccess', _353 => _353.length]) ? added.map(_chunk5BR7LC24cjs.stickerRect) : void 0);
6601
+ this.commitStickers("Add stickers", layer, [..._nullishCoalesce(existing, () => ( [])), ...added], _optionalChain([existing, 'optionalAccess', _411 => _411.length]) ? added.map(_chunk5FZS7J7Ccjs.stickerRect) : void 0);
6152
6602
  return added.map((s) => ({ ...s }));
6153
6603
  }
6154
6604
  /** Brings stickers to the top (`front`) or bottom of their layer (one undo step). */
@@ -6158,7 +6608,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6158
6608
  const picked = stickers.filter((s) => ids.includes(s.id));
6159
6609
  const rest = stickers.filter((s) => !ids.includes(s.id));
6160
6610
  const next = to === "front" ? [...rest, ...picked] : [...picked, ...rest];
6161
- this.commitStickers("Arrange stickers", layer, next, picked.map(_chunk5BR7LC24cjs.stickerRect));
6611
+ this.commitStickers("Arrange stickers", layer, next, picked.map(_chunk5FZS7J7Ccjs.stickerRect));
6162
6612
  return next.map((s) => s.id);
6163
6613
  }
6164
6614
  /** Deletes stickers (`ids`, or all of them on the layer) (one undo step). Returns the ids removed. */
@@ -6168,7 +6618,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6168
6618
  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.`);
6169
6619
  }
6170
6620
  const removed = ids ? [...ids] : stickers.map((s) => s.id);
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);
6621
+ this.commitStickers("Remove stickers", layer, stickers.filter((s) => !removed.includes(s.id)), ids ? stickers.filter((s) => removed.includes(s.id)).map(_chunk5FZS7J7Ccjs.stickerRect) : void 0);
6172
6622
  return removed;
6173
6623
  }
6174
6624
  /** Sets a layer's stickers (replays). */
@@ -6187,20 +6637,20 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6187
6637
  }
6188
6638
  liveStrokeLayer(layerId, what) {
6189
6639
  const l = this.wetLayer(layerId, what);
6190
- const strokes = _optionalChain([this, 'access', _354 => _354.doc, 'access', _355 => _355.liveStrokes, 'optionalAccess', _356 => _356[l.id]]);
6640
+ const strokes = _optionalChain([this, 'access', _412 => _412.doc, 'access', _413 => _413.liveStrokes, 'optionalAccess', _414 => _414[l.id]]);
6191
6641
  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
6642
  return { layer: l, strokes };
6193
6643
  }
6194
6644
  /** Whether a layer is a live stroke layer. */
6195
6645
  isLiveStrokeLayer(layerId) {
6196
- return Boolean(_optionalChain([this, 'access', _357 => _357.doc, 'access', _358 => _358.liveStrokes, 'optionalAccess', _359 => _359[layerId]]));
6646
+ return Boolean(_optionalChain([this, 'access', _415 => _415.doc, 'access', _416 => _416.liveStrokes, 'optionalAccess', _417 => _417[layerId]]));
6197
6647
  }
6198
6648
  /** Makes an empty pixel layer a live stroke layer (one undo step). */
6199
6649
  convertToLiveStrokes(layerId) {
6200
6650
  this.requireIdle("converting a layer");
6201
6651
  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"}).`);
6652
+ if (_optionalChain([this, 'access', _418 => _418.doc, 'access', _419 => _419.liveStrokes, 'optionalAccess', _420 => _420[l.id]])) return this.describeLayer(l);
6653
+ if (_optionalChain([this, 'access', _421 => _421.doc, 'access', _422 => _422.stickers, 'optionalAccess', _423 => _423[l.id], 'optionalAccess', _424 => _424.length])) throw new PaintEditorError(`Layer "${l.name}" is a sticker layer; add a stroke layer instead (add_layer {type: "strokes"}).`);
6204
6654
  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
6655
  this.commit("Convert to stroke layer", { ...this.doc, liveStrokes: { ..._nullishCoalesce(this.doc.liveStrokes, () => ( {})), [l.id]: [] } });
6206
6656
  return this.describeLayer(this.getLayer(l.id));
@@ -6213,7 +6663,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6213
6663
  findLiveStroke(id) {
6214
6664
  for (const [layerId, strokes] of Object.entries(_nullishCoalesce(this.doc.liveStrokes, () => ( {})))) {
6215
6665
  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]);
6666
+ const layer = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, layerId), 'optionalAccess', _425 => _425.layer]);
6217
6667
  if (index >= 0 && isPixelLayer(layer)) return { layer, strokes, index };
6218
6668
  }
6219
6669
  const baked = this.doc.strokes.some((x) => x.id === id);
@@ -6233,15 +6683,15 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6233
6683
  let brush = was.brush;
6234
6684
  if (update.brushId !== void 0) {
6235
6685
  const b = this.requireBrush(update.brushId);
6236
- brush = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { tip: { ...b.tip, size: was.brush.tip.size } }, b);
6686
+ brush = _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, { tip: { ...b.tip, size: was.brush.tip.size } }, b);
6237
6687
  }
6238
6688
  if (update.brush !== void 0) {
6239
6689
  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);
6690
+ brush = _chunk5FZS7J7Ccjs.normalizePaintBrush.call(void 0, { ...update.brush, id: brush.id }, brush);
6241
6691
  }
6242
6692
  const why = liveStrokeRefusal(brush);
6243
6693
  if (why) throw new PaintEditorError(why);
6244
- const next = { ...was, brush: _chunk5BR7LC24cjs.brushToJSON.call(void 0, brush) };
6694
+ const next = { ...was, brush: _chunk5FZS7J7Ccjs.brushToJSON.call(void 0, brush) };
6245
6695
  if (update.seed !== void 0) {
6246
6696
  if (!Number.isInteger(update.seed) || update.seed < 0) throw new PaintEditorError(`seed ${update.seed} must be a whole number of 0 or more.`);
6247
6697
  next.seed = update.seed;
@@ -6252,7 +6702,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6252
6702
  }
6253
6703
  if (update.paint !== void 0) {
6254
6704
  if (update.paint === null) delete next.paint;
6255
- else if (_chunk5BR7LC24cjs.isPaintEffectPaint.call(void 0, update.paint)) {
6705
+ else if (_chunk5FZS7J7Ccjs.isPaintEffectPaint.call(void 0, update.paint)) {
6256
6706
  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
6707
  next.paint = JSON.parse(JSON.stringify(update.paint));
6258
6708
  } else {
@@ -6263,7 +6713,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6263
6713
  } else next.paint = JSON.parse(JSON.stringify(p));
6264
6714
  }
6265
6715
  }
6266
- const rgb = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, next.color).map((v) => v / 255);
6716
+ const rgb = _chunk5FZS7J7Ccjs.hexToRgb8.call(void 0, next.color).map((v) => v / 255);
6267
6717
  const load = brushLoadForStroke(brush, _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD, rgb);
6268
6718
  if (load.reservoir || load.pickup) next.load = load;
6269
6719
  else delete next.load;
@@ -6308,9 +6758,9 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6308
6758
  const layers = /* @__PURE__ */ new Map();
6309
6759
  for (const f of found) layers.set(f.layer.id, f.layer);
6310
6760
  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));
6761
+ const gone = (_nullishCoalesce(_optionalChain([doc, 'access', _426 => _426.liveStrokes, 'optionalAccess', _427 => _427[layerId]]), () => ( []))).filter((x) => ids.includes(x.id));
6312
6762
  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));
6763
+ doc = this.withLiveStrokes(doc, layer, (_nullishCoalesce(_optionalChain([doc, 'access', _428 => _428.liveStrokes, 'optionalAccess', _429 => _429[layerId]]), () => ( []))).filter((x) => !ids.includes(x.id)), this.strokeAreas(gone));
6314
6764
  }
6315
6765
  this.commit(ids.length === 1 ? "Remove stroke" : "Remove strokes", doc);
6316
6766
  return { removed: [...ids], bounds };
@@ -6378,7 +6828,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6378
6828
  }
6379
6829
  withLiveStrokes(doc, layer, list, dirty) {
6380
6830
  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]);
6831
+ const current = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, doc.layers, layer.id), 'optionalAccess', _430 => _430.layer]);
6382
6832
  const surface = this.drawLiveStrokes(_nullishCoalesce(current, () => ( layer)), list, dirty);
6383
6833
  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
6834
  return this.withPaintEvent(next, { kind: "live-strokes", layerId: layer.id, data: { strokes: list.map((x) => JSON.parse(JSON.stringify(x))) } });
@@ -6456,7 +6906,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6456
6906
  if (!this.animatedLayers.size) return this.doc.layers;
6457
6907
  let layers = this.doc.layers;
6458
6908
  for (const [id, a] of this.animatedLayers) {
6459
- const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, layers, id), 'optionalAccess', _373 => _373.layer]);
6909
+ const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, layers, id), 'optionalAccess', _431 => _431.layer]);
6460
6910
  if (isPixelLayer(l) && l.surface === a.base) layers = _chunkHB4ICNNAcjs.mapLayer.call(void 0, layers, id, () => ({ ...l, surface: a.surface }));
6461
6911
  }
6462
6912
  return layers;
@@ -6466,11 +6916,11 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6466
6916
  const layers = [];
6467
6917
  const sources = /* @__PURE__ */ new Map();
6468
6918
  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]);
6919
+ const layer = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, doc.layers, id), 'optionalAccess', _432 => _432.layer]);
6470
6920
  const live = strokes.filter(isLiveEffect);
6471
6921
  if (!isPixelLayer(layer) || !live.length) continue;
6472
6922
  layers.push({ layer, strokes });
6473
- for (const st of live) sources.set(_chunk5BR7LC24cjs.effectPaintKey.call(void 0, st.paint), st.paint);
6923
+ for (const st of live) sources.set(_chunk5FZS7J7Ccjs.effectPaintKey.call(void 0, st.paint), st.paint);
6474
6924
  }
6475
6925
  return { layers, sources: [...sources.values()] };
6476
6926
  }
@@ -6591,13 +7041,13 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6591
7041
  return this.withEffectTime(time, () => this.compositePixels(options));
6592
7042
  }
6593
7043
  // --- Stencils --------------------------------------------------------------------
6594
- __init123() {this.stencilCache = null}
6595
- __init124() {this.nextStencilId = 1}
7044
+ __init129() {this.stencilCache = null}
7045
+ __init130() {this.nextStencilId = 1}
6596
7046
  /** The mask stencils' coverage over the canvas (cached), or `null` when none masks. */
6597
7047
  stencilSelection(stencils = this.guides.stencils) {
6598
7048
  const { width, height } = this.doc.canvas;
6599
7049
  const size = `${width}x${height}`;
6600
- if (_optionalChain([this, 'access', _375 => _375.stencilCache, 'optionalAccess', _376 => _376.key]) === stencils && this.stencilCache.size === size) return this.stencilCache.mask;
7050
+ if (_optionalChain([this, 'access', _433 => _433.stencilCache, 'optionalAccess', _434 => _434.key]) === stencils && this.stencilCache.size === size) return this.stencilCache.mask;
6601
7051
  const mask = stencilMask(stencils, width, height);
6602
7052
  this.stencilCache = { key: stencils, size, mask };
6603
7053
  return mask;
@@ -6659,7 +7109,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6659
7109
  makeSwatch(color, name) {
6660
7110
  const p = typeof color === "string" ? { type: "color", color: parseColor(color) } : this.readStrokePaint(color);
6661
7111
  const out = { color: _chunkPQ3H2AANcjs.paintSourceColor.call(void 0, p, { paint: this.color, secondary: this.secondaryColor }) };
6662
- if (_optionalChain([name, 'optionalAccess', _377 => _377.trim, 'call', _378 => _378()])) out.name = name.trim();
7112
+ if (_optionalChain([name, 'optionalAccess', _435 => _435.trim, 'call', _436 => _436()])) out.name = name.trim();
6663
7113
  if (p.type === "gradient") out.paint = p;
6664
7114
  return out;
6665
7115
  }
@@ -6711,7 +7161,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6711
7161
  * mix becomes the paint colour. Returns the mixed colour and the pile's bounds.
6712
7162
  */
6713
7163
  mixOnPad(paints, options = {}) {
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]) }));
7164
+ const parsed = (_nullishCoalesce(paints, () => ( []))).map((p, i) => ({ color: parseColor(_optionalChain([p, 'optionalAccess', _437 => _437.color]), `paints[${i}].color`), parts: _optionalChain([p, 'optionalAccess', _438 => _438.parts]) }));
6715
7165
  let r;
6716
7166
  try {
6717
7167
  r = this.pad.mix(parsed, options);
@@ -6800,15 +7250,28 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6800
7250
  }
6801
7251
  /** A layer's tile surface (live while stroking), for thumbnails. */
6802
7252
  layerSurface(id) {
6803
- const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.currentLayers(), id), 'optionalAccess', _381 => _381.layer]);
7253
+ const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.currentLayers(), id), 'optionalAccess', _439 => _439.layer]);
6804
7254
  return isPixelLayer(l) ? l.surface : null;
6805
7255
  }
6806
- }, _class10.__initStatic(), _class10);
7256
+ }, _class11.__initStatic(), _class11);
6807
7257
  var MAX_IMAGE_SIDE = 8192;
6808
7258
  var movePoint = (p, dx, dy) => ({ x: p.x + dx, y: p.y + dy });
6809
7259
  var moveRect = (r, dx, dy) => ({ ...r, x: r.x + dx, y: r.y + dy });
7260
+ var SPILL_SUFFIX = "::light";
7261
+ function withLayerLight(light, layerId, next) {
7262
+ if (!next) return light ? { ...light } : void 0;
7263
+ const out = { ..._nullishCoalesce(light, () => ( {})) };
7264
+ if (next.tiles.size) out[layerId] = next;
7265
+ else delete out[layerId];
7266
+ return out;
7267
+ }
7268
+ function insertAfter(layers, id, node) {
7269
+ const i = layers.findIndex((l) => l.id === id);
7270
+ if (i >= 0) return [...layers.slice(0, i + 1), node, ...layers.slice(i + 1)];
7271
+ return layers.map((l) => l.type === "group" ? { ...l, children: insertAfter(l.children, id, node) } : l);
7272
+ }
6810
7273
  function isLiveEffect(s) {
6811
- return _optionalChain([s, 'access', _382 => _382.paint, 'optionalAccess', _383 => _383.type]) === "effect" && s.paint.live === true;
7274
+ return _optionalChain([s, 'access', _440 => _440.paint, 'optionalAccess', _441 => _441.type]) === "effect" && s.paint.live === true;
6812
7275
  }
6813
7276
  function framePaint(p, t) {
6814
7277
  return { ...p, time: p.time + t };
@@ -6833,7 +7296,7 @@ function clipRect(r, width, height) {
6833
7296
  return x1 > x0 && y1 > y0 ? { x: x0, y: y0, width: x1 - x0, height: y1 - y0 } : null;
6834
7297
  }
6835
7298
  function withLiveStroke(doc, stroke) {
6836
- const list = _optionalChain([doc, 'access', _384 => _384.liveStrokes, 'optionalAccess', _385 => _385[stroke.layerId]]);
7299
+ const list = _optionalChain([doc, 'access', _442 => _442.liveStrokes, 'optionalAccess', _443 => _443[stroke.layerId]]);
6837
7300
  return list ? { ...doc, liveStrokes: { ...doc.liveStrokes, [stroke.layerId]: [...list, stroke] } } : doc;
6838
7301
  }
6839
7302
  function shiftDocument(doc, dx, dy) {
@@ -6860,9 +7323,9 @@ function shiftDocument(doc, dx, dy) {
6860
7323
  if (doc.liveStrokes) out.liveStrokes = Object.fromEntries(Object.entries(doc.liveStrokes).map(([id, list]) => [id, list.map(moveStroke)]));
6861
7324
  if (doc.paintEvents) {
6862
7325
  out.paintEvents = doc.paintEvents.map((e) => {
6863
- const list = e.kind === "stickers" ? _optionalChain([e, 'access', _386 => _386.data, 'optionalAccess', _387 => _387.stickers]) : void 0;
7326
+ const list = e.kind === "stickers" ? _optionalChain([e, 'access', _444 => _444.data, 'optionalAccess', _445 => _445.stickers]) : void 0;
6864
7327
  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;
7328
+ const strokes = e.kind === "live-strokes" ? _optionalChain([e, 'access', _446 => _446.data, 'optionalAccess', _447 => _447.strokes]) : void 0;
6866
7329
  return strokes ? { ...e, data: { ...e.data, strokes: strokes.map(moveStroke) } } : e;
6867
7330
  });
6868
7331
  }
@@ -6944,8 +7407,8 @@ function strokeSchedule(points) {
6944
7407
  return total > 0 ? acc.map((d) => d / total) : points.map(() => 1);
6945
7408
  }
6946
7409
  function strokeDuration(points, max = 1800) {
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]);
7410
+ const t0 = _optionalChain([points, 'access', _448 => _448[0], 'optionalAccess', _449 => _449.t]);
7411
+ const t1 = _optionalChain([points, 'access', _450 => _450[points.length - 1], 'optionalAccess', _451 => _451.t]);
6949
7412
  let ms;
6950
7413
  if (points.every((p) => Number.isFinite(p.t)) && t1 > t0) ms = t1 - t0;
6951
7414
  else {
@@ -6999,7 +7462,7 @@ var PaintDirector = class {
6999
7462
  }
7000
7463
  where(id) {
7001
7464
  const a = this.presence.get(id);
7002
- return { x: _nullishCoalesce(_optionalChain([a, 'optionalAccess', _394 => _394.x]), () => ( 0)), y: _nullishCoalesce(_optionalChain([a, 'optionalAccess', _395 => _395.y]), () => ( 0)) };
7465
+ return { x: _nullishCoalesce(_optionalChain([a, 'optionalAccess', _452 => _452.x]), () => ( 0)), y: _nullishCoalesce(_optionalChain([a, 'optionalAccess', _453 => _453.y]), () => ( 0)) };
7003
7466
  }
7004
7467
  /** Glides to `to`; an actor that just appeared starts there instead of the corner. */
7005
7468
  async glide(id, to, ms = 380) {
@@ -7046,8 +7509,8 @@ var PaintDirector = class {
7046
7509
  }
7047
7510
  case "replay": {
7048
7511
  await this.say(id, command.note, 500);
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]]);
7512
+ const source = c.getStrokes().filter((s) => _optionalChain([command, 'access', _454 => _454.replay, 'access', _455 => _455.strokeIds, 'optionalAccess', _456 => _456.includes, 'call', _457 => _457(s.id)]));
7513
+ const first = _optionalChain([source, 'access', _458 => _458[0], 'optionalAccess', _459 => _459.points, 'access', _460 => _460[0]]);
7051
7514
  if (first) await this.glide(id, first);
7052
7515
  const result = c.replayStrokes(command.replay);
7053
7516
  if (result.bounds) await this.glide(id, centre(result.bounds), 520);
@@ -7228,7 +7691,7 @@ function createPaintStickerTools(c) {
7228
7691
  items: {
7229
7692
  type: "object",
7230
7693
  properties: {
7231
- stamp: { type: "string", enum: [..._chunk5BR7LC24cjs.STAMP_KINDS], description: "The stamp." },
7694
+ stamp: { type: "string", enum: [..._chunk5FZS7J7Ccjs.STAMP_KINDS], description: "The stamp." },
7232
7695
  x: { type: "number", description: "Centre x in document pixels." },
7233
7696
  y: { type: "number", description: "Centre y in document pixels." },
7234
7697
  size: { type: "number", minimum: 2, maximum: 2048, description: "Diameter in pixels (default 48)." },
@@ -7258,7 +7721,7 @@ function createPaintStickerTools(c) {
7258
7721
  properties: {
7259
7722
  layerId: LAYER_ID,
7260
7723
  id: { type: "string", description: "Sticker id from list_stickers." },
7261
- stamp: { type: "string", enum: [..._chunk5BR7LC24cjs.STAMP_KINDS], description: "Another stamp." },
7724
+ stamp: { type: "string", enum: [..._chunk5FZS7J7Ccjs.STAMP_KINDS], description: "Another stamp." },
7262
7725
  x: { type: "number", description: "Centre x in document pixels." },
7263
7726
  y: { type: "number", description: "Centre y in document pixels." },
7264
7727
  size: { type: "number", minimum: 2, maximum: 2048, description: "Diameter in pixels." },
@@ -7955,6 +8418,39 @@ var WRITE4 = { readOnlyHint: false, destructiveHint: false, idempotentHint: fals
7955
8418
  var LAYER_ID3 = { type: "string", description: "Pixel layer id from list_layers (default the active layer)." };
7956
8419
  function createPaintThickTools(c) {
7957
8420
  return [
8421
+ {
8422
+ name: "get_light",
8423
+ title: "Read a layer\u2019s light paint",
8424
+ description: "Reads a layer\u2019s light paint (laid by brushes with light.amount above 0): whether it has any, where it is (bounds, document pixels), its brightest value (peak 0..255) and its spill (radius px, strength). Changes nothing.",
8425
+ inputSchema: { type: "object", properties: { layerId: LAYER_ID3 }, additionalProperties: false },
8426
+ annotations: { title: "Read a layer\u2019s light paint", ...READ4 },
8427
+ coordinates: { out: ["bounds"] },
8428
+ handler: ({ layerId }) => c.lightOf(layerId)
8429
+ },
8430
+ {
8431
+ name: "set_light_spill",
8432
+ title: "Set how far a layer\u2019s light spills",
8433
+ description: "Sets how far a layer\u2019s light paint spills onto the paint and paper around it, in each stroke\u2019s own colour: radius (px, 0 turns spill off, default 24) and strength (0..2, default 1). Nothing is repainted; saved with the document, not an undo step. Returns the layer\u2019s spill.",
8434
+ inputSchema: {
8435
+ type: "object",
8436
+ properties: {
8437
+ layerId: LAYER_ID3,
8438
+ radius: { type: "number", minimum: 0, maximum: 512, description: "How far the light reaches, px (0 turns spill off; default 24)." },
8439
+ strength: { type: "number", minimum: 0, maximum: 2, description: "How strong the spill is, 0..2 (default 1)." }
8440
+ },
8441
+ additionalProperties: false
8442
+ },
8443
+ annotations: { title: "Set how far a layer\u2019s light spills", ...SET3 },
8444
+ handler: ({ layerId, radius, strength }) => c.setLightSpill(layerId, { radius, strength })
8445
+ },
8446
+ {
8447
+ name: "clear_light",
8448
+ title: "Clear a layer\u2019s light",
8449
+ description: "Takes a layer\u2019s light paint away; its paint keeps its colour (and relief). One undo step. Returns whether anything was cleared.",
8450
+ inputSchema: { type: "object", properties: { layerId: LAYER_ID3 }, additionalProperties: false },
8451
+ annotations: { title: "Clear a layer\u2019s light", ...WRITE4 },
8452
+ handler: ({ layerId }) => c.clearLight(layerId)
8453
+ },
7958
8454
  {
7959
8455
  name: "set_lighting",
7960
8456
  title: "Move the light",
@@ -8136,7 +8632,7 @@ var BEHAVIOR = {
8136
8632
  type: "object",
8137
8633
  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.",
8138
8634
  properties: {
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." },
8635
+ mode: { type: "string", enum: [..._chunk5FZS7J7Ccjs.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." },
8140
8636
  colors: { type: "array", maxItems: 8, items: { ...COLOR3 }, description: "2..8 colours #rrggbb; fewer than 2 uses the paint colour and the secondary colour." },
8141
8637
  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)." },
8142
8638
  length: { type: "integer", minimum: 1, maximum: 500, description: "dab mode: dabs per colour (default 12)." },
@@ -8177,10 +8673,10 @@ var WET_BRUSH = {
8177
8673
  properties: {
8178
8674
  tool: {
8179
8675
  type: "string",
8180
- enum: [..._chunk5BR7LC24cjs.WET_TOOLS],
8676
+ enum: [..._chunk5FZS7J7Ccjs.WET_TOOLS],
8181
8677
  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)."
8182
8678
  },
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)." },
8679
+ medium: { type: "string", enum: [..._chunk5FZS7J7Ccjs.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)." },
8184
8680
  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)." },
8185
8681
  thinner: { type: "number", minimum: 0, maximum: 1, description: "Thinner (water or solvent): more transparent, runnier paint." },
8186
8682
  water: { type: "number", minimum: 0, maximum: 1, description: "Wetness: water laid per stroke, 0.2 a damp brush .. 1 a dripping one." },
@@ -8197,7 +8693,7 @@ var IMPASTO_BRUSH = {
8197
8693
  properties: {
8198
8694
  tool: {
8199
8695
  type: "string",
8200
- enum: [..._chunk5BR7LC24cjs.IMPASTO_TOOLS],
8696
+ enum: [..._chunk5FZS7J7Ccjs.IMPASTO_TOOLS],
8201
8697
  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."
8202
8698
  },
8203
8699
  thickness: { type: "number", minimum: 0, maximum: 1, description: "How far the paint stands up." },
@@ -8205,7 +8701,18 @@ var IMPASTO_BRUSH = {
8205
8701
  metallic: { type: "number", minimum: 0, maximum: 1, description: "Metallic paint: highlights take the paint colour (gold, copper, silver)." },
8206
8702
  load: { type: "number", minimum: 0, maximum: 1, description: "Paint loading: 1 never runs out; lower, the stroke thins and fades as the brush empties." },
8207
8703
  thinner: { type: "number", minimum: 0, maximum: 1, description: "Thinner: flatter, more transparent paint." },
8208
- drag: { type: "number", minimum: 0, maximum: 1, description: "Knife and brush: how much of the paint under the tool is dragged along." }
8704
+ drag: { type: "number", minimum: 0, maximum: 1, description: "Knife and brush: how much of the paint under the tool is dragged along." },
8705
+ rim: { type: "number", minimum: 0, maximum: 1, description: "Light paint (with light.amount): the bead's light is stronger where its surface turns away, as on backlit glass (default 0)." },
8706
+ clarity: { type: "number", minimum: 0, maximum: 1, description: "Glassy paint: the middle of a bead lets the layers below show through while its edges stay solid (default 0). flatten_relief clears it." }
8707
+ },
8708
+ additionalProperties: false
8709
+ };
8710
+ var LIGHT_BRUSH = {
8711
+ type: "object",
8712
+ description: "Light paint: the light the brush lays with its colour, glowing in the paint's own colour and spilling onto its surroundings (set_light_spill). Dab, particle, bristle, Mixer Brush and impasto brushes take it; wet and sticker brushes refuse light above 0. Missing fields keep their current values.",
8713
+ properties: {
8714
+ amount: { type: "number", minimum: 0, maximum: 1, description: "Light each dab lays, scaled by its coverage and flow (0 none, default; 1 full)." },
8715
+ pressure: { type: "boolean", description: "Pressure scales the light (default false)." }
8209
8716
  },
8210
8717
  additionalProperties: false
8211
8718
  };
@@ -8213,8 +8720,8 @@ var PARTICLE_BRUSH = {
8213
8720
  type: "object",
8214
8721
  description: "Particle engine settings (procedural brushes). Missing fields keep their current values.",
8215
8722
  properties: {
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." },
8723
+ kind: { type: "string", enum: [..._chunk5FZS7J7Ccjs.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." },
8724
+ render: { type: "string", enum: [..._chunk5FZS7J7Ccjs.PARTICLE_RENDERS], description: "dot: a mark at each step; line: each particle draws a continuous line; sequence: beads spaced along each path." },
8218
8725
  count: { type: "integer", minimum: 1, maximum: 64, description: "Particles (flow: how many are thrown per tip of travel)." },
8219
8726
  size: { type: "number", minimum: 0, maximum: 1, description: "Mark size as a fraction of the tip size." },
8220
8727
  spread: { type: "number", minimum: 0, maximum: 1, description: "How far apart the particles start, as a fraction of the tip size." },
@@ -8244,7 +8751,7 @@ var STICKER_BRUSH = {
8244
8751
  type: "object",
8245
8752
  description: "Sticker engine settings: stamps sprayed along the stroke as editable stickers. Missing fields keep their current values.",
8246
8753
  properties: {
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)." },
8754
+ stamps: { type: "array", minItems: 1, maxItems: 9, items: { type: "string", enum: [..._chunk5FZS7J7Ccjs.STAMP_KINDS], description: "A stamp." }, description: "The stamps to spray (one picked at random per sticker)." },
8248
8755
  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." },
8249
8756
  sizeJitter: { type: "number", minimum: 0, maximum: 1, description: "Random size change." },
8250
8757
  rotationJitter: { type: "number", minimum: 0, maximum: 1, description: "Random turn as a fraction of \xB1180\xB0." },
@@ -8255,16 +8762,16 @@ var STICKER_BRUSH = {
8255
8762
  };
8256
8763
  var BRUSH_FIELDS = {
8257
8764
  name: { type: "string", minLength: 1, maxLength: 80, description: "Display name." },
8258
- category: { type: "string", enum: [..._chunk5BR7LC24cjs.PAINT_BRUSH_CATEGORIES], description: "Library category." },
8765
+ category: { type: "string", enum: [..._chunk5FZS7J7Ccjs.PAINT_BRUSH_CATEGORIES], description: "Library category." },
8259
8766
  engine: {
8260
8767
  type: "string",
8261
- enum: [..._chunk5BR7LC24cjs.PAINT_BRUSH_ENGINES],
8768
+ enum: [..._chunk5FZS7J7Ccjs.PAINT_BRUSH_ENGINES],
8262
8769
  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)."
8263
8770
  },
8264
8771
  tip: TIP,
8265
8772
  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." },
8266
8773
  pressureSmoothing: { type: "number", minimum: 0, maximum: 0.95, description: "Pressure smoothing, separate from the tip position smoothing." },
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)." },
8774
+ rotation: { type: "string", enum: [..._chunk5FZS7J7Ccjs.BRUSH_ROTATIONS], description: "What turns the tip: fixed (tip.angle), stroke (follows the direction of travel) or barrel (the pen barrel rotation, twist)." },
8268
8775
  dynamics: DYNAMICS,
8269
8776
  mixer: MIXER,
8270
8777
  loadsColor: { type: "boolean", description: "Mixer engine: load the colour each stroke (false for a mixer that only moves paint)." },
@@ -8276,6 +8783,7 @@ var BRUSH_FIELDS = {
8276
8783
  particle: PARTICLE_BRUSH,
8277
8784
  bristle: BRISTLE_BRUSH,
8278
8785
  sticker: STICKER_BRUSH,
8786
+ light: LIGHT_BRUSH,
8279
8787
  paint: BRUSH_PAINT
8280
8788
  };
8281
8789
  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)." };
@@ -8315,12 +8823,13 @@ function brushSummary(b) {
8315
8823
  size: b.tip.size,
8316
8824
  grain: b.grain,
8317
8825
  rotation: b.rotation,
8318
- wetCapable: _chunk5BR7LC24cjs.isWetCapable.call(void 0, b),
8826
+ wetCapable: _chunk5FZS7J7Ccjs.isWetCapable.call(void 0, b),
8319
8827
  pickup: b.behavior.pickup,
8320
8828
  dirty: b.behavior.dirty,
8321
8829
  multicolor: b.behavior.multicolor.mode,
8322
8830
  ...b.engine === "wet" ? { wetTool: b.wet.tool, medium: b.wet.medium } : {},
8323
8831
  ...b.engine === "impasto" ? { impastoTool: b.impasto.tool } : {},
8832
+ ...b.light.amount > 0 ? { light: b.light.amount } : {},
8324
8833
  builtin: Boolean(b.builtin)
8325
8834
  };
8326
8835
  }
@@ -8423,8 +8932,8 @@ function createPaintEditorToolProvider(controller, options = {}) {
8423
8932
  inputSchema: EMPTY3,
8424
8933
  annotations: { title: "List papers", ...READ6 },
8425
8934
  handler: () => ({
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],
8935
+ papers: _chunk5FZS7J7Ccjs.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 })),
8936
+ families: [..._chunk5FZS7J7Ccjs.PAPER_FAMILIES],
8428
8937
  patterns: [..._chunkPQ3H2AANcjs.GRAIN_PATTERNS]
8429
8938
  })
8430
8939
  });
@@ -8639,12 +9148,12 @@ function createPaintEditorToolProvider(controller, options = {}) {
8639
9148
  add({
8640
9149
  name: "list_brushes",
8641
9150
  title: "List brushes",
8642
- description: "Lists the brush library: built-in dry media (pencils, pastels, charcoal, airbrush, marker, ink, erasers), paint brushes (dirty round, multi-colour flat, palette dabber), blending tools (blender, smudge, pigment smudge, Mixer Brush) and saved user brushes, with id, name, category, engine, size, grain response, rotation mode, whether it can pick up paint (wetCapable), its pick-up ratio, dirty and multi-colour mode, plus the active brush id.",
9151
+ description: "Lists the brush library: built-in dry media (pencils, pastels, charcoal, airbrush, marker, ink, erasers), paint brushes (dirty round, multi-colour flat, palette dabber), blending tools (blender, smudge, pigment smudge, Mixer Brush), light paint (category light: glow gel, neon marker, light airbrush, spark trail; light is the amount they lay) and saved user brushes, with id, name, category, engine, size, grain response, rotation mode, whether it can pick up paint (wetCapable), its pick-up ratio, dirty and multi-colour mode, plus the active brush id.",
8643
9152
  inputSchema: EMPTY3,
8644
9153
  annotations: { title: "List brushes", ...READ6 },
8645
9154
  handler: () => {
8646
9155
  const s = c.getState();
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) };
9156
+ return { activeBrushId: s.brush.id, activeEdited: !s.brush.builtin && Boolean(_optionalChain([c, 'access', _461 => _461.getBrush, 'call', _462 => _462(s.brush.id), 'optionalAccess', _463 => _463.builtin])), brushes: s.brushes.map(brushSummary) };
8648
9157
  }
8649
9158
  });
8650
9159
  add({
@@ -8655,10 +9164,10 @@ function createPaintEditorToolProvider(controller, options = {}) {
8655
9164
  annotations: { title: "Get a brush", ...READ6 },
8656
9165
  handler: ({ id }) => {
8657
9166
  const st = c.getState();
8658
- if (id === void 0) return { ..._chunk5BR7LC24cjs.brushToJSON.call(void 0, st.brush), strokePaint: _nullishCoalesce(st.brush.paint, () => ( st.paint)) };
9167
+ if (id === void 0) return { ..._chunk5FZS7J7Ccjs.brushToJSON.call(void 0, st.brush), strokePaint: _nullishCoalesce(st.brush.paint, () => ( st.paint)) };
8659
9168
  const b = c.getBrush(id);
8660
9169
  if (!b) throw new Error(`Unknown brush "${id}". Call list_brushes for the ids.`);
8661
- return { ..._chunk5BR7LC24cjs.brushToJSON.call(void 0, b), builtin: Boolean(b.builtin), strokePaint: _nullishCoalesce(b.paint, () => ( st.paint)) };
9170
+ return { ..._chunk5FZS7J7Ccjs.brushToJSON.call(void 0, b), builtin: Boolean(b.builtin), strokePaint: _nullishCoalesce(b.paint, () => ( st.paint)) };
8662
9171
  }
8663
9172
  });
8664
9173
  add({
@@ -8667,7 +9176,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
8667
9176
  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.",
8668
9177
  inputSchema: { type: "object", properties: { id: { type: "string", description: "Brush id from list_brushes to start from." }, ...BRUSH_FIELDS }, additionalProperties: false },
8669
9178
  annotations: { title: "Set the brush", ...SET4 },
8670
- handler: (a) => _chunk5BR7LC24cjs.brushToJSON.call(void 0, c.setBrush(a))
9179
+ handler: (a) => _chunk5FZS7J7Ccjs.brushToJSON.call(void 0, c.setBrush(a))
8671
9180
  });
8672
9181
  add({
8673
9182
  name: "save_brush",
@@ -8683,7 +9192,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
8683
9192
  additionalProperties: false
8684
9193
  },
8685
9194
  annotations: { title: "Save a brush", ...SET4 },
8686
- handler: ({ id, name }) => _chunk5BR7LC24cjs.brushToJSON.call(void 0, c.saveBrush({ id, name }))
9195
+ handler: ({ id, name }) => _chunk5FZS7J7Ccjs.brushToJSON.call(void 0, c.saveBrush({ id, name }))
8687
9196
  });
8688
9197
  add({
8689
9198
  name: "remove_brush",
@@ -8874,7 +9383,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
8874
9383
  if (ids) {
8875
9384
  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.`);
8876
9385
  }
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)) };
9386
+ return { total: all.length, strokes: picked.map((s) => strokeSummary(s, Boolean(ids), live ? true : liveIds.has(s.id) && _optionalChain([c, 'access', _464 => _464.document, 'access', _465 => _465.liveStrokes, 'optionalAccess', _466 => _466[s.layerId], 'optionalAccess', _467 => _467.includes, 'call', _468 => _468(s)]) === true)) };
8878
9387
  }
8879
9388
  });
8880
9389
  add({
@@ -9164,16 +9673,16 @@ function createPaintEditorToolProvider(controller, options = {}) {
9164
9673
  }
9165
9674
  function hostTool({ coordinates, ...tool }, { origin, onChange }) {
9166
9675
  if (!origin && !onChange) return tool;
9167
- const write = !_optionalChain([tool, 'access', _411 => _411.annotations, 'optionalAccess', _412 => _412.readOnlyHint]);
9676
+ const write = !_optionalChain([tool, 'access', _469 => _469.annotations, 'optionalAccess', _470 => _470.readOnlyHint]);
9168
9677
  const done = (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;
9678
+ if (write) _optionalChain([onChange, 'optionalCall', _471 => _471({ tool: tool.name })]);
9679
+ return origin && _optionalChain([coordinates, 'optionalAccess', _472 => _472.out]) ? fromDocument(result, coordinates.out, origin) : result;
9171
9680
  };
9172
9681
  return {
9173
9682
  ...tool,
9174
9683
  ...origin ? { description: hostedText(_nullishCoalesce(tool.description, () => ( ""))), inputSchema: hostedSchema(tool.inputSchema) } : {},
9175
9684
  handler: (args, context) => {
9176
- const r = tool.handler(origin && _optionalChain([coordinates, 'optionalAccess', _415 => _415.in]) ? toDocument(args, coordinates.in, origin) : args, context);
9685
+ const r = tool.handler(origin && _optionalChain([coordinates, 'optionalAccess', _473 => _473.in]) ? toDocument(args, coordinates.in, origin) : args, context);
9177
9686
  return r instanceof Promise ? r.then(done) : done(r);
9178
9687
  }
9179
9688
  };
@@ -9190,7 +9699,7 @@ function strokeSummary(s, withPoints, live) {
9190
9699
  color: s.color,
9191
9700
  points: withPoints ? s.points : s.points.length,
9192
9701
  seed: s.seed,
9193
- copies: _nullishCoalesce(_optionalChain([s, 'access', _416 => _416.copies, 'optionalAccess', _417 => _417.length]), () => ( 0)),
9702
+ copies: _nullishCoalesce(_optionalChain([s, 'access', _474 => _474.copies, 'optionalAccess', _475 => _475.length]), () => ( 0)),
9194
9703
  live,
9195
9704
  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
9705
  };
@@ -9205,21 +9714,21 @@ var PAINT_TOOLS = [
9205
9714
  { id: "pan", label: "Pan", key: "h" },
9206
9715
  { id: "rotate", label: "Rotate view", key: "r" }
9207
9716
  ];
9208
- var PaintGesture = (_class11 = class {
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);
9717
+ var PaintGesture = (_class12 = class {
9718
+ constructor(controller, box) {;_class12.prototype.__init131.call(this);_class12.prototype.__init132.call(this);_class12.prototype.__init133.call(this);_class12.prototype.__init134.call(this);_class12.prototype.__init135.call(this);_class12.prototype.__init136.call(this);
9210
9719
  this.controller = controller;
9211
9720
  this.box = box;
9212
9721
  }
9213
9722
 
9214
9723
 
9215
- __init125() {this.tool = "brush"}
9724
+ __init131() {this.tool = "brush"}
9216
9725
  /** The last refusal (a hidden or locked layer), for the editor to show. */
9217
- __init126() {this.error = null}
9726
+ __init132() {this.error = null}
9218
9727
  /** The predicted tail of the current stroke in document pixels (draw it as a thin preview). */
9219
- __init127() {this.predicted = []}
9220
- __init128() {this.mode = null}
9221
- __init129() {this.penAt = null}
9222
- __init130() {this.penDown = false}
9728
+ __init133() {this.predicted = []}
9729
+ __init134() {this.mode = null}
9730
+ __init135() {this.penAt = null}
9731
+ __init136() {this.penDown = false}
9223
9732
  doc(e) {
9224
9733
  const b = this.box();
9225
9734
  const s = this.controller.getState();
@@ -9275,13 +9784,13 @@ var PaintGesture = (_class11 = class {
9275
9784
  const p = this.doc(e);
9276
9785
  const s = this.controller.getState();
9277
9786
  const layerId = s.document.activeLayerId;
9278
- if (!layerId || !_optionalChain([s, 'access', _418 => _418.document, 'access', _419 => _419.liveStrokes, 'optionalAccess', _420 => _420[layerId]])) {
9787
+ if (!layerId || !_optionalChain([s, 'access', _476 => _476.document, 'access', _477 => _477.liveStrokes, 'optionalAccess', _478 => _478[layerId]])) {
9279
9788
  this.error = "Pick a stroke layer in the Layers panel (or add one) to select its strokes.";
9280
9789
  return false;
9281
9790
  }
9282
9791
  try {
9283
9792
  const hit = this.controller.liveStrokeAt(layerId, p.x, p.y);
9284
- this.controller.selectStroke(_nullishCoalesce(_optionalChain([hit, 'optionalAccess', _421 => _421.id]), () => ( null)));
9793
+ this.controller.selectStroke(_nullishCoalesce(_optionalChain([hit, 'optionalAccess', _479 => _479.id]), () => ( null)));
9285
9794
  if (!hit) return false;
9286
9795
  this.mode = { kind: "move-stroke", pointerId: e.pointerId, id: hit.id, from: p, to: p };
9287
9796
  return true;
@@ -9293,7 +9802,7 @@ var PaintGesture = (_class11 = class {
9293
9802
  /** While a stroke is dragged: how far it has moved (document pixels), to draw its outline there. */
9294
9803
  get strokeDrag() {
9295
9804
  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;
9805
+ return _optionalChain([m, 'optionalAccess', _480 => _480.kind]) === "move-stroke" ? { id: m.id, dx: m.to.x - m.from.x, dy: m.to.y - m.from.y } : null;
9297
9806
  }
9298
9807
  /**
9299
9808
  * The Stickers tool: pressing a sticker picks it and dragging moves it (one undo step at the
@@ -9367,7 +9876,7 @@ var PaintGesture = (_class11 = class {
9367
9876
  if (!m || m.pointerId !== e.pointerId) return;
9368
9877
  this.mode = null;
9369
9878
  this.predicted = [];
9370
- if (m.kind === "stroke") _optionalChain([this, 'access', _423 => _423.controller, 'access', _424 => _424.activeStroke, 'optionalAccess', _425 => _425.end, 'call', _426 => _426()]);
9879
+ if (m.kind === "stroke") _optionalChain([this, 'access', _481 => _481.controller, 'access', _482 => _482.activeStroke, 'optionalAccess', _483 => _483.end, 'call', _484 => _484()]);
9371
9880
  if (m.kind === "move-stroke") {
9372
9881
  const dx = Math.round((this.doc(e).x - m.from.x) * 100) / 100;
9373
9882
  const dy = Math.round((this.doc(e).y - m.from.y) * 100) / 100;
@@ -9391,8 +9900,8 @@ var PaintGesture = (_class11 = class {
9391
9900
  cancel() {
9392
9901
  this.penDown = false;
9393
9902
  this.predicted = [];
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();
9903
+ if (_optionalChain([this, 'access', _485 => _485.mode, 'optionalAccess', _486 => _486.kind]) === "stroke") _optionalChain([this, 'access', _487 => _487.controller, 'access', _488 => _488.activeStroke, 'optionalAccess', _489 => _489.cancel, 'call', _490 => _490()]);
9904
+ if (_optionalChain([this, 'access', _491 => _491.mode, 'optionalAccess', _492 => _492.kind]) === "sticker") this.controller.cancelStickerPreview();
9396
9905
  this.mode = null;
9397
9906
  }
9398
9907
  get active() {
@@ -9412,7 +9921,7 @@ var PaintGesture = (_class11 = class {
9412
9921
  const py = e.clientY - b.top - b.height / 2;
9413
9922
  this.controller.setView({ zoom, pan: { x: px - (px - s.view.pan.x) * k, y: py - (py - s.view.pan.y) * k } });
9414
9923
  }
9415
- }, _class11);
9924
+ }, _class12);
9416
9925
  var round2 = (v) => Math.round(v * 100) / 100;
9417
9926
  function handlePaintShortcut(controller, gesture, e) {
9418
9927
  const mod = e.ctrlKey || e.metaKey;
@@ -9453,14 +9962,14 @@ function handlePaintShortcut(controller, gesture, e) {
9453
9962
  }
9454
9963
  return false;
9455
9964
  }
9456
- var PaintCanvasPresenter = (_class12 = class {
9457
- constructor(controller) {;_class12.prototype.__init131.call(this);_class12.prototype.__init132.call(this);_class12.prototype.__init133.call(this);
9965
+ var PaintCanvasPresenter = (_class13 = class {
9966
+ constructor(controller) {;_class13.prototype.__init137.call(this);_class13.prototype.__init138.call(this);_class13.prototype.__init139.call(this);
9458
9967
  this.controller = controller;
9459
9968
  }
9460
9969
 
9461
- __init131() {this.uploaded = /* @__PURE__ */ new Map()}
9462
- __init132() {this.scratch = null}
9463
- __init133() {this.size = ""}
9970
+ __init137() {this.uploaded = /* @__PURE__ */ new Map()}
9971
+ __init138() {this.scratch = null}
9972
+ __init139() {this.size = ""}
9464
9973
  /** Draws what changed into `ctx` (a canvas the size of the document); returns the tiles drawn. */
9465
9974
  render(ctx) {
9466
9975
  const { width, height } = this.controller.document.canvas;
@@ -9486,13 +9995,13 @@ var PaintCanvasPresenter = (_class12 = class {
9486
9995
  }, { dirty });
9487
9996
  return drawn;
9488
9997
  }
9489
- }, _class12);
9998
+ }, _class13);
9490
9999
  function guideOverlay(controller, drag) {
9491
10000
  const s = controller.getState();
9492
10001
  const size = s.document.canvas;
9493
10002
  const out = [];
9494
10003
  const layerId = s.document.activeLayerId;
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;
10004
+ const strokePicked = layerId && s.selectedStroke ? _optionalChain([s, 'access', _493 => _493.document, 'access', _494 => _494.liveStrokes, 'optionalAccess', _495 => _495[layerId], 'optionalAccess', _496 => _496.some, 'call', _497 => _497((x) => x.id === s.selectedStroke)]) : false;
9496
10005
  const area = strokePicked ? controller.liveStrokeArea(s.selectedStroke) : null;
9497
10006
  if (area) {
9498
10007
  const dx = drag && drag.id === s.selectedStroke ? drag.dx : 0;
@@ -9505,7 +10014,7 @@ function guideOverlay(controller, drag) {
9505
10014
  ];
9506
10015
  for (let i = 0; i < 4; i++) out.push({ from: box[i], to: box[(i + 1) % 4], kind: "stroke" });
9507
10016
  }
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;
10017
+ const picked = layerId && s.selectedSticker ? _optionalChain([s, 'access', _498 => _498.document, 'access', _499 => _499.stickers, 'optionalAccess', _500 => _500[layerId], 'optionalAccess', _501 => _501.find, 'call', _502 => _502((x) => x.id === s.selectedSticker)]) : void 0;
9509
10018
  if (picked) {
9510
10019
  const r = picked.size / 2 + 2;
9511
10020
  const a = picked.rotation * Math.PI / 180;
@@ -9549,7 +10058,7 @@ var PAPER_SLIDERS = [
9549
10058
  ];
9550
10059
  function readPath(obj, path) {
9551
10060
  let v = obj;
9552
- for (const k of path.split(".")) v = _optionalChain([v, 'optionalAccess', _445 => _445[k]]);
10061
+ for (const k of path.split(".")) v = _optionalChain([v, 'optionalAccess', _503 => _503[k]]);
9553
10062
  return typeof v === "number" ? v : 0;
9554
10063
  }
9555
10064
  function patchPath(obj, path, value) {
@@ -9594,11 +10103,18 @@ var BLEND_MENU_GROUPS = [
9594
10103
  { label: "Standard", modes: _chunkOLNIPTB3cjs.BLEND_MODES.filter((m) => !m.painterly && m.id !== "pigment") },
9595
10104
  { label: "Paint", modes: _chunkOLNIPTB3cjs.BLEND_MODES.filter((m) => m.painterly || m.id === "pigment") }
9596
10105
  ];
10106
+ var LIGHT_SLIDER = { path: "light.amount", label: "Light", min: 0, max: 1, step: 0.01 };
9597
10107
  var PICKUP_SLIDERS = [
9598
10108
  { path: "behavior.pickup", label: "Pick-up", min: 0, max: 1, step: 0.01 },
9599
10109
  { path: "behavior.mix", label: "Mix", min: 0, max: 1, step: 0.01 }
9600
10110
  ];
9601
10111
  function brushEngineControls(brush) {
10112
+ const c = engineControls(brush);
10113
+ if (!c || !["dab", "mixer", "particle", "bristle", "impasto"].includes(brush.engine)) return c;
10114
+ const extra = brush.engine === "impasto" ? [{ path: "impasto.rim", label: "Rim", min: 0, max: 1, step: 0.01 }, { path: "impasto.clarity", label: "Clarity", min: 0, max: 1, step: 0.01 }] : [];
10115
+ return { ...c, sliders: [...c.sliders, LIGHT_SLIDER, ...extra], light: true };
10116
+ }
10117
+ function engineControls(brush) {
9602
10118
  switch (brush.engine) {
9603
10119
  case "particle":
9604
10120
  return {
@@ -9671,7 +10187,7 @@ function brushEngineControls(brush) {
9671
10187
  impastoTool: true
9672
10188
  };
9673
10189
  case "wet": {
9674
- const tool = _nullishCoalesce(_optionalChain([brush, 'access', _446 => _446.wet, 'optionalAccess', _447 => _447.tool]), () => ( "paint"));
10190
+ const tool = _nullishCoalesce(_optionalChain([brush, 'access', _504 => _504.wet, 'optionalAccess', _505 => _505.tool]), () => ( "paint"));
9675
10191
  const sliders = tool === "paint" ? [
9676
10192
  { path: "wet.water", label: "Water", min: 0, max: 1, step: 0.01 },
9677
10193
  { path: "wet.pigment", label: "Pigment", min: 0, max: 1, step: 0.01 },
@@ -9804,7 +10320,7 @@ function startWetClock(controller, frame, cancel) {
9804
10320
  id = raf(loop);
9805
10321
  return () => {
9806
10322
  stopped = true;
9807
- _optionalChain([caf, 'optionalCall', _448 => _448(id)]);
10323
+ _optionalChain([caf, 'optionalCall', _506 => _506(id)]);
9808
10324
  };
9809
10325
  }
9810
10326
  function drawWaterOverlay(controller, ctx) {
@@ -9903,7 +10419,7 @@ function createPaintEffectsToolProvider(controller, options = {}) {
9903
10419
  if (changes.enabled !== void 0) throw new Error("enabled does not apply here: hide an effect layer with update_layer visible false, or remove the effect paper.");
9904
10420
  };
9905
10421
  const values = (v) => v;
9906
- return withPaintPlayback(_chunkJAOSL334cjs.createEffectToolProvider.call(void 0, {
10422
+ return withPaintPlayback(_chunkGHCSC2WPcjs.createEffectToolProvider.call(void 0, {
9907
10423
  id: _nullishCoalesce(options.id, () => ( "paint-effects")),
9908
10424
  namespace: options.namespace,
9909
10425
  title: "Paint effects",
@@ -10450,4 +10966,6 @@ function createPaintEditorDocument(controller, options = {}) {
10450
10966
 
10451
10967
 
10452
10968
 
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;
10969
+
10970
+
10971
+ exports.BLEND_MENU_GROUPS = BLEND_MENU_GROUPS; exports.BLEND_MODES = _chunkOLNIPTB3cjs.BLEND_MODES; exports.BRUSH_ID = _chunk5FZS7J7Ccjs.BRUSH_ID; exports.BRUSH_ROTATIONS = _chunk5FZS7J7Ccjs.BRUSH_ROTATIONS; exports.BRUSH_SLIDERS = BRUSH_SLIDERS; exports.BUILTIN_BRUSHES = _chunk5FZS7J7Ccjs.BUILTIN_BRUSHES; exports.BristleStroke = _chunk5FZS7J7Ccjs.BristleStroke; exports.DEFAULT_BEHAVIOR = _chunk5FZS7J7Ccjs.DEFAULT_BEHAVIOR; exports.DEFAULT_BRISTLE = _chunk5FZS7J7Ccjs.DEFAULT_BRISTLE; exports.DEFAULT_BRUSH_ID = _chunk5FZS7J7Ccjs.DEFAULT_BRUSH_ID; exports.DEFAULT_DPI = DEFAULT_DPI; exports.DEFAULT_DYNAMICS = _chunk5FZS7J7Ccjs.DEFAULT_DYNAMICS; exports.DEFAULT_FLOW = _chunkXFYLA3ZOcjs.DEFAULT_FLOW; exports.DEFAULT_IMPASTO = _chunk5FZS7J7Ccjs.DEFAULT_IMPASTO; exports.DEFAULT_LIGHT = _chunk5FZS7J7Ccjs.DEFAULT_LIGHT; exports.DEFAULT_LIGHTING = DEFAULT_LIGHTING; exports.DEFAULT_MULTI_COLOR = _chunk5FZS7J7Ccjs.DEFAULT_MULTI_COLOR; exports.DEFAULT_PAINT_GRADIENT = _chunkPQ3H2AANcjs.DEFAULT_PAINT_GRADIENT; exports.DEFAULT_PAINT_GRADIENT_MAPPING = _chunkPQ3H2AANcjs.DEFAULT_PAINT_GRADIENT_MAPPING; exports.DEFAULT_PAPER = _chunk5FZS7J7Ccjs.DEFAULT_PAPER; exports.DEFAULT_PARTICLE = _chunk5FZS7J7Ccjs.DEFAULT_PARTICLE; exports.DEFAULT_STICKER = _chunk5FZS7J7Ccjs.DEFAULT_STICKER; exports.DEFAULT_SWATCHES = DEFAULT_SWATCHES; exports.DEFAULT_VIEW = DEFAULT_VIEW; exports.DEFAULT_WET_BRUSH = _chunk5FZS7J7Ccjs.DEFAULT_WET_BRUSH; exports.DEFAULT_WET_PARAMS = _chunkXFYLA3ZOcjs.DEFAULT_WET_PARAMS; exports.DryStroke = _chunk5FZS7J7Ccjs.DryStroke; exports.EFFECT_MAP_REPEATS = _chunk5FZS7J7Ccjs.EFFECT_MAP_REPEATS; exports.EFFECT_STRIP_HEIGHT = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.IDENTITY_COPY; exports.IMPASTO_TOOLS = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.PAINT_BRUSH_CATEGORIES; exports.PAINT_BRUSH_ENGINES = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.PAPER_FAMILIES; exports.PAPER_LIBRARY = _chunk5FZS7J7Ccjs.PAPER_LIBRARY; exports.PAPER_NODE_ID = PAPER_NODE_ID; exports.PAPER_SLIDERS = PAPER_SLIDERS; exports.PAPER_TARGET = PAPER_TARGET; exports.PARTICLE_KINDS = _chunk5FZS7J7Ccjs.PARTICLE_KINDS; exports.PARTICLE_KIND_LABELS = PARTICLE_KIND_LABELS; exports.PARTICLE_RENDERS = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.ParticleStroke; exports.PenDabWalker = _chunk5FZS7J7Ccjs.PenDabWalker; exports.PenSmoother = _chunk5FZS7J7Ccjs.PenSmoother; exports.PerspectiveSnapper = PerspectiveSnapper; exports.SNAP_DECIDE_DISTANCE = SNAP_DECIDE_DISTANCE; exports.STAMP_KINDS = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.brushTakesPaint; exports.brushToJSON = _chunk5FZS7J7Ccjs.brushToJSON; exports.buildTimeLapse = buildTimeLapse; exports.checkPaintEffectPlace = checkPaintEffectPlace; exports.clonePaper = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.drawSticker; exports.drawWaterOverlay = drawWaterOverlay; exports.effectDabField = _chunk5FZS7J7Ccjs.effectDabField; exports.effectLayersToJSON = effectLayersToJSON; exports.effectPaintKey = _chunk5FZS7J7Ccjs.effectPaintKey; exports.effectPaintSize = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.hexToRgb8; exports.hslToRgb = hslToRgb; exports.hsvToRgb = hsvToRgb; exports.hueToWheel = _chunk7PT4RJL5cjs.hueToWheel; exports.invertAffine = invertAffine; exports.isBlendMode = _chunkOLNIPTB3cjs.isBlendMode; exports.isGradientPaint = _chunkPQ3H2AANcjs.isGradientPaint; exports.isPaintEffectPaint = _chunk5FZS7J7Ccjs.isPaintEffectPaint; exports.isPainterlyBlendMode = _chunkOLNIPTB3cjs.isPainterlyBlendMode; exports.isPalmTouch = isPalmTouch; exports.isWetBrush = _chunk5FZS7J7Ccjs.isWetBrush; exports.isWetCapable = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.mixerTip; exports.multiColorForDab = multiColorForDab; exports.multiColorGradient = multiColorGradient; exports.multiColorPalette = multiColorPalette; exports.multiColorSliders = multiColorSliders; exports.multiColorToPaint = _chunk5FZS7J7Ccjs.multiColorToPaint; exports.newPaintLayerId = newPaintLayerId; exports.nextPaperEffectVersion = nextPaperEffectVersion; exports.normalizeBehavior = _chunk5FZS7J7Ccjs.normalizeBehavior; exports.normalizeBristle = _chunk5FZS7J7Ccjs.normalizeBristle; exports.normalizeBrushStroke = normalizeBrushStroke; exports.normalizeCanvas = normalizeCanvas; exports.normalizeDynamics = _chunk5FZS7J7Ccjs.normalizeDynamics; exports.normalizeFlow = _chunkXFYLA3ZOcjs.normalizeFlow; exports.normalizeHexColor = _chunkPQ3H2AANcjs.normalizeHexColor; exports.normalizeImpasto = _chunk5FZS7J7Ccjs.normalizeImpasto; exports.normalizeLight = _chunk5FZS7J7Ccjs.normalizeLight; exports.normalizeLighting = normalizeLighting; exports.normalizeMultiColor = _chunk5FZS7J7Ccjs.normalizeMultiColor; exports.normalizePaintBrush = _chunk5FZS7J7Ccjs.normalizePaintBrush; exports.normalizePaper = _chunk5FZS7J7Ccjs.normalizePaper; exports.normalizeParticle = _chunk5FZS7J7Ccjs.normalizeParticle; exports.normalizePerspective = normalizePerspective; exports.normalizeRotation = normalizeRotation; exports.normalizeStencil = normalizeStencil; exports.normalizeStickerSettings = _chunk5FZS7J7Ccjs.normalizeStickerSettings; exports.normalizeStickerUpdate = _chunk5FZS7J7Ccjs.normalizeStickerUpdate; exports.normalizeSymmetry = normalizeSymmetry; exports.normalizeView = normalizeView; exports.normalizeWetBrush = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.paintToMultiColor; exports.paperEffectFromJSON = paperEffectFromJSON; exports.paperEffectToJSON = paperEffectToJSON; exports.paperGrain = paperGrain; exports.paperKey = paperKey; exports.paperNode = paperNode; exports.paperPreset = _chunk5FZS7J7Ccjs.paperPreset; exports.parseBrushLibrary = _chunk5FZS7J7Ccjs.parseBrushLibrary; exports.parseHexColor = parseHexColor; exports.parsePaintSource = _chunkPQ3H2AANcjs.parsePaintSource; exports.patchBrushValue = patchBrushValue; exports.patchPath = patchPath; exports.penOrientation = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.readEffectStrokeMap; exports.readPaintEffectPaint = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.renderPaper; exports.renderStickers = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.sampleEffectAt; exports.sampleEffectField = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.stampCoverage; exports.standardBlendMode = _chunkOLNIPTB3cjs.standardBlendMode; exports.startWetClock = startWetClock; exports.stencilMask = stencilMask; exports.stencilOutline = stencilOutline; exports.stickerAt = _chunk5FZS7J7Ccjs.stickerAt; exports.stickerRect = _chunk5FZS7J7Ccjs.stickerRect; exports.stickerSurface = _chunk5FZS7J7Ccjs.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 = _chunk5FZS7J7Ccjs.tiltToOrientation; exports.timeLapseVideo = timeLapseVideo; exports.timeLapseZip = timeLapseZip; exports.toStrokePoint = _chunk5FZS7J7Ccjs.toStrokePoint; exports.toggleStamp = toggleStamp; exports.translateStencil = translateStencil; exports.updatePaintGradientStop = _chunkPQ3H2AANcjs.updatePaintGradientStop; exports.usesPaintExchange = _chunk5FZS7J7Ccjs.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;