@pixerate/editor 0.242.0 → 0.244.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 (78) hide show
  1. package/dist/animation-editor/index.cjs +59 -59
  2. package/dist/animation-editor/index.js +3 -3
  3. package/dist/{brushstrokeRender-MOYGEXOC.js → brushstrokeRender-4542T5HP.js} +3 -3
  4. package/dist/{brushstrokeRender-UKADOV5D.cjs → brushstrokeRender-QA5WVIDT.cjs} +5 -5
  5. package/dist/canvas/index.cjs +8 -8
  6. package/dist/canvas/index.js +1 -1
  7. package/dist/{chunk-ILM6GYAX.js → chunk-4EQY4TZJ.js} +1 -1
  8. package/dist/{chunk-5BR7LC24.cjs → chunk-4T47Z4J3.cjs} +68 -6
  9. package/dist/{chunk-ZDPLZDTT.js → chunk-5RFENYO5.js} +1 -1
  10. package/dist/{chunk-NE6OOKWJ.js → chunk-7YQNKSQ7.js} +1 -1
  11. package/dist/{chunk-AFLMXITL.cjs → chunk-BDIH4DYR.cjs} +23 -23
  12. package/dist/{chunk-HGGJ36HR.cjs → chunk-HZ547SGQ.cjs} +1 -1
  13. package/dist/{chunk-KCMV2534.cjs → chunk-IZL5V5RK.cjs} +2 -2
  14. package/dist/{chunk-QP2B3MID.js → chunk-L5PB7EG3.js} +1 -1
  15. package/dist/{chunk-HW72ABGR.cjs → chunk-M6JORA5Z.cjs} +86 -86
  16. package/dist/{chunk-UDBDYZET.js → chunk-OSTKS3YN.js} +1 -1
  17. package/dist/{chunk-HLBTRSQJ.js → chunk-P3NIPIKS.js} +263 -63
  18. package/dist/{chunk-7F4G74UQ.cjs → chunk-SD23XMVJ.cjs} +35 -35
  19. package/dist/{chunk-XBZK47LV.js → chunk-TBJEMELS.js} +67 -5
  20. package/dist/{chunk-XFYLA3ZO.cjs → chunk-VMROIWLL.cjs} +290 -90
  21. package/dist/image-editor/index.cjs +785 -777
  22. package/dist/image-editor/index.js +13 -5
  23. package/dist/kernel-wasm-4KVONDAU.js +9 -0
  24. package/dist/kernel-wasm-TTYRBGAK.cjs +9 -0
  25. package/dist/paint/index.cjs +10 -2
  26. package/dist/paint/index.js +9 -1
  27. package/dist/paint/wet/film.d.cts +19 -1
  28. package/dist/paint/wet/film.d.ts +19 -1
  29. package/dist/paint/wet/gpu-kernels.d.cts +2 -2
  30. package/dist/paint/wet/gpu-kernels.d.ts +2 -2
  31. package/dist/paint/wet/kernel-wasm.d.cts +5 -5
  32. package/dist/paint/wet/kernel-wasm.d.ts +5 -5
  33. package/dist/paint/wet/kernel.d.cts +1 -1
  34. package/dist/paint/wet/kernel.d.ts +1 -1
  35. package/dist/paint/wet/medium.d.cts +3 -1
  36. package/dist/paint/wet/medium.d.ts +3 -1
  37. package/dist/paint/wet/model.d.cts +19 -4
  38. package/dist/paint/wet/model.d.ts +19 -4
  39. package/dist/paint/wet/world.d.cts +34 -8
  40. package/dist/paint/wet/world.d.ts +34 -8
  41. package/dist/paint-editor/backlight.d.cts +77 -0
  42. package/dist/paint-editor/backlight.d.ts +77 -0
  43. package/dist/paint-editor/brushes.d.cts +18 -2
  44. package/dist/paint-editor/brushes.d.ts +18 -2
  45. package/dist/paint-editor/controller.d.cts +39 -0
  46. package/dist/paint-editor/controller.d.ts +39 -0
  47. package/dist/paint-editor/document.d.cts +3 -0
  48. package/dist/paint-editor/document.d.ts +3 -0
  49. package/dist/paint-editor/index.cjs +1335 -529
  50. package/dist/paint-editor/index.d.cts +1 -0
  51. package/dist/paint-editor/index.d.ts +1 -0
  52. package/dist/paint-editor/index.js +856 -50
  53. package/dist/paint-editor/light.d.cts +74 -0
  54. package/dist/paint-editor/light.d.ts +74 -0
  55. package/dist/paint-editor/paper.d.cts +7 -0
  56. package/dist/paint-editor/paper.d.ts +7 -0
  57. package/dist/paint-editor/stroke.d.cts +14 -0
  58. package/dist/paint-editor/stroke.d.ts +14 -0
  59. package/dist/paint-editor/thick-tools.d.cts +2 -2
  60. package/dist/paint-editor/thick-tools.d.ts +2 -2
  61. package/dist/paint-editor/thick.d.cts +5 -1
  62. package/dist/paint-editor/thick.d.ts +5 -1
  63. package/dist/paint-editor/ui.d.cts +2 -0
  64. package/dist/paint-editor/ui.d.ts +2 -0
  65. package/dist/paint-editor/wet.d.cts +9 -0
  66. package/dist/paint-editor/wet.d.ts +9 -0
  67. package/dist/vector/index.cjs +962 -962
  68. package/dist/vector/index.js +4 -4
  69. package/package.json +1 -1
  70. package/skills/pixerate-paint-editor/SKILL.md +11 -11
  71. package/skills/pixerate-paint-editor/references/canvas-hosted.md +8 -0
  72. package/skills/pixerate-paint-editor/references/light-paint.md +48 -0
  73. package/skills/pixerate-paint-oil-impasto/SKILL.md +1 -0
  74. package/skills/pixerate-paint-watercolour/SKILL.md +10 -1
  75. package/skills/pixerate-paint-watercolour/evals/glowing-wash.json +22 -0
  76. package/skills/pixerate-paint-watercolour/evals/stained-glass.json +16 -0
  77. package/dist/kernel-wasm-6FPOLVUZ.cjs +0 -9
  78. package/dist/kernel-wasm-WIMMJUF2.js +0 -9
@@ -1,4 +1,4 @@
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; var _class14;
2
2
 
3
3
  var _chunkXNZFVYC2cjs = require('../chunk-XNZFVYC2.cjs');
4
4
 
@@ -7,7 +7,7 @@ var _chunkXNZFVYC2cjs = require('../chunk-XNZFVYC2.cjs');
7
7
 
8
8
 
9
9
 
10
- var _chunkHW72ABGRcjs = require('../chunk-HW72ABGR.cjs');
10
+ var _chunkM6JORA5Zcjs = require('../chunk-M6JORA5Z.cjs');
11
11
  require('../chunk-A2IS7EHE.cjs');
12
12
  require('../chunk-SS53NNZO.cjs');
13
13
 
@@ -44,7 +44,7 @@ require('../chunk-K6PXS6DO.cjs');
44
44
 
45
45
 
46
46
 
47
- var _chunkXFYLA3ZOcjs = require('../chunk-XFYLA3ZO.cjs');
47
+ var _chunkVMROIWLLcjs = require('../chunk-VMROIWLL.cjs');
48
48
  require('../chunk-BROJYMWU.cjs');
49
49
 
50
50
 
@@ -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-IZL5V5RK.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 _chunk4T47Z4J3cjs = require('../chunk-4T47Z4J3.cjs');
135
137
  require('../chunk-CVFAAZXE.cjs');
136
138
  require('../chunk-GF2RBSDM.cjs');
137
139
  require('../chunk-NM3G4VQT.cjs');
@@ -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: _chunk4T47Z4J3cjs.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, _chunk4T47Z4J3cjs.PenSmoother)(brush.tip.smoothing, brush.pressureSmoothing);
353
+ this.copies = _optionalChain([options, 'access', _2 => _2.copies, 'optionalAccess', _3 => _3.length]) ? options.copies : [_chunk4T47Z4J3cjs.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, _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.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);
@@ -631,10 +636,10 @@ function lightTile(color, height, heightAround, material, light) {
631
636
  const nh = Math.max(0, nx * hx + ny * hy + nz * hz);
632
637
  const spec = Math.max(0, Math.pow(nh, shininess) - Math.pow(hz, shininess)) * (0.25 + 0.75 * gloss) * light.relief;
633
638
  for (let c = 0; c < 3; c++) {
634
- const a = _chunkXFYLA3ZOcjs.SRGB_TO_LINEAR[color[i + c]];
639
+ const a = _chunkVMROIWLLcjs.SRGB_TO_LINEAR[color[i + c]];
635
640
  const tint = metal * a + (1 - metal);
636
641
  const v = a * shade * (1 - 0.35 * metal) + spec * tint * (1 + metal);
637
- out[i + c] = _chunkXFYLA3ZOcjs.linearToSrgb8.call(void 0, v);
642
+ out[i + c] = _chunkVMROIWLLcjs.linearToSrgb8.call(void 0, v);
638
643
  }
639
644
  }
640
645
  }
@@ -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, _chunk4T47Z4J3cjs.PenSmoother)(b.tip.smoothing, b.pressureSmoothing);
703
+ const copies = init.copies.length ? init.copies : [_chunk4T47Z4J3cjs.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, _chunk4T47Z4J3cjs.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
+ _chunk4T47Z4J3cjs.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
+ _chunk4T47Z4J3cjs.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 _chunk4T47Z4J3cjs.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 : [_chunk4T47Z4J3cjs.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, _chunk4T47Z4J3cjs.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" || _chunk4T47Z4J3cjs.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, _chunk4T47Z4J3cjs.ParticleStroke)(brush, opts) : brush.engine === "bristle" ? new (0, _chunk4T47Z4J3cjs.BristleStroke)(brush, opts) : new (0, _chunk4T47Z4J3cjs.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 (_chunk4T47Z4J3cjs.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: _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.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 ? _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.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 } = _chunkM6JORA5Zcjs.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 _chunkM6JORA5Zcjs.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 = _chunk4T47Z4J3cjs.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(_chunkM6JORA5Zcjs.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 _chunkM6JORA5Zcjs.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
  }
@@ -1825,13 +2143,13 @@ function viewPenToDocument(view, p) {
1825
2143
 
1826
2144
  // src/paint-editor/input.ts
1827
2145
  function samplePressure(e) {
1828
- return _chunkXFYLA3ZOcjs.pointerPressure.call(void 0, e);
2146
+ return _chunkVMROIWLLcjs.pointerPressure.call(void 0, e);
1829
2147
  }
1830
2148
  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 = _chunk4T47Z4J3cjs.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) {
@@ -1895,7 +2213,8 @@ function downloadBytes(bytes, fileName, mimeType) {
1895
2213
 
1896
2214
  // src/paint-editor/wet.ts
1897
2215
  var STROKE_WATER = 0.9;
1898
- var STROKE_MASS = _chunkXFYLA3ZOcjs.PIGMENT_UNIT * 2.5;
2216
+ var STROKE_MASS = _chunkVMROIWLLcjs.PIGMENT_UNIT * 2.5;
2217
+ var STROKE_LIGHT = 1.5;
1899
2218
  var COVERAGE_STEPS = 4096;
1900
2219
  function union2(a, b) {
1901
2220
  if (!a) return { ...b };
@@ -1910,28 +2229,30 @@ function wetPaperInput(paper) {
1910
2229
  absorption: paper.absorption,
1911
2230
  permeability: paper.permeability,
1912
2231
  roughness: flat ? 0 : paper.roughness,
1913
- color: _chunk5BR7LC24cjs.hexToRgb8.call(void 0, paper.color),
2232
+ color: _chunk4T47Z4J3cjs.hexToRgb8.call(void 0, paper.color),
1914
2233
  heightAt: flat ? () => 0.5 : (x, y) => grain.at(x, y)
1915
2234
  };
1916
2235
  }
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);
2236
+ var WetStrokeDriver = (_class6 = class {
2237
+ 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
2238
  this.init = init;
1920
2239
  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];
2240
+ this.smoother = new (0, _chunk4T47Z4J3cjs.PenSmoother)(brush.tip.smoothing, brush.pressureSmoothing);
2241
+ const copies = init.copies.length ? init.copies : [_chunk4T47Z4J3cjs.IDENTITY_COPY];
1923
2242
  const wet = brush.wet;
1924
2243
  const spacing = Math.max(0.02, brush.tip.spacing);
1925
2244
  const perDab = Math.min(1, spacing * 1.2);
1926
2245
  const thin = wet.thinner;
1927
- this.medium = new (0, _chunkXFYLA3ZOcjs.WetMedium)(init.world, init.laneId, {
2246
+ this.medium = new (0, _chunkVMROIWLLcjs.WetMedium)(init.world, init.laneId, {
1928
2247
  water: STROKE_WATER * Math.min(1, wet.water * (1 + thin)) * perDab,
1929
2248
  mass: STROKE_MASS * wet.pigment * (1 - 0.6 * thin) * perDab,
1930
- body: wet.body * (1 - thin)
2249
+ body: wet.body * (1 - thin),
2250
+ light: 0
1931
2251
  });
2252
+ this.lightBase = wet.tool === "paint" ? STROKE_LIGHT * brush.light.amount * (1 - 0.6 * thin) * perDab : 0;
1932
2253
  const rates = _chunkPQ3H2AANcjs.exchangeRates.call(void 0, brush.behavior.pickup, brush.behavior.mix, wet.load, brush.behavior.mixing);
1933
2254
  this.states = copies.map((c, i) => ({
1934
- walker: new (0, _chunk5BR7LC24cjs.PenDabWalker)(brush, init.seed + i * 7919 | 0, c.linear),
2255
+ walker: new (0, _chunk4T47Z4J3cjs.PenDabWalker)(brush, init.seed + i * 7919 | 0, c.linear),
1935
2256
  exchange: wet.tool === "paint" ? new (0, _chunkPQ3H2AANcjs.PaintExchange)(rates, init.load) : null,
1936
2257
  last: null
1937
2258
  }));
@@ -1941,29 +2262,30 @@ var WetStrokeDriver = (_class5 = class {
1941
2262
  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
2263
  }
1943
2264
 
1944
- __init36() {this.dabCount = 0}
1945
- __init37() {this.bounds = null}
2265
+ __init40() {this.dabCount = 0}
2266
+ __init41() {this.bounds = null}
1946
2267
  /** The world's clock at which each raw point was applied (recorded with the stroke). */
1947
- __init38() {this.steps = []}
2268
+ __init42() {this.steps = []}
1948
2269
 
1949
2270
 
1950
2271
 
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]}
2272
+ __init43() {this.queue = []}
2273
+ __init44() {this.finishing = false}
2274
+ __init45() {this.finished = false}
2275
+ __init46() {this.lastRaw = null}
2276
+ __init47() {this.lastFed = null}
2277
+ __init48() {this.tip = new Float32Array(0)}
2278
+ __init49() {this.cov = new Float32Array(0)}
2279
+ __init50() {this.paint = [0, 0, 0]}
2280
+ __init51() {this.centre = [0, 0, 0]}
1960
2281
 
1961
2282
 
1962
- __init48() {this.sample = { rgb: [0, 0, 0], alpha: 1 }}
2283
+
2284
+ __init52() {this.sample = { rgb: [0, 0, 0], alpha: 1 }}
1963
2285
 
1964
2286
  /** What copy 0's brush carries after the dabs so far. */
1965
2287
  get load() {
1966
- return _nullishCoalesce(_optionalChain([this, 'access', _83 => _83.states, 'access', _84 => _84[0], 'access', _85 => _85.exchange, 'optionalAccess', _86 => _86.load]), () => ( null));
2288
+ return _nullishCoalesce(_optionalChain([this, 'access', _111 => _111.states, 'access', _112 => _112[0], 'access', _113 => _113.exchange, 'optionalAccess', _114 => _114.load]), () => ( null));
1967
2289
  }
1968
2290
  /** Whether every point (and the end) has been applied. */
1969
2291
  get done() {
@@ -2056,10 +2378,11 @@ var WetStrokeDriver = (_class5 = class {
2056
2378
  switch (wet.tool) {
2057
2379
  case "paint": {
2058
2380
  const ex = st.exchange;
2381
+ this.medium.options.light = this.lightBase * (brush.light.pressure ? d.pressure : 1);
2059
2382
  let flow = 1;
2060
2383
  const effect = this.init.effect;
2061
2384
  if (effect) {
2062
- _chunk5BR7LC24cjs.sampleEffectAt.call(void 0, effect, d.x, d.y, this.centre, d);
2385
+ _chunk4T47Z4J3cjs.sampleEffectAt.call(void 0, effect, d.x, d.y, this.centre, d);
2063
2386
  ex.setReservoirColor(this.centre[0], this.centre[1], this.centre[2]);
2064
2387
  } else if (this.sampler) {
2065
2388
  const g = this.sampler.dab(d, this.sample);
@@ -2071,7 +2394,7 @@ var WetStrokeDriver = (_class5 = class {
2071
2394
  if (total > 0 && flow > 0 && effect) {
2072
2395
  const ec = cov.slice();
2073
2396
  const field = new Float32Array(ec.length * 3);
2074
- _chunk5BR7LC24cjs.effectDabField.call(void 0, effect, rect, ec, this.centre, this.paint, field, COVERAGE_STEPS, d);
2397
+ _chunk4T47Z4J3cjs.effectDabField.call(void 0, effect, rect, ec, this.centre, this.paint, field, COVERAGE_STEPS, d);
2075
2398
  this.medium.deposit(rect, ec, Math.min(1, total) * k, this.paint, null, field);
2076
2399
  } else if (total > 0 && flow > 0) this.medium.deposit(rect, cov, Math.min(1, total) * k * flow, this.paint, null);
2077
2400
  else world.paint(laneId, rect, cov, this.medium.options.water * k, null);
@@ -2105,7 +2428,7 @@ var WetStrokeDriver = (_class5 = class {
2105
2428
  }
2106
2429
  st.last = d;
2107
2430
  }
2108
- }, _class5);
2431
+ }, _class6);
2109
2432
  var WET_MEDIUM_PARAMS = {
2110
2433
  watercolor: {},
2111
2434
  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 +2437,8 @@ var WET_MEDIUM_PARAMS = {
2114
2437
  var WET_GPU_MODES = ["auto", "off"];
2115
2438
  var GPU_LEAD = 16;
2116
2439
  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);
2440
+ var PaintWetLayers = (_class7 = class {
2441
+ 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
2442
  this.host = host;
2120
2443
  this.options = options;
2121
2444
  this.speed = Math.max(1, Math.min(2e3, _nullishCoalesce(options.speed, () => ( 120))));
@@ -2125,33 +2448,33 @@ var PaintWetLayers = (_class6 = class {
2125
2448
  }
2126
2449
 
2127
2450
 
2128
- __init49() {this.world = null}
2129
- __init50() {this.tilt = { ..._chunkXFYLA3ZOcjs.FLAT }}
2130
- __init51() {this.settings = /* @__PURE__ */ new Map()}
2131
- __init52() {this.paused = false}
2451
+ __init53() {this.world = null}
2452
+ __init54() {this.tilt = { ..._chunkVMROIWLLcjs.FLAT }}
2453
+ __init55() {this.settings = /* @__PURE__ */ new Map()}
2454
+ __init56() {this.paused = false}
2132
2455
  /** Wet strokes dry as soon as they end (one undo step with the stroke). */
2133
- __init53() {this.instantDry = false}
2456
+ __init57() {this.instantDry = false}
2134
2457
 
2135
- __init54() {this.overlay = false}
2458
+ __init58() {this.overlay = false}
2136
2459
 
2137
2460
  /** 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}
2461
+ __init59() {this.driver = null}
2462
+ __init60() {this.lastTick = null}
2463
+ __init61() {this.ticking = null}
2464
+ __init62() {this.stepsOwed = 0}
2465
+ __init63() {this.displaying = null}
2466
+ __init64() {this.leadAt = 0}
2144
2467
 
2145
2468
 
2146
- __init61() {this.gpu = null}
2147
- __init62() {this.gpuSupport = null}
2148
- __init63() {this.gpuStarting = null}
2469
+ __init65() {this.gpu = null}
2470
+ __init66() {this.gpuSupport = null}
2471
+ __init67() {this.gpuStarting = null}
2149
2472
  // --- Engine B ------------------------------------------------------------------
2150
2473
  /** Starts the GPU engine for the world (once; it may turn out unavailable). */
2151
2474
  startGpu() {
2152
2475
  if (this.gpuMode === "off" || this.gpu || this.gpuStarting || this.gpuSupport) return;
2153
- const provider = this.options.gpuProvider === void 0 ? _chunkXFYLA3ZOcjs.findWebGpu.call(void 0, ) : this.options.gpuProvider;
2154
- this.gpuStarting = _chunkXFYLA3ZOcjs.createWetGpuEngine.call(void 0, provider).then(({ engine, support }) => {
2476
+ const provider = this.options.gpuProvider === void 0 ? _chunkVMROIWLLcjs.findWebGpu.call(void 0, ) : this.options.gpuProvider;
2477
+ this.gpuStarting = _chunkVMROIWLLcjs.createWetGpuEngine.call(void 0, provider).then(({ engine, support }) => {
2155
2478
  this.gpuSupport = support;
2156
2479
  if (!engine) return;
2157
2480
  if (this.gpuMode === "off" || !this.world) {
@@ -2181,8 +2504,8 @@ var PaintWetLayers = (_class6 = class {
2181
2504
  if (mode === this.gpuMode) return this.engineInfo();
2182
2505
  this.gpuMode = mode;
2183
2506
  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()]);
2507
+ _optionalChain([this, 'access', _115 => _115.world, 'optionalAccess', _116 => _116.attachGpu, 'call', _117 => _117(null)]);
2508
+ _optionalChain([this, 'access', _118 => _118.gpu, 'optionalAccess', _119 => _119.destroy, 'call', _120 => _120()]);
2186
2509
  this.gpu = null;
2187
2510
  this.gpuSupport = null;
2188
2511
  } else if (this.world) this.startGpu();
@@ -2190,13 +2513,13 @@ var PaintWetLayers = (_class6 = class {
2190
2513
  }
2191
2514
  engineInfo() {
2192
2515
  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() };
2516
+ 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
2517
  if (this.gpuMode === "off") return { ...base, state: "off", reason: "Forced to the CPU." };
2195
2518
  if (this.gpuStarting) return { ...base, state: "starting", reason: null };
2196
- if (_optionalChain([w, 'optionalAccess', _99 => _99.gpuError])) return { ...base, state: "failed", reason: w.gpuError };
2519
+ if (_optionalChain([w, 'optionalAccess', _127 => _127.gpuError])) return { ...base, state: "failed", reason: w.gpuError };
2197
2520
  if (this.gpuSupport && !this.gpuSupport.supported) return { ...base, state: "unavailable", reason: this.gpuSupport.reason };
2198
2521
  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 };
2522
+ if (_optionalChain([w, 'optionalAccess', _128 => _128.gpuActive])) return { ...base, state: "active", reason: null };
2200
2523
  return { ...base, state: "standby", reason: `The GPU takes over at ${this.gpuMinTiles} wet tiles (256 px).` };
2201
2524
  }
2202
2525
  /** The world (made on first use, at the canvas size). */
@@ -2210,7 +2533,7 @@ var PaintWetLayers = (_class6 = class {
2210
2533
  return this.world;
2211
2534
  }
2212
2535
  makeWorld(width, height, paper) {
2213
- const world = new (0, _chunkXFYLA3ZOcjs.WetWorld)(width, height, wetPaperInput(paper), { kernel: this.options.kernel, threads: this.options.threads });
2536
+ const world = new (0, _chunkVMROIWLLcjs.WetWorld)(width, height, wetPaperInput(paper), { kernel: this.options.kernel, threads: this.options.threads });
2214
2537
  world.setTilt(this.tilt);
2215
2538
  world.gpuMinTiles = this.gpuMinTiles;
2216
2539
  if (this.gpu) world.attachGpu(this.gpu);
@@ -2222,7 +2545,7 @@ var PaintWetLayers = (_class6 = class {
2222
2545
  * sides: the shift has to be whole wet tiles (256 px), and no wet stroke may be live.
2223
2546
  */
2224
2547
  canExtend(left, top) {
2225
- return left % _chunkXFYLA3ZOcjs.WET_TILE === 0 && top % _chunkXFYLA3ZOcjs.WET_TILE === 0 && !this.driver;
2548
+ return left % _chunkVMROIWLLcjs.WET_TILE === 0 && top % _chunkVMROIWLLcjs.WET_TILE === 0 && !this.driver;
2226
2549
  }
2227
2550
  /**
2228
2551
  * Keeps the wet paint while the canvas grows to `width`×`height` by `left` / `top` on those
@@ -2235,16 +2558,17 @@ var PaintWetLayers = (_class6 = class {
2235
2558
  if (!this.canExtend(left, top)) throw new Error("Wet paint only moves by whole 256 px tiles, between strokes.");
2236
2559
  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
2560
  const old = this.world;
2238
- const snap = _optionalChain([old, 'optionalAccess', _101 => _101.laneIds, 'call', _102 => _102(), 'access', _103 => _103.length]) ? old.snapshot() : null;
2561
+ const snap = _optionalChain([old, 'optionalAccess', _129 => _129.laneIds, 'call', _130 => _130(), 'access', _131 => _131.length]) ? old.snapshot() : null;
2239
2562
  this.dropWorld();
2240
2563
  this.lastTick = null;
2241
2564
  this.stepsOwed = 0;
2242
2565
  if (!old || !snap) return;
2243
- 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]);
2566
+ const moved = _chunkVMROIWLLcjs.moveWetSnapshot.call(void 0, snap, old, { width, height }, left, top, (l) => {
2567
+ const flow = _optionalChain([this, 'access', _132 => _132.settings, 'access', _133 => _133.get, 'call', _134 => _134(l.id), 'optionalAccess', _135 => _135.flow]);
2245
2568
  return {
2246
2569
  base: _chunk474IQG74cjs.translateSurface.call(void 0, l.base, left, top),
2247
- flow: l.flow && flow && flow.kind !== "none" ? _chunkXFYLA3ZOcjs.flowField.call(void 0, flow, width, height) : l.flow
2570
+ ...l.light ? { light: _chunk474IQG74cjs.translateSurface.call(void 0, l.light, left, top) } : {},
2571
+ flow: l.flow && flow && flow.kind !== "none" ? _chunkVMROIWLLcjs.flowField.call(void 0, flow, width, height) : l.flow
2248
2572
  };
2249
2573
  });
2250
2574
  this.world = this.makeWorld(width, height, paper);
@@ -2259,11 +2583,11 @@ var PaintWetLayers = (_class6 = class {
2259
2583
  }
2260
2584
  /** Layers holding a wet layer (wet or drying). */
2261
2585
  wetLayerIds() {
2262
- return _nullishCoalesce(_optionalChain([this, 'access', _108 => _108.world, 'optionalAccess', _109 => _109.laneIds, 'call', _110 => _110()]), () => ( []));
2586
+ return _nullishCoalesce(_optionalChain([this, 'access', _136 => _136.world, 'optionalAccess', _137 => _137.laneIds, 'call', _138 => _138()]), () => ( []));
2263
2587
  }
2264
2588
  /** Whether a batch is running (edits wait). */
2265
2589
  get busy() {
2266
- return Boolean(_optionalChain([this, 'access', _111 => _111.world, 'optionalAccess', _112 => _112.busy]));
2590
+ return Boolean(_optionalChain([this, 'access', _139 => _139.world, 'optionalAccess', _140 => _140.busy]));
2267
2591
  }
2268
2592
  /** Resolves when no batch is running. */
2269
2593
  async idle() {
@@ -2272,7 +2596,7 @@ var PaintWetLayers = (_class6 = class {
2272
2596
  settingsOf(layerId, medium = "watercolor") {
2273
2597
  let s = this.settings.get(layerId);
2274
2598
  if (!s) {
2275
- s = { params: _chunkXFYLA3ZOcjs.normalizeWetParams.call(void 0, WET_MEDIUM_PARAMS[medium], _chunkXFYLA3ZOcjs.DEFAULT_WET_PARAMS), flow: { ..._chunkXFYLA3ZOcjs.DEFAULT_FLOW } };
2599
+ s = { params: _chunkVMROIWLLcjs.normalizeWetParams.call(void 0, WET_MEDIUM_PARAMS[medium], _chunkVMROIWLLcjs.DEFAULT_WET_PARAMS), flow: { ..._chunkVMROIWLLcjs.DEFAULT_FLOW } };
2276
2600
  this.settings.set(layerId, s);
2277
2601
  }
2278
2602
  return s;
@@ -2283,52 +2607,52 @@ var PaintWetLayers = (_class6 = class {
2283
2607
  if (!w.hasLane(layerId)) {
2284
2608
  const base = _nullishCoalesce(this.host.layerSurface(layerId), () => ( new (0, _chunk474IQG74cjs.TiledSurface)(4, /* @__PURE__ */ new Map(), 0)));
2285
2609
  const s = this.settingsOf(layerId, medium);
2286
- w.ensureLane(layerId, { params: s.params, base });
2610
+ w.ensureLane(layerId, { params: s.params, base, light: this.host.layerLight(layerId) });
2287
2611
  const { width, height } = this.host.canvas();
2288
- if (s.flow.kind !== "none") w.setFlow(layerId, _chunkXFYLA3ZOcjs.flowField.call(void 0, s.flow, width, height), s.flow.strength);
2612
+ if (s.flow.kind !== "none") w.setFlow(layerId, _chunkVMROIWLLcjs.flowField.call(void 0, s.flow, width, height), s.flow.strength);
2289
2613
  }
2290
2614
  return w;
2291
2615
  }
2292
2616
  setParams(layerId, update) {
2293
2617
  const s = this.settingsOf(layerId);
2294
- 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);
2618
+ s.params = _chunkVMROIWLLcjs.normalizeWetParams.call(void 0, update, s.params);
2619
+ if (_optionalChain([this, 'access', _141 => _141.world, 'optionalAccess', _142 => _142.hasLane, 'call', _143 => _143(layerId)])) this.world.setParams(layerId, s.params);
2296
2620
  return { ...s.params };
2297
2621
  }
2298
2622
  setFlow(layerId, update) {
2299
2623
  const s = this.settingsOf(layerId);
2300
- 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)])) {
2624
+ s.flow = _chunkVMROIWLLcjs.normalizeFlow.call(void 0, update, s.flow);
2625
+ if (_optionalChain([this, 'access', _144 => _144.world, 'optionalAccess', _145 => _145.hasLane, 'call', _146 => _146(layerId)])) {
2302
2626
  const { width, height } = this.host.canvas();
2303
- this.world.setFlow(layerId, _chunkXFYLA3ZOcjs.flowField.call(void 0, s.flow, width, height), s.flow.strength);
2627
+ this.world.setFlow(layerId, _chunkVMROIWLLcjs.flowField.call(void 0, s.flow, width, height), s.flow.strength);
2304
2628
  }
2305
2629
  return { ...s.flow };
2306
2630
  }
2307
2631
  setTilt(update) {
2308
- 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)]);
2632
+ this.tilt = _chunkVMROIWLLcjs.normalizeTilt.call(void 0, update, this.tilt);
2633
+ _optionalChain([this, 'access', _147 => _147.world, 'optionalAccess', _148 => _148.setTilt, 'call', _149 => _149(this.tilt)]);
2310
2634
  return { ...this.tilt };
2311
2635
  }
2312
2636
  /** The paper changed. */
2313
2637
  paperChanged() {
2314
- _optionalChain([this, 'access', _122 => _122.world, 'optionalAccess', _123 => _123.setPaper, 'call', _124 => _124(wetPaperInput(this.host.paper()))]);
2638
+ _optionalChain([this, 'access', _150 => _150.world, 'optionalAccess', _151 => _151.setPaper, 'call', _152 => _152(wetPaperInput(this.host.paper()))]);
2315
2639
  }
2316
2640
  /** A layer as tools describe it. */
2317
2641
  info(layerId) {
2318
2642
  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;
2643
+ const st = _optionalChain([this, 'access', _153 => _153.world, 'optionalAccess', _154 => _154.hasLane, 'call', _155 => _155(layerId)]) ? this.world.stats(layerId) : null;
2320
2644
  return {
2321
2645
  layerId,
2322
2646
  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)),
2647
+ wetCells: _nullishCoalesce(_optionalChain([st, 'optionalAccess', _156 => _156.wetCells]), () => ( 0)),
2648
+ wetBounds: _nullishCoalesce(_optionalChain([st, 'optionalAccess', _157 => _157.wetBounds]), () => ( null)),
2649
+ activeTiles: _nullishCoalesce(_optionalChain([st, 'optionalAccess', _158 => _158.activeTiles]), () => ( 0)),
2326
2650
  params: { ...s.params },
2327
2651
  flow: { ...s.flow }
2328
2652
  };
2329
2653
  }
2330
2654
  backend() {
2331
- return _nullishCoalesce(_optionalChain([this, 'access', _131 => _131.world, 'optionalAccess', _132 => _132.backend, 'call', _133 => _133()]), () => ( null));
2655
+ return _nullishCoalesce(_optionalChain([this, 'access', _159 => _159.world, 'optionalAccess', _160 => _160.backend, 'call', _161 => _161()]), () => ( null));
2332
2656
  }
2333
2657
  /** Shows the current wet pixels (at most `maxTiles` reshaded per layer). */
2334
2658
  syncDisplays(maxTiles = Infinity) {
@@ -2341,6 +2665,7 @@ var PaintWetLayers = (_class6 = class {
2341
2665
  if (this.host.layerSurface(id) !== d.surface) surfaces.set(id, d.surface);
2342
2666
  rects.push(...d.changed);
2343
2667
  }
2668
+ this.syncLights();
2344
2669
  if (surfaces.size) this.host.updateSurfaces(surfaces, rects);
2345
2670
  return rects;
2346
2671
  }
@@ -2356,17 +2681,31 @@ var PaintWetLayers = (_class6 = class {
2356
2681
  if (this.host.layerSurface(id) !== d.surface) surfaces.set(id, d.surface);
2357
2682
  rects.push(...d.changed);
2358
2683
  }
2684
+ this.syncLights();
2359
2685
  if (surfaces.size) this.host.updateSurfaces(surfaces, rects);
2360
2686
  return rects;
2361
2687
  }
2688
+ /** Shows each wet layer's live light paint (the light under it with the wet light shaded in). */
2689
+ syncLights() {
2690
+ const w = this.world;
2691
+ if (!w) return;
2692
+ const lights = /* @__PURE__ */ new Map();
2693
+ for (const id of w.laneIds()) {
2694
+ const l = w.lightDisplay(id);
2695
+ if (l && this.host.layerLight(id) !== l) lights.set(id, l);
2696
+ }
2697
+ if (lights.size) this.host.updateLights(lights);
2698
+ }
2362
2699
  /** Drops the wet layers whose paint has all dried (they become ordinary pixel layers). */
2363
2700
  retireDry() {
2364
2701
  const w = this.world;
2365
2702
  if (!w) return;
2366
2703
  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;
2704
+ if (w.isActive(id) || _optionalChain([this, 'access', _162 => _162.driver, 'optionalAccess', _163 => _163.init, 'access', _164 => _164.laneId]) === id) continue;
2368
2705
  const base = w.getBase(id);
2706
+ const light = w.isLit(id) ? w.getLight(id) : null;
2369
2707
  w.removeLane(id);
2708
+ if (light && this.host.layerLight(id) !== light) this.host.updateLights(/* @__PURE__ */ new Map([[id, light]]));
2370
2709
  this.host.updateSurfaces(/* @__PURE__ */ new Map([[id, base]]), []);
2371
2710
  }
2372
2711
  }
@@ -2444,10 +2783,10 @@ var PaintWetLayers = (_class6 = class {
2444
2783
  if (!d || !this.world) return;
2445
2784
  const rects = d.flush();
2446
2785
  if (rects.length) this.syncDisplays(DISPLAY_TILES_PER_TICK);
2447
- if (d.done) _optionalChain([this, 'access', _137 => _137.onDriverDone, 'optionalCall', _138 => _138(d)]);
2786
+ if (d.done) _optionalChain([this, 'access', _165 => _165.onDriverDone, 'optionalCall', _166 => _166(d)]);
2448
2787
  }
2449
2788
  /** Called when a live stroke that ended mid-batch has finished applying. */
2450
- __init64() {this.onDriverDone = null}
2789
+ __init68() {this.onDriverDone = null}
2451
2790
  /** Steps `steps` times now (agents, tests). Returns the steps run. */
2452
2791
  async advance(steps) {
2453
2792
  await this.idle();
@@ -2485,10 +2824,16 @@ var PaintWetLayers = (_class6 = class {
2485
2824
  this.retireDry();
2486
2825
  return out;
2487
2826
  }
2827
+ /** The layer's light paint with all its wet paint dried (`null` without a wet layer or light), without changing anything. */
2828
+ bakedLight(layerId) {
2829
+ const w = this.world;
2830
+ if (!_optionalChain([w, 'optionalAccess', _167 => _167.hasLane, 'call', _168 => _168(layerId)]) || !w.isLit(layerId)) return null;
2831
+ return w.bakedLight(layerId);
2832
+ }
2488
2833
  /** The layer's pixels with all its wet paint dried (deterministic), without changing anything. */
2489
2834
  bakedSurface(layerId) {
2490
2835
  const w = this.world;
2491
- if (!_optionalChain([w, 'optionalAccess', _139 => _139.hasLane, 'call', _140 => _140(layerId)])) return null;
2836
+ if (!_optionalChain([w, 'optionalAccess', _169 => _169.hasLane, 'call', _170 => _170(layerId)])) return null;
2492
2837
  return w.bakedSurface(layerId);
2493
2838
  }
2494
2839
  /** A snapshot of the world for undo (`null` when there is none). */
@@ -2509,13 +2854,13 @@ var PaintWetLayers = (_class6 = class {
2509
2854
  this.syncDisplays();
2510
2855
  }
2511
2856
  dropWorld() {
2512
- _optionalChain([this, 'access', _141 => _141.world, 'optionalAccess', _142 => _142.destroy, 'call', _143 => _143()]);
2857
+ _optionalChain([this, 'access', _171 => _171.world, 'optionalAccess', _172 => _172.destroy, 'call', _173 => _173()]);
2513
2858
  this.world = null;
2514
2859
  this.driver = null;
2515
2860
  }
2516
2861
  /** Forgets everything wet (a new canvas, a resize, opening a document). */
2517
2862
  reset() {
2518
- _optionalChain([this, 'access', _144 => _144.world, 'optionalAccess', _145 => _145.destroy, 'call', _146 => _146()]);
2863
+ _optionalChain([this, 'access', _174 => _174.world, 'optionalAccess', _175 => _175.destroy, 'call', _176 => _176()]);
2519
2864
  this.world = null;
2520
2865
  this.driver = null;
2521
2866
  this.settings.clear();
@@ -2545,32 +2890,32 @@ var PaintWetLayers = (_class6 = class {
2545
2890
  return out;
2546
2891
  }
2547
2892
  destroy() {
2548
- _optionalChain([this, 'access', _147 => _147.world, 'optionalAccess', _148 => _148.destroy, 'call', _149 => _149()]);
2893
+ _optionalChain([this, 'access', _177 => _177.world, 'optionalAccess', _178 => _178.destroy, 'call', _179 => _179()]);
2549
2894
  this.world = null;
2550
- _optionalChain([this, 'access', _150 => _150.gpu, 'optionalAccess', _151 => _151.destroy, 'call', _152 => _152()]);
2895
+ _optionalChain([this, 'access', _180 => _180.gpu, 'optionalAccess', _181 => _181.destroy, 'call', _182 => _182()]);
2551
2896
  this.gpu = null;
2552
2897
  }
2553
- }, _class6);
2898
+ }, _class7);
2554
2899
  function wetPigmentOf(hex, body) {
2555
- const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, hex);
2556
- return Array.from(_chunkXFYLA3ZOcjs.pigmentOf.call(void 0, [r / 255, g / 255, b / 255], body));
2900
+ const [r, g, b] = _chunk4T47Z4J3cjs.hexToRgb8.call(void 0, hex);
2901
+ return Array.from(_chunkVMROIWLLcjs.pigmentOf.call(void 0, [r / 255, g / 255, b / 255], body));
2557
2902
  }
2558
2903
 
2559
2904
  // src/paint-editor/thumbnails.ts
2560
2905
  var thumbWorld = null;
2561
2906
  function paintWetTestStroke(brush, paper, color, width, height) {
2562
2907
  if (!thumbWorld || thumbWorld.width !== width || thumbWorld.height !== height) {
2563
- _optionalChain([thumbWorld, 'optionalAccess', _153 => _153.destroy, 'call', _154 => _154()]);
2564
- thumbWorld = new (0, _chunkXFYLA3ZOcjs.WetWorld)(width, height, wetPaperInput(paper), { kernel: "typescript", threads: 0 });
2908
+ _optionalChain([thumbWorld, 'optionalAccess', _183 => _183.destroy, 'call', _184 => _184()]);
2909
+ thumbWorld = new (0, _chunkVMROIWLLcjs.WetWorld)(width, height, wetPaperInput(paper), { kernel: "typescript", threads: 0 });
2565
2910
  } else thumbWorld.setPaper(wetPaperInput(paper));
2566
2911
  const w = thumbWorld;
2567
2912
  if (w.hasLane("thumb")) w.removeLane("thumb");
2568
2913
  w.ensureLane("thumb");
2569
- const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, color);
2914
+ const [r, g, b] = _chunk4T47Z4J3cjs.hexToRgb8.call(void 0, color);
2570
2915
  if (brush.wet.tool !== "paint" && brush.wet.tool !== "water") {
2571
2916
  const half = Math.ceil(height / 2);
2572
- w.paint("thumb", { x: 0, y: 0, width, height: half }, null, 0.4, Array.from(_chunkXFYLA3ZOcjs.pigmentOf.call(void 0, [r / 255, g / 255, b / 255], 0), (v) => v * 0.05));
2573
- w.paint("thumb", { x: 0, y: half, width, height: height - half }, null, 0.4, Array.from(_chunkXFYLA3ZOcjs.pigmentOf.call(void 0, [232 / 255, 196 / 255, 64 / 255], 0), (v) => v * 0.05));
2917
+ w.paint("thumb", { x: 0, y: 0, width, height: half }, null, 0.4, Array.from(_chunkVMROIWLLcjs.pigmentOf.call(void 0, [r / 255, g / 255, b / 255], 0), (v) => v * 0.05));
2918
+ w.paint("thumb", { x: 0, y: half, width, height: height - half }, null, 0.4, Array.from(_chunkVMROIWLLcjs.pigmentOf.call(void 0, [232 / 255, 196 / 255, 64 / 255], 0), (v) => v * 0.05));
2574
2919
  }
2575
2920
  const driver = new WetStrokeDriver({
2576
2921
  world: w,
@@ -2580,9 +2925,9 @@ function paintWetTestStroke(brush, paper, color, width, height) {
2580
2925
  load: brushLoadForStroke(brush, _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD, [r / 255, g / 255, b / 255]),
2581
2926
  grain: paperGrain(paper),
2582
2927
  paperDepth: paper.depth,
2583
- paperColor: _chunk5BR7LC24cjs.hexToRgb8.call(void 0, paper.color),
2928
+ paperColor: _chunk4T47Z4J3cjs.hexToRgb8.call(void 0, paper.color),
2584
2929
  seed: 7,
2585
- copies: [_chunk5BR7LC24cjs.IDENTITY_COPY]
2930
+ copies: [_chunk4T47Z4J3cjs.IDENTITY_COPY]
2586
2931
  });
2587
2932
  driver.push(thumbnailStroke(width, height));
2588
2933
  driver.finish();
@@ -2602,9 +2947,10 @@ function thumbnailStroke(width, height) {
2602
2947
  return pts;
2603
2948
  }
2604
2949
  function paintTestStroke(surface, brush, paper, color, width, height) {
2605
- const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, color);
2950
+ const [r, g, b] = _chunk4T47Z4J3cjs.hexToRgb8.call(void 0, color);
2606
2951
  let result = surface;
2607
2952
  let thick = null;
2953
+ let light = null;
2608
2954
  const session = new PaintStrokeSession(
2609
2955
  {
2610
2956
  update: () => {
@@ -2612,6 +2958,7 @@ function paintTestStroke(surface, brush, paper, color, width, height) {
2612
2958
  commit: (sess, s) => {
2613
2959
  result = _nullishCoalesce(s, () => ( result));
2614
2960
  thick = sess.thickChannels();
2961
+ light = sess.lightChannel();
2615
2962
  }
2616
2963
  },
2617
2964
  {
@@ -2621,28 +2968,30 @@ function paintTestStroke(surface, brush, paper, color, width, height) {
2621
2968
  lockTransparency: false,
2622
2969
  brush,
2623
2970
  color: [r, g, b, 255],
2624
- background: [..._chunk5BR7LC24cjs.hexToRgb8.call(void 0, paper.color), 255],
2971
+ background: [..._chunk4T47Z4J3cjs.hexToRgb8.call(void 0, paper.color), 255],
2625
2972
  grain: paperGrain(paper),
2626
2973
  paperDepth: paper.depth,
2627
2974
  seed: 7,
2628
2975
  colorHex: color,
2629
2976
  affineCopies: [],
2630
- copies: [_chunk5BR7LC24cjs.IDENTITY_COPY],
2977
+ copies: [_chunk4T47Z4J3cjs.IDENTITY_COPY],
2631
2978
  mixerLoad: brushLoadForStroke(brush, _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD, [r / 255, g / 255, b / 255]),
2632
2979
  thick: brush.engine === "impasto" ? { channels: emptyThickChannels() } : void 0,
2633
- sticker: brush.engine === "sticker" ? { nextId: 1 } : void 0
2980
+ sticker: brush.engine === "sticker" ? { nextId: 1 } : void 0,
2981
+ light: brush.light.amount > 0 && brush.engine !== "wet" && brush.engine !== "sticker" ? { surface: emptyLight() } : null
2634
2982
  }
2635
2983
  );
2636
2984
  session.push(thumbnailStroke(width, height));
2637
2985
  session.end();
2638
- return thick ? new LitCache().lit(result, thick, DEFAULT_LIGHTING) : result;
2986
+ const lit = thick ? new LitCache().lit(result, thick, DEFAULT_LIGHTING) : result;
2987
+ return { surface: light || thick ? new LightCache().glow(lit, _nullishCoalesce(light, () => ( void 0)), _nullishCoalesce(thick, () => ( void 0))) : lit, light };
2639
2988
  }
2640
2989
  function renderBrushThumbnail(brush, options) {
2641
2990
  const width = Math.max(16, Math.min(512, Math.round(_nullishCoalesce(options.width, () => ( 160)))));
2642
2991
  const height = Math.max(16, Math.min(512, Math.round(_nullishCoalesce(options.height, () => ( 48)))));
2643
2992
  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];
2993
+ const b = brush.tip.size > maxSize ? _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, { tip: { ...brush.tip, size: maxSize } }, brush) : brush;
2994
+ const rgba = [..._chunk4T47Z4J3cjs.hexToRgb8.call(void 0, options.color), 255];
2646
2995
  let surface = _chunk474IQG74cjs.emptySurface.call(void 0, 4);
2647
2996
  if (b.engine === "eraser") {
2648
2997
  surface = _chunk474IQG74cjs.fillRect.call(void 0, surface, { x: 0, y: 0, width, height }, rgba);
@@ -2652,9 +3001,15 @@ function renderBrushThumbnail(brush, options) {
2652
3001
  surface = _chunk474IQG74cjs.fillRect.call(void 0, surface, { x: 0, y: 0, width, height: half }, rgba);
2653
3002
  surface = _chunk474IQG74cjs.fillRect.call(void 0, surface, { x: 0, y: half, width, height: height - half }, [232, 196, 64, 255]);
2654
3003
  }
2655
- surface = b.engine === "wet" ? paintWetTestStroke(b, options.paper, options.color, width, height) : paintTestStroke(surface, b, options.paper, options.color, width, height);
3004
+ let light = null;
3005
+ if (b.engine === "wet") surface = paintWetTestStroke(b, options.paper, options.color, width, height);
3006
+ else ({ surface, light } = paintTestStroke(surface, b, options.paper, options.color, width, height));
2656
3007
  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 });
3008
+ const paper = light ? { ...options.paper, color: "#141414" } : options.paper;
3009
+ const spill = light ? computeSpill(surface, light, { radius: Math.max(4, height / 6), strength: 1 }) : null;
3010
+ const layers = [paperNode(paper), layer];
3011
+ 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 } });
3012
+ const data = createPaintCompositor().composite(layers, { x: 0, y: 0, width, height }, { width, height });
2658
3013
  return { width, height, data };
2659
3014
  }
2660
3015
 
@@ -2783,13 +3138,13 @@ function perspectiveDirections(g, p) {
2783
3138
  return out;
2784
3139
  }
2785
3140
  var SNAP_DECIDE_DISTANCE = 6;
2786
- var PerspectiveSnapper = (_class7 = class {
2787
- constructor(guide) {;_class7.prototype.__init65.call(this);_class7.prototype.__init66.call(this);
3141
+ var PerspectiveSnapper = (_class8 = class {
3142
+ constructor(guide) {;_class8.prototype.__init69.call(this);_class8.prototype.__init70.call(this);
2788
3143
  this.guide = guide;
2789
3144
  }
2790
3145
 
2791
- __init65() {this.start = null}
2792
- __init66() {this.dir = null}
3146
+ __init69() {this.start = null}
3147
+ __init70() {this.dir = null}
2793
3148
  /** The line the stroke follows, once chosen. */
2794
3149
  get line() {
2795
3150
  return this.start && this.dir ? { from: this.start, direction: this.dir } : null;
@@ -2822,7 +3177,7 @@ var PerspectiveSnapper = (_class7 = class {
2822
3177
  const t = (p.x - s.x) * d.x + (p.y - s.y) * d.y;
2823
3178
  return { ...p, x: s.x + d.x * t, y: s.y + d.y * t };
2824
3179
  }
2825
- }, _class7);
3180
+ }, _class8);
2826
3181
  function perspectiveGuideLines(g, size, count = 12) {
2827
3182
  const out = [];
2828
3183
  const far = Math.hypot(size.width, size.height) * 4;
@@ -2895,11 +3250,11 @@ function readSwatch(raw) {
2895
3250
  if (!raw || typeof raw !== "object") return null;
2896
3251
  const r = raw;
2897
3252
  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;
3253
+ const color = typeof r.color === "string" && /^#[0-9a-f]{6}$/i.test(r.color) ? r.color.toLowerCase() : _optionalChain([g, 'optionalAccess', _185 => _185.type]) === "gradient" ? _chunkPQ3H2AANcjs.paintSourceColor.call(void 0, g) : null;
2899
3254
  if (!color) return null;
2900
3255
  const out = { color };
2901
3256
  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;
3257
+ if (_optionalChain([g, 'optionalAccess', _186 => _186.type]) === "gradient") out.paint = g;
2903
3258
  return out;
2904
3259
  }
2905
3260
  var DEFAULT_SWATCHES = [
@@ -2918,7 +3273,7 @@ var DEFAULT_SWATCHES = [
2918
3273
  // src/paint-editor/mixing-pad.ts
2919
3274
  var MIXING_PAD_WIDTH = 360;
2920
3275
  var MIXING_PAD_HEIGHT = 220;
2921
- var PAD_PAINT = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, {
3276
+ var PAD_PAINT = _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, {
2922
3277
  id: "pad-paint",
2923
3278
  name: "Pad paint",
2924
3279
  category: "mixer",
@@ -2926,7 +3281,7 @@ var PAD_PAINT = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, {
2926
3281
  tip: { size: 28, hardness: 0.75, spacing: 0.1, opacity: 1, flow: 0.7, pressureSize: false, pressureOpacity: false },
2927
3282
  mixer: { wet: 0.45, load: 1, mix: 0.4, loadEachStroke: true, cleanEachStroke: true, mixing: "pigment" }
2928
3283
  });
2929
- var PAD_KNIFE = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, {
3284
+ var PAD_KNIFE = _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, {
2930
3285
  id: "pad-knife",
2931
3286
  name: "Palette knife",
2932
3287
  category: "smudge",
@@ -2936,19 +3291,19 @@ var PAD_KNIFE = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, {
2936
3291
  // A knife drags streaks of paint and folds them into each other, mixing them as pigment.
2937
3292
  smudge: { strength: 0.6, length: 0.8, mixing: "pigment" }
2938
3293
  });
2939
- var PAD_BLOB = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, {
3294
+ var PAD_BLOB = _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, {
2940
3295
  id: "pad-blob",
2941
3296
  name: "Pad blob",
2942
3297
  category: "custom",
2943
3298
  engine: "dab",
2944
3299
  tip: { size: 20, hardness: 1, spacing: 0.1, opacity: 1, flow: 1, pressureSize: false, pressureOpacity: false }
2945
3300
  });
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)}
3301
+ 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); }
3302
+ __init71() {this.width = MIXING_PAD_WIDTH}
3303
+ __init72() {this.height = MIXING_PAD_HEIGHT}
3304
+ __init73() {this.surface = _chunk474IQG74cjs.emptySurface.call(void 0, 4)}
2950
3305
  /** Increments on every change (for redrawing). */
2951
- __init70() {this.version = 0}
3306
+ __init74() {this.version = 0}
2952
3307
  /** Paints (`paint`, with `color`) or smears (`knife`) through `points` in pad pixels. */
2953
3308
  stroke(points, options) {
2954
3309
  if (!points.length) throw new Error("points is empty; give at least one {x, y} in pad pixels.");
@@ -2958,12 +3313,12 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init67.cal
2958
3313
  }
2959
3314
  }
2960
3315
  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);
3316
+ if (options.size !== void 0) brush = _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, { tip: { ...brush.tip, size: options.size } }, brush);
2962
3317
  return this.run(brush, points, options.color);
2963
3318
  }
2964
3319
  /** Paints `points` with `brush` in `color` on the pad. */
2965
3320
  run(brush, points, color) {
2966
- const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, color);
3321
+ const [r, g, b] = _chunk4T47Z4J3cjs.hexToRgb8.call(void 0, color);
2967
3322
  let result = null;
2968
3323
  const session = new PaintStrokeSession(
2969
3324
  { update: () => {
@@ -2981,7 +3336,7 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init67.cal
2981
3336
  seed: 1,
2982
3337
  colorHex: color,
2983
3338
  affineCopies: [],
2984
- copies: [_chunk5BR7LC24cjs.IDENTITY_COPY],
3339
+ copies: [_chunk4T47Z4J3cjs.IDENTITY_COPY],
2985
3340
  mixerLoad: brushLoadForStroke(brush, _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD, [r / 255, g / 255, b / 255])
2986
3341
  }
2987
3342
  );
@@ -3057,10 +3412,10 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init67.cal
3057
3412
  const off = used.length === 1 ? 0 : 0.45 * R;
3058
3413
  const x = Math.min(cx + R - r, Math.max(cx - R + r, cx + Math.cos(a) * off));
3059
3414
  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);
3415
+ const blob = _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_BLOB.tip, size: Math.max(2, 2 * r) } }, PAD_BLOB);
3061
3416
  this.run(blob, [{ x, y }], p.color.toLowerCase());
3062
3417
  });
3063
- const knife = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_KNIFE.tip, size: Math.max(12, R * 0.5) } }, PAD_KNIFE);
3418
+ const knife = _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_KNIFE.tip, size: Math.max(12, R * 0.5) } }, PAD_KNIFE);
3064
3419
  for (let k = 0; k < passes; k++) {
3065
3420
  const stir = [];
3066
3421
  const turns = 1.6;
@@ -3072,10 +3427,10 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init67.cal
3072
3427
  this.run(knife, stir, "#000000");
3073
3428
  }
3074
3429
  const mixed = _chunkOLNIPTB3cjs.mixPigments.call(void 0,
3075
- paints.map((p) => _chunk5BR7LC24cjs.hexToRgb8.call(void 0, p.color).map((v) => v / 255)),
3430
+ paints.map((p) => _chunk4T47Z4J3cjs.hexToRgb8.call(void 0, p.color).map((v) => v / 255)),
3076
3431
  paints.map((p) => p.parts)
3077
3432
  );
3078
- const core = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_BLOB.tip, size: R * 1.1, hardness: 0.55 } }, PAD_BLOB);
3433
+ const core = _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, { tip: { ...PAD_BLOB.tip, size: R * 1.1, hardness: 0.55 } }, PAD_BLOB);
3079
3434
  const hex = "#" + mixed.map((v) => Math.round(v * 255).toString(16).padStart(2, "0")).join("");
3080
3435
  this.run(core, [{ x: cx, y: cy }], hex);
3081
3436
  return { color: this.pick(cx, cy, 2), bounds };
@@ -3086,7 +3441,7 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init67.cal
3086
3441
  }
3087
3442
  /** Replaces the pad with a PNG (data URL or base64) of the pad's size, as `toPng` writes. */
3088
3443
  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);
3444
+ const bytes = png.startsWith("data:") ? _optionalChain([_chunkOLNIPTB3cjs.parseDataUrl.call(void 0, png), 'optionalAccess', _187 => _187.bytes]) : _chunkOLNIPTB3cjs.base64ToBytes.call(void 0, png);
3090
3445
  if (!bytes || !_chunkOLNIPTB3cjs.isPng.call(void 0, bytes)) throw new Error("The pad image is not a PNG data URL or base64 PNG.");
3091
3446
  const img = _chunkOLNIPTB3cjs.decodePng.call(void 0, bytes);
3092
3447
  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 +3464,7 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init67.cal
3109
3464
  }
3110
3465
  return px;
3111
3466
  }
3112
- }, _class8);
3467
+ }, _class9);
3113
3468
 
3114
3469
  // src/paint-editor/live-strokes.ts
3115
3470
  var LIVE_STROKE_ENGINES = ["dab", "particle", "bristle", "mixer"];
@@ -3127,6 +3482,7 @@ function liveStrokeRefusal(brush) {
3127
3482
  case "smudge":
3128
3483
  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
3484
  }
3485
+ 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
3486
  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
3487
  return null;
3132
3488
  }
@@ -3148,6 +3504,212 @@ function liveStrokeBounds(stroke) {
3148
3504
  return { x: Math.floor(x0 - pad), y: Math.floor(y0 - pad), width: Math.ceil(x1 - x0 + 2 * pad), height: Math.ceil(y1 - y0 + 2 * pad) };
3149
3505
  }
3150
3506
 
3507
+ // src/paint-editor/backlight.ts
3508
+ var DEFAULT_BACKLIGHT_COLOR = "#fff1d6";
3509
+ var BACKLIGHT_SPILL = { radius: 24, strength: 1.5 };
3510
+ var BLOCK_HEIGHT = 96;
3511
+ var VALLEY_GAIN = 0.35;
3512
+ function backlightOf(paper) {
3513
+ return _nullishCoalesce(paper.backlight, () => ( 0));
3514
+ }
3515
+ var LIN = new Float64Array(256);
3516
+ for (let i = 0; i < 256; i++) LIN[i] = _chunk7AHBW2CQcjs.srgbToLinear.call(void 0, i / 255);
3517
+ function needAlpha(e, p) {
3518
+ if (e < p) return 1 - e / p;
3519
+ if (e > p) return (e - p) / (255 - p);
3520
+ return 0;
3521
+ }
3522
+ function tilesUnder(surface, rect, offset, out) {
3523
+ if (!surface) {
3524
+ out.push(null);
3525
+ return;
3526
+ }
3527
+ const x0 = Math.floor((rect.x - offset.x) / _chunk474IQG74cjs.TILE);
3528
+ const y0 = Math.floor((rect.y - offset.y) / _chunk474IQG74cjs.TILE);
3529
+ const x1 = Math.floor((rect.x - offset.x + rect.width - 1) / _chunk474IQG74cjs.TILE);
3530
+ const y1 = Math.floor((rect.y - offset.y + rect.height - 1) / _chunk474IQG74cjs.TILE);
3531
+ for (let ty = y0; ty <= y1; ty++) for (let tx = x0; tx <= x1; tx++) out.push(_nullishCoalesce(surface.tiles.get(_chunk474IQG74cjs.tileKey.call(void 0, tx, ty)), () => ( null)));
3532
+ }
3533
+ var sameRefs = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
3534
+ var BacklightCache = (_class10 = class {constructor() { _class10.prototype.__init75.call(this);_class10.prototype.__init76.call(this);_class10.prototype.__init77.call(this);_class10.prototype.__init78.call(this); }
3535
+ __init75() {this.tiles = /* @__PURE__ */ new Map()}
3536
+ __init76() {this.key = ""}
3537
+ __init77() {this.spillOf = null}
3538
+ __init78() {this.out = null}
3539
+ /**
3540
+ * The backlight layer's pixels for `layers` (bottom first) on `paper` over a `width` × `height`
3541
+ * canvas, or `null` when the paper has no backlight. `spillFrom` is the committed painting (the
3542
+ * spill is drawn from it, so a stroke in progress does not redo the blur every frame).
3543
+ */
3544
+ surface(paper, grain, width, height, layers, spillFrom) {
3545
+ const amount = backlightOf(paper);
3546
+ if (!(amount > 0)) return null;
3547
+ const key = JSON.stringify([amount, _nullishCoalesce(paper.backlightColor, () => ( DEFAULT_BACKLIGHT_COLOR)), paper.color, paper.depth, grain.flat, width, height, layers.length, layers.map((l) => [l.opacity, l.offset.x, l.offset.y, l.surface.channels])]);
3548
+ if (key !== this.key) {
3549
+ this.tiles.clear();
3550
+ this.key = key;
3551
+ }
3552
+ const light = /* @__PURE__ */ new Map();
3553
+ const color = /* @__PURE__ */ new Map();
3554
+ const painted = /* @__PURE__ */ new Map();
3555
+ const tilesX = Math.ceil(width / _chunk474IQG74cjs.TILE);
3556
+ const tilesY = Math.ceil(height / _chunk474IQG74cjs.TILE);
3557
+ const refs = [];
3558
+ for (let ty = 0; ty < tilesY; ty++) {
3559
+ for (let tx = 0; tx < tilesX; tx++) {
3560
+ const rect = { x: tx * _chunk474IQG74cjs.TILE, y: ty * _chunk474IQG74cjs.TILE, width: Math.min(_chunk474IQG74cjs.TILE, width - tx * _chunk474IQG74cjs.TILE), height: Math.min(_chunk474IQG74cjs.TILE, height - ty * _chunk474IQG74cjs.TILE) };
3561
+ refs.length = 0;
3562
+ for (const l of layers) {
3563
+ tilesUnder(l.surface, rect, l.offset, refs);
3564
+ tilesUnder(_optionalChain([l, 'access', _188 => _188.thick, 'optionalAccess', _189 => _189.height]), rect, l.offset, refs);
3565
+ tilesUnder(_optionalChain([l, 'access', _190 => _190.thick, 'optionalAccess', _191 => _191.material]), rect, l.offset, refs);
3566
+ }
3567
+ const k = _chunk474IQG74cjs.tileKey.call(void 0, tx, ty);
3568
+ let r = this.tiles.get(k);
3569
+ if (!r || !sameRefs(r.refs, refs)) {
3570
+ r = { refs: [...refs], ...computeTile(paper, grain, amount, rect, layers) };
3571
+ this.tiles.set(k, r);
3572
+ }
3573
+ if (r.light) light.set(k, r.light);
3574
+ if (r.color) color.set(k, r.color);
3575
+ if (r.painted) painted.set(k, r.painted);
3576
+ }
3577
+ }
3578
+ return { light: new (0, _chunk474IQG74cjs.TiledSurface)(4, light, 0), color: new (0, _chunk474IQG74cjs.TiledSurface)(4, color, 0), painted: new (0, _chunk474IQG74cjs.TiledSurface)(1, painted, 0) };
3579
+ }
3580
+ /**
3581
+ * The full Backlight layer: the light through the paint (`current`) plus the spill of the light
3582
+ * through the committed paint (`committed`), both Linear Dodge. Cached on their tiles.
3583
+ */
3584
+ layer(current, committed, amount) {
3585
+ let spill = null;
3586
+ if (committed) {
3587
+ const s = this.spillOf;
3588
+ if (s && s.color.id === committed.color.id && s.painted.id === committed.painted.id) spill = s.out;
3589
+ else {
3590
+ spill = computeSpill(committed.color, committed.painted, { radius: BACKLIGHT_SPILL.radius, strength: BACKLIGHT_SPILL.strength * amount });
3591
+ this.spillOf = { color: committed.color, painted: committed.painted, out: spill };
3592
+ }
3593
+ }
3594
+ if (!spill) return current.light;
3595
+ const parts = [...current.light.tiles.values(), ...spill.tiles.values()];
3596
+ if (this.out && sameRefs(this.out.parts, parts)) return this.out.surface;
3597
+ const keys = /* @__PURE__ */ new Set([...current.light.tiles.keys(), ...spill.tiles.keys()]);
3598
+ const tiles = /* @__PURE__ */ new Map();
3599
+ for (const key of keys) {
3600
+ const a = current.light.tiles.get(key);
3601
+ const b = spill.tiles.get(key);
3602
+ const t = !a ? b : !b ? a : addTiles(a, b);
3603
+ if (t) tiles.set(key, t);
3604
+ }
3605
+ const surface = new (0, _chunk474IQG74cjs.TiledSurface)(4, tiles, 0);
3606
+ this.out = { parts, surface };
3607
+ return surface;
3608
+ }
3609
+ }, _class10);
3610
+ var full4 = (t) => {
3611
+ if (t.data.length === _chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE * 4) return t.data;
3612
+ const out = new Uint8ClampedArray(_chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE * 4);
3613
+ const u = _nullishCoalesce(t.uniform, () => ( 0));
3614
+ const bytes = [u >>> 24 & 255, u >>> 16 & 255, u >>> 8 & 255, u & 255];
3615
+ for (let i = 0; i < out.length; i += 4) out.set(bytes, i);
3616
+ return out;
3617
+ };
3618
+ function addTiles(a, b) {
3619
+ const x = full4(a);
3620
+ const y = full4(b);
3621
+ const out = new Uint8ClampedArray(_chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE * 4);
3622
+ for (let i = 0; i < out.length; i += 4) {
3623
+ const ay = y[i + 3] / 255;
3624
+ const r = x[i] + y[i] * ay;
3625
+ const g = x[i + 1] + y[i + 1] * ay;
3626
+ const bl = x[i + 2] + y[i + 2] * ay;
3627
+ if (r < 0.5 && g < 0.5 && bl < 0.5) continue;
3628
+ out[i] = Math.round(r);
3629
+ out[i + 1] = Math.round(g);
3630
+ out[i + 2] = Math.round(bl);
3631
+ out[i + 3] = 255;
3632
+ }
3633
+ return _chunk474IQG74cjs.finishTile.call(void 0, out, 4, 0);
3634
+ }
3635
+ function computeTile(paper, grain, amount, rect, layers) {
3636
+ const n = rect.width * rect.height;
3637
+ const [p0, p1, p2] = _chunk4T47Z4J3cjs.hexToRgb8.call(void 0, paper.color);
3638
+ const pb = [p0, p1, p2];
3639
+ const pl = [Math.max(1e-4, LIN[p0]), Math.max(1e-4, LIN[p1]), Math.max(1e-4, LIN[p2])];
3640
+ const [c0, c1, c2] = _chunk4T47Z4J3cjs.hexToRgb8.call(void 0, _nullishCoalesce(paper.backlightColor, () => ( DEFAULT_BACKLIGHT_COLOR)));
3641
+ const t0 = new Float32Array(n).fill(amount * LIN[c0]);
3642
+ const t1 = new Float32Array(n).fill(amount * LIN[c1]);
3643
+ const t2 = new Float32Array(n).fill(amount * LIN[c2]);
3644
+ const depth = grain.flat ? 0 : paper.depth;
3645
+ if (depth > 0) {
3646
+ for (let y = 0, i = 0; y < rect.height; y++)
3647
+ for (let x = 0; x < rect.width; x++, i++) {
3648
+ const g = 1 + VALLEY_GAIN * depth * (0.5 - grain.at(rect.x + x + 0.5, rect.y + y + 0.5));
3649
+ t0[i] *= g;
3650
+ t1[i] *= g;
3651
+ t2[i] *= g;
3652
+ }
3653
+ }
3654
+ const clear = new Float32Array(n).fill(1);
3655
+ for (const l of layers) {
3656
+ const lr = { x: rect.x - l.offset.x, y: rect.y - l.offset.y, width: rect.width, height: rect.height };
3657
+ if (!l.surface.tiles.size) continue;
3658
+ const px = _chunk474IQG74cjs.readRegion.call(void 0, l.surface, lr);
3659
+ const h = _optionalChain([l, 'access', _192 => _192.thick, 'optionalAccess', _193 => _193.height, 'access', _194 => _194.tiles, 'access', _195 => _195.size]) ? _chunk474IQG74cjs.readRegion.call(void 0, l.thick.height, lr) : null;
3660
+ const m = h && _optionalChain([l, 'access', _196 => _196.thick, 'optionalAccess', _197 => _197.material, 'access', _198 => _198.tiles, 'access', _199 => _199.size]) ? _chunk474IQG74cjs.readRegion.call(void 0, l.thick.material, lr) : null;
3661
+ const op = l.opacity;
3662
+ for (let i = 0; i < n; i++) {
3663
+ const a8 = px[i * 4 + 3];
3664
+ if (!a8 && !(h && h[i])) continue;
3665
+ const a = a8 / 255;
3666
+ let aMin = 0;
3667
+ const tr = [1, 1, 1];
3668
+ for (let c = 0; c < 3; c++) {
3669
+ const e = a * px[i * 4 + c] + (1 - a) * pb[c];
3670
+ aMin = Math.max(aMin, needAlpha(e, pb[c]));
3671
+ tr[c] = Math.min(1, LIN[Math.round(e)] / pl[c]);
3672
+ }
3673
+ const hide = aMin < 1 && a > aMin ? (a - aMin) / (1 - aMin) : 0;
3674
+ let k = 1 - hide;
3675
+ if (h && h[i]) {
3676
+ const ma = m ? m[i * 4 + 3] : 0;
3677
+ const clarity = ma ? (255 - ma) / 254 : 0;
3678
+ k *= 1 - Math.min(1, h[i] / BLOCK_HEIGHT) * (1 - clarity);
3679
+ }
3680
+ t0[i] *= 1 - op + op * tr[0] * k;
3681
+ t1[i] *= 1 - op + op * tr[1] * k;
3682
+ t2[i] *= 1 - op + op * tr[2] * k;
3683
+ clear[i] *= 1 - op * Math.max(a, h && h[i] ? 1 : 0);
3684
+ }
3685
+ }
3686
+ const light = new Uint8ClampedArray(_chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE * 4);
3687
+ const color = new Uint8ClampedArray(_chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE * 4);
3688
+ const painted = new Uint8ClampedArray(_chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE);
3689
+ for (let y = 0, i = 0; y < rect.height; y++) {
3690
+ for (let x = 0; x < rect.width; x++, i++) {
3691
+ const o = (y * _chunk474IQG74cjs.TILE + x) * 4;
3692
+ const add = [Math.min(1, t0[i]), Math.min(1, t1[i]), Math.min(1, t2[i])];
3693
+ const mx = Math.max(add[0], add[1], add[2]);
3694
+ if (mx > 0.5 / 255) {
3695
+ light[o] = Math.round(add[0] * 255);
3696
+ light[o + 1] = Math.round(add[1] * 255);
3697
+ light[o + 2] = Math.round(add[2] * 255);
3698
+ light[o + 3] = 255;
3699
+ }
3700
+ const pv = Math.round((1 - clear[i]) * 255);
3701
+ if (pv && mx > 0.5 / 255) {
3702
+ painted[y * _chunk474IQG74cjs.TILE + x] = pv;
3703
+ color[o] = Math.round(_chunk7AHBW2CQcjs.linearToSrgb.call(void 0, add[0]) * 255);
3704
+ color[o + 1] = Math.round(_chunk7AHBW2CQcjs.linearToSrgb.call(void 0, add[1]) * 255);
3705
+ color[o + 2] = Math.round(_chunk7AHBW2CQcjs.linearToSrgb.call(void 0, add[2]) * 255);
3706
+ color[o + 3] = 255;
3707
+ }
3708
+ }
3709
+ }
3710
+ return { light: _chunk474IQG74cjs.finishTile.call(void 0, light, 4, 0), color: _chunk474IQG74cjs.finishTile.call(void 0, color, 4, 0), painted: _chunk474IQG74cjs.finishTile.call(void 0, painted, 1, 0) };
3711
+ }
3712
+
3151
3713
  // src/paint-editor/replay.ts
3152
3714
  function replayItems(doc) {
3153
3715
  const items = [];
@@ -3170,7 +3732,7 @@ async function replayPainting(source, target, options = {}) {
3170
3732
  const maxStrokes = _nullishCoalesce(options.strokes, () => ( source.strokes.length));
3171
3733
  const chunk = Math.max(1, _nullishCoalesce(options.wetChunk, () => ( 0))) || Infinity;
3172
3734
  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] : []));
3735
+ await target.prepareEffectPaints(source.strokes.slice(0, maxStrokes).flatMap((st) => _optionalChain([st, 'access', _200 => _200.paint, 'optionalAccess', _201 => _201.type]) === "effect" ? [st.paint] : []));
3174
3736
  const advanceTo = async (clock) => {
3175
3737
  const now = target.getState().wet.clock;
3176
3738
  let left = clock - now;
@@ -3178,7 +3740,7 @@ async function replayPainting(source, target, options = {}) {
3178
3740
  const n = Math.min(left, chunk);
3179
3741
  await target.advanceWet({ steps: n });
3180
3742
  left -= n;
3181
- if (Number.isFinite(chunk)) await _optionalChain([options, 'access', _160 => _160.onWetSteps, 'optionalCall', _161 => _161(target.getState().wet.clock)]);
3743
+ if (Number.isFinite(chunk)) await _optionalChain([options, 'access', _202 => _202.onWetSteps, 'optionalCall', _203 => _203(target.getState().wet.clock)]);
3182
3744
  }
3183
3745
  };
3184
3746
  const sr = (r) => ({ x: r.x * s, y: r.y * s, width: r.width * s, height: r.height * s });
@@ -3191,14 +3753,14 @@ async function replayPainting(source, target, options = {}) {
3191
3753
  else if (e.kind === "clear") target.clearLayer(e.layerId);
3192
3754
  else if (e.kind === "live-strokes") {
3193
3755
  const d = e.data;
3194
- if (_optionalChain([d, 'optionalAccess', _162 => _162.rasterized])) target.replaceLiveStrokes(e.layerId, null);
3756
+ if (_optionalChain([d, 'optionalAccess', _204 => _204.rasterized])) target.replaceLiveStrokes(e.layerId, null);
3195
3757
  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] : []));
3758
+ const list = (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _205 => _205.strokes]), () => ( []))).map((st2) => s === 1 ? st2 : { ...varyStroke(st2, scaleM, { scale: s }), ...st2.stencils ? { stencils: st2.stencils.map((x) => scaleStencil(x, s)) } : {} });
3759
+ await target.prepareEffectPaints(list.flatMap((st2) => _optionalChain([st2, 'access', _206 => _206.paint, 'optionalAccess', _207 => _207.type]) === "effect" ? [st2.paint] : []));
3198
3760
  target.replaceLiveStrokes(e.layerId, list);
3199
3761
  }
3200
3762
  } 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 });
3763
+ const list = (_nullishCoalesce(_optionalChain([e, 'access', _208 => _208.data, 'optionalAccess', _209 => _209.stickers]), () => ( []))).map((x) => s === 1 ? x : { ...x, x: x.x * s, y: x.y * s, size: x.size * s });
3202
3764
  target.replaceStickers(e.layerId, list);
3203
3765
  } else target.flattenRelief(e.layerId);
3204
3766
  continue;
@@ -3211,8 +3773,8 @@ async function replayPainting(source, target, options = {}) {
3211
3773
  switch (e.kind) {
3212
3774
  case "dry": {
3213
3775
  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 });
3776
+ const ids = _optionalChain([e, 'access', _210 => _210.layerId, 'optionalAccess', _211 => _211.split, 'call', _212 => _212(","), 'access', _213 => _213.filter, 'call', _214 => _214(Boolean)]);
3777
+ if (_optionalChain([ids, 'optionalAccess', _215 => _215.length])) for (const id of ids) target.dryPaint({ layerId: id, rect: rect ? sr(rect) : void 0 });
3216
3778
  else target.dryPaint({ rect: rect ? sr(rect) : void 0 });
3217
3779
  break;
3218
3780
  }
@@ -3243,7 +3805,7 @@ async function replayPainting(source, target, options = {}) {
3243
3805
  strokes++;
3244
3806
  if (!target.getLayer(st.layerId)) continue;
3245
3807
  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) {
3808
+ if (st.brush.engine === "wet" && _optionalChain([st, 'access', _216 => _216.steps, 'optionalAccess', _217 => _217.length]) === st.points.length) {
3247
3809
  await target.wetIdle();
3248
3810
  await advanceTo(st.steps[0]);
3249
3811
  const session = target.beginStroke({ ...init, start: st.points[0] });
@@ -3263,7 +3825,7 @@ async function replayPainting(source, target, options = {}) {
3263
3825
  await target.wetIdle();
3264
3826
  target.paintStroke({ ...init, points: st.points });
3265
3827
  }
3266
- await _optionalChain([options, 'access', _176 => _176.onStroke, 'optionalCall', _177 => _177(strokes)]);
3828
+ await _optionalChain([options, 'access', _218 => _218.onStroke, 'optionalCall', _219 => _219(strokes)]);
3267
3829
  }
3268
3830
  void IDENTITY_AFFINE;
3269
3831
  }
@@ -3308,7 +3870,7 @@ async function buildTimeLapse(source, target, options = {}) {
3308
3870
  wetChunk: wetEvery > 0 ? wetEvery : 0,
3309
3871
  onStroke: (i) => {
3310
3872
  if (i % perFrame === 0 || i === total) shoot(i);
3311
- _optionalChain([options, 'access', _178 => _178.onProgress, 'optionalCall', _179 => _179(total ? i / total : 1)]);
3873
+ _optionalChain([options, 'access', _220 => _220.onProgress, 'optionalCall', _221 => _221(total ? i / total : 1)]);
3312
3874
  },
3313
3875
  onWetSteps: wetEvery > 0 ? () => shoot(-1) : void 0
3314
3876
  });
@@ -3395,10 +3957,10 @@ function mixPixels(before, after, amount) {
3395
3957
  }
3396
3958
  return out;
3397
3959
  }
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}
3960
+ var PaintEffectsRenderer = (_class11 = class {constructor() { _class11.prototype.__init79.call(this);_class11.prototype.__init80.call(this);_class11.prototype.__init81.call(this); }
3961
+ __init79() {this.pool = new (0, _chunkPY4GWSE2cjs.EffectRendererPool)({ maxRenderers: 3 })}
3962
+ __init80() {this.descriptions = /* @__PURE__ */ new Map()}
3963
+ __init81() {this.provider = null}
3402
3964
  /** Forgets cached descriptions and renderers when the provider changes. */
3403
3965
  follow(provider) {
3404
3966
  if (provider === this.provider) return;
@@ -3428,7 +3990,7 @@ var PaintEffectsRenderer = (_class9 = class {constructor() { _class9.prototype._
3428
3990
  this.pool.clear();
3429
3991
  this.descriptions.clear();
3430
3992
  }
3431
- }, _class9);
3993
+ }, _class11);
3432
3994
  var sourceOut = (s) => ({
3433
3995
  effect: s.effect,
3434
3996
  ...s.provider ? { provider: s.provider } : {},
@@ -3483,7 +4045,7 @@ function parseColor(value, what = "color") {
3483
4045
  return value.trim().toLowerCase();
3484
4046
  }
3485
4047
  function rgbaOf(hex) {
3486
- const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, hex);
4048
+ const [r, g, b] = _chunk4T47Z4J3cjs.hexToRgb8.call(void 0, hex);
3487
4049
  return [r, g, b, 255];
3488
4050
  }
3489
4051
  function rgbToHex(r, g, b) {
@@ -3496,87 +4058,93 @@ function unionRect(a, b) {
3496
4058
  const y = Math.min(a.y, b.y);
3497
4059
  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
4060
  }
3499
- var isPixelLayer = (l) => _optionalChain([l, 'optionalAccess', _180 => _180.type]) === "pixel";
3500
- var PaintEditorController = (_class10 = class _PaintEditorController {
4061
+ var isPixelLayer = (l) => _optionalChain([l, 'optionalAccess', _222 => _222.type]) === "pixel";
4062
+ var PaintEditorController = (_class12 = class _PaintEditorController {
3501
4063
 
3502
- __init74() {this.extensions = {}}
3503
- __init75() {this.undoStack = []}
3504
- __init76() {this.redoStack = []}
4064
+ __init82() {this.extensions = {}}
4065
+ __init83() {this.undoStack = []}
4066
+ __init84() {this.redoStack = []}
3505
4067
 
3506
4068
 
3507
4069
 
3508
4070
  /** The library list in the state (the same array until the user brushes change, so thumbnails are not redrawn). */
3509
- __init77() {this.library = null}
4071
+ __init85() {this.library = null}
3510
4072
 
3511
- __init78() {this.secondaryColor = "#ffffff"}
4073
+ __init86() {this.secondaryColor = "#ffffff"}
3512
4074
  /** Gradient paint chosen with `setPaint` (`null`: the paint colour). */
3513
- __init79() {this.gradient = null}
4075
+ __init87() {this.gradient = null}
3514
4076
  /** Effect paint chosen with `setEffectPaint` (rendered), and one still rendering. */
3515
- __init80() {this.effectPaint = null}
3516
- __init81() {this.effectPaintPending = null}
4077
+ __init88() {this.effectPaint = null}
4078
+ __init89() {this.effectPaintPending = null}
3517
4079
  /** Changes whenever the paint is chosen, so an effect paint that finishes rendering late does not take over. */
3518
- __init82() {this.paintEpoch = 0}
4080
+ __init90() {this.paintEpoch = 0}
3519
4081
  /** 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 } }}
4082
+ __init91() {this.effectPaintPixels = /* @__PURE__ */ new Map()}
4083
+ __init92() {this.mixerLoad = _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD}
4084
+ __init93() {this.view = { ...DEFAULT_VIEW, pan: { ...DEFAULT_VIEW.pan } }}
3523
4085
 
3524
- __init86() {this.selectedStroke = null}
3525
- __init87() {this.selectedSticker = null}
4086
+ __init94() {this.selectedStroke = null}
4087
+ __init95() {this.selectedSticker = null}
3526
4088
  /** 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}
4089
+ __init96() {this.stickerPreview = null}
3528
4090
 
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}
4091
+ __init97() {this.listeners = /* @__PURE__ */ new Set()}
4092
+ __init98() {this.version = 0}
4093
+ __init99() {this.cached = null}
4094
+ __init100() {this.session = null}
4095
+ __init101() {this.live = null}
4096
+ __init102() {this.liveLayers = null}
4097
+ __init103() {this.dirty = "all"}
4098
+ __init104() {this.seedCounter = 1}
4099
+ __init105() {this.strokeCounter = 1}
4100
+ /** Light paint: each layer's spill settings (saved with the document, not undo steps; missing = the defaults). */
4101
+ __init106() {this.lightSpills = {}}
4102
+ __init107() {this.lightCaches = /* @__PURE__ */ new Map()}
4103
+ /** Backlit paper: the light through the paint as drawn (live strokes too), and as committed (its spill). */
4104
+ __init108() {this.backlightView = new BacklightCache()}
4105
+ __init109() {this.backlightCommitted = new BacklightCache()}
3538
4106
  /** 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}
4107
+ __init110() {this.effectPlaybackState = { playing: true, fps: 30 }}
4108
+ __init111() {this.effectTime = 0}
3541
4109
  /** While a frame is drawn: the time live effect strokes are drawn at (else their own time). */
3542
- __init100() {this.liveEffectAt = null}
4110
+ __init112() {this.liveEffectAt = null}
3543
4111
  /** 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()}
4112
+ __init113() {this.animatedLayers = /* @__PURE__ */ new Map()}
3545
4113
  /** `renderAnimationFrame` / `withEffectTime`: layer surfaces at another time, for one render. */
3546
- __init102() {this.frameOverride = null}
4114
+ __init114() {this.frameOverride = null}
3547
4115
  /** 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()}
4116
+ __init115() {this.paintedBounds = /* @__PURE__ */ new WeakMap()}
3549
4117
 
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()}
4118
+ __init116() {this.swatches = DEFAULT_SWATCHES.map((s) => ({ ...s }))}
4119
+ __init117() {this.pad = new MixingPad()}
4120
+ __init118() {this.savedPads = []}
4121
+ __init119() {this.nextPadId = 1}
4122
+ __init120() {this.tracingVersion = 1}
4123
+ __init121() {this.tracingCache = null}
4124
+ __init122() {this.imagePixels = /* @__PURE__ */ new Map()}
4125
+ __init123() {this.nextReferenceId = 1}
4126
+ __init124() {this.viewCompositor = createPaintCompositor()}
4127
+ __init125() {this.exportCompositor = createPaintCompositor()}
3560
4128
  /** Wet media: the simulation, its settings and time. */
3561
4129
 
3562
4130
 
3563
4131
  /** 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()}
4132
+ __init126() {this.lighting = { ...DEFAULT_LIGHTING }}
4133
+ __init127() {this.litCaches = /* @__PURE__ */ new Map()}
3566
4134
  /** The live impasto stroke's channels. */
3567
- __init116() {this.liveThick = null}
4135
+ __init128() {this.liveThick = null}
3568
4136
  /** Set while an operation that changes wet paint runs: the simulation before it (for undo). */
3569
- __init117() {this.wetOp = null}
4137
+ __init129() {this.wetOp = null}
3570
4138
  /** The effects provider slot (see `setEffectsProvider`). */
3571
- __init118() {this.effects = new (0, _chunkGHCSC2WPcjs.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);
4139
+ __init130() {this.effects = new (0, _chunkGHCSC2WPcjs.EffectsSlot)()}
4140
+ __init131() {this.effectsRenderer = new PaintEffectsRenderer()}
4141
+ __init132() {this.paperEffectNodeCache = null}
4142
+ constructor(options = {}) {;_class12.prototype.__init82.call(this);_class12.prototype.__init83.call(this);_class12.prototype.__init84.call(this);_class12.prototype.__init85.call(this);_class12.prototype.__init86.call(this);_class12.prototype.__init87.call(this);_class12.prototype.__init88.call(this);_class12.prototype.__init89.call(this);_class12.prototype.__init90.call(this);_class12.prototype.__init91.call(this);_class12.prototype.__init92.call(this);_class12.prototype.__init93.call(this);_class12.prototype.__init94.call(this);_class12.prototype.__init95.call(this);_class12.prototype.__init96.call(this);_class12.prototype.__init97.call(this);_class12.prototype.__init98.call(this);_class12.prototype.__init99.call(this);_class12.prototype.__init100.call(this);_class12.prototype.__init101.call(this);_class12.prototype.__init102.call(this);_class12.prototype.__init103.call(this);_class12.prototype.__init104.call(this);_class12.prototype.__init105.call(this);_class12.prototype.__init106.call(this);_class12.prototype.__init107.call(this);_class12.prototype.__init108.call(this);_class12.prototype.__init109.call(this);_class12.prototype.__init110.call(this);_class12.prototype.__init111.call(this);_class12.prototype.__init112.call(this);_class12.prototype.__init113.call(this);_class12.prototype.__init114.call(this);_class12.prototype.__init115.call(this);_class12.prototype.__init116.call(this);_class12.prototype.__init117.call(this);_class12.prototype.__init118.call(this);_class12.prototype.__init119.call(this);_class12.prototype.__init120.call(this);_class12.prototype.__init121.call(this);_class12.prototype.__init122.call(this);_class12.prototype.__init123.call(this);_class12.prototype.__init124.call(this);_class12.prototype.__init125.call(this);_class12.prototype.__init126.call(this);_class12.prototype.__init127.call(this);_class12.prototype.__init128.call(this);_class12.prototype.__init129.call(this);_class12.prototype.__init130.call(this);_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);
3575
4143
  this.historyLimit = Math.max(1, _nullishCoalesce(options.historyLimit, () => ( 200)));
3576
4144
  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);
4145
+ this.userBrushes = (_nullishCoalesce(options.brushes, () => ( []))).map((b) => _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, { ...b, builtin: false }));
4146
+ const start = _nullishCoalesce(this.findBrush(_nullishCoalesce(options.brushId, () => ( _chunk4T47Z4J3cjs.DEFAULT_BRUSH_ID))), () => ( this.findBrush(_chunk4T47Z4J3cjs.DEFAULT_BRUSH_ID)));
4147
+ this.brush = _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, start);
3580
4148
  this.color = options.color ? parseColor(options.color) : "#1f2937";
3581
4149
  this.hand = options.hand === "left" ? "left" : "right";
3582
4150
  this.encodeImage = options.encodeImage;
@@ -3587,7 +4155,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3587
4155
  canvas: () => this.doc.canvas,
3588
4156
  paper: () => this.doc.paper,
3589
4157
  layerSurface: (id) => {
3590
- const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _181 => _181.layer]);
4158
+ const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _223 => _223.layer]);
3591
4159
  return isPixelLayer(l) ? l.surface : null;
3592
4160
  },
3593
4161
  updateSurfaces: (surfaces, rects) => {
@@ -3596,12 +4164,18 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3596
4164
  this.doc = { ...this.doc, layers };
3597
4165
  this.emit(rects.length ? rects : "all");
3598
4166
  },
4167
+ layerLight: (id) => _nullishCoalesce(_optionalChain([this, 'access', _224 => _224.doc, 'access', _225 => _225.light, 'optionalAccess', _226 => _226[id]]), () => ( null)),
4168
+ updateLights: (lights) => {
4169
+ let light = this.doc.light;
4170
+ for (const [id, surface] of lights) if (_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id)) light = withLayerLight(light, id, surface);
4171
+ this.doc = { ...this.doc, ...light ? { light } : {} };
4172
+ },
3599
4173
  changed: () => {
3600
4174
  }
3601
4175
  },
3602
4176
  options.wet
3603
4177
  );
3604
- this.wet.onDriverDone = () => _optionalChain([this, 'access', _182 => _182.session, 'optionalAccess', _183 => _183.completeWetEnd, 'call', _184 => _184()]);
4178
+ this.wet.onDriverDone = () => _optionalChain([this, 'access', _227 => _227.session, 'optionalAccess', _228 => _228.completeWetEnd, 'call', _229 => _229()]);
3605
4179
  }
3606
4180
  // --- State and events ----------------------------------------------------------
3607
4181
  subscribe(listener) {
@@ -3623,7 +4197,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3623
4197
  this.cached = {
3624
4198
  document: this.doc,
3625
4199
  brush: this.brush,
3626
- brushes: this.library ??= [..._chunk5BR7LC24cjs.BUILTIN_BRUSHES, ...this.userBrushes],
4200
+ brushes: this.library ??= [..._chunk4T47Z4J3cjs.BUILTIN_BRUSHES, ...this.userBrushes],
3627
4201
  color: this.color,
3628
4202
  secondaryColor: this.secondaryColor,
3629
4203
  paint: _nullishCoalesce(this.gradient, () => ( { type: "color", color: this.color })),
@@ -3637,8 +4211,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3637
4211
  selectedSticker: this.selectedSticker,
3638
4212
  canUndo: this.undoStack.length > 0 && !this.session,
3639
4213
  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)),
4214
+ undoLabel: _nullishCoalesce(_optionalChain([last, 'optionalAccess', _230 => _230.label]), () => ( null)),
4215
+ redoLabel: _nullishCoalesce(_optionalChain([next, 'optionalAccess', _231 => _231.label]), () => ( null)),
3642
4216
  guides: this.guides,
3643
4217
  swatches: this.swatches,
3644
4218
  mixingPadVersion: this.pad.version,
@@ -3651,10 +4225,10 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3651
4225
  overlay: this.wet.overlay,
3652
4226
  instantDry: this.wet.instantDry,
3653
4227
  speed: this.wet.speed,
3654
- clock: _nullishCoalesce(_optionalChain([this, 'access', _187 => _187.wet, 'access', _188 => _188.world, 'optionalAccess', _189 => _189.clock]), () => ( 0)),
4228
+ clock: _nullishCoalesce(_optionalChain([this, 'access', _232 => _232.wet, 'access', _233 => _233.world, 'optionalAccess', _234 => _234.clock]), () => ( 0)),
3655
4229
  tilt: { ...this.wet.tilt },
3656
4230
  gpu: this.wet.gpuMode,
3657
- gpuActive: Boolean(_optionalChain([this, 'access', _190 => _190.wet, 'access', _191 => _191.world, 'optionalAccess', _192 => _192.gpuActive]))
4231
+ gpuActive: Boolean(_optionalChain([this, 'access', _235 => _235.wet, 'access', _236 => _236.world, 'optionalAccess', _237 => _237.gpuActive]))
3658
4232
  },
3659
4233
  version: this.version
3660
4234
  };
@@ -3673,7 +4247,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3673
4247
  return d;
3674
4248
  }
3675
4249
  // --- History -------------------------------------------------------------------
3676
- __init121() {this.docRevision = 0}
4250
+ __init133() {this.docRevision = 0}
3677
4251
  /**
3678
4252
  * Bumped on every change to the painting (a committed or amended step, undo, redo, opening a
3679
4253
  * document), not on wet-paint frames or view changes. `createPaintEditorDocument` uses it.
@@ -3756,9 +4330,9 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3756
4330
  return wetIds;
3757
4331
  }
3758
4332
  /** Records a wet event (for replays) in the document being built. */
3759
- __init122() {this.pendingWetEvents = []}
4333
+ __init134() {this.pendingWetEvents = []}
3760
4334
  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 });
4335
+ this.pendingWetEvents.push({ clock: _nullishCoalesce(_optionalChain([this, 'access', _238 => _238.wet, 'access', _239 => _239.world, 'optionalAccess', _240 => _240.clock]), () => ( 0)), at: this.doc.strokes.length, ...e });
3762
4336
  }
3763
4337
  /** The document with a paint event appended (for replays). */
3764
4338
  withPaintEvent(doc, e) {
@@ -3819,7 +4393,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3819
4393
  // --- Canvas and paper ----------------------------------------------------------
3820
4394
  resolvePaper(paper) {
3821
4395
  if (typeof paper === "string") {
3822
- const p = _chunk5BR7LC24cjs.paperPreset.call(void 0, paper);
4396
+ const p = _chunk4T47Z4J3cjs.paperPreset.call(void 0, paper);
3823
4397
  if (!p) throw new PaintEditorError(`Unknown paper "${paper}". Call list_papers for the ids.`);
3824
4398
  return p;
3825
4399
  }
@@ -3902,7 +4476,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3902
4476
  }
3903
4477
  let next = { ...shiftDocument(this.withWetEvents(this.doc), left, top), canvas: { ...was, width, height } };
3904
4478
  for (const id of keptWet) {
3905
- const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, next.layers, id), 'optionalAccess', _196 => _196.layer]);
4479
+ const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, next.layers, id), 'optionalAccess', _241 => _241.layer]);
3906
4480
  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
4481
  }
3908
4482
  if (this.wet.world) {
@@ -3930,23 +4504,23 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3930
4504
  this.requireIdle("changing the paper");
3931
4505
  let base = this.doc.paper;
3932
4506
  if (update.preset !== void 0 && update.preset !== this.doc.paper.preset) {
3933
- const p = _chunk5BR7LC24cjs.paperPreset.call(void 0, update.preset);
4507
+ const p = _chunk4T47Z4J3cjs.paperPreset.call(void 0, update.preset);
3934
4508
  if (!p) throw new PaintEditorError(`Unknown paper "${update.preset}". Call list_papers for the ids.`);
3935
4509
  base = p;
3936
4510
  }
3937
4511
  const { preset: _p, ...rest } = update;
3938
4512
  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);
4513
+ const paper = _chunk4T47Z4J3cjs.normalizePaper.call(void 0, { ...rest, preset: base.preset, offset: _nullishCoalesce(rest.offset, () => ( this.doc.paper.offset)) }, base);
3940
4514
  const changedKeys = Object.keys(rest).filter((k) => k !== "name");
3941
4515
  if (changedKeys.length && update.preset === void 0) paper.preset = base.preset;
3942
4516
  if (this.wet.world) this.beginWetOp();
3943
4517
  this.commit("Paper", { ...this.doc, paper });
3944
4518
  this.wet.paperChanged();
3945
- return _chunk5BR7LC24cjs.clonePaper.call(void 0, paper);
4519
+ return _chunk4T47Z4J3cjs.clonePaper.call(void 0, paper);
3946
4520
  }
3947
4521
  // --- Layers --------------------------------------------------------------------
3948
4522
  getLayer(id) {
3949
- return _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _197 => _197.layer]);
4523
+ return _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _242 => _242.layer]);
3950
4524
  }
3951
4525
  /** The active pixel layer (or `undefined`). */
3952
4526
  getActiveLayer() {
@@ -3965,7 +4539,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3965
4539
  walk(this.doc.layers, null, 0);
3966
4540
  return out;
3967
4541
  }
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))) {
4542
+ describeLayer(l, parentId = _nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, l.id), 'optionalAccess', _243 => _243.parent, 'optionalAccess', _244 => _244.id]), () => ( null)), depth = _nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, l.id), 'optionalAccess', _245 => _245.path, 'access', _246 => _246.length]), () => ( 0))) {
3969
4543
  let bounds = null;
3970
4544
  if (l.type === "pixel") {
3971
4545
  const b = _chunk474IQG74cjs.contentBounds.call(void 0, l.surface);
@@ -3982,7 +4556,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
3982
4556
  locks: { ..._nullishCoalesce(l.locks, () => ( {})) },
3983
4557
  active: l.id === this.doc.activeLayerId,
3984
4558
  bounds,
3985
- ..._optionalChain([this, 'access', _202 => _202.doc, 'access', _203 => _203.liveStrokes, 'optionalAccess', _204 => _204[l.id]]) ? { liveStrokes: this.doc.liveStrokes[l.id].length } : {},
4559
+ ..._optionalChain([this, 'access', _247 => _247.doc, 'access', _248 => _248.liveStrokes, 'optionalAccess', _249 => _249[l.id]]) ? { liveStrokes: this.doc.liveStrokes[l.id].length } : {},
3986
4560
  parentId,
3987
4561
  depth
3988
4562
  };
@@ -4014,7 +4588,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4014
4588
  this.requireIdle("adding a layer");
4015
4589
  const count = _chunkHB4ICNNAcjs.flattenLayers.call(void 0, this.doc.layers).length;
4016
4590
  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}`);
4591
+ const name = _optionalChain([options, 'access', _250 => _250.name, 'optionalAccess', _251 => _251.trim, 'call', _252 => _252()]) || (type === "group" ? `Group ${count + 1}` : type === "strokes" ? `Strokes ${count + 1}` : `Layer ${count + 1}`);
4018
4592
  const layer = type === "group" ? { type: "group", id: newPaintLayerId(), name, visible: true, opacity: 1, blend: "pass-through", children: [] } : createPaintLayer(name);
4019
4593
  let parentId = null;
4020
4594
  let index;
@@ -4026,7 +4600,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4026
4600
  } else if (above) {
4027
4601
  const loc = _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, above);
4028
4602
  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));
4603
+ parentId = _nullishCoalesce(_optionalChain([loc, 'access', _253 => _253.parent, 'optionalAccess', _254 => _254.id]), () => ( null));
4030
4604
  index = loc.index + 1;
4031
4605
  }
4032
4606
  const layers = _chunkHB4ICNNAcjs.insertLayer.call(void 0, this.doc.layers, layer, parentId, index);
@@ -4067,8 +4641,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4067
4641
  }
4068
4642
  const layers = _chunkHB4ICNNAcjs.mapLayer.call(void 0, this.doc.layers, id, () => null);
4069
4643
  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)) });
4644
+ if (active && !_chunkHB4ICNNAcjs.findLayer.call(void 0, layers, active)) active = _nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.flattenLayers.call(void 0, layers), 'access', _255 => _255.filter, 'call', _256 => _256((x) => x.type === "pixel"), 'access', _257 => _257.pop, 'call', _258 => _258(), 'optionalAccess', _259 => _259.id]), () => ( null));
4645
+ 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
4646
  return { removed: id, activeLayerId: active };
4073
4647
  }
4074
4648
  duplicateLayer(id) {
@@ -4082,16 +4656,19 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4082
4656
  const thick = { ..._nullishCoalesce(this.doc.thick, () => ( {})) };
4083
4657
  const stickers = { ..._nullishCoalesce(this.doc.stickers, () => ( {})) };
4084
4658
  const liveStrokes = { ..._nullishCoalesce(this.doc.liveStrokes, () => ( {})) };
4659
+ const light = { ..._nullishCoalesce(this.doc.light, () => ( {})) };
4085
4660
  const copy = (l) => {
4086
4661
  const id2 = newPaintLayerId();
4662
+ if (light[l.id]) light[id2] = light[l.id];
4663
+ if (this.lightSpills[l.id]) this.lightSpills[id2] = { ...this.lightSpills[l.id] };
4087
4664
  if (thick[l.id]) thick[id2] = thick[l.id];
4088
4665
  if (stickers[l.id]) stickers[id2] = stickers[l.id].map((x) => ({ ...x }));
4089
4666
  if (liveStrokes[l.id]) liveStrokes[id2] = liveStrokes[l.id].map((x) => ({ ...x, id: `s${this.strokeCounter++}`, layerId: id2 }));
4090
4667
  return l.type === "group" ? { ...l, id: id2, children: l.children.map(copy) } : { ...l, id: id2 };
4091
4668
  };
4092
4669
  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 } : {} }));
4670
+ const layers = _chunkHB4ICNNAcjs.insertLayer.call(void 0, this.doc.layers, dup, _nullishCoalesce(_optionalChain([loc, 'access', _260 => _260.parent, 'optionalAccess', _261 => _261.id]), () => ( null)), loc.index + 1);
4671
+ 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
4672
  return this.describeLayer(dup);
4096
4673
  }
4097
4674
  /** Moves a layer to `index` (0 = bottom) within `parentId` (default its own parent; `null` = top level). */
@@ -4099,7 +4676,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4099
4676
  this.requireIdle("moving a layer");
4100
4677
  const loc = _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id);
4101
4678
  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;
4679
+ const target = parentId === void 0 ? _nullishCoalesce(_optionalChain([loc, 'access', _262 => _262.parent, 'optionalAccess', _263 => _263.id]), () => ( null)) : parentId;
4103
4680
  if (target) {
4104
4681
  const g = this.requireLayer(target);
4105
4682
  if (g.type !== "group") throw new PaintEditorError(`Layer "${g.name}" is not a group; pass a group id as parentId.`);
@@ -4118,7 +4695,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4118
4695
  const pre = _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id);
4119
4696
  if (pre) {
4120
4697
  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)));
4698
+ this.dryLayersFirst([id, _optionalChain([sib, 'access', _264 => _264[pre.index - 1], 'optionalAccess', _265 => _265.id])].filter((x) => Boolean(x)));
4122
4699
  }
4123
4700
  const loc = _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id);
4124
4701
  if (!loc) throw new PaintEditorError(`Unknown layer "${id}". Call list_layers for the ids.`);
@@ -4137,7 +4714,11 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4137
4714
  }
4138
4715
  const merged = { ...below, surface, offset: { x: 0, y: 0 } };
4139
4716
  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]) }));
4717
+ const toDoc = (t, l) => t ? _chunk474IQG74cjs.translateSurface.call(void 0, t, l.offset.x, l.offset.y) : void 0;
4718
+ const mergedLight = _optionalChain([this, 'access', _266 => _266.doc, 'access', _267 => _267.light, 'optionalAccess', _268 => _268[upper.id]]) || _optionalChain([this, 'access', _269 => _269.doc, 'access', _270 => _270.light, 'optionalAccess', _271 => _271[below.id]]) ? mergeLight(toDoc(_optionalChain([this, 'access', _272 => _272.doc, 'access', _273 => _273.light, 'optionalAccess', _274 => _274[below.id]]), below), upper.visible ? toDoc(_optionalChain([this, 'access', _275 => _275.doc, 'access', _276 => _276.light, 'optionalAccess', _277 => _277[upper.id]]), upper) : void 0, _chunk474IQG74cjs.translateSurface.call(void 0, upper.surface, upper.offset.x, upper.offset.y)) : void 0;
4719
+ const lightAfter = _nullishCoalesce(this.lightWithout([upper.id, below.id]).light, () => ( (this.doc.light ? { ...this.doc.light } : void 0)));
4720
+ const light = mergedLight ? { ..._nullishCoalesce(lightAfter, () => ( {})), [below.id]: mergedLight } : lightAfter;
4721
+ 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
4722
  return this.describeLayer(merged);
4142
4723
  }
4143
4724
  /** Removes every pixel of a layer. */
@@ -4149,7 +4730,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4149
4730
  this.wet.world.removeLane(l.id);
4150
4731
  }
4151
4732
  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 }));
4733
+ 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', _278 => _278.doc, 'access', _279 => _279.liveStrokes, 'optionalAccess', _280 => _280[l.id]]) ? { liveStrokes: { ...this.doc.liveStrokes, [l.id]: [] } } : {}, ...this.lightWithout([l.id]) }, { kind: "clear", layerId: l.id }));
4153
4734
  return { layerId: l.id, bounds };
4154
4735
  }
4155
4736
  /**
@@ -4159,8 +4740,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4159
4740
  fillLayer(options = {}) {
4160
4741
  this.requireIdle("filling");
4161
4742
  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).`);
4743
+ if (_optionalChain([this, 'access', _281 => _281.doc, 'access', _282 => _282.stickers, 'optionalAccess', _283 => _283[fillTarget.id], 'optionalAccess', _284 => _284.length])) throw new PaintEditorError(`Layer "${fillTarget.name}" is a sticker layer; fill another layer (add_layer).`);
4744
+ if (_optionalChain([this, 'access', _285 => _285.doc, 'access', _286 => _286.liveStrokes, 'optionalAccess', _287 => _287[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
4745
  this.dryLayersFirst([_nullishCoalesce(_nullishCoalesce(options.layerId, () => ( this.doc.activeLayerId)), () => ( ""))]);
4165
4746
  const l = this.paintTarget(options.layerId, "fill");
4166
4747
  const color = options.color === void 0 ? this.color : parseColor(options.color);
@@ -4193,7 +4774,9 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4193
4774
  offset: l.offset
4194
4775
  };
4195
4776
  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 } }));
4777
+ const lit = _optionalChain([this, 'access', _288 => _288.doc, 'access', _289 => _289.light, 'optionalAccess', _290 => _290[l.id]]);
4778
+ const light = lit ? withLayerLight(this.doc.light, l.id, scaleLight(lit, 1 - opacity)) : void 0;
4779
+ 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
4780
  return { layerId: l.id, bounds: rect };
4198
4781
  }
4199
4782
  applyCoverage(l, coverage, rect, apply) {
@@ -4201,7 +4784,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4201
4784
  const tiles = new Map(base.tiles);
4202
4785
  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
4786
  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)));
4787
+ const src = _nullishCoalesce(_optionalChain([base, 'access', _291 => _291.tiles, 'access', _292 => _292.get, 'call', _293 => _293(key), 'optionalAccess', _294 => _294.data]), () => ( new Uint8ClampedArray(_chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE * 4)));
4205
4788
  const tile = _chunk474IQG74cjs.finishTile.call(void 0, _chunkPQ3H2AANcjs.applyStrokeToTile.call(void 0, coverage, src, 4, tx, ty, apply), 4, 0);
4206
4789
  if (tile) tiles.set(key, tile);
4207
4790
  else tiles.delete(key);
@@ -4234,7 +4817,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4234
4817
  }
4235
4818
  /** The effect a layer is drawn from, or `undefined` for other layers. */
4236
4819
  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;
4820
+ return _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, layerId) ? _optionalChain([this, 'access', _295 => _295.doc, 'access', _296 => _296.effectLayers, 'optionalAccess', _297 => _297[layerId]]) : void 0;
4238
4821
  }
4239
4822
  /** The effect paper (without its pixels), or `null`. */
4240
4823
  getPaperEffect() {
@@ -4271,8 +4854,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4271
4854
  /** A pixel layer that takes effect pixels: unlocked, visible, no sticker layer, no wet paint. */
4272
4855
  effectTarget(layerId, what) {
4273
4856
  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).`);
4857
+ if (_optionalChain([this, 'access', _298 => _298.doc, 'access', _299 => _299.stickers, 'optionalAccess', _300 => _300[l.id], 'optionalAccess', _301 => _301.length])) throw new PaintEditorError(`Layer "${l.name}" is a sticker layer; ${what} another layer (add_layer).`);
4858
+ if (_optionalChain([this, 'access', _302 => _302.doc, 'access', _303 => _303.liveStrokes, 'optionalAccess', _304 => _304[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
4859
  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
4860
  return l;
4278
4861
  }
@@ -4311,8 +4894,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4311
4894
  const was = this.doc.paperEffect;
4312
4895
  const paperEffect = {
4313
4896
  ...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")),
4897
+ opacity: _nullishCoalesce(_nullishCoalesce(options.opacity, () => ( _optionalChain([was, 'optionalAccess', _305 => _305.opacity]))), () => ( 1)),
4898
+ blend: _nullishCoalesce(_nullishCoalesce(options.blend, () => ( _optionalChain([was, 'optionalAccess', _306 => _306.blend]))), () => ( "multiply")),
4316
4899
  image: { width: pixels.width, height: pixels.height, data: new Uint8ClampedArray(pixels.data) },
4317
4900
  version: nextPaperEffectVersion()
4318
4901
  };
@@ -4365,8 +4948,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4365
4948
  const above = _nullishCoalesce(options.aboveId, () => ( this.doc.activeLayerId));
4366
4949
  const loc = above ? _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, above) : null;
4367
4950
  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);
4951
+ const layer = { ...createPaintLayer(_optionalChain([options, 'access', _307 => _307.name, 'optionalAccess', _308 => _308.trim, 'call', _309 => _309()]) || d.name), surface: _chunk474IQG74cjs.surfaceFromPixels.call(void 0, pixels, 4), opacity: _nullishCoalesce(options.opacity, () => ( 1)), blend: _nullishCoalesce(options.blend, () => ( "normal")) };
4952
+ const layers = _chunkHB4ICNNAcjs.insertLayer.call(void 0, this.doc.layers, layer, _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _310 => _310.parent, 'optionalAccess', _311 => _311.id]), () => ( null)), loc ? loc.index + 1 : void 0);
4370
4953
  const source = this.sourceOf(d, values, options.time);
4371
4954
  this.commit("Add effect layer", { ...this.doc, layers, activeLayerId: layer.id, effectLayers: { ..._nullishCoalesce(this.doc.effectLayers, () => ( {})), [layer.id]: source } });
4372
4955
  return { layer: this.describeLayer(layer), source };
@@ -4444,7 +5027,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4444
5027
  this.checkTime(source.time);
4445
5028
  let map;
4446
5029
  try {
4447
- map = _chunk5BR7LC24cjs.readEffectStrokeMap.call(void 0, source.map);
5030
+ map = _chunk4T47Z4J3cjs.readEffectStrokeMap.call(void 0, source.map);
4448
5031
  } catch (e) {
4449
5032
  throw new PaintEditorError(e.message);
4450
5033
  }
@@ -4475,9 +5058,9 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4475
5058
  }
4476
5059
  /** The rendered pixels of an effect paint at the current canvas size, or `null` when it has not been rendered. */
4477
5060
  effectPaintImage(p) {
4478
- const key = _chunk5BR7LC24cjs.effectPaintKey.call(void 0, p);
5061
+ const key = _chunk4T47Z4J3cjs.effectPaintKey.call(void 0, p);
4479
5062
  const img = this.effectPaintPixels.get(key);
4480
- const size = _chunk5BR7LC24cjs.effectPaintSize.call(void 0, p, this.doc.canvas);
5063
+ const size = _chunk4T47Z4J3cjs.effectPaintSize.call(void 0, p, this.doc.canvas);
4481
5064
  if (!img || img.width !== size.width || img.height !== size.height) return null;
4482
5065
  this.effectPaintPixels.delete(key);
4483
5066
  this.effectPaintPixels.set(key, img);
@@ -4491,7 +5074,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4491
5074
  const what = `the effect "${p.effect}"${p.provider ? ` from provider "${p.provider}"` : ""}`;
4492
5075
  if (!provider) throw new PaintEditorError(`Effect paint needs ${what}, but no effects provider is registered; register it (setEffectsProvider) and try again.`);
4493
5076
  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);
5077
+ const { width, height } = _chunk4T47Z4J3cjs.effectPaintSize.call(void 0, p, this.doc.canvas);
4495
5078
  let pixels;
4496
5079
  try {
4497
5080
  pixels = await this.effectsRenderer.render(provider, p.effect, { width, height }, { time: p.time, values: p.values });
@@ -4499,7 +5082,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4499
5082
  throw new PaintEditorError(`Effect paint could not render ${what}: ${e instanceof Error ? e.message : String(e)}`);
4500
5083
  }
4501
5084
  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);
5085
+ this.effectPaintPixels.set(_chunk4T47Z4J3cjs.effectPaintKey.call(void 0, p), img);
4503
5086
  while (this.effectPaintPixels.size > _PaintEditorController.EFFECT_PAINT_CACHE) this.effectPaintPixels.delete(this.effectPaintPixels.keys().next().value);
4504
5087
  return img;
4505
5088
  }
@@ -4510,7 +5093,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4510
5093
  async prepareEffectPaints(paints) {
4511
5094
  const seen = /* @__PURE__ */ new Set();
4512
5095
  for (const p of paints) {
4513
- const key = _chunk5BR7LC24cjs.effectPaintKey.call(void 0, p);
5096
+ const key = _chunk4T47Z4J3cjs.effectPaintKey.call(void 0, p);
4514
5097
  if (seen.has(key)) continue;
4515
5098
  seen.add(key);
4516
5099
  await this.renderEffectPaint(p);
@@ -4519,7 +5102,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4519
5102
  /** Renders the effect paints of recorded strokes (default all of them) before a replay. */
4520
5103
  async prepareStrokeEffects(strokeIds) {
4521
5104
  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);
5105
+ const paints = this.doc.strokes.filter((s) => (!ids || ids.has(s.id)) && _optionalChain([s, 'access', _312 => _312.paint, 'optionalAccess', _313 => _313.type]) === "effect").map((s) => s.paint);
4523
5106
  await this.prepareEffectPaints(paints);
4524
5107
  }
4525
5108
  /** A small preview of the effect paint (a PNG data URL `size` px wide), or `null` without a rendered one. */
@@ -4548,11 +5131,11 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4548
5131
  * its pixels are not rendered.
4549
5132
  */
4550
5133
  strokeEffect(init, brush) {
4551
- if (!_chunk5BR7LC24cjs.brushTakesPaint.call(void 0, brush)) return null;
5134
+ if (!_chunk4T47Z4J3cjs.brushTakesPaint.call(void 0, brush)) return null;
4552
5135
  let source = null;
4553
5136
  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);
5137
+ if (!_chunk4T47Z4J3cjs.isPaintEffectPaint.call(void 0, init.paint)) return null;
5138
+ source = _chunk4T47Z4J3cjs.readPaintEffectPaint.call(void 0, init.paint);
4556
5139
  if (!source) throw new PaintEditorError("The stroke's effect paint needs an effect id and values (getEffectPaint returns one).");
4557
5140
  } else if (init.color === void 0 && !brush.paint) {
4558
5141
  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 +5165,14 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4582
5165
  const before = _chunk474IQG74cjs.readRegion.call(void 0, l.surface, rect);
4583
5166
  const inputs = { source: { width: rect.width, height: rect.height, data: before } };
4584
5167
  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]]);
5168
+ const relief = _optionalChain([this, 'access', _314 => _314.doc, 'access', _315 => _315.thick, 'optionalAccess', _316 => _316[l.id]]);
4586
5169
  if (heightInput && relief) {
4587
5170
  const h = _chunk474IQG74cjs.readRegion.call(void 0, relief.height, rect);
4588
5171
  const rgba = new Uint8ClampedArray(rect.width * rect.height * 4);
4589
5172
  for (let i = 0; i < h.length; i++) rgba.set([h[i], h[i], h[i], 255], i * 4);
4590
5173
  inputs[heightInput.name] = { width: rect.width, height: rect.height, data: rgba };
4591
5174
  }
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"));
5175
+ const sourceInput = _nullishCoalesce(_optionalChain([d, 'access', _317 => _317.inputs, 'access', _318 => _318.find, 'call', _319 => _319((i) => i.kind === "image" && i.name !== _optionalChain([heightInput, 'optionalAccess', _320 => _320.name])), 'optionalAccess', _321 => _321.name]), () => ( "source"));
4593
5176
  if (sourceInput !== "source") {
4594
5177
  inputs[sourceInput] = inputs.source;
4595
5178
  delete inputs.source;
@@ -4602,12 +5185,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4602
5185
  }
4603
5186
  // --- Brushes -------------------------------------------------------------------
4604
5187
  findBrush(id) {
4605
- return _nullishCoalesce(_chunk5BR7LC24cjs.BUILTIN_BRUSHES.find((b) => b.id === id), () => ( this.userBrushes.find((b) => b.id === id)));
5188
+ return _nullishCoalesce(_chunk4T47Z4J3cjs.BUILTIN_BRUSHES.find((b) => b.id === id), () => ( this.userBrushes.find((b) => b.id === id)));
4606
5189
  }
4607
5190
  /** A library brush by id (`undefined` when unknown). */
4608
5191
  getBrush(id) {
4609
5192
  const b = this.findBrush(id);
4610
- return b ? _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, b, b) : void 0;
5193
+ return b ? _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, b, b) : void 0;
4611
5194
  }
4612
5195
  requireBrush(id) {
4613
5196
  const b = this.findBrush(id);
@@ -4622,7 +5205,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4622
5205
  const { id, ...changes } = update;
4623
5206
  const base = id !== void 0 ? this.requireBrush(id) : this.brush;
4624
5207
  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);
5208
+ const next = _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, { ...changes, id: base.id }, base);
4626
5209
  if (base.builtin && !Object.keys(changes).length) next.builtin = true;
4627
5210
  else delete next.builtin;
4628
5211
  this.brush = next;
@@ -4631,10 +5214,10 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4631
5214
  }
4632
5215
  /** Saves the active brush (or `brush`) into the user library under `id` / `name`. */
4633
5216
  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)) });
5217
+ if (!_chunk4T47Z4J3cjs.BRUSH_ID.test(options.id)) throw new PaintEditorError(`Brush id "${options.id}" is invalid; use lowercase letters, digits and dashes (e.g. "my-pencil").`);
5218
+ if (_chunk4T47Z4J3cjs.BUILTIN_BRUSHES.some((b) => b.id === options.id)) throw new PaintEditorError(`"${options.id}" is a built-in brush; save under another id.`);
5219
+ const source = options.brush ? _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, options.brush, this.brush) : this.brush;
5220
+ const saved = _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, { ...source, id: options.id, name: _nullishCoalesce(options.name, () => ( source.name)) });
4638
5221
  delete saved.builtin;
4639
5222
  const i = this.userBrushes.findIndex((b) => b.id === options.id);
4640
5223
  if (i >= 0) this.userBrushes[i] = saved;
@@ -4644,7 +5227,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4644
5227
  return saved;
4645
5228
  }
4646
5229
  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.`);
5230
+ if (_chunk4T47Z4J3cjs.BUILTIN_BRUSHES.some((b) => b.id === id)) throw new PaintEditorError(`"${id}" is a built-in brush and cannot be removed.`);
4648
5231
  const i = this.userBrushes.findIndex((b) => b.id === id);
4649
5232
  if (i < 0) throw new PaintEditorError(`Unknown brush "${id}". Call list_brushes for the ids.`);
4650
5233
  this.userBrushes.splice(i, 1);
@@ -4653,12 +5236,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4653
5236
  }
4654
5237
  /** The user's saved brushes. */
4655
5238
  getUserBrushes() {
4656
- return this.userBrushes.map((b) => _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, b, b));
5239
+ return this.userBrushes.map((b) => _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, b, b));
4657
5240
  }
4658
5241
  /** A brush library file of the given brushes (default: the user's brushes). */
4659
5242
  exportBrushes(ids) {
4660
5243
  const list = ids ? ids.map((id) => this.requireBrush(id)) : this.userBrushes;
4661
- return { format: "pixerate-brushes", version: 1, brushes: list.map(_chunk5BR7LC24cjs.brushToJSON) };
5244
+ return { format: "pixerate-brushes", version: 1, brushes: list.map(_chunk4T47Z4J3cjs.brushToJSON) };
4662
5245
  }
4663
5246
  /**
4664
5247
  * Adds the brushes of a library file to the user library. A brush whose id is taken is skipped
@@ -4668,14 +5251,14 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4668
5251
  const problems = [];
4669
5252
  let list;
4670
5253
  try {
4671
- list = _chunk5BR7LC24cjs.parseBrushLibrary.call(void 0, file, problems);
5254
+ list = _chunk4T47Z4J3cjs.parseBrushLibrary.call(void 0, file, problems);
4672
5255
  } catch (e) {
4673
5256
  throw new PaintEditorError(e.message);
4674
5257
  }
4675
5258
  const added = [];
4676
5259
  const skipped = [];
4677
5260
  for (const b of list) {
4678
- if (_chunk5BR7LC24cjs.BUILTIN_BRUSHES.some((x) => x.id === b.id)) {
5261
+ if (_chunk4T47Z4J3cjs.BUILTIN_BRUSHES.some((x) => x.id === b.id)) {
4679
5262
  skipped.push(b.id);
4680
5263
  problems.push(`"${b.id}" is a built-in brush id; rename it to import it.`);
4681
5264
  continue;
@@ -4740,10 +5323,10 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4740
5323
  * brush's gradient, else the editor's gradient paint. Erasers, blenders and smudges take none.
4741
5324
  */
4742
5325
  strokeGradient(init, brush, color) {
4743
- if (!_chunk5BR7LC24cjs.brushTakesPaint.call(void 0, brush)) return null;
5326
+ if (!_chunk4T47Z4J3cjs.brushTakesPaint.call(void 0, brush)) return null;
4744
5327
  let g = null;
4745
5328
  if (init.paint !== void 0) {
4746
- if (_chunk5BR7LC24cjs.isPaintEffectPaint.call(void 0, init.paint)) return null;
5329
+ if (_chunk4T47Z4J3cjs.isPaintEffectPaint.call(void 0, init.paint)) return null;
4747
5330
  const p = this.readStrokePaint(init.paint);
4748
5331
  g = p.type === "gradient" ? p : null;
4749
5332
  } else g = _nullishCoalesce(brush.paint, () => ( (init.color === void 0 ? this.gradient : null)));
@@ -4787,7 +5370,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4787
5370
  // --- Mixer Brush ---------------------------------------------------------------
4788
5371
  /** Fills the Mixer Brush reservoir with a colour (default the editor colour). */
4789
5372
  loadMixerBrush(color) {
4790
- const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, color === void 0 ? this.color : parseColor(color));
5373
+ const [r, g, b] = _chunk4T47Z4J3cjs.hexToRgb8.call(void 0, color === void 0 ? this.color : parseColor(color));
4791
5374
  this.mixerLoad = _chunkPQ3H2AANcjs.loadMixer.call(void 0, this.mixerLoad, [r / 255, g / 255, b / 255]);
4792
5375
  this.emit([]);
4793
5376
  return this.mixerLoad;
@@ -4817,7 +5400,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4817
5400
  // --- Strokes -------------------------------------------------------------------
4818
5401
  strokeBrush(init) {
4819
5402
  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);
5403
+ if (init.brush) brush = _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, { ...init.brush, id: brush.id }, brush);
4821
5404
  return brush;
4822
5405
  }
4823
5406
  /**
@@ -4833,7 +5416,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4833
5416
  return symmetryCopies(this.guides.symmetry);
4834
5417
  }
4835
5418
  sessionFor(doc, init, host) {
4836
- if (init.paint !== void 0 && !_chunk5BR7LC24cjs.isPaintEffectPaint.call(void 0, init.paint)) {
5419
+ if (init.paint !== void 0 && !_chunk4T47Z4J3cjs.isPaintEffectPaint.call(void 0, init.paint)) {
4837
5420
  const p = this.readStrokePaint(init.paint);
4838
5421
  if (p.type === "color") init = { ...init, color: p.color, paint: void 0 };
4839
5422
  else init = { ...init, paint: p };
@@ -4851,25 +5434,28 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4851
5434
  })();
4852
5435
  const brush = this.strokeBrush(init);
4853
5436
  const effect = this.strokeEffect(init, brush);
4854
- if (_optionalChain([doc, 'access', _262 => _262.liveStrokes, 'optionalAccess', _263 => _263[layer.id]])) {
5437
+ if (brush.light.amount > 0) {
5438
+ 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).`);
5439
+ }
5440
+ if (_optionalChain([doc, 'access', _322 => _322.liveStrokes, 'optionalAccess', _323 => _323[layer.id]])) {
4855
5441
  const why = liveStrokeRefusal(brush);
4856
5442
  if (why) throw new PaintEditorError(`Layer "${layer.name}" is a stroke layer. ${why}`);
4857
5443
  }
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);
5444
+ if (_optionalChain([doc, 'access', _324 => _324.liveStrokes, 'optionalAccess', _325 => _325[layer.id]]) && _optionalChain([effect, 'optionalAccess', _326 => _326.source, 'access', _327 => _327.live])) {
5445
+ const key = _chunk4T47Z4J3cjs.effectPaintKey.call(void 0, effect.source);
4860
5446
  const { sources } = this.liveEffects(doc);
4861
- if (!sources.some((p) => _chunk5BR7LC24cjs.effectPaintKey.call(void 0, p) === key) && sources.length >= MAX_LIVE_EFFECT_SOURCES) {
5447
+ if (!sources.some((p) => _chunk4T47Z4J3cjs.effectPaintKey.call(void 0, p) === key) && sources.length >= MAX_LIVE_EFFECT_SOURCES) {
4862
5448
  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
5449
  }
4864
5450
  }
4865
- const stickerLayer = Boolean(_optionalChain([doc, 'access', _268 => _268.stickers, 'optionalAccess', _269 => _269[layer.id], 'optionalAccess', _270 => _270.length]));
5451
+ const stickerLayer = Boolean(_optionalChain([doc, 'access', _328 => _328.stickers, 'optionalAccess', _329 => _329[layer.id], 'optionalAccess', _330 => _330.length]));
4866
5452
  if (brush.engine === "sticker") {
4867
5453
  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
5454
  } else if (stickerLayer) {
4869
5455
  throw new PaintEditorError(`Layer "${layer.name}" is a sticker layer (its pixels are drawn from its stickers); paint on another layer (add_layer).`);
4870
5456
  }
4871
5457
  let wet;
4872
- if (_chunk5BR7LC24cjs.isWetBrush.call(void 0, brush)) {
5458
+ if (_chunk4T47Z4J3cjs.isWetBrush.call(void 0, brush)) {
4873
5459
  this.requireWetIdle("painting");
4874
5460
  if (layer.offset.x || layer.offset.y) {
4875
5461
  const moved = { ...layer, surface: _chunk474IQG74cjs.translateSurface.call(void 0, layer.surface, layer.offset.x, layer.offset.y), offset: { x: 0, y: 0 } };
@@ -4877,14 +5463,14 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4877
5463
  if (doc !== this.doc) doc = { ...doc, layers: _chunkHB4ICNNAcjs.mapLayer.call(void 0, doc.layers, layer.id, () => moved) };
4878
5464
  layer = moved;
4879
5465
  }
4880
- if (!this.wet.wetLayerIds().includes(layer.id) && this.wet.wetLayerIds().length >= _chunkXFYLA3ZOcjs.MAX_WET_LANES) {
4881
- throw new PaintEditorError(`At most ${_chunkXFYLA3ZOcjs.MAX_WET_LANES} layers can hold wet paint at once; dry one first (dry_paint).`);
5466
+ if (!this.wet.wetLayerIds().includes(layer.id) && this.wet.wetLayerIds().length >= _chunkVMROIWLLcjs.MAX_WET_LANES) {
5467
+ throw new PaintEditorError(`At most ${_chunkVMROIWLLcjs.MAX_WET_LANES} layers can hold wet paint at once; dry one first (dry_paint).`);
4882
5468
  }
4883
5469
  this.beginWetOp();
4884
5470
  const world = this.wet.ensureLane(layer.id, brush.wet.medium);
4885
5471
  const id = layer.id;
4886
5472
  const wetColor = init.color === void 0 ? this.color : parseColor(init.color);
4887
- const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, wetColor);
5473
+ const [r, g, b] = _chunk4T47Z4J3cjs.hexToRgb8.call(void 0, wetColor);
4888
5474
  const wetGradient = this.strokeGradient(init, brush, wetColor);
4889
5475
  const load = _nullishCoalesce(init.load, () => ( brushLoadForStroke(brush, this.mixerLoad, [r / 255, g / 255, b / 255])));
4890
5476
  const seedW = _nullishCoalesce(init.seed, () => ( _chunkPQ3H2AANcjs.mix32.call(void 0, this.seedCounter * 2654435761 >>> 0) & 2147483647));
@@ -4897,12 +5483,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4897
5483
  load,
4898
5484
  grain: paperGrain(doc.paper),
4899
5485
  paperDepth: doc.paper.depth,
4900
- paperColor: _chunk5BR7LC24cjs.hexToRgb8.call(void 0, doc.paper.color),
5486
+ paperColor: _chunk4T47Z4J3cjs.hexToRgb8.call(void 0, doc.paper.color),
4901
5487
  seed: seedW,
4902
5488
  copies: [IDENTITY_AFFINE, ...copiesW].map(affineCopy),
4903
5489
  mask: init.maskStencils ? this.stencilSelection(init.maskStencils) : init.stencils !== false ? this.stencilSelection() : null,
4904
5490
  paint: effect ? null : wetGradient,
4905
- effect: _nullishCoalesce(_optionalChain([effect, 'optionalAccess', _271 => _271.pixels]), () => ( null))
5491
+ effect: _nullishCoalesce(_optionalChain([effect, 'optionalAccess', _331 => _331.pixels]), () => ( null))
4906
5492
  });
4907
5493
  init = { ...init, seed: seedW, load };
4908
5494
  wet = { driver, surface: () => world.display(id).surface };
@@ -4913,7 +5499,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4913
5499
  }
4914
5500
  let color = init.color === void 0 ? this.color : parseColor(init.color);
4915
5501
  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));
5502
+ if (init.color === void 0 && init.start && _optionalChain([tracing, 'optionalAccess', _332 => _332.visible]) && tracing.sampleColors) color = _nullishCoalesce(this.sampleTracingColor(init.start.x, init.start.y), () => ( color));
4917
5503
  const perspective = this.guides.perspective;
4918
5504
  const snapper = init.snapToPerspective && perspective.enabled && perspective.snap ? new PerspectiveSnapper(perspective) : null;
4919
5505
  const useStencils = init.stencils !== false;
@@ -4946,7 +5532,9 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4946
5532
  mixerLoad: _nullishCoalesce(init.load, () => ( brushLoadForStroke(brush, this.mixerLoad, [rgba[0] / 255, rgba[1] / 255, rgba[2] / 255]))),
4947
5533
  secondary: rgbaOf(this.secondaryColor),
4948
5534
  wet,
4949
- thick: brush.engine === "impasto" ? { channels: _nullishCoalesce(_optionalChain([doc, 'access', _273 => _273.thick, 'optionalAccess', _274 => _274[layer.id]]), () => ( emptyThickChannels())) } : void 0,
5535
+ thick: brush.engine === "impasto" ? { channels: _nullishCoalesce(_optionalChain([doc, 'access', _333 => _333.thick, 'optionalAccess', _334 => _334[layer.id]]), () => ( emptyThickChannels())) } : void 0,
5536
+ // Light paint: a twin of the stroke paints the light channel (laying, dimming, moving light).
5537
+ light: !wet && brush.engine !== "sticker" && (brush.light.amount > 0 || _optionalChain([doc, 'access', _335 => _335.light, 'optionalAccess', _336 => _336[layer.id]])) ? { surface: _nullishCoalesce(_optionalChain([doc, 'access', _337 => _337.light, 'optionalAccess', _338 => _338[layer.id]]), () => ( emptyLight())) } : null,
4950
5538
  sticker: brush.engine === "sticker" ? { nextId: this.nextStickerNumber(doc) } : void 0,
4951
5539
  paint: effect ? null : this.strokeGradient(init, brush, color),
4952
5540
  effect
@@ -4960,7 +5548,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4960
5548
  const stroke = {
4961
5549
  id: `s${this.strokeCounter++}`,
4962
5550
  layerId: session.layerId,
4963
- brush: _chunk5BR7LC24cjs.brushToJSON.call(void 0, init.brush),
5551
+ brush: _chunk4T47Z4J3cjs.brushToJSON.call(void 0, init.brush),
4964
5552
  color: init.colorHex,
4965
5553
  points: session.points.map((p) => ({ ...p })),
4966
5554
  seed: init.seed
@@ -4985,12 +5573,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
4985
5573
  const before = this.doc;
4986
5574
  const host = {
4987
5575
  update: (layerId, surface, rects) => {
4988
- if (_optionalChain([this, 'access', _275 => _275.session, 'optionalAccess', _276 => _276.init, 'access', _277 => _277.wet])) {
5576
+ if (_optionalChain([this, 'access', _339 => _339.session, 'optionalAccess', _340 => _340.init, 'access', _341 => _341.wet])) {
4989
5577
  this.wet.syncDisplays();
4990
5578
  return;
4991
5579
  }
4992
5580
  this.live = { layerId, surface };
4993
- const ch = _optionalChain([this, 'access', _278 => _278.session, 'optionalAccess', _279 => _279.thickChannels, 'call', _280 => _280()]);
5581
+ const ch = _optionalChain([this, 'access', _342 => _342.session, 'optionalAccess', _343 => _343.thickChannels, 'call', _344 => _344()]);
4994
5582
  this.liveThick = ch ? { layerId, channels: ch } : null;
4995
5583
  this.emit(rects);
4996
5584
  },
@@ -5002,7 +5590,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5002
5590
  const wetStroke = Boolean(s.init.wet);
5003
5591
  if (wetStroke) this.wet.driver = null;
5004
5592
  const brush = s.init.brush;
5005
- if (!cancelled && _optionalChain([s, 'access', _281 => _281.result, 'optionalAccess', _282 => _282.mixerLoad])) this.keepLoad(brush, s.result.mixerLoad);
5593
+ if (!cancelled && _optionalChain([s, 'access', _345 => _345.result, 'optionalAccess', _346 => _346.mixerLoad])) this.keepLoad(brush, s.result.mixerLoad);
5006
5594
  if (cancelled || !surface) {
5007
5595
  if (this.wetOp) {
5008
5596
  const snap = this.wetOp.before;
@@ -5017,7 +5605,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5017
5605
  }
5018
5606
  const stroke = this.record(s);
5019
5607
  s.result.strokeId = stroke.id;
5020
- this.paintedBounds.set(stroke, _nullishCoalesce(_optionalChain([s, 'access', _283 => _283.result, 'optionalAccess', _284 => _284.bounds]), () => ( null)));
5608
+ this.paintedBounds.set(stroke, _nullishCoalesce(_optionalChain([s, 'access', _347 => _347.result, 'optionalAccess', _348 => _348.bounds]), () => ( null)));
5021
5609
  if (wetStroke && this.wet.instantDry) {
5022
5610
  this.wet.dry(s.layerId);
5023
5611
  this.logWet({ kind: "dry", layerId: s.layerId, at: this.doc.strokes.length + 1 });
@@ -5028,8 +5616,10 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5028
5616
  const ch = s.thickChannels();
5029
5617
  const thick = ch ? { ..._nullishCoalesce(this.doc.thick, () => ( {})), [l.id]: ch } : this.doc.thick;
5030
5618
  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);
5619
+ const stickers = sprayed ? { ..._nullishCoalesce(this.doc.stickers, () => ( {})), [l.id]: [..._nullishCoalesce(_optionalChain([this, 'access', _349 => _349.doc, 'access', _350 => _350.stickers, 'optionalAccess', _351 => _351[l.id]]), () => ( [])), ...sprayed] } : this.doc.stickers;
5620
+ const wetLight = wetStroke && _optionalChain([this, 'access', _352 => _352.wet, 'access', _353 => _353.world, 'optionalAccess', _354 => _354.hasLane, 'call', _355 => _355(l.id)]) ? this.wet.world.lightDisplay(l.id) : null;
5621
+ const light = withLayerLight(this.doc.light, l.id, _nullishCoalesce(s.lightChannel(), () => ( wetLight)));
5622
+ 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
5623
  }
5034
5624
  };
5035
5625
  let session;
@@ -5057,17 +5647,19 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5057
5647
  if (cancelled || !surface) return;
5058
5648
  const stroke = this.record(s);
5059
5649
  s.result.strokeId = stroke.id;
5060
- this.paintedBounds.set(stroke, _nullishCoalesce(_optionalChain([s, 'access', _288 => _288.result, 'optionalAccess', _289 => _289.bounds]), () => ( null)));
5650
+ this.paintedBounds.set(stroke, _nullishCoalesce(_optionalChain([s, 'access', _356 => _356.result, 'optionalAccess', _357 => _357.bounds]), () => ( null)));
5061
5651
  const l = _chunkHB4ICNNAcjs.findLayer.call(void 0, out.layers, s.layerId).layer;
5062
5652
  const offset = s.init.wet ? { x: 0, y: 0 } : l.offset;
5063
5653
  const ch = s.thickChannels();
5064
5654
  const sprayed = s.stickers();
5655
+ const lit = _nullishCoalesce(s.lightChannel(), () => ( (s.init.wet && _optionalChain([this, 'access', _358 => _358.wet, 'access', _359 => _359.world, 'optionalAccess', _360 => _360.hasLane, 'call', _361 => _361(l.id)]) ? this.wet.world.lightDisplay(l.id) : null)));
5065
5656
  out = withLiveStroke({
5066
5657
  ...out,
5067
5658
  layers: _chunkHB4ICNNAcjs.mapLayer.call(void 0, out.layers, l.id, () => ({ ...l, surface, offset })),
5068
5659
  strokes: [...out.strokes, stroke],
5069
5660
  ...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] } } : {}
5661
+ ...sprayed ? { stickers: { ..._nullishCoalesce(out.stickers, () => ( {})), [l.id]: [..._nullishCoalesce(_optionalChain([out, 'access', _362 => _362.stickers, 'optionalAccess', _363 => _363[l.id]]), () => ( [])), ...sprayed] } } : {},
5662
+ ...lit ? { light: withLayerLight(out.light, l.id, lit) } : {}
5071
5663
  }, stroke);
5072
5664
  }
5073
5665
  };
@@ -5125,7 +5717,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5125
5717
  */
5126
5718
  replayStrokes(options) {
5127
5719
  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.");
5720
+ if (!_optionalChain([options, 'access', _364 => _364.strokeIds, 'optionalAccess', _365 => _365.length])) throw new PaintEditorError("strokeIds is empty; pass ids from list_strokes.");
5129
5721
  const strokes = options.strokeIds.map((id) => {
5130
5722
  const s = this.doc.strokes.find((x) => x.id === id);
5131
5723
  if (!s) throw new PaintEditorError(`Unknown stroke "${id}". Call list_strokes for the ids.`);
@@ -5139,7 +5731,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5139
5731
  if (options.layerId !== void 0) this.paintTarget(options.layerId, "replay onto");
5140
5732
  if (options.color === void 0) {
5141
5733
  for (const s of strokes) {
5142
- if (_optionalChain([s, 'access', _294 => _294.paint, 'optionalAccess', _295 => _295.type]) === "effect" && !this.effectPaintImage(s.paint)) {
5734
+ if (_optionalChain([s, 'access', _366 => _366.paint, 'optionalAccess', _367 => _367.type]) === "effect" && !this.effectPaintImage(s.paint)) {
5143
5735
  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
5736
  }
5145
5737
  }
@@ -5162,13 +5754,13 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5162
5754
  let brush = v.brush;
5163
5755
  if (options.brushId !== void 0) {
5164
5756
  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);
5757
+ brush = _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, { tip: { ...b.tip, size: b.tip.size * (v.brush.tip.size / s.brush.tip.size) } }, b);
5166
5758
  }
5167
5759
  const layerId = _nullishCoalesce(options.layerId, () => ( s.layerId));
5168
5760
  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
5761
  let load = s.load;
5170
5762
  if (load && options.color !== void 0 && load.reservoir) {
5171
- const [r, g, b] = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, options.color);
5763
+ const [r, g, b] = _chunk4T47Z4J3cjs.hexToRgb8.call(void 0, options.color);
5172
5764
  load = { ...load, reservoir: [r / 255, g / 255, b / 255] };
5173
5765
  }
5174
5766
  const paint = v.paint && options.color === void 0 ? { paint: v.paint } : {};
@@ -5182,6 +5774,46 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5182
5774
  if (doc !== this.doc) this.commit(strokes.length === 1 && repeat === 1 ? "Replay stroke" : "Replay strokes", doc);
5183
5775
  return { strokeIds: ids, bounds, layerIds: [...layerIds] };
5184
5776
  }
5777
+ // --- Light paint --------------------------------------------------------------------
5778
+ /** The document's `light` without these layers (as a spread: `{}` when unchanged). */
5779
+ lightWithout(ids) {
5780
+ const t = this.doc.light;
5781
+ if (!t || !ids.some((id) => t[id])) return {};
5782
+ const out = { ...t };
5783
+ for (const id of ids) delete out[id];
5784
+ return { light: out };
5785
+ }
5786
+ lightSpillOf(layerId) {
5787
+ return _nullishCoalesce(this.lightSpills[layerId], () => ( DEFAULT_LIGHT_SPILL));
5788
+ }
5789
+ /** A layer's light paint: whether it has any, where (document pixels), its brightest value 0..255, and its spill. */
5790
+ lightOf(layerId) {
5791
+ const l = this.wetLayer(layerId, "read the light of");
5792
+ const info = lightInfo(_optionalChain([this, 'access', _368 => _368.doc, 'access', _369 => _369.light, 'optionalAccess', _370 => _370[l.id]]));
5793
+ const bounds = info.bounds ? { ...info.bounds, x: info.bounds.x + l.offset.x, y: info.bounds.y + l.offset.y } : null;
5794
+ return { layerId: l.id, hasLight: info.peak > 0, bounds, peak: info.peak, spill: { ...this.lightSpillOf(l.id) } };
5795
+ }
5796
+ /** 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. */
5797
+ setLightSpill(layerId, update) {
5798
+ const l = this.wetLayer(layerId, "set the light spill of");
5799
+ try {
5800
+ this.lightSpills[l.id] = normalizeLightSpill(update, this.lightSpillOf(l.id));
5801
+ } catch (e) {
5802
+ throw new PaintEditorError(e.message);
5803
+ }
5804
+ this.emit("all");
5805
+ return { ...this.lightSpills[l.id] };
5806
+ }
5807
+ /** Takes a layer's light away; its paint keeps its colour. One undo step. Returns whether there was any. */
5808
+ clearLight(layerId) {
5809
+ this.requireIdle("clearing light");
5810
+ const l = this.paintTarget(layerId, "clear the light of");
5811
+ if (this.wet.bakedLight(l.id)) this.dryLayersFirst([l.id]);
5812
+ const t = this.lightWithout([l.id]);
5813
+ if (!t.light) return { layerId: l.id, cleared: false };
5814
+ this.commit("Clear light", { ...this.doc, light: t.light });
5815
+ return { layerId: l.id, cleared: true };
5816
+ }
5185
5817
  // --- Thick paint and light -------------------------------------------------------
5186
5818
  /** The document's `thick` without these layers (as a spread: `{}` when unchanged). */
5187
5819
  thickWithout(ids) {
@@ -5208,7 +5840,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5208
5840
  const id = _nullishCoalesce(layerId, () => ( this.doc.activeLayerId));
5209
5841
  if (!id) return null;
5210
5842
  this.requireLayer(id);
5211
- const ch = _optionalChain([this, 'access', _296 => _296.doc, 'access', _297 => _297.thick, 'optionalAccess', _298 => _298[id]]);
5843
+ const ch = _optionalChain([this, 'access', _371 => _371.doc, 'access', _372 => _372.thick, 'optionalAccess', _373 => _373[id]]);
5212
5844
  if (!ch) return null;
5213
5845
  const r = reliefInfo(ch);
5214
5846
  return { layerId: id, bounds: r.bounds, maxHeight: r.maxHeight };
@@ -5316,7 +5948,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5316
5948
  wetState() {
5317
5949
  const st = this.getState().wet;
5318
5950
  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() };
5951
+ return { ...st, layers: this.wet.wetLayerIds().map((id) => this.wet.info(id)), backend, message: _nullishCoalesce(_optionalChain([backend, 'optionalAccess', _374 => _374.message]), () => ( _chunkVMROIWLLcjs.wetSupport.call(void 0, ).message)), engine: this.wet.engineInfo() };
5320
5952
  }
5321
5953
  /**
5322
5954
  * Engine B, the WebGPU accelerator for large wet areas: whether it may run, its state (`off`,
@@ -5389,7 +6021,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5389
6021
  const amount = _nullishCoalesce(options.amount, () => ( 1));
5390
6022
  if (!(amount > 0 && amount <= 1)) throw new PaintEditorError(`amount ${amount} is out of range; use above 0 up to 1.`);
5391
6023
  const rect = _nullishCoalesce(this.checkRect(options.rect), () => ( { x: 0, y: 0, width: this.doc.canvas.width, height: this.doc.canvas.height }));
5392
- if (!this.wet.wetLayerIds().includes(l.id) && this.wet.wetLayerIds().length >= _chunkXFYLA3ZOcjs.MAX_WET_LANES) throw new PaintEditorError(`At most ${_chunkXFYLA3ZOcjs.MAX_WET_LANES} layers can hold wet paint at once; dry one first (dry_paint).`);
6024
+ if (!this.wet.wetLayerIds().includes(l.id) && this.wet.wetLayerIds().length >= _chunkVMROIWLLcjs.MAX_WET_LANES) throw new PaintEditorError(`At most ${_chunkVMROIWLLcjs.MAX_WET_LANES} layers can hold wet paint at once; dry one first (dry_paint).`);
5393
6025
  const before = this.doc;
5394
6026
  this.beginWetOp();
5395
6027
  const world = this.wet.ensureLane(l.id);
@@ -5401,7 +6033,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5401
6033
  }
5402
6034
  /** Records a settings change as a wet event (not an undo step). */
5403
6035
  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 }] };
6036
+ this.doc = { ...this.doc, wetEvents: [..._nullishCoalesce(this.doc.wetEvents, () => ( [])), { clock: _nullishCoalesce(_optionalChain([this, 'access', _375 => _375.wet, 'access', _376 => _376.world, 'optionalAccess', _377 => _377.clock]), () => ( 0)), at: this.doc.strokes.length, ...e }] };
5405
6037
  }
5406
6038
  /** Changes a layer's wet settings (viscosity, evaporation, granulation…). Editor settings saved with the document, not an undo step. */
5407
6039
  setWetParams(layerId, update) {
@@ -5470,7 +6102,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5470
6102
  if (override) return Array.from(override).reduce((ls, [id, surface]) => _chunkHB4ICNNAcjs.mapLayer.call(void 0, ls, id, (l) => ({ ...l, surface })), this.doc.layers);
5471
6103
  const live = this.live;
5472
6104
  if (!live) return this.animatedView();
5473
- if (_optionalChain([this, 'access', _303 => _303.liveLayers, 'optionalAccess', _304 => _304.key]) === live.surface) return this.liveLayers.layers;
6105
+ if (_optionalChain([this, 'access', _378 => _378.liveLayers, 'optionalAccess', _379 => _379.key]) === live.surface) return this.liveLayers.layers;
5474
6106
  const layers = _chunkHB4ICNNAcjs.mapLayer.call(void 0, this.doc.layers, live.layerId, (l) => ({ ...l, surface: live.surface }));
5475
6107
  this.liveLayers = { key: live.surface, layers };
5476
6108
  return layers;
@@ -5493,7 +6125,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5493
6125
  tracingNode(t) {
5494
6126
  const { width, height } = this.doc.canvas;
5495
6127
  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;
6128
+ if (_optionalChain([this, 'access', _380 => _380.tracingCache, 'optionalAccess', _381 => _381.key]) === key && this.tracingCache.node.opacity === t.opacity) return this.tracingCache.node;
5497
6129
  const src = _nullishCoalesce(this.imagePixels.get(t.src), () => ( this.decodeImage(t.src)));
5498
6130
  const image = resampleInto(src, t.rect, width, height);
5499
6131
  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 +6152,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5520
6152
  let out = layers;
5521
6153
  const ids = /* @__PURE__ */ new Set([...Object.keys(_nullishCoalesce(thick, () => ( {}))), ...live ? [live.layerId] : []]);
5522
6154
  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]);
6155
+ const ch = _optionalChain([live, 'optionalAccess', _382 => _382.layerId]) === id ? live.channels : _optionalChain([thick, 'optionalAccess', _383 => _383[id]]);
6156
+ const l = ch && _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, out, id), 'optionalAccess', _384 => _384.layer]);
5525
6157
  if (!ch || !isPixelLayer(l)) continue;
5526
6158
  let cache = this.litCaches.get(id);
5527
6159
  if (!cache) this.litCaches.set(id, cache = new LitCache());
@@ -5530,11 +6162,65 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5530
6162
  }
5531
6163
  return out;
5532
6164
  }
6165
+ /**
6166
+ * Light paint over the (lit) layers: glow, rim and clarity in each layer's pixels, and its spill
6167
+ * as a Linear Dodge (Add) layer directly above it (from the painting as committed, so a stroke in
6168
+ * progress does not redraw the blur every frame).
6169
+ */
6170
+ lightLayers(layers) {
6171
+ const light = _nullishCoalesce(this.doc.light, () => ( {}));
6172
+ const ids = /* @__PURE__ */ new Set([...Object.keys(light), ...Object.keys(_nullishCoalesce(this.doc.thick, () => ( {})))]);
6173
+ if (!ids.size) return layers;
6174
+ let out = layers;
6175
+ for (const id of ids) {
6176
+ const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, out, id), 'optionalAccess', _385 => _385.layer]);
6177
+ if (!isPixelLayer(l)) continue;
6178
+ let cache = this.lightCaches.get(id);
6179
+ if (!cache) this.lightCaches.set(id, cache = new LightCache());
6180
+ const glowed = cache.glow(l.surface, light[id], _optionalChain([this, 'access', _386 => _386.liveThick, 'optionalAccess', _387 => _387.layerId]) === id ? this.liveThick.channels : _optionalChain([this, 'access', _388 => _388.doc, 'access', _389 => _389.thick, 'optionalAccess', _390 => _390[id]]));
6181
+ if (glowed !== l.surface) out = _chunkHB4ICNNAcjs.mapLayer.call(void 0, out, id, () => ({ ...l, surface: glowed }));
6182
+ const committed = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _391 => _391.layer]);
6183
+ const spill = light[id] && isPixelLayer(committed) ? cache.spill(committed.surface, light[id], this.lightSpillOf(id)) : null;
6184
+ 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 } });
6185
+ }
6186
+ return out;
6187
+ }
6188
+ /** Visible pixel layers bottom first, as the backlight reads them (group opacities multiplied in). */
6189
+ backlitLayers(layers, committed, opacity = 1, out = []) {
6190
+ for (const l of layers) {
6191
+ if (!l.visible) continue;
6192
+ if (l.type === "group") this.backlitLayers(l.children, committed, opacity * l.opacity, out);
6193
+ else if (isPixelLayer(l)) {
6194
+ const thick = !committed && _optionalChain([this, 'access', _392 => _392.liveThick, 'optionalAccess', _393 => _393.layerId]) === l.id ? this.liveThick.channels : _optionalChain([this, 'access', _394 => _394.doc, 'access', _395 => _395.thick, 'optionalAccess', _396 => _396[l.id]]);
6195
+ out.push({ surface: l.surface, offset: l.offset, opacity: opacity * l.opacity, ...thick ? { thick } : {} });
6196
+ }
6197
+ }
6198
+ return out;
6199
+ }
6200
+ /**
6201
+ * Backlit paper: the light that makes it through `base` (the layers as drawn), with the spill of
6202
+ * the light through the committed painting, as a Linear Dodge (Add) layer for the top of the
6203
+ * stack; `null` when the paper has no backlight.
6204
+ */
6205
+ backlightLayer(base) {
6206
+ const paper = this.doc.paper;
6207
+ const amount = backlightOf(paper);
6208
+ if (!(amount > 0)) return null;
6209
+ const { width, height } = this.doc.canvas;
6210
+ const grain = paperGrain(paper);
6211
+ const current = this.backlightView.surface(paper, grain, width, height, this.backlitLayers(base, false), null);
6212
+ const committed = base === this.doc.layers ? current : this.backlightCommitted.surface(paper, grain, width, height, this.backlitLayers(this.doc.layers, true), null);
6213
+ const surface = this.backlightView.layer(current, committed, amount);
6214
+ return { type: "pixel", id: BACKLIGHT_LAYER_ID, name: "Backlight", visible: true, opacity: 1, blend: "linear-dodge", surface, offset: { x: 0, y: 0 } };
6215
+ }
5533
6216
  renderLayers(options, view = false) {
5534
- let layers = this.litLayers((_nullishCoalesce(options.wet, () => ( "display"))) === "dried" ? this.driedLayers() : this.currentLayers());
6217
+ const base = (_nullishCoalesce(options.wet, () => ( "display"))) === "dried" ? this.driedLayers() : this.currentLayers();
6218
+ let layers = this.lightLayers(this.litLayers(base));
6219
+ const backlight = options.paper !== false && !options.layerIds ? this.backlightLayer(base) : null;
6220
+ if (backlight) layers = [...layers, backlight];
5535
6221
  if (options.layerIds) {
5536
6222
  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))));
6223
+ for (const id of options.layerIds) picked.push(_nullishCoalesce(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, layers, id), 'optionalAccess', _397 => _397.layer]), () => ( this.requireLayer(id))));
5538
6224
  layers = picked.map((l) => ({ ...l, visible: true }));
5539
6225
  }
5540
6226
  const under = options.paper === false ? [] : [paperNode(this.doc.paper, this.lighting)];
@@ -5550,7 +6236,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5550
6236
  */
5551
6237
  compositeTiles(rect, onTile, options = {}) {
5552
6238
  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)) });
6239
+ this.viewCompositor.compositeTiles(this.renderLayers({ paper: options.paper }, true), rect, this.sources(), onTile, { dirty, checkpointLayerId: _nullishCoalesce(_optionalChain([this, 'access', _398 => _398.live, 'optionalAccess', _399 => _399.layerId]), () => ( this.doc.activeLayerId)) });
5554
6240
  }
5555
6241
  /** The canvas as canvas-sized RGBA (paper included by default). */
5556
6242
  compositePixels(options = {}) {
@@ -5560,13 +6246,13 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5560
6246
  }
5561
6247
  /** The canvas (or some layers), scaled so the longest side is at most `maxSide`. */
5562
6248
  renderPreview(options = {}) {
5563
- const full = this.compositePixels({ ...options, wet: _nullishCoalesce(options.wet, () => ( "display")) });
6249
+ const full2 = this.compositePixels({ ...options, wet: _nullishCoalesce(options.wet, () => ( "display")) });
5564
6250
  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) };
6251
+ const k = Math.min(1, side / Math.max(full2.width, full2.height));
6252
+ if (k >= 1) return full2;
6253
+ const w = Math.max(1, Math.round(full2.width * k));
6254
+ const h = Math.max(1, Math.round(full2.height * k));
6255
+ return { width: w, height: h, data: downsampleRgba(full2.data, full2.width, full2.height, w, h) };
5570
6256
  }
5571
6257
  // --- Save, load and export -------------------------------------------------------
5572
6258
  /** The document as JSON with inline PNG assets. */
@@ -5587,7 +6273,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5587
6273
  this.wet.reset();
5588
6274
  if (!raw || typeof raw !== "object") return;
5589
6275
  const r = raw;
5590
- this.wet.setTilt(_chunkXFYLA3ZOcjs.normalizeTilt.call(void 0, _nullishCoalesce(r.tilt, () => ( {}))));
6276
+ this.wet.setTilt(_chunkVMROIWLLcjs.normalizeTilt.call(void 0, _nullishCoalesce(r.tilt, () => ( {}))));
5591
6277
  if (r.layers && typeof r.layers === "object") {
5592
6278
  for (const [id, v] of Object.entries(r.layers)) {
5593
6279
  if (!v || typeof v !== "object") {
@@ -5595,7 +6281,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5595
6281
  continue;
5596
6282
  }
5597
6283
  const x = v;
5598
- this.wet.settings.set(id, { params: _chunkXFYLA3ZOcjs.normalizeWetParams.call(void 0, _nullishCoalesce(x.params, () => ( {}))), flow: _chunkXFYLA3ZOcjs.normalizeFlow.call(void 0, _nullishCoalesce(x.flow, () => ( {}))) });
6284
+ this.wet.settings.set(id, { params: _chunkVMROIWLLcjs.normalizeWetParams.call(void 0, _nullishCoalesce(x.params, () => ( {}))), flow: _chunkVMROIWLLcjs.normalizeFlow.call(void 0, _nullishCoalesce(x.flow, () => ( {}))) });
5599
6285
  }
5600
6286
  }
5601
6287
  }
@@ -5618,7 +6304,18 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5618
6304
  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
6305
  if (Object.keys(liveStrokes).length) ext.liveStrokes = liveStrokes;
5620
6306
  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])));
6307
+ const light = {};
6308
+ for (const [id, t] of Object.entries(_nullishCoalesce(this.doc.light, () => ( {})))) {
6309
+ if (!_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id)) continue;
6310
+ const saved = lightToJSON(_nullishCoalesce(this.wet.bakedLight(id), () => ( t)));
6311
+ if (saved) light[id] = saved;
6312
+ }
6313
+ if (Object.keys(light).length) ext.light = light;
6314
+ else delete ext.light;
6315
+ const spills = Object.fromEntries(Object.entries(this.lightSpills).filter(([id]) => _chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id)));
6316
+ if (Object.keys(spills).length) ext.lightSpill = spills;
6317
+ else delete ext.lightSpill;
6318
+ const effectLayers = effectLayersToJSON(_nullishCoalesce(this.doc.effectLayers, () => ( {})), (id) => isPixelLayer(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _400 => _400.layer])));
5622
6319
  if (effectLayers) ext.effectLayers = effectLayers;
5623
6320
  else delete ext.effectLayers;
5624
6321
  if (this.doc.paperEffect) ext.paperEffect = paperEffectToJSON(this.doc.paperEffect);
@@ -5635,7 +6332,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5635
6332
  const stickers = {};
5636
6333
  for (const [id, list] of Object.entries(ext.stickers)) {
5637
6334
  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);
6335
+ const ok = list.filter((x) => x && typeof x.id === "string" && _chunk4T47Z4J3cjs.STAMP_KINDS.includes(x.stamp) && Number.isFinite(x.x) && Number.isFinite(x.y) && x.size > 0);
5639
6336
  if (ok.length !== list.length) problems.push(`Some stickers of layer "${id}" could not be read; they were dropped.`);
5640
6337
  stickers[id] = ok.map((x) => ({ ...x }));
5641
6338
  }
@@ -5644,7 +6341,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5644
6341
  if (ext.liveStrokes && typeof ext.liveStrokes === "object") {
5645
6342
  const liveStrokes = {};
5646
6343
  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;
6344
+ if (!isPixelLayer(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, state.layers, id), 'optionalAccess', _401 => _401.layer])) || !Array.isArray(list)) continue;
5648
6345
  const ok = list.map(normalizeBrushStroke).filter((x) => x !== null).map((x) => ({ ...x, layerId: id }));
5649
6346
  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
6347
  liveStrokes[id] = ok;
@@ -5654,7 +6351,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5654
6351
  if (ext.effectLayers && typeof ext.effectLayers === "object") {
5655
6352
  const effectLayers = {};
5656
6353
  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;
6354
+ if (!isPixelLayer(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, state.layers, id), 'optionalAccess', _402 => _402.layer]))) continue;
5658
6355
  const source = readPaintEffectSource(v);
5659
6356
  if (source) effectLayers[id] = source;
5660
6357
  else problems.push(`The effect of layer "${id}" could not be read; it loads as a plain layer.`);
@@ -5668,6 +6365,29 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5668
6365
  problems.push(`The effect paper could not be read (${e.message}); the plain paper shows.`);
5669
6366
  }
5670
6367
  }
6368
+ this.lightSpills = {};
6369
+ this.lightCaches.clear();
6370
+ if (ext.lightSpill && typeof ext.lightSpill === "object") {
6371
+ for (const [id, v] of Object.entries(ext.lightSpill)) {
6372
+ try {
6373
+ if (_chunkHB4ICNNAcjs.findLayer.call(void 0, state.layers, id)) this.lightSpills[id] = normalizeLightSpill(_nullishCoalesce(v, () => ( {})));
6374
+ } catch (e4) {
6375
+ problems.push(`The light spill of layer "${id}" could not be read; it takes the defaults.`);
6376
+ }
6377
+ }
6378
+ }
6379
+ if (ext.light && typeof ext.light === "object") {
6380
+ const light = {};
6381
+ for (const [id, v] of Object.entries(ext.light)) {
6382
+ if (!isPixelLayer(_optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, state.layers, id), 'optionalAccess', _403 => _403.layer]))) continue;
6383
+ try {
6384
+ light[id] = lightFromJSON(v);
6385
+ } catch (e) {
6386
+ problems.push(`The light paint of layer "${id}" could not be read (${e.message}); it loads without light.`);
6387
+ }
6388
+ }
6389
+ if (Object.keys(light).length) state = { ...state, light };
6390
+ }
5671
6391
  const raw = ext.layerChannels;
5672
6392
  if (!raw || typeof raw !== "object") return state;
5673
6393
  const thick = {};
@@ -5695,7 +6415,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5695
6415
  this.readGuides(loaded.guides, loaded.state, loaded.problems);
5696
6416
  if (Array.isArray(loaded.swatches)) this.swatches = loaded.swatches.map(readSwatch).filter((x) => x !== null);
5697
6417
  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)));
6418
+ const n = Number(_nullishCoalesce(_optionalChain([/^s(\d+)$/, 'access', _404 => _404.exec, 'call', _405 => _405(s.id), 'optionalAccess', _406 => _406[1]]), () => ( 0)));
5699
6419
  if (n >= this.strokeCounter) this.strokeCounter = n + 1;
5700
6420
  }
5701
6421
  this.undoStack = [];
@@ -5703,12 +6423,14 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5703
6423
  this.doc = this.readThick(loaded.state, loaded.extensions, loaded.problems);
5704
6424
  for (const list of Object.values(_nullishCoalesce(this.doc.liveStrokes, () => ( {})))) {
5705
6425
  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)));
6426
+ const n = Number(_nullishCoalesce(_optionalChain([/^s(\d+)$/, 'access', _407 => _407.exec, 'call', _408 => _408(s.id), 'optionalAccess', _409 => _409[1]]), () => ( 0)));
5707
6427
  if (n >= this.strokeCounter) this.strokeCounter = n + 1;
5708
6428
  }
5709
6429
  }
5710
6430
  this.docRevision++;
5711
6431
  delete this.extensions.layerChannels;
6432
+ delete this.extensions.light;
6433
+ delete this.extensions.lightSpill;
5712
6434
  delete this.extensions.lighting;
5713
6435
  delete this.extensions.stickers;
5714
6436
  delete this.extensions.liveStrokes;
@@ -5742,13 +6464,13 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5742
6464
  return _chunkOLNIPTB3cjs.encodePng.call(void 0, img.width, img.height, img.data);
5743
6465
  }
5744
6466
  scaled(options) {
5745
- const full = this.compositePixels(options);
6467
+ const full2 = this.compositePixels(options);
5746
6468
  const s = _nullishCoalesce(options.scale, () => ( 1));
5747
6469
  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) };
6470
+ if (s === 1) return full2;
6471
+ const w = Math.max(1, Math.round(full2.width * s));
6472
+ const h = Math.max(1, Math.round(full2.height * s));
6473
+ return { width: w, height: h, data: downsampleRgba(full2.data, full2.width, full2.height, w, h) };
5752
6474
  }
5753
6475
  /**
5754
6476
  * The canvas as PNG, JPEG or WebP bytes. JPEG has no alpha, so it always includes the paper.
@@ -5771,7 +6493,9 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5771
6493
  * pixels), and the composite as the merged image.
5772
6494
  */
5773
6495
  exportPsd() {
5774
- const dried = this.litLayers(this.driedLayers(this.doc.layers));
6496
+ const driedBase = this.driedLayers(this.doc.layers);
6497
+ const backlight = this.backlightLayer(driedBase);
6498
+ const dried = [...this.lightLayers(this.litLayers(driedBase)), ...backlight ? [backlight] : []];
5775
6499
  const { width, height, dpi } = this.doc.canvas;
5776
6500
  const paperPixels = this.compositePixels({ layerIds: [], paper: true });
5777
6501
  const paperLayer = {
@@ -5794,14 +6518,20 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5794
6518
  return next.type === "group" ? { ...next, children: toStandard(next.children) } : next;
5795
6519
  });
5796
6520
  const layers = toStandard(dried);
5797
- const result = _chunkHW72ABGRcjs.exportPsd.call(void 0, { width, height, layers: [paperLayer, ...layers], merged, resolution: dpi });
6521
+ const result = _chunkM6JORA5Zcjs.exportPsd.call(void 0, { width, height, layers: [paperLayer, ...layers], merged, resolution: dpi });
5798
6522
  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)));
6523
+ const label = _nullishCoalesce(_optionalChain([_chunkOLNIPTB3cjs.BLEND_MODES, 'access', _410 => _410.find, 'call', _411 => _411((m) => m.id === mode), 'optionalAccess', _412 => _412.label]), () => ( mode));
6524
+ const std = _nullishCoalesce(_optionalChain([_chunkOLNIPTB3cjs.BLEND_MODES, 'access', _413 => _413.find, 'call', _414 => _414((m) => m.id === _chunkOLNIPTB3cjs.standardBlendMode.call(void 0, mode)), 'optionalAccess', _415 => _415.label]), () => ( _chunkOLNIPTB3cjs.standardBlendMode.call(void 0, mode)));
5801
6525
  const list = names.map((n) => `"${n}"`).join(", ");
5802
6526
  const one = names.length === 1;
5803
6527
  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
6528
  }
6529
+ if (backlight) {
6530
+ result.warnings.push('Backlit paper is written as pixels: the light that comes through the paint is a "Backlight" Linear Dodge (Add) layer at the top. Opening the PSD again gives a plain pixel layer, not a backlight.');
6531
+ }
6532
+ if (Object.values(_nullishCoalesce(this.doc.light, () => ( {}))).some((l) => l.tiles.size)) {
6533
+ 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.');
6534
+ }
5805
6535
  return result;
5806
6536
  }
5807
6537
  /** `render_preview` as a base64 PNG. */
@@ -5913,7 +6643,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5913
6643
  const { src, pixels } = this.importImage(options, "The reference image");
5914
6644
  const ref2 = {
5915
6645
  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}`,
6646
+ name: _optionalChain([options, 'access', _416 => _416.name, 'optionalAccess', _417 => _417.trim, 'call', _418 => _418()]) || `Reference ${this.guides.references.length + 1}`,
5917
6647
  src,
5918
6648
  width: pixels.width,
5919
6649
  height: pixels.height,
@@ -5992,7 +6722,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
5992
6722
  try {
5993
6723
  const px = this.decodeImage(ref2.src, `Reference "${ref2.name}"`);
5994
6724
  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)));
6725
+ const n = Number(_nullishCoalesce(_optionalChain([/^ref(\d+)$/, 'access', _419 => _419.exec, 'call', _420 => _420(ref2.id), 'optionalAccess', _421 => _421[1]]), () => ( 0)));
5996
6726
  if (n >= this.nextReferenceId) this.nextReferenceId = n + 1;
5997
6727
  } catch (e) {
5998
6728
  problems.push(e.message);
@@ -6003,7 +6733,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6003
6733
  guides.stencils = [];
6004
6734
  r.stencils.forEach((raw2, i) => {
6005
6735
  try {
6006
- const id = typeof _optionalChain([raw2, 'optionalAccess', _334 => _334.id]) === "string" && /^stencil\d+$/.test(raw2.id) ? raw2.id : `stencil${this.nextStencilId}`;
6736
+ const id = typeof _optionalChain([raw2, 'optionalAccess', _422 => _422.id]) === "string" && /^stencil\d+$/.test(raw2.id) ? raw2.id : `stencil${this.nextStencilId}`;
6007
6737
  guides.stencils.push(normalizeStencil(raw2, id));
6008
6738
  const n = Number(id.slice(7));
6009
6739
  if (n >= this.nextStencilId) this.nextStencilId = n + 1;
@@ -6026,12 +6756,12 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6026
6756
  /** The next free sticker number (ids `st<n>` are unique across the document). */
6027
6757
  nextStickerNumber(doc = this.doc) {
6028
6758
  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))));
6759
+ for (const list of Object.values(_nullishCoalesce(doc.stickers, () => ( {})))) for (const st of list) n = Math.max(n, Number(_nullishCoalesce(_optionalChain([/^st(\d+)$/, 'access', _423 => _423.exec, 'call', _424 => _424(st.id), 'optionalAccess', _425 => _425[1]]), () => ( 0))));
6030
6760
  return n + 1;
6031
6761
  }
6032
6762
  stickerLayer(layerId) {
6033
6763
  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]]);
6764
+ const stickers = _optionalChain([this, 'access', _426 => _426.doc, 'access', _427 => _427.stickers, 'optionalAccess', _428 => _428[l.id]]);
6035
6765
  if (!stickers) throw new PaintEditorError(`Layer "${l.name}" has no stickers; spray some with a sticker brush (list_brushes category sticker).`);
6036
6766
  return { layer: l, stickers };
6037
6767
  }
@@ -6040,7 +6770,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6040
6770
  const id = _nullishCoalesce(layerId, () => ( this.doc.activeLayerId));
6041
6771
  if (!id) return [];
6042
6772
  this.requireLayer(id);
6043
- return (_nullishCoalesce(_optionalChain([this, 'access', _341 => _341.doc, 'access', _342 => _342.stickers, 'optionalAccess', _343 => _343[id]]), () => ( []))).map((s) => ({ ...s }));
6773
+ return (_nullishCoalesce(_optionalChain([this, 'access', _429 => _429.doc, 'access', _430 => _430.stickers, 'optionalAccess', _431 => _431[id]]), () => ( []))).map((s) => ({ ...s }));
6044
6774
  }
6045
6775
  /**
6046
6776
  * Replaces a layer's stickers and redraws its pixels from them (one undo step). With `dirty`,
@@ -6056,15 +6786,15 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6056
6786
  }
6057
6787
  redrawStickers(layer, stickers, dirty) {
6058
6788
  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);
6789
+ return partial ? _chunk4T47Z4J3cjs.stickerSurface.call(void 0, stickers, layer.surface, dirty) : _chunk4T47Z4J3cjs.stickerSurface.call(void 0, stickers);
6060
6790
  }
6061
6791
  /** The document from before the sticker preview on screen, or `null` when none is (anything else changed the document since). */
6062
6792
  previewBase() {
6063
- return _optionalChain([this, 'access', _344 => _344.stickerPreview, 'optionalAccess', _345 => _345.shown]) === this.doc ? this.stickerPreview.base : null;
6793
+ return _optionalChain([this, 'access', _432 => _432.stickerPreview, 'optionalAccess', _433 => _433.shown]) === this.doc ? this.stickerPreview.base : null;
6064
6794
  }
6065
6795
  readStickerUpdate(update) {
6066
6796
  try {
6067
- return _chunk5BR7LC24cjs.normalizeStickerUpdate.call(void 0, update);
6797
+ return _chunk4T47Z4J3cjs.normalizeStickerUpdate.call(void 0, update);
6068
6798
  } catch (e) {
6069
6799
  throw new PaintEditorError(e.message);
6070
6800
  }
@@ -6075,7 +6805,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6075
6805
  const id = _nullishCoalesce(layerId, () => ( this.doc.activeLayerId));
6076
6806
  if (!id) return null;
6077
6807
  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);
6808
+ const hit = _chunk4T47Z4J3cjs.stickerAt.call(void 0, _nullishCoalesce(_optionalChain([this, 'access', _434 => _434.doc, 'access', _435 => _435.stickers, 'optionalAccess', _436 => _436[id]]), () => ( [])), x, y);
6079
6809
  return hit ? { ...hit } : null;
6080
6810
  }
6081
6811
  /** Picks a sticker for the panel and canvas (`null` for none). Editor state, not an undo step. */
@@ -6096,7 +6826,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6096
6826
  if (i < 0) throw new PaintEditorError(`Layer "${layer.name}" has no sticker "${id}". Call list_stickers for the ids.`);
6097
6827
  const next = { ...stickers[i], ...this.readStickerUpdate(update) };
6098
6828
  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)];
6829
+ const dirty = [_chunk4T47Z4J3cjs.stickerRect.call(void 0, stickers[i]), _chunk4T47Z4J3cjs.stickerRect.call(void 0, next)];
6100
6830
  const base = _nullishCoalesce(this.previewBase(), () => ( this.doc));
6101
6831
  const surface = this.redrawStickers(layer, list, dirty);
6102
6832
  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 +6849,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6119
6849
  if (i < 0) throw new PaintEditorError(`Layer "${layer.name}" has no sticker "${id}". Call list_stickers for the ids.`);
6120
6850
  const u = this.readStickerUpdate(update);
6121
6851
  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)]);
6852
+ this.commitStickers("Edit sticker", layer, stickers.map((s, j) => j === i ? next : s), [_chunk4T47Z4J3cjs.stickerRect.call(void 0, stickers[i]), _chunk4T47Z4J3cjs.stickerRect.call(void 0, next)]);
6123
6853
  return { ...next };
6124
6854
  }
6125
6855
  /**
@@ -6131,8 +6861,8 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6131
6861
  const layer = this.wetLayer(layerId, "add stickers to");
6132
6862
  if (!input.length) throw new PaintEditorError("Pass at least one sticker (stamp, x, y).");
6133
6863
  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])) {
6864
+ const existing = _optionalChain([this, 'access', _437 => _437.doc, 'access', _438 => _438.stickers, 'optionalAccess', _439 => _439[layer.id]]);
6865
+ if (!_optionalChain([existing, 'optionalAccess', _440 => _440.length])) {
6136
6866
  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
6867
  }
6138
6868
  let n = this.nextStickerNumber();
@@ -6148,7 +6878,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6148
6878
  const id = n++;
6149
6879
  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
6880
  });
6151
- this.commitStickers("Add stickers", layer, [..._nullishCoalesce(existing, () => ( [])), ...added], _optionalChain([existing, 'optionalAccess', _353 => _353.length]) ? added.map(_chunk5BR7LC24cjs.stickerRect) : void 0);
6881
+ this.commitStickers("Add stickers", layer, [..._nullishCoalesce(existing, () => ( [])), ...added], _optionalChain([existing, 'optionalAccess', _441 => _441.length]) ? added.map(_chunk4T47Z4J3cjs.stickerRect) : void 0);
6152
6882
  return added.map((s) => ({ ...s }));
6153
6883
  }
6154
6884
  /** Brings stickers to the top (`front`) or bottom of their layer (one undo step). */
@@ -6158,7 +6888,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6158
6888
  const picked = stickers.filter((s) => ids.includes(s.id));
6159
6889
  const rest = stickers.filter((s) => !ids.includes(s.id));
6160
6890
  const next = to === "front" ? [...rest, ...picked] : [...picked, ...rest];
6161
- this.commitStickers("Arrange stickers", layer, next, picked.map(_chunk5BR7LC24cjs.stickerRect));
6891
+ this.commitStickers("Arrange stickers", layer, next, picked.map(_chunk4T47Z4J3cjs.stickerRect));
6162
6892
  return next.map((s) => s.id);
6163
6893
  }
6164
6894
  /** Deletes stickers (`ids`, or all of them on the layer) (one undo step). Returns the ids removed. */
@@ -6168,7 +6898,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6168
6898
  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
6899
  }
6170
6900
  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);
6901
+ this.commitStickers("Remove stickers", layer, stickers.filter((s) => !removed.includes(s.id)), ids ? stickers.filter((s) => removed.includes(s.id)).map(_chunk4T47Z4J3cjs.stickerRect) : void 0);
6172
6902
  return removed;
6173
6903
  }
6174
6904
  /** Sets a layer's stickers (replays). */
@@ -6187,20 +6917,20 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6187
6917
  }
6188
6918
  liveStrokeLayer(layerId, what) {
6189
6919
  const l = this.wetLayer(layerId, what);
6190
- const strokes = _optionalChain([this, 'access', _354 => _354.doc, 'access', _355 => _355.liveStrokes, 'optionalAccess', _356 => _356[l.id]]);
6920
+ const strokes = _optionalChain([this, 'access', _442 => _442.doc, 'access', _443 => _443.liveStrokes, 'optionalAccess', _444 => _444[l.id]]);
6191
6921
  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
6922
  return { layer: l, strokes };
6193
6923
  }
6194
6924
  /** Whether a layer is a live stroke layer. */
6195
6925
  isLiveStrokeLayer(layerId) {
6196
- return Boolean(_optionalChain([this, 'access', _357 => _357.doc, 'access', _358 => _358.liveStrokes, 'optionalAccess', _359 => _359[layerId]]));
6926
+ return Boolean(_optionalChain([this, 'access', _445 => _445.doc, 'access', _446 => _446.liveStrokes, 'optionalAccess', _447 => _447[layerId]]));
6197
6927
  }
6198
6928
  /** Makes an empty pixel layer a live stroke layer (one undo step). */
6199
6929
  convertToLiveStrokes(layerId) {
6200
6930
  this.requireIdle("converting a layer");
6201
6931
  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"}).`);
6932
+ if (_optionalChain([this, 'access', _448 => _448.doc, 'access', _449 => _449.liveStrokes, 'optionalAccess', _450 => _450[l.id]])) return this.describeLayer(l);
6933
+ if (_optionalChain([this, 'access', _451 => _451.doc, 'access', _452 => _452.stickers, 'optionalAccess', _453 => _453[l.id], 'optionalAccess', _454 => _454.length])) throw new PaintEditorError(`Layer "${l.name}" is a sticker layer; add a stroke layer instead (add_layer {type: "strokes"}).`);
6204
6934
  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
6935
  this.commit("Convert to stroke layer", { ...this.doc, liveStrokes: { ..._nullishCoalesce(this.doc.liveStrokes, () => ( {})), [l.id]: [] } });
6206
6936
  return this.describeLayer(this.getLayer(l.id));
@@ -6213,7 +6943,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6213
6943
  findLiveStroke(id) {
6214
6944
  for (const [layerId, strokes] of Object.entries(_nullishCoalesce(this.doc.liveStrokes, () => ( {})))) {
6215
6945
  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]);
6946
+ const layer = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.doc.layers, layerId), 'optionalAccess', _455 => _455.layer]);
6217
6947
  if (index >= 0 && isPixelLayer(layer)) return { layer, strokes, index };
6218
6948
  }
6219
6949
  const baked = this.doc.strokes.some((x) => x.id === id);
@@ -6233,15 +6963,15 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6233
6963
  let brush = was.brush;
6234
6964
  if (update.brushId !== void 0) {
6235
6965
  const b = this.requireBrush(update.brushId);
6236
- brush = _chunk5BR7LC24cjs.normalizePaintBrush.call(void 0, { tip: { ...b.tip, size: was.brush.tip.size } }, b);
6966
+ brush = _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, { tip: { ...b.tip, size: was.brush.tip.size } }, b);
6237
6967
  }
6238
6968
  if (update.brush !== void 0) {
6239
6969
  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);
6970
+ brush = _chunk4T47Z4J3cjs.normalizePaintBrush.call(void 0, { ...update.brush, id: brush.id }, brush);
6241
6971
  }
6242
6972
  const why = liveStrokeRefusal(brush);
6243
6973
  if (why) throw new PaintEditorError(why);
6244
- const next = { ...was, brush: _chunk5BR7LC24cjs.brushToJSON.call(void 0, brush) };
6974
+ const next = { ...was, brush: _chunk4T47Z4J3cjs.brushToJSON.call(void 0, brush) };
6245
6975
  if (update.seed !== void 0) {
6246
6976
  if (!Number.isInteger(update.seed) || update.seed < 0) throw new PaintEditorError(`seed ${update.seed} must be a whole number of 0 or more.`);
6247
6977
  next.seed = update.seed;
@@ -6252,7 +6982,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6252
6982
  }
6253
6983
  if (update.paint !== void 0) {
6254
6984
  if (update.paint === null) delete next.paint;
6255
- else if (_chunk5BR7LC24cjs.isPaintEffectPaint.call(void 0, update.paint)) {
6985
+ else if (_chunk4T47Z4J3cjs.isPaintEffectPaint.call(void 0, update.paint)) {
6256
6986
  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
6987
  next.paint = JSON.parse(JSON.stringify(update.paint));
6258
6988
  } else {
@@ -6263,7 +6993,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6263
6993
  } else next.paint = JSON.parse(JSON.stringify(p));
6264
6994
  }
6265
6995
  }
6266
- const rgb = _chunk5BR7LC24cjs.hexToRgb8.call(void 0, next.color).map((v) => v / 255);
6996
+ const rgb = _chunk4T47Z4J3cjs.hexToRgb8.call(void 0, next.color).map((v) => v / 255);
6267
6997
  const load = brushLoadForStroke(brush, _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD, rgb);
6268
6998
  if (load.reservoir || load.pickup) next.load = load;
6269
6999
  else delete next.load;
@@ -6308,9 +7038,9 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6308
7038
  const layers = /* @__PURE__ */ new Map();
6309
7039
  for (const f of found) layers.set(f.layer.id, f.layer);
6310
7040
  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));
7041
+ const gone = (_nullishCoalesce(_optionalChain([doc, 'access', _456 => _456.liveStrokes, 'optionalAccess', _457 => _457[layerId]]), () => ( []))).filter((x) => ids.includes(x.id));
6312
7042
  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));
7043
+ doc = this.withLiveStrokes(doc, layer, (_nullishCoalesce(_optionalChain([doc, 'access', _458 => _458.liveStrokes, 'optionalAccess', _459 => _459[layerId]]), () => ( []))).filter((x) => !ids.includes(x.id)), this.strokeAreas(gone));
6314
7044
  }
6315
7045
  this.commit(ids.length === 1 ? "Remove stroke" : "Remove strokes", doc);
6316
7046
  return { removed: [...ids], bounds };
@@ -6378,7 +7108,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6378
7108
  }
6379
7109
  withLiveStrokes(doc, layer, list, dirty) {
6380
7110
  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]);
7111
+ const current = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, doc.layers, layer.id), 'optionalAccess', _460 => _460.layer]);
6382
7112
  const surface = this.drawLiveStrokes(_nullishCoalesce(current, () => ( layer)), list, dirty);
6383
7113
  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
7114
  return this.withPaintEvent(next, { kind: "live-strokes", layerId: layer.id, data: { strokes: list.map((x) => JSON.parse(JSON.stringify(x))) } });
@@ -6456,7 +7186,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6456
7186
  if (!this.animatedLayers.size) return this.doc.layers;
6457
7187
  let layers = this.doc.layers;
6458
7188
  for (const [id, a] of this.animatedLayers) {
6459
- const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, layers, id), 'optionalAccess', _373 => _373.layer]);
7189
+ const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, layers, id), 'optionalAccess', _461 => _461.layer]);
6460
7190
  if (isPixelLayer(l) && l.surface === a.base) layers = _chunkHB4ICNNAcjs.mapLayer.call(void 0, layers, id, () => ({ ...l, surface: a.surface }));
6461
7191
  }
6462
7192
  return layers;
@@ -6466,11 +7196,11 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6466
7196
  const layers = [];
6467
7197
  const sources = /* @__PURE__ */ new Map();
6468
7198
  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]);
7199
+ const layer = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, doc.layers, id), 'optionalAccess', _462 => _462.layer]);
6470
7200
  const live = strokes.filter(isLiveEffect);
6471
7201
  if (!isPixelLayer(layer) || !live.length) continue;
6472
7202
  layers.push({ layer, strokes });
6473
- for (const st of live) sources.set(_chunk5BR7LC24cjs.effectPaintKey.call(void 0, st.paint), st.paint);
7203
+ for (const st of live) sources.set(_chunk4T47Z4J3cjs.effectPaintKey.call(void 0, st.paint), st.paint);
6474
7204
  }
6475
7205
  return { layers, sources: [...sources.values()] };
6476
7206
  }
@@ -6591,13 +7321,13 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6591
7321
  return this.withEffectTime(time, () => this.compositePixels(options));
6592
7322
  }
6593
7323
  // --- Stencils --------------------------------------------------------------------
6594
- __init123() {this.stencilCache = null}
6595
- __init124() {this.nextStencilId = 1}
7324
+ __init135() {this.stencilCache = null}
7325
+ __init136() {this.nextStencilId = 1}
6596
7326
  /** The mask stencils' coverage over the canvas (cached), or `null` when none masks. */
6597
7327
  stencilSelection(stencils = this.guides.stencils) {
6598
7328
  const { width, height } = this.doc.canvas;
6599
7329
  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;
7330
+ if (_optionalChain([this, 'access', _463 => _463.stencilCache, 'optionalAccess', _464 => _464.key]) === stencils && this.stencilCache.size === size) return this.stencilCache.mask;
6601
7331
  const mask = stencilMask(stencils, width, height);
6602
7332
  this.stencilCache = { key: stencils, size, mask };
6603
7333
  return mask;
@@ -6659,7 +7389,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6659
7389
  makeSwatch(color, name) {
6660
7390
  const p = typeof color === "string" ? { type: "color", color: parseColor(color) } : this.readStrokePaint(color);
6661
7391
  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();
7392
+ if (_optionalChain([name, 'optionalAccess', _465 => _465.trim, 'call', _466 => _466()])) out.name = name.trim();
6663
7393
  if (p.type === "gradient") out.paint = p;
6664
7394
  return out;
6665
7395
  }
@@ -6711,7 +7441,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6711
7441
  * mix becomes the paint colour. Returns the mixed colour and the pile's bounds.
6712
7442
  */
6713
7443
  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]) }));
7444
+ const parsed = (_nullishCoalesce(paints, () => ( []))).map((p, i) => ({ color: parseColor(_optionalChain([p, 'optionalAccess', _467 => _467.color]), `paints[${i}].color`), parts: _optionalChain([p, 'optionalAccess', _468 => _468.parts]) }));
6715
7445
  let r;
6716
7446
  try {
6717
7447
  r = this.pad.mix(parsed, options);
@@ -6800,15 +7530,29 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
6800
7530
  }
6801
7531
  /** A layer's tile surface (live while stroking), for thumbnails. */
6802
7532
  layerSurface(id) {
6803
- const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.currentLayers(), id), 'optionalAccess', _381 => _381.layer]);
7533
+ const l = _optionalChain([_chunkHB4ICNNAcjs.findLayer.call(void 0, this.currentLayers(), id), 'optionalAccess', _469 => _469.layer]);
6804
7534
  return isPixelLayer(l) ? l.surface : null;
6805
7535
  }
6806
- }, _class10.__initStatic(), _class10);
7536
+ }, _class12.__initStatic(), _class12);
6807
7537
  var MAX_IMAGE_SIDE = 8192;
6808
7538
  var movePoint = (p, dx, dy) => ({ x: p.x + dx, y: p.y + dy });
6809
7539
  var moveRect = (r, dx, dy) => ({ ...r, x: r.x + dx, y: r.y + dy });
7540
+ var SPILL_SUFFIX = "::light";
7541
+ function withLayerLight(light, layerId, next) {
7542
+ if (!next) return light ? { ...light } : void 0;
7543
+ const out = { ..._nullishCoalesce(light, () => ( {})) };
7544
+ if (next.tiles.size) out[layerId] = next;
7545
+ else delete out[layerId];
7546
+ return out;
7547
+ }
7548
+ var BACKLIGHT_LAYER_ID = "__backlight";
7549
+ function insertAfter(layers, id, node) {
7550
+ const i = layers.findIndex((l) => l.id === id);
7551
+ if (i >= 0) return [...layers.slice(0, i + 1), node, ...layers.slice(i + 1)];
7552
+ return layers.map((l) => l.type === "group" ? { ...l, children: insertAfter(l.children, id, node) } : l);
7553
+ }
6810
7554
  function isLiveEffect(s) {
6811
- return _optionalChain([s, 'access', _382 => _382.paint, 'optionalAccess', _383 => _383.type]) === "effect" && s.paint.live === true;
7555
+ return _optionalChain([s, 'access', _470 => _470.paint, 'optionalAccess', _471 => _471.type]) === "effect" && s.paint.live === true;
6812
7556
  }
6813
7557
  function framePaint(p, t) {
6814
7558
  return { ...p, time: p.time + t };
@@ -6833,7 +7577,7 @@ function clipRect(r, width, height) {
6833
7577
  return x1 > x0 && y1 > y0 ? { x: x0, y: y0, width: x1 - x0, height: y1 - y0 } : null;
6834
7578
  }
6835
7579
  function withLiveStroke(doc, stroke) {
6836
- const list = _optionalChain([doc, 'access', _384 => _384.liveStrokes, 'optionalAccess', _385 => _385[stroke.layerId]]);
7580
+ const list = _optionalChain([doc, 'access', _472 => _472.liveStrokes, 'optionalAccess', _473 => _473[stroke.layerId]]);
6837
7581
  return list ? { ...doc, liveStrokes: { ...doc.liveStrokes, [stroke.layerId]: [...list, stroke] } } : doc;
6838
7582
  }
6839
7583
  function shiftDocument(doc, dx, dy) {
@@ -6860,9 +7604,9 @@ function shiftDocument(doc, dx, dy) {
6860
7604
  if (doc.liveStrokes) out.liveStrokes = Object.fromEntries(Object.entries(doc.liveStrokes).map(([id, list]) => [id, list.map(moveStroke)]));
6861
7605
  if (doc.paintEvents) {
6862
7606
  out.paintEvents = doc.paintEvents.map((e) => {
6863
- const list = e.kind === "stickers" ? _optionalChain([e, 'access', _386 => _386.data, 'optionalAccess', _387 => _387.stickers]) : void 0;
7607
+ const list = e.kind === "stickers" ? _optionalChain([e, 'access', _474 => _474.data, 'optionalAccess', _475 => _475.stickers]) : void 0;
6864
7608
  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;
7609
+ const strokes = e.kind === "live-strokes" ? _optionalChain([e, 'access', _476 => _476.data, 'optionalAccess', _477 => _477.strokes]) : void 0;
6866
7610
  return strokes ? { ...e, data: { ...e.data, strokes: strokes.map(moveStroke) } } : e;
6867
7611
  });
6868
7612
  }
@@ -6944,8 +7688,8 @@ function strokeSchedule(points) {
6944
7688
  return total > 0 ? acc.map((d) => d / total) : points.map(() => 1);
6945
7689
  }
6946
7690
  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]);
7691
+ const t0 = _optionalChain([points, 'access', _478 => _478[0], 'optionalAccess', _479 => _479.t]);
7692
+ const t1 = _optionalChain([points, 'access', _480 => _480[points.length - 1], 'optionalAccess', _481 => _481.t]);
6949
7693
  let ms;
6950
7694
  if (points.every((p) => Number.isFinite(p.t)) && t1 > t0) ms = t1 - t0;
6951
7695
  else {
@@ -6999,7 +7743,7 @@ var PaintDirector = class {
6999
7743
  }
7000
7744
  where(id) {
7001
7745
  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)) };
7746
+ return { x: _nullishCoalesce(_optionalChain([a, 'optionalAccess', _482 => _482.x]), () => ( 0)), y: _nullishCoalesce(_optionalChain([a, 'optionalAccess', _483 => _483.y]), () => ( 0)) };
7003
7747
  }
7004
7748
  /** Glides to `to`; an actor that just appeared starts there instead of the corner. */
7005
7749
  async glide(id, to, ms = 380) {
@@ -7046,8 +7790,8 @@ var PaintDirector = class {
7046
7790
  }
7047
7791
  case "replay": {
7048
7792
  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]]);
7793
+ const source = c.getStrokes().filter((s) => _optionalChain([command, 'access', _484 => _484.replay, 'access', _485 => _485.strokeIds, 'optionalAccess', _486 => _486.includes, 'call', _487 => _487(s.id)]));
7794
+ const first = _optionalChain([source, 'access', _488 => _488[0], 'optionalAccess', _489 => _489.points, 'access', _490 => _490[0]]);
7051
7795
  if (first) await this.glide(id, first);
7052
7796
  const result = c.replayStrokes(command.replay);
7053
7797
  if (result.bounds) await this.glide(id, centre(result.bounds), 520);
@@ -7228,7 +7972,7 @@ function createPaintStickerTools(c) {
7228
7972
  items: {
7229
7973
  type: "object",
7230
7974
  properties: {
7231
- stamp: { type: "string", enum: [..._chunk5BR7LC24cjs.STAMP_KINDS], description: "The stamp." },
7975
+ stamp: { type: "string", enum: [..._chunk4T47Z4J3cjs.STAMP_KINDS], description: "The stamp." },
7232
7976
  x: { type: "number", description: "Centre x in document pixels." },
7233
7977
  y: { type: "number", description: "Centre y in document pixels." },
7234
7978
  size: { type: "number", minimum: 2, maximum: 2048, description: "Diameter in pixels (default 48)." },
@@ -7258,7 +8002,7 @@ function createPaintStickerTools(c) {
7258
8002
  properties: {
7259
8003
  layerId: LAYER_ID,
7260
8004
  id: { type: "string", description: "Sticker id from list_stickers." },
7261
- stamp: { type: "string", enum: [..._chunk5BR7LC24cjs.STAMP_KINDS], description: "Another stamp." },
8005
+ stamp: { type: "string", enum: [..._chunk4T47Z4J3cjs.STAMP_KINDS], description: "Another stamp." },
7262
8006
  x: { type: "number", description: "Centre x in document pixels." },
7263
8007
  y: { type: "number", description: "Centre y in document pixels." },
7264
8008
  size: { type: "number", minimum: 2, maximum: 2048, description: "Diameter in pixels." },
@@ -7796,7 +8540,7 @@ function createPaintWetTools(c) {
7796
8540
  {
7797
8541
  name: "advance_wet_paint",
7798
8542
  title: "Let the wet paint flow",
7799
- description: "Lets time pass for wet paint: the water spreads, runs downhill on a tilted canvas, granulates, blooms and evaporates, and pigment settles. Give steps (simulation steps; at the default 120 per second, 120 steps is one second) or seconds (default 1). Not an undo step, as time passing never is. Returns the steps run, the clock, stillWet and each wet layer\u2019s state. Paint dries by itself after a few thousand steps; dry_paint dries it at once.",
8543
+ description: "Lets time pass for wet paint: the water spreads, runs downhill on a tilted canvas, granulates, blooms and evaporates, and pigment settles. Give steps (simulation steps; at the default 120 per second, 120 steps is one second) or seconds (default 1). Not an undo step, as time passing never is. Returns the steps run, the clock, stillWet and each wet layer\u2019s state. Paint dries by itself after a few thousand steps; dry_paint dries it at once. Wet light paint (wet brushes with light.amount) flows with the pigment and settles where it dries into the layer\u2019s light (get_light).",
7800
8544
  inputSchema: {
7801
8545
  type: "object",
7802
8546
  properties: {
@@ -7858,7 +8602,7 @@ function createPaintWetTools(c) {
7858
8602
  type: "object",
7859
8603
  properties: {
7860
8604
  layerId: LAYER_ID2,
7861
- kind: { type: "string", enum: [..._chunkXFYLA3ZOcjs.WET_FLOW_KINDS], description: "none, linear, swirl, radial or turbulent." },
8605
+ kind: { type: "string", enum: [..._chunkVMROIWLLcjs.WET_FLOW_KINDS], description: "none, linear, swirl, radial or turbulent." },
7862
8606
  strength: UNIT("How hard it pushes, 0..1 (1 is about a 25\xB0 tilt; default 0.5)."),
7863
8607
  angle: { type: "number", minimum: -360, maximum: 360, description: "linear: direction in degrees clockwise from canvas down (default 0)." },
7864
8608
  center: {
@@ -7955,6 +8699,39 @@ var WRITE4 = { readOnlyHint: false, destructiveHint: false, idempotentHint: fals
7955
8699
  var LAYER_ID3 = { type: "string", description: "Pixel layer id from list_layers (default the active layer)." };
7956
8700
  function createPaintThickTools(c) {
7957
8701
  return [
8702
+ {
8703
+ name: "get_light",
8704
+ title: "Read a layer\u2019s light paint",
8705
+ 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.",
8706
+ inputSchema: { type: "object", properties: { layerId: LAYER_ID3 }, additionalProperties: false },
8707
+ annotations: { title: "Read a layer\u2019s light paint", ...READ4 },
8708
+ coordinates: { out: ["bounds"] },
8709
+ handler: ({ layerId }) => c.lightOf(layerId)
8710
+ },
8711
+ {
8712
+ name: "set_light_spill",
8713
+ title: "Set how far a layer\u2019s light spills",
8714
+ 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.",
8715
+ inputSchema: {
8716
+ type: "object",
8717
+ properties: {
8718
+ layerId: LAYER_ID3,
8719
+ radius: { type: "number", minimum: 0, maximum: 512, description: "How far the light reaches, px (0 turns spill off; default 24)." },
8720
+ strength: { type: "number", minimum: 0, maximum: 2, description: "How strong the spill is, 0..2 (default 1)." }
8721
+ },
8722
+ additionalProperties: false
8723
+ },
8724
+ annotations: { title: "Set how far a layer\u2019s light spills", ...SET3 },
8725
+ handler: ({ layerId, radius, strength }) => c.setLightSpill(layerId, { radius, strength })
8726
+ },
8727
+ {
8728
+ name: "clear_light",
8729
+ title: "Clear a layer\u2019s light",
8730
+ description: "Takes a layer\u2019s light paint away; its paint keeps its colour (and relief). One undo step. Returns whether anything was cleared.",
8731
+ inputSchema: { type: "object", properties: { layerId: LAYER_ID3 }, additionalProperties: false },
8732
+ annotations: { title: "Clear a layer\u2019s light", ...WRITE4 },
8733
+ handler: ({ layerId }) => c.clearLight(layerId)
8734
+ },
7958
8735
  {
7959
8736
  name: "set_lighting",
7960
8737
  title: "Move the light",
@@ -8136,7 +8913,7 @@ var BEHAVIOR = {
8136
8913
  type: "object",
8137
8914
  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
8915
  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." },
8916
+ mode: { type: "string", enum: [..._chunk4T47Z4J3cjs.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
8917
  colors: { type: "array", maxItems: 8, items: { ...COLOR3 }, description: "2..8 colours #rrggbb; fewer than 2 uses the paint colour and the secondary colour." },
8141
8918
  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
8919
  length: { type: "integer", minimum: 1, maximum: 500, description: "dab mode: dabs per colour (default 12)." },
@@ -8177,10 +8954,10 @@ var WET_BRUSH = {
8177
8954
  properties: {
8178
8955
  tool: {
8179
8956
  type: "string",
8180
- enum: [..._chunk5BR7LC24cjs.WET_TOOLS],
8957
+ enum: [..._chunk4T47Z4J3cjs.WET_TOOLS],
8181
8958
  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
8959
  },
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)." },
8960
+ medium: { type: "string", enum: [..._chunk4T47Z4J3cjs.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
8961
  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
8962
  thinner: { type: "number", minimum: 0, maximum: 1, description: "Thinner (water or solvent): more transparent, runnier paint." },
8186
8963
  water: { type: "number", minimum: 0, maximum: 1, description: "Wetness: water laid per stroke, 0.2 a damp brush .. 1 a dripping one." },
@@ -8197,7 +8974,7 @@ var IMPASTO_BRUSH = {
8197
8974
  properties: {
8198
8975
  tool: {
8199
8976
  type: "string",
8200
- enum: [..._chunk5BR7LC24cjs.IMPASTO_TOOLS],
8977
+ enum: [..._chunk4T47Z4J3cjs.IMPASTO_TOOLS],
8201
8978
  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
8979
  },
8203
8980
  thickness: { type: "number", minimum: 0, maximum: 1, description: "How far the paint stands up." },
@@ -8205,7 +8982,18 @@ var IMPASTO_BRUSH = {
8205
8982
  metallic: { type: "number", minimum: 0, maximum: 1, description: "Metallic paint: highlights take the paint colour (gold, copper, silver)." },
8206
8983
  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
8984
  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." }
8985
+ drag: { type: "number", minimum: 0, maximum: 1, description: "Knife and brush: how much of the paint under the tool is dragged along." },
8986
+ 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)." },
8987
+ 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." }
8988
+ },
8989
+ additionalProperties: false
8990
+ };
8991
+ var LIGHT_BRUSH = {
8992
+ type: "object",
8993
+ 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, impasto and wet paint brushes take it (wet light flows, pools, granulates and blooms with the pigment until the paint dries, then is ordinary light paint); wet tools other than paint lay none, and sticker brushes refuse light above 0. Missing fields keep their current values.",
8994
+ properties: {
8995
+ amount: { type: "number", minimum: 0, maximum: 1, description: "Light each dab lays, scaled by its coverage and flow (0 none, default; 1 full)." },
8996
+ pressure: { type: "boolean", description: "Pressure scales the light (default false)." }
8209
8997
  },
8210
8998
  additionalProperties: false
8211
8999
  };
@@ -8213,8 +9001,8 @@ var PARTICLE_BRUSH = {
8213
9001
  type: "object",
8214
9002
  description: "Particle engine settings (procedural brushes). Missing fields keep their current values.",
8215
9003
  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." },
9004
+ kind: { type: "string", enum: [..._chunk4T47Z4J3cjs.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." },
9005
+ render: { type: "string", enum: [..._chunk4T47Z4J3cjs.PARTICLE_RENDERS], description: "dot: a mark at each step; line: each particle draws a continuous line; sequence: beads spaced along each path." },
8218
9006
  count: { type: "integer", minimum: 1, maximum: 64, description: "Particles (flow: how many are thrown per tip of travel)." },
8219
9007
  size: { type: "number", minimum: 0, maximum: 1, description: "Mark size as a fraction of the tip size." },
8220
9008
  spread: { type: "number", minimum: 0, maximum: 1, description: "How far apart the particles start, as a fraction of the tip size." },
@@ -8244,7 +9032,7 @@ var STICKER_BRUSH = {
8244
9032
  type: "object",
8245
9033
  description: "Sticker engine settings: stamps sprayed along the stroke as editable stickers. Missing fields keep their current values.",
8246
9034
  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)." },
9035
+ stamps: { type: "array", minItems: 1, maxItems: 9, items: { type: "string", enum: [..._chunk4T47Z4J3cjs.STAMP_KINDS], description: "A stamp." }, description: "The stamps to spray (one picked at random per sticker)." },
8248
9036
  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
9037
  sizeJitter: { type: "number", minimum: 0, maximum: 1, description: "Random size change." },
8250
9038
  rotationJitter: { type: "number", minimum: 0, maximum: 1, description: "Random turn as a fraction of \xB1180\xB0." },
@@ -8255,16 +9043,16 @@ var STICKER_BRUSH = {
8255
9043
  };
8256
9044
  var BRUSH_FIELDS = {
8257
9045
  name: { type: "string", minLength: 1, maxLength: 80, description: "Display name." },
8258
- category: { type: "string", enum: [..._chunk5BR7LC24cjs.PAINT_BRUSH_CATEGORIES], description: "Library category." },
9046
+ category: { type: "string", enum: [..._chunk4T47Z4J3cjs.PAINT_BRUSH_CATEGORIES], description: "Library category." },
8259
9047
  engine: {
8260
9048
  type: "string",
8261
- enum: [..._chunk5BR7LC24cjs.PAINT_BRUSH_ENGINES],
9049
+ enum: [..._chunk4T47Z4J3cjs.PAINT_BRUSH_ENGINES],
8262
9050
  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
9051
  },
8264
9052
  tip: TIP,
8265
9053
  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
9054
  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)." },
9055
+ rotation: { type: "string", enum: [..._chunk4T47Z4J3cjs.BRUSH_ROTATIONS], description: "What turns the tip: fixed (tip.angle), stroke (follows the direction of travel) or barrel (the pen barrel rotation, twist)." },
8268
9056
  dynamics: DYNAMICS,
8269
9057
  mixer: MIXER,
8270
9058
  loadsColor: { type: "boolean", description: "Mixer engine: load the colour each stroke (false for a mixer that only moves paint)." },
@@ -8276,6 +9064,7 @@ var BRUSH_FIELDS = {
8276
9064
  particle: PARTICLE_BRUSH,
8277
9065
  bristle: BRISTLE_BRUSH,
8278
9066
  sticker: STICKER_BRUSH,
9067
+ light: LIGHT_BRUSH,
8279
9068
  paint: BRUSH_PAINT
8280
9069
  };
8281
9070
  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 +9104,13 @@ function brushSummary(b) {
8315
9104
  size: b.tip.size,
8316
9105
  grain: b.grain,
8317
9106
  rotation: b.rotation,
8318
- wetCapable: _chunk5BR7LC24cjs.isWetCapable.call(void 0, b),
9107
+ wetCapable: _chunk4T47Z4J3cjs.isWetCapable.call(void 0, b),
8319
9108
  pickup: b.behavior.pickup,
8320
9109
  dirty: b.behavior.dirty,
8321
9110
  multicolor: b.behavior.multicolor.mode,
8322
9111
  ...b.engine === "wet" ? { wetTool: b.wet.tool, medium: b.wet.medium } : {},
8323
9112
  ...b.engine === "impasto" ? { impastoTool: b.impasto.tool } : {},
9113
+ ...b.light.amount > 0 ? { light: b.light.amount } : {},
8324
9114
  builtin: Boolean(b.builtin)
8325
9115
  };
8326
9116
  }
@@ -8423,15 +9213,15 @@ function createPaintEditorToolProvider(controller, options = {}) {
8423
9213
  inputSchema: EMPTY3,
8424
9214
  annotations: { title: "List papers", ...READ6 },
8425
9215
  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],
9216
+ papers: _chunk4T47Z4J3cjs.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 })),
9217
+ families: [..._chunk4T47Z4J3cjs.PAPER_FAMILIES],
8428
9218
  patterns: [..._chunkPQ3H2AANcjs.GRAIN_PATTERNS]
8429
9219
  })
8430
9220
  });
8431
9221
  add({
8432
9222
  name: "get_paper",
8433
9223
  title: "Get the paper",
8434
- description: "Reads every setting of the current paper (preset, pattern, seed, colour, scale, tiling, depth, shading, dry and wet tuning, absorption, permeability, roughness).",
9224
+ description: "Reads every setting of the current paper (preset, pattern, seed, colour, scale, tiling, depth, shading, dry and wet tuning, absorption, permeability, roughness, and metallic, backlight and backlightColor when set).",
8435
9225
  inputSchema: EMPTY3,
8436
9226
  annotations: { title: "Get the paper", ...READ6 },
8437
9227
  handler: () => JSON.parse(JSON.stringify(c.document.paper))
@@ -8447,7 +9237,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
8447
9237
  add({
8448
9238
  name: "set_paper",
8449
9239
  title: "Set the paper",
8450
- description: "Changes the paper under the whole painting: a preset from list_papers and / or its settings. Grain depth decides how much grain-responsive brushes (pencil, pastel, charcoal) skip the valleys; changing it re-renders the paper but not strokes already painted. One undo step. Returns the paper.",
9240
+ description: "Changes the paper under the whole painting: a preset from list_papers and / or its settings. Grain depth decides how much grain-responsive brushes (pencil, pastel, charcoal) skip the valleys; changing it re-renders the paper but not strokes already painted. backlight lights the paper from behind (the stained-glass preset has it on). One undo step. Returns the paper.",
8451
9241
  inputSchema: {
8452
9242
  type: "object",
8453
9243
  properties: {
@@ -8465,6 +9255,8 @@ function createPaintEditorToolProvider(controller, options = {}) {
8465
9255
  permeability: { type: "number", minimum: 0, maximum: 1, description: "Wet media: how freely water flows over the fibres (low: more ragged, slower spreading)." },
8466
9256
  roughness: { type: "number", minimum: 0, maximum: 1, description: "Wet media: how much the grain pins wet edges and catches granulating pigment." },
8467
9257
  metallic: { type: "number", minimum: 0, maximum: 1, description: "Metallic paper (gold or silver leaf, pearlescent card): the grain catches the light (see set_lighting). 0 by default." },
9258
+ backlight: { type: "number", minimum: 0, maximum: 1, description: "Backlit paper (a lightbox, stained glass): light from behind the paper, 0 (none, the default) .. 1. Transparent glazes (watercolour, airbrush, thin marker) glow in their own colour; opaque paint (gouache, oil, solid ink) and thick paint block it; the light through paint spills softly around it. Nothing is repainted." },
9259
+ backlightColor: { ...COLOR3, description: "The backlight\u2019s colour #rrggbb (default #fff1d6, warm white)." },
8468
9260
  name: { type: "string", minLength: 1, description: "Display name." }
8469
9261
  },
8470
9262
  additionalProperties: false
@@ -8639,12 +9431,12 @@ function createPaintEditorToolProvider(controller, options = {}) {
8639
9431
  add({
8640
9432
  name: "list_brushes",
8641
9433
  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.",
9434
+ 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, glow wash; 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
9435
  inputSchema: EMPTY3,
8644
9436
  annotations: { title: "List brushes", ...READ6 },
8645
9437
  handler: () => {
8646
9438
  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) };
9439
+ return { activeBrushId: s.brush.id, activeEdited: !s.brush.builtin && Boolean(_optionalChain([c, 'access', _491 => _491.getBrush, 'call', _492 => _492(s.brush.id), 'optionalAccess', _493 => _493.builtin])), brushes: s.brushes.map(brushSummary) };
8648
9440
  }
8649
9441
  });
8650
9442
  add({
@@ -8655,10 +9447,10 @@ function createPaintEditorToolProvider(controller, options = {}) {
8655
9447
  annotations: { title: "Get a brush", ...READ6 },
8656
9448
  handler: ({ id }) => {
8657
9449
  const st = c.getState();
8658
- if (id === void 0) return { ..._chunk5BR7LC24cjs.brushToJSON.call(void 0, st.brush), strokePaint: _nullishCoalesce(st.brush.paint, () => ( st.paint)) };
9450
+ if (id === void 0) return { ..._chunk4T47Z4J3cjs.brushToJSON.call(void 0, st.brush), strokePaint: _nullishCoalesce(st.brush.paint, () => ( st.paint)) };
8659
9451
  const b = c.getBrush(id);
8660
9452
  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)) };
9453
+ return { ..._chunk4T47Z4J3cjs.brushToJSON.call(void 0, b), builtin: Boolean(b.builtin), strokePaint: _nullishCoalesce(b.paint, () => ( st.paint)) };
8662
9454
  }
8663
9455
  });
8664
9456
  add({
@@ -8667,7 +9459,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
8667
9459
  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
9460
  inputSchema: { type: "object", properties: { id: { type: "string", description: "Brush id from list_brushes to start from." }, ...BRUSH_FIELDS }, additionalProperties: false },
8669
9461
  annotations: { title: "Set the brush", ...SET4 },
8670
- handler: (a) => _chunk5BR7LC24cjs.brushToJSON.call(void 0, c.setBrush(a))
9462
+ handler: (a) => _chunk4T47Z4J3cjs.brushToJSON.call(void 0, c.setBrush(a))
8671
9463
  });
8672
9464
  add({
8673
9465
  name: "save_brush",
@@ -8683,7 +9475,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
8683
9475
  additionalProperties: false
8684
9476
  },
8685
9477
  annotations: { title: "Save a brush", ...SET4 },
8686
- handler: ({ id, name }) => _chunk5BR7LC24cjs.brushToJSON.call(void 0, c.saveBrush({ id, name }))
9478
+ handler: ({ id, name }) => _chunk4T47Z4J3cjs.brushToJSON.call(void 0, c.saveBrush({ id, name }))
8687
9479
  });
8688
9480
  add({
8689
9481
  name: "remove_brush",
@@ -8823,7 +9615,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
8823
9615
  add({
8824
9616
  name: "paint_stroke",
8825
9617
  title: "Paint a brush stroke",
8826
- description: "Paints one stroke through points (document pixels, with optional pressure, time, tilt and barrel rotation) on a pixel layer with a library brush or the active one, optionally changed for this stroke. Grain-responsive brushes follow the paper; the mixer engine (mixer-brush) picks up and mixes paint already on the layer, the blender mixes nearby colours, the smudge drags paint, and dab brushes with behavior.pickup lift and carry paint, and wet brushes (engine wet: watercolour, gouache, add water, dry, blow, wet blend, wet smudge, blot) paint on the layer\u2019s wet layer, where the paint keeps moving until it dries (the result says wet: true while it is; see advance_wet_paint and dry_paint; a dry brush on wet paint dries it first; wet oil brushes blend into the wet oil they meet), impasto brushes (engine impasto: impasto brush, paint tube, palette knife, roller, gloop pen, metallic paint) lay thick paint whose relief is lit by set_lighting, particle and bristle brushes draw procedural marks, and sticker brushes spray editable stickers onto an empty or sticker layer (list_stickers). Guide stencils make strokes that start near them follow them; mask stencils keep paint inside them. The symmetry guide adds its mirror copies; with a tracing image that samples colours, a stroke without color takes the image colour at its first point. With an agent cursor (a director), the stroke is painted live along the points at the pace of their times. One undo step. Returns the layer id, changed bounds, dab count, the recorded strokeId (for replay_strokes), for wet brushes wet (paint still wet) and, for brushes that pick up paint, mixerLoad: what the brush carries after the stroke.",
9618
+ description: "Paints one stroke through points (document pixels, with optional pressure, time, tilt and barrel rotation) on a pixel layer with a library brush or the active one, optionally changed for this stroke. Grain-responsive brushes follow the paper; the mixer engine (mixer-brush) picks up and mixes paint already on the layer, the blender mixes nearby colours, the smudge drags paint, and dab brushes with behavior.pickup lift and carry paint, and wet brushes (engine wet: watercolour, gouache, add water, dry, blow, wet blend, wet smudge, blot) paint on the layer\u2019s wet layer, where the paint keeps moving until it dries (the result says wet: true while it is; see advance_wet_paint and dry_paint; a dry brush on wet paint dries it first; wet oil brushes blend into the wet oil they meet; with light.amount, as glow-wash has, their light flows with the paint), impasto brushes (engine impasto: impasto brush, paint tube, palette knife, roller, gloop pen, metallic paint) lay thick paint whose relief is lit by set_lighting, particle and bristle brushes draw procedural marks, and sticker brushes spray editable stickers onto an empty or sticker layer (list_stickers). Guide stencils make strokes that start near them follow them; mask stencils keep paint inside them. The symmetry guide adds its mirror copies; with a tracing image that samples colours, a stroke without color takes the image colour at its first point. With an agent cursor (a director), the stroke is painted live along the points at the pace of their times. One undo step. Returns the layer id, changed bounds, dab count, the recorded strokeId (for replay_strokes), for wet brushes wet (paint still wet) and, for brushes that pick up paint, mixerLoad: what the brush carries after the stroke.",
8827
9619
  inputSchema: {
8828
9620
  type: "object",
8829
9621
  properties: {
@@ -8874,7 +9666,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
8874
9666
  if (ids) {
8875
9667
  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
9668
  }
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)) };
9669
+ return { total: all.length, strokes: picked.map((s) => strokeSummary(s, Boolean(ids), live ? true : liveIds.has(s.id) && _optionalChain([c, 'access', _494 => _494.document, 'access', _495 => _495.liveStrokes, 'optionalAccess', _496 => _496[s.layerId], 'optionalAccess', _497 => _497.includes, 'call', _498 => _498(s)]) === true)) };
8878
9670
  }
8879
9671
  });
8880
9672
  add({
@@ -9150,7 +9942,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
9150
9942
  tools.push(...createPaintThickTools(c));
9151
9943
  tools.push(...createPaintReplayTools(c));
9152
9944
  tools.push(...createPaintStickerTools(c));
9153
- tools.push(..._chunkXFYLA3ZOcjs.createPigmentTools.call(void 0, ));
9945
+ tools.push(..._chunkVMROIWLLcjs.createPigmentTools.call(void 0, ));
9154
9946
  const exclude = new Set(_nullishCoalesce(options.exclude, () => ( [])));
9155
9947
  const description = "Natural-media painting: paper and grain, dry and wet brushes, the brush library, strokes as data, pigment mixing, stencils, guides, layers and export.";
9156
9948
  return withPaintPlayback({
@@ -9164,16 +9956,16 @@ function createPaintEditorToolProvider(controller, options = {}) {
9164
9956
  }
9165
9957
  function hostTool({ coordinates, ...tool }, { origin, onChange }) {
9166
9958
  if (!origin && !onChange) return tool;
9167
- const write = !_optionalChain([tool, 'access', _411 => _411.annotations, 'optionalAccess', _412 => _412.readOnlyHint]);
9959
+ const write = !_optionalChain([tool, 'access', _499 => _499.annotations, 'optionalAccess', _500 => _500.readOnlyHint]);
9168
9960
  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;
9961
+ if (write) _optionalChain([onChange, 'optionalCall', _501 => _501({ tool: tool.name })]);
9962
+ return origin && _optionalChain([coordinates, 'optionalAccess', _502 => _502.out]) ? fromDocument(result, coordinates.out, origin) : result;
9171
9963
  };
9172
9964
  return {
9173
9965
  ...tool,
9174
9966
  ...origin ? { description: hostedText(_nullishCoalesce(tool.description, () => ( ""))), inputSchema: hostedSchema(tool.inputSchema) } : {},
9175
9967
  handler: (args, context) => {
9176
- const r = tool.handler(origin && _optionalChain([coordinates, 'optionalAccess', _415 => _415.in]) ? toDocument(args, coordinates.in, origin) : args, context);
9968
+ const r = tool.handler(origin && _optionalChain([coordinates, 'optionalAccess', _503 => _503.in]) ? toDocument(args, coordinates.in, origin) : args, context);
9177
9969
  return r instanceof Promise ? r.then(done) : done(r);
9178
9970
  }
9179
9971
  };
@@ -9190,7 +9982,7 @@ function strokeSummary(s, withPoints, live) {
9190
9982
  color: s.color,
9191
9983
  points: withPoints ? s.points : s.points.length,
9192
9984
  seed: s.seed,
9193
- copies: _nullishCoalesce(_optionalChain([s, 'access', _416 => _416.copies, 'optionalAccess', _417 => _417.length]), () => ( 0)),
9985
+ copies: _nullishCoalesce(_optionalChain([s, 'access', _504 => _504.copies, 'optionalAccess', _505 => _505.length]), () => ( 0)),
9194
9986
  live,
9195
9987
  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
9988
  };
@@ -9205,21 +9997,21 @@ var PAINT_TOOLS = [
9205
9997
  { id: "pan", label: "Pan", key: "h" },
9206
9998
  { id: "rotate", label: "Rotate view", key: "r" }
9207
9999
  ];
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);
10000
+ var PaintGesture = (_class13 = class {
10001
+ constructor(controller, box) {;_class13.prototype.__init137.call(this);_class13.prototype.__init138.call(this);_class13.prototype.__init139.call(this);_class13.prototype.__init140.call(this);_class13.prototype.__init141.call(this);_class13.prototype.__init142.call(this);
9210
10002
  this.controller = controller;
9211
10003
  this.box = box;
9212
10004
  }
9213
10005
 
9214
10006
 
9215
- __init125() {this.tool = "brush"}
10007
+ __init137() {this.tool = "brush"}
9216
10008
  /** The last refusal (a hidden or locked layer), for the editor to show. */
9217
- __init126() {this.error = null}
10009
+ __init138() {this.error = null}
9218
10010
  /** 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}
10011
+ __init139() {this.predicted = []}
10012
+ __init140() {this.mode = null}
10013
+ __init141() {this.penAt = null}
10014
+ __init142() {this.penDown = false}
9223
10015
  doc(e) {
9224
10016
  const b = this.box();
9225
10017
  const s = this.controller.getState();
@@ -9275,13 +10067,13 @@ var PaintGesture = (_class11 = class {
9275
10067
  const p = this.doc(e);
9276
10068
  const s = this.controller.getState();
9277
10069
  const layerId = s.document.activeLayerId;
9278
- if (!layerId || !_optionalChain([s, 'access', _418 => _418.document, 'access', _419 => _419.liveStrokes, 'optionalAccess', _420 => _420[layerId]])) {
10070
+ if (!layerId || !_optionalChain([s, 'access', _506 => _506.document, 'access', _507 => _507.liveStrokes, 'optionalAccess', _508 => _508[layerId]])) {
9279
10071
  this.error = "Pick a stroke layer in the Layers panel (or add one) to select its strokes.";
9280
10072
  return false;
9281
10073
  }
9282
10074
  try {
9283
10075
  const hit = this.controller.liveStrokeAt(layerId, p.x, p.y);
9284
- this.controller.selectStroke(_nullishCoalesce(_optionalChain([hit, 'optionalAccess', _421 => _421.id]), () => ( null)));
10076
+ this.controller.selectStroke(_nullishCoalesce(_optionalChain([hit, 'optionalAccess', _509 => _509.id]), () => ( null)));
9285
10077
  if (!hit) return false;
9286
10078
  this.mode = { kind: "move-stroke", pointerId: e.pointerId, id: hit.id, from: p, to: p };
9287
10079
  return true;
@@ -9293,7 +10085,7 @@ var PaintGesture = (_class11 = class {
9293
10085
  /** While a stroke is dragged: how far it has moved (document pixels), to draw its outline there. */
9294
10086
  get strokeDrag() {
9295
10087
  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;
10088
+ return _optionalChain([m, 'optionalAccess', _510 => _510.kind]) === "move-stroke" ? { id: m.id, dx: m.to.x - m.from.x, dy: m.to.y - m.from.y } : null;
9297
10089
  }
9298
10090
  /**
9299
10091
  * The Stickers tool: pressing a sticker picks it and dragging moves it (one undo step at the
@@ -9367,7 +10159,7 @@ var PaintGesture = (_class11 = class {
9367
10159
  if (!m || m.pointerId !== e.pointerId) return;
9368
10160
  this.mode = null;
9369
10161
  this.predicted = [];
9370
- if (m.kind === "stroke") _optionalChain([this, 'access', _423 => _423.controller, 'access', _424 => _424.activeStroke, 'optionalAccess', _425 => _425.end, 'call', _426 => _426()]);
10162
+ if (m.kind === "stroke") _optionalChain([this, 'access', _511 => _511.controller, 'access', _512 => _512.activeStroke, 'optionalAccess', _513 => _513.end, 'call', _514 => _514()]);
9371
10163
  if (m.kind === "move-stroke") {
9372
10164
  const dx = Math.round((this.doc(e).x - m.from.x) * 100) / 100;
9373
10165
  const dy = Math.round((this.doc(e).y - m.from.y) * 100) / 100;
@@ -9391,8 +10183,8 @@ var PaintGesture = (_class11 = class {
9391
10183
  cancel() {
9392
10184
  this.penDown = false;
9393
10185
  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();
10186
+ if (_optionalChain([this, 'access', _515 => _515.mode, 'optionalAccess', _516 => _516.kind]) === "stroke") _optionalChain([this, 'access', _517 => _517.controller, 'access', _518 => _518.activeStroke, 'optionalAccess', _519 => _519.cancel, 'call', _520 => _520()]);
10187
+ if (_optionalChain([this, 'access', _521 => _521.mode, 'optionalAccess', _522 => _522.kind]) === "sticker") this.controller.cancelStickerPreview();
9396
10188
  this.mode = null;
9397
10189
  }
9398
10190
  get active() {
@@ -9412,7 +10204,7 @@ var PaintGesture = (_class11 = class {
9412
10204
  const py = e.clientY - b.top - b.height / 2;
9413
10205
  this.controller.setView({ zoom, pan: { x: px - (px - s.view.pan.x) * k, y: py - (py - s.view.pan.y) * k } });
9414
10206
  }
9415
- }, _class11);
10207
+ }, _class13);
9416
10208
  var round2 = (v) => Math.round(v * 100) / 100;
9417
10209
  function handlePaintShortcut(controller, gesture, e) {
9418
10210
  const mod = e.ctrlKey || e.metaKey;
@@ -9453,14 +10245,14 @@ function handlePaintShortcut(controller, gesture, e) {
9453
10245
  }
9454
10246
  return false;
9455
10247
  }
9456
- var PaintCanvasPresenter = (_class12 = class {
9457
- constructor(controller) {;_class12.prototype.__init131.call(this);_class12.prototype.__init132.call(this);_class12.prototype.__init133.call(this);
10248
+ var PaintCanvasPresenter = (_class14 = class {
10249
+ constructor(controller) {;_class14.prototype.__init143.call(this);_class14.prototype.__init144.call(this);_class14.prototype.__init145.call(this);
9458
10250
  this.controller = controller;
9459
10251
  }
9460
10252
 
9461
- __init131() {this.uploaded = /* @__PURE__ */ new Map()}
9462
- __init132() {this.scratch = null}
9463
- __init133() {this.size = ""}
10253
+ __init143() {this.uploaded = /* @__PURE__ */ new Map()}
10254
+ __init144() {this.scratch = null}
10255
+ __init145() {this.size = ""}
9464
10256
  /** Draws what changed into `ctx` (a canvas the size of the document); returns the tiles drawn. */
9465
10257
  render(ctx) {
9466
10258
  const { width, height } = this.controller.document.canvas;
@@ -9486,13 +10278,13 @@ var PaintCanvasPresenter = (_class12 = class {
9486
10278
  }, { dirty });
9487
10279
  return drawn;
9488
10280
  }
9489
- }, _class12);
10281
+ }, _class14);
9490
10282
  function guideOverlay(controller, drag) {
9491
10283
  const s = controller.getState();
9492
10284
  const size = s.document.canvas;
9493
10285
  const out = [];
9494
10286
  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;
10287
+ const strokePicked = layerId && s.selectedStroke ? _optionalChain([s, 'access', _523 => _523.document, 'access', _524 => _524.liveStrokes, 'optionalAccess', _525 => _525[layerId], 'optionalAccess', _526 => _526.some, 'call', _527 => _527((x) => x.id === s.selectedStroke)]) : false;
9496
10288
  const area = strokePicked ? controller.liveStrokeArea(s.selectedStroke) : null;
9497
10289
  if (area) {
9498
10290
  const dx = drag && drag.id === s.selectedStroke ? drag.dx : 0;
@@ -9505,7 +10297,7 @@ function guideOverlay(controller, drag) {
9505
10297
  ];
9506
10298
  for (let i = 0; i < 4; i++) out.push({ from: box[i], to: box[(i + 1) % 4], kind: "stroke" });
9507
10299
  }
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;
10300
+ const picked = layerId && s.selectedSticker ? _optionalChain([s, 'access', _528 => _528.document, 'access', _529 => _529.stickers, 'optionalAccess', _530 => _530[layerId], 'optionalAccess', _531 => _531.find, 'call', _532 => _532((x) => x.id === s.selectedSticker)]) : void 0;
9509
10301
  if (picked) {
9510
10302
  const r = picked.size / 2 + 2;
9511
10303
  const a = picked.rotation * Math.PI / 180;
@@ -9545,11 +10337,12 @@ var PAPER_SLIDERS = [
9545
10337
  { path: "absorption", label: "Absorption", min: 0, max: 1, step: 0.01 },
9546
10338
  { path: "permeability", label: "Permeability", min: 0, max: 1, step: 0.01 },
9547
10339
  { path: "roughness", label: "Roughness", min: 0, max: 1, step: 0.01 },
9548
- { path: "metallic", label: "Metallic", min: 0, max: 1, step: 0.01 }
10340
+ { path: "metallic", label: "Metallic", min: 0, max: 1, step: 0.01 },
10341
+ { path: "backlight", label: "Backlight", min: 0, max: 1, step: 0.01 }
9549
10342
  ];
9550
10343
  function readPath(obj, path) {
9551
10344
  let v = obj;
9552
- for (const k of path.split(".")) v = _optionalChain([v, 'optionalAccess', _445 => _445[k]]);
10345
+ for (const k of path.split(".")) v = _optionalChain([v, 'optionalAccess', _533 => _533[k]]);
9553
10346
  return typeof v === "number" ? v : 0;
9554
10347
  }
9555
10348
  function patchPath(obj, path, value) {
@@ -9594,11 +10387,19 @@ var BLEND_MENU_GROUPS = [
9594
10387
  { label: "Standard", modes: _chunkOLNIPTB3cjs.BLEND_MODES.filter((m) => !m.painterly && m.id !== "pigment") },
9595
10388
  { label: "Paint", modes: _chunkOLNIPTB3cjs.BLEND_MODES.filter((m) => m.painterly || m.id === "pigment") }
9596
10389
  ];
10390
+ var LIGHT_SLIDER = { path: "light.amount", label: "Light", min: 0, max: 1, step: 0.01 };
9597
10391
  var PICKUP_SLIDERS = [
9598
10392
  { path: "behavior.pickup", label: "Pick-up", min: 0, max: 1, step: 0.01 },
9599
10393
  { path: "behavior.mix", label: "Mix", min: 0, max: 1, step: 0.01 }
9600
10394
  ];
9601
10395
  function brushEngineControls(brush) {
10396
+ const c = engineControls(brush);
10397
+ const wetPaint = brush.engine === "wet" && _optionalChain([brush, 'access', _534 => _534.wet, 'optionalAccess', _535 => _535.tool]) === "paint";
10398
+ if (!c || !(wetPaint || ["dab", "mixer", "particle", "bristle", "impasto"].includes(brush.engine))) return c;
10399
+ 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 }] : [];
10400
+ return { ...c, sliders: [...c.sliders, LIGHT_SLIDER, ...extra], light: true };
10401
+ }
10402
+ function engineControls(brush) {
9602
10403
  switch (brush.engine) {
9603
10404
  case "particle":
9604
10405
  return {
@@ -9671,7 +10472,7 @@ function brushEngineControls(brush) {
9671
10472
  impastoTool: true
9672
10473
  };
9673
10474
  case "wet": {
9674
- const tool = _nullishCoalesce(_optionalChain([brush, 'access', _446 => _446.wet, 'optionalAccess', _447 => _447.tool]), () => ( "paint"));
10475
+ const tool = _nullishCoalesce(_optionalChain([brush, 'access', _536 => _536.wet, 'optionalAccess', _537 => _537.tool]), () => ( "paint"));
9675
10476
  const sliders = tool === "paint" ? [
9676
10477
  { path: "wet.water", label: "Water", min: 0, max: 1, step: 0.01 },
9677
10478
  { path: "wet.pigment", label: "Pigment", min: 0, max: 1, step: 0.01 },
@@ -9804,7 +10605,7 @@ function startWetClock(controller, frame, cancel) {
9804
10605
  id = raf(loop);
9805
10606
  return () => {
9806
10607
  stopped = true;
9807
- _optionalChain([caf, 'optionalCall', _448 => _448(id)]);
10608
+ _optionalChain([caf, 'optionalCall', _538 => _538(id)]);
9808
10609
  };
9809
10610
  }
9810
10611
  function drawWaterOverlay(controller, ctx) {
@@ -10450,4 +11251,9 @@ function createPaintEditorDocument(controller, options = {}) {
10450
11251
 
10451
11252
 
10452
11253
 
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;
11254
+
11255
+
11256
+
11257
+
11258
+
11259
+ exports.BACKLIGHT_SPILL = BACKLIGHT_SPILL; exports.BLEND_MENU_GROUPS = BLEND_MENU_GROUPS; exports.BLEND_MODES = _chunkOLNIPTB3cjs.BLEND_MODES; exports.BRUSH_ID = _chunk4T47Z4J3cjs.BRUSH_ID; exports.BRUSH_ROTATIONS = _chunk4T47Z4J3cjs.BRUSH_ROTATIONS; exports.BRUSH_SLIDERS = BRUSH_SLIDERS; exports.BUILTIN_BRUSHES = _chunk4T47Z4J3cjs.BUILTIN_BRUSHES; exports.BristleStroke = _chunk4T47Z4J3cjs.BristleStroke; exports.DEFAULT_BACKLIGHT_COLOR = DEFAULT_BACKLIGHT_COLOR; exports.DEFAULT_BEHAVIOR = _chunk4T47Z4J3cjs.DEFAULT_BEHAVIOR; exports.DEFAULT_BRISTLE = _chunk4T47Z4J3cjs.DEFAULT_BRISTLE; exports.DEFAULT_BRUSH_ID = _chunk4T47Z4J3cjs.DEFAULT_BRUSH_ID; exports.DEFAULT_DPI = DEFAULT_DPI; exports.DEFAULT_DYNAMICS = _chunk4T47Z4J3cjs.DEFAULT_DYNAMICS; exports.DEFAULT_FLOW = _chunkVMROIWLLcjs.DEFAULT_FLOW; exports.DEFAULT_IMPASTO = _chunk4T47Z4J3cjs.DEFAULT_IMPASTO; exports.DEFAULT_LIGHT = _chunk4T47Z4J3cjs.DEFAULT_LIGHT; exports.DEFAULT_LIGHTING = DEFAULT_LIGHTING; exports.DEFAULT_MULTI_COLOR = _chunk4T47Z4J3cjs.DEFAULT_MULTI_COLOR; exports.DEFAULT_PAINT_GRADIENT = _chunkPQ3H2AANcjs.DEFAULT_PAINT_GRADIENT; exports.DEFAULT_PAINT_GRADIENT_MAPPING = _chunkPQ3H2AANcjs.DEFAULT_PAINT_GRADIENT_MAPPING; exports.DEFAULT_PAPER = _chunk4T47Z4J3cjs.DEFAULT_PAPER; exports.DEFAULT_PARTICLE = _chunk4T47Z4J3cjs.DEFAULT_PARTICLE; exports.DEFAULT_STICKER = _chunk4T47Z4J3cjs.DEFAULT_STICKER; exports.DEFAULT_SWATCHES = DEFAULT_SWATCHES; exports.DEFAULT_VIEW = DEFAULT_VIEW; exports.DEFAULT_WET_BRUSH = _chunk4T47Z4J3cjs.DEFAULT_WET_BRUSH; exports.DEFAULT_WET_PARAMS = _chunkVMROIWLLcjs.DEFAULT_WET_PARAMS; exports.DryStroke = _chunk4T47Z4J3cjs.DryStroke; exports.EFFECT_MAP_REPEATS = _chunk4T47Z4J3cjs.EFFECT_MAP_REPEATS; exports.EFFECT_STRIP_HEIGHT = _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.IDENTITY_COPY; exports.IMPASTO_TOOLS = _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.PAINT_BRUSH_CATEGORIES; exports.PAINT_BRUSH_ENGINES = _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.PAPER_FAMILIES; exports.PAPER_LIBRARY = _chunk4T47Z4J3cjs.PAPER_LIBRARY; exports.PAPER_NODE_ID = PAPER_NODE_ID; exports.PAPER_SLIDERS = PAPER_SLIDERS; exports.PAPER_TARGET = PAPER_TARGET; exports.PARTICLE_KINDS = _chunk4T47Z4J3cjs.PARTICLE_KINDS; exports.PARTICLE_KIND_LABELS = PARTICLE_KIND_LABELS; exports.PARTICLE_RENDERS = _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.ParticleStroke; exports.PenDabWalker = _chunk4T47Z4J3cjs.PenDabWalker; exports.PenSmoother = _chunk4T47Z4J3cjs.PenSmoother; exports.PerspectiveSnapper = PerspectiveSnapper; exports.SNAP_DECIDE_DISTANCE = SNAP_DECIDE_DISTANCE; exports.STAMP_KINDS = _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.StickerSpray; exports.TRACING_NODE_ID = TRACING_NODE_ID; exports.ThickStrokeDriver = ThickStrokeDriver; exports.TileableNoise = _chunkPQ3H2AANcjs.TileableNoise; exports.WET_FLOW_KINDS = _chunkVMROIWLLcjs.WET_FLOW_KINDS; exports.WET_GPU_MODES = WET_GPU_MODES; exports.WET_GPU_OPTIONS = WET_GPU_OPTIONS; exports.WET_MEDIA = _chunk4T47Z4J3cjs.WET_MEDIA; exports.WET_MEDIUM_LABELS = WET_MEDIUM_LABELS; exports.WET_MEDIUM_PARAMS = WET_MEDIUM_PARAMS; exports.WET_PARAM_KEYS = _chunkVMROIWLLcjs.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 = _chunk4T47Z4J3cjs.WET_TOOLS; exports.WET_TOOL_LABELS = WET_TOOL_LABELS; exports.WetStrokeDriver = WetStrokeDriver; exports.addPaintGradientStop = _chunkPQ3H2AANcjs.addPaintGradientStop; exports.affineCopy = affineCopy; exports.applyAffine = applyAffine; exports.backlightOf = backlightOf; exports.brushEngineControls = brushEngineControls; exports.brushLoadColor = brushLoadColor; exports.brushLoadForStroke = brushLoadForStroke; exports.brushTakesPaint = _chunk4T47Z4J3cjs.brushTakesPaint; exports.brushToJSON = _chunk4T47Z4J3cjs.brushToJSON; exports.buildTimeLapse = buildTimeLapse; exports.checkPaintEffectPlace = checkPaintEffectPlace; exports.clonePaper = _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.drawSticker; exports.drawWaterOverlay = drawWaterOverlay; exports.effectDabField = _chunk4T47Z4J3cjs.effectDabField; exports.effectLayersToJSON = effectLayersToJSON; exports.effectPaintKey = _chunk4T47Z4J3cjs.effectPaintKey; exports.effectPaintSize = _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.hexToRgb8; exports.hslToRgb = hslToRgb; exports.hsvToRgb = hsvToRgb; exports.hueToWheel = _chunk7PT4RJL5cjs.hueToWheel; exports.invertAffine = invertAffine; exports.isBlendMode = _chunkOLNIPTB3cjs.isBlendMode; exports.isGradientPaint = _chunkPQ3H2AANcjs.isGradientPaint; exports.isPaintEffectPaint = _chunk4T47Z4J3cjs.isPaintEffectPaint; exports.isPainterlyBlendMode = _chunkOLNIPTB3cjs.isPainterlyBlendMode; exports.isPalmTouch = isPalmTouch; exports.isWetBrush = _chunk4T47Z4J3cjs.isWetBrush; exports.isWetCapable = _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.mixerTip; exports.multiColorForDab = multiColorForDab; exports.multiColorGradient = multiColorGradient; exports.multiColorPalette = multiColorPalette; exports.multiColorSliders = multiColorSliders; exports.multiColorToPaint = _chunk4T47Z4J3cjs.multiColorToPaint; exports.newPaintLayerId = newPaintLayerId; exports.nextPaperEffectVersion = nextPaperEffectVersion; exports.normalizeBehavior = _chunk4T47Z4J3cjs.normalizeBehavior; exports.normalizeBristle = _chunk4T47Z4J3cjs.normalizeBristle; exports.normalizeBrushStroke = normalizeBrushStroke; exports.normalizeCanvas = normalizeCanvas; exports.normalizeDynamics = _chunk4T47Z4J3cjs.normalizeDynamics; exports.normalizeFlow = _chunkVMROIWLLcjs.normalizeFlow; exports.normalizeHexColor = _chunkPQ3H2AANcjs.normalizeHexColor; exports.normalizeImpasto = _chunk4T47Z4J3cjs.normalizeImpasto; exports.normalizeLight = _chunk4T47Z4J3cjs.normalizeLight; exports.normalizeLighting = normalizeLighting; exports.normalizeMultiColor = _chunk4T47Z4J3cjs.normalizeMultiColor; exports.normalizePaintBrush = _chunk4T47Z4J3cjs.normalizePaintBrush; exports.normalizePaper = _chunk4T47Z4J3cjs.normalizePaper; exports.normalizeParticle = _chunk4T47Z4J3cjs.normalizeParticle; exports.normalizePerspective = normalizePerspective; exports.normalizeRotation = normalizeRotation; exports.normalizeStencil = normalizeStencil; exports.normalizeStickerSettings = _chunk4T47Z4J3cjs.normalizeStickerSettings; exports.normalizeStickerUpdate = _chunk4T47Z4J3cjs.normalizeStickerUpdate; exports.normalizeSymmetry = normalizeSymmetry; exports.normalizeView = normalizeView; exports.normalizeWetBrush = _chunk4T47Z4J3cjs.normalizeWetBrush; exports.normalizeWetParams = _chunkVMROIWLLcjs.normalizeWetParams; exports.overPixels = overPixels; exports.padSampleRadius = padSampleRadius; exports.paintGradientMappingFor = _chunkPQ3H2AANcjs.paintGradientMappingFor; exports.paintPlaybackPanel = paintPlaybackPanel; exports.paintSourceColor = _chunkPQ3H2AANcjs.paintSourceColor; exports.paintSourceCss = _chunkPQ3H2AANcjs.paintSourceCss; exports.paintToMultiColor = _chunk4T47Z4J3cjs.paintToMultiColor; exports.paperEffectFromJSON = paperEffectFromJSON; exports.paperEffectToJSON = paperEffectToJSON; exports.paperGrain = paperGrain; exports.paperKey = paperKey; exports.paperNode = paperNode; exports.paperPreset = _chunk4T47Z4J3cjs.paperPreset; exports.parseBrushLibrary = _chunk4T47Z4J3cjs.parseBrushLibrary; exports.parseHexColor = parseHexColor; exports.parsePaintSource = _chunkPQ3H2AANcjs.parsePaintSource; exports.patchBrushValue = patchBrushValue; exports.patchPath = patchPath; exports.penOrientation = _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.readEffectStrokeMap; exports.readPaintEffectPaint = _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.renderPaper; exports.renderStickers = _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.sampleEffectAt; exports.sampleEffectField = _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.stampCoverage; exports.standardBlendMode = _chunkOLNIPTB3cjs.standardBlendMode; exports.startWetClock = startWetClock; exports.stencilMask = stencilMask; exports.stencilOutline = stencilOutline; exports.stickerAt = _chunk4T47Z4J3cjs.stickerAt; exports.stickerRect = _chunk4T47Z4J3cjs.stickerRect; exports.stickerSurface = _chunk4T47Z4J3cjs.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 = _chunk4T47Z4J3cjs.tiltToOrientation; exports.timeLapseVideo = timeLapseVideo; exports.timeLapseZip = timeLapseZip; exports.toStrokePoint = _chunk4T47Z4J3cjs.toStrokePoint; exports.toggleStamp = toggleStamp; exports.translateStencil = translateStencil; exports.updatePaintGradientStop = _chunkPQ3H2AANcjs.updatePaintGradientStop; exports.usesPaintExchange = _chunk4T47Z4J3cjs.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 = _chunkVMROIWLLcjs.wetSupport; exports.wheelAngle = wheelAngle; exports.wheelPick = wheelPick; exports.wheelPoint = wheelPoint; exports.wheelToHue = _chunk7PT4RJL5cjs.wheelToHue; exports.withPaintPlayback = withPaintPlayback;