@pixerate/editor 0.253.0 → 0.254.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/animation-editor/index.cjs +53 -53
  2. package/dist/animation-editor/index.js +2 -2
  3. package/dist/canvas/changeFeed.d.cts +6 -0
  4. package/dist/canvas/changeFeed.d.ts +6 -0
  5. package/dist/canvas/index.cjs +16 -9
  6. package/dist/canvas/index.js +10 -3
  7. package/dist/{chunk-QKXO6JPP.cjs → chunk-2OQGPEHH.cjs} +14 -14
  8. package/dist/{chunk-Z3BRH4JK.cjs → chunk-557BDWKO.cjs} +6 -6
  9. package/dist/{chunk-ZBCT5MEA.cjs → chunk-CBVNCWJM.cjs} +7 -7
  10. package/dist/{chunk-226QDRO3.js → chunk-DHQPRKXF.js} +1 -1
  11. package/dist/{chunk-ABL7IWFZ.js → chunk-GBEF2D4P.js} +1 -1
  12. package/dist/{chunk-ISIO63Q5.cjs → chunk-L2JTLW6B.cjs} +150 -25
  13. package/dist/{chunk-JA57JIT5.js → chunk-MV7CYFMJ.js} +133 -8
  14. package/dist/{chunk-5E2Q7ZHI.js → chunk-MWUZCEFT.js} +1 -1
  15. package/dist/effects/conformance.d.cts +1 -0
  16. package/dist/effects/conformance.d.ts +1 -0
  17. package/dist/effects/fake.d.cts +7 -1
  18. package/dist/effects/fake.d.ts +7 -1
  19. package/dist/effects/helpers.d.cts +36 -4
  20. package/dist/effects/helpers.d.ts +36 -4
  21. package/dist/effects/index.cjs +331 -102
  22. package/dist/effects/index.js +238 -9
  23. package/dist/effects/types.d.cts +84 -5
  24. package/dist/effects/types.d.ts +84 -5
  25. package/dist/image-editor/index.cjs +15 -15
  26. package/dist/image-editor/index.js +1 -1
  27. package/dist/paint-editor/index.cjs +7 -7
  28. package/dist/paint-editor/index.js +2 -2
  29. package/dist/vector/index.cjs +6 -6
  30. package/dist/vector/index.js +1 -1
  31. package/dist/video-editor/index.cjs +344 -344
  32. package/dist/video-editor/index.js +4 -4
  33. package/package.json +1 -1
@@ -16,7 +16,7 @@ var _chunkDGWOGQJ5cjs = require('../chunk-DGWOGQJ5.cjs');
16
16
 
17
17
 
18
18
 
19
- var _chunkZ3BRH4JKcjs = require('../chunk-Z3BRH4JK.cjs');
19
+ var _chunk557BDWKOcjs = require('../chunk-557BDWKO.cjs');
20
20
 
21
21
 
22
22
 
@@ -71,7 +71,7 @@ var _chunkPY4GWSE2cjs = require('../chunk-PY4GWSE2.cjs');
71
71
 
72
72
 
73
73
 
74
- var _chunkISIO63Q5cjs = require('../chunk-ISIO63Q5.cjs');
74
+ var _chunkL2JTLW6Bcjs = require('../chunk-L2JTLW6B.cjs');
75
75
  require('../chunk-WOFXZ3XU.cjs');
76
76
  require('../chunk-PXDIAYB6.cjs');
77
77
  require('../chunk-FQPUF2W2.cjs');
@@ -506,11 +506,11 @@ function shotLayersShapeProblems(shot, name) {
506
506
  if (layer.effect === void 0 && layer.subject === void 0) problems.push(`${where} needs an "effect", a "subject" or both.`);
507
507
  if (layer.effect !== void 0 && (typeof layer.effect !== "string" || !layer.effect)) problems.push(`${where}: "effect" must be an effect id.`);
508
508
  if (layer.subject !== void 0) {
509
- const kind = _chunkZ3BRH4JKcjs.effectSubjectKind.call(void 0, layer.subject);
509
+ const kind = _chunk557BDWKOcjs.effectSubjectKind.call(void 0, layer.subject);
510
510
  if (kind === "video") problems.push(`${where}: an animation layer cannot play a video; use { "image" }, { "svg" }, { "text" } or { "slot" } (the video editor plays video).`);
511
511
  else if (!kind || !String(layer.subject[kind]).trim()) problems.push(`${where}: "subject" must be { "image": url }, { "svg": markup }, { "text": words } or { "slot": name }.`);
512
512
  }
513
- if (layer.role !== void 0 && !_chunkISIO63Q5cjs.EFFECT_LAYER_ROLES.includes(layer.role)) problems.push(`${where}: "role" must be one of ${_chunkISIO63Q5cjs.EFFECT_LAYER_ROLES.join(", ")}.`);
513
+ if (layer.role !== void 0 && !_chunkL2JTLW6Bcjs.EFFECT_LAYER_ROLES.includes(layer.role)) problems.push(`${where}: "role" must be one of ${_chunkL2JTLW6Bcjs.EFFECT_LAYER_ROLES.join(", ")}.`);
514
514
  if (layer.rect !== void 0) {
515
515
  const r = layer.rect;
516
516
  if (!isObject(r) || !["x", "y", "width", "height"].every((k) => isNum(r[k])) || !(r.width > 0 && r.height > 0)) problems.push(`${where}: "rect" must be { x, y, width, height } in artwork units, width and height > 0.`);
@@ -1131,9 +1131,9 @@ function compileMotionScene(scene) {
1131
1131
  const start = t;
1132
1132
  let cursor = start;
1133
1133
  const f = motionShotTimeScale(shot);
1134
- const moveIn = shot.camera ? _chunkISIO63Q5cjs.cameraMoveParts.call(void 0, shot.camera.in) : void 0;
1135
- const transitionMs = index > 0 && shot.transition ? (_nullishCoalesce(shot.transition.ms, () => ( _chunkISIO63Q5cjs.EFFECT_TRANSITION_MS))) * f : 0;
1136
- const moveDuring = shot.camera ? _chunkISIO63Q5cjs.cameraMoveParts.call(void 0, shot.camera.during) : void 0;
1134
+ const moveIn = shot.camera ? _chunkL2JTLW6Bcjs.cameraMoveParts.call(void 0, shot.camera.in) : void 0;
1135
+ const transitionMs = index > 0 && shot.transition ? (_nullishCoalesce(shot.transition.ms, () => ( _chunkL2JTLW6Bcjs.EFFECT_TRANSITION_MS))) * f : 0;
1136
+ const moveDuring = shot.camera ? _chunkL2JTLW6Bcjs.cameraMoveParts.call(void 0, shot.camera.during) : void 0;
1137
1137
  let playsIn = false;
1138
1138
  if (shot.camera) {
1139
1139
  const framed = motionCameraFramed(shot.camera);
@@ -1502,7 +1502,7 @@ function motionShotAt(timeline, time) {
1502
1502
  // src/animation-editor/camera.ts
1503
1503
  var windowCache = /* @__PURE__ */ new WeakMap();
1504
1504
  function motionShotCameraMove(shot, role) {
1505
- return _chunkISIO63Q5cjs.cameraMoveParts.call(void 0, _optionalChain([shot, 'optionalAccess', _55 => _55.camera, 'optionalAccess', _56 => _56[role === "transition" ? "in" : "during"]]));
1505
+ return _chunkL2JTLW6Bcjs.cameraMoveParts.call(void 0, _optionalChain([shot, 'optionalAccess', _55 => _55.camera, 'optionalAccess', _56 => _56[role === "transition" ? "in" : "during"]]));
1506
1506
  }
1507
1507
  var motionCameraStage = (scene) => ({ width: scene.artwork.width, height: scene.artwork.height });
1508
1508
  var effectsPlayCamera = (provider) => !!provider && provider.capabilities.supportsCamera === true && typeof provider.cameraView === "function";
@@ -1524,7 +1524,7 @@ function motionCameraWindows(scene, provider) {
1524
1524
  const track = timeline.tracks.get(motionTrackKey("", "camera"));
1525
1525
  const boxView = (t) => {
1526
1526
  const b = trackValue(track, t);
1527
- return _chunkISIO63Q5cjs.cameraViewOfBox.call(void 0, Array.isArray(b) ? { x: b[0], y: b[1], width: b[2], height: b[3] } : full, stage);
1527
+ return _chunkL2JTLW6Bcjs.cameraViewOfBox.call(void 0, Array.isArray(b) ? { x: b[0], y: b[1], width: b[2], height: b[3] } : full, stage);
1528
1528
  };
1529
1529
  const view = (move, values, from, to, u, ms2) => {
1530
1530
  try {
@@ -1540,7 +1540,7 @@ function motionCameraWindows(scene, provider) {
1540
1540
  const eases = timing.commandsStart > timing.start && !!_optionalChain([track, 'optionalAccess', _57 => _57.some, 'call', _58 => _58((seg) => seg.shot === timing.id)]);
1541
1541
  const framed = !!_optionalChain([track, 'optionalAccess', _59 => _59.some, 'call', _60 => _60((seg) => seg.shot === timing.id)]);
1542
1542
  if (timing.cameraIn && timing.commandsStart > timing.start) {
1543
- const spec = _chunkISIO63Q5cjs.cameraMoveParts.call(void 0, _optionalChain([shot, 'access', _61 => _61.camera, 'optionalAccess', _62 => _62.in]));
1543
+ const spec = _chunkL2JTLW6Bcjs.cameraMoveParts.call(void 0, _optionalChain([shot, 'access', _61 => _61.camera, 'optionalAccess', _62 => _62.in]));
1544
1544
  const from = _nullishCoalesce(carry, () => ( boxView(timing.start)));
1545
1545
  const to = boxView(timing.commandsStart);
1546
1546
  if (view(spec.move, spec.values, from, to, 0, timing.commandsStart - timing.start)) {
@@ -1552,7 +1552,7 @@ function motionCameraWindows(scene, provider) {
1552
1552
  carry = null;
1553
1553
  } else if (framed) carry = null;
1554
1554
  if (timing.cameraDuring && timing.end > timing.commandsEnd) {
1555
- const spec = _chunkISIO63Q5cjs.cameraMoveParts.call(void 0, _optionalChain([shot, 'access', _63 => _63.camera, 'optionalAccess', _64 => _64.during]));
1555
+ const spec = _chunkL2JTLW6Bcjs.cameraMoveParts.call(void 0, _optionalChain([shot, 'access', _63 => _63.camera, 'optionalAccess', _64 => _64.during]));
1556
1556
  const from = _nullishCoalesce(carry, () => ( boxView(timing.commandsEnd)));
1557
1557
  const ms2 = timing.end - timing.commandsEnd;
1558
1558
  const last = view(spec.move, spec.values, from, from, 1, ms2);
@@ -1569,8 +1569,8 @@ var lerp = (a, b, t) => a + (b - a) * t;
1569
1569
  var smooth = (t) => t * t * (3 - 2 * t);
1570
1570
  function easeViews(from, to, u, stage) {
1571
1571
  const k = smooth(Math.min(1, Math.max(0, u)));
1572
- const a = _chunkISIO63Q5cjs.cameraBoxOfView.call(void 0, from, stage);
1573
- const b = _chunkISIO63Q5cjs.cameraBoxOfView.call(void 0, to, stage);
1572
+ const a = _chunkL2JTLW6Bcjs.cameraBoxOfView.call(void 0, from, stage);
1573
+ const b = _chunkL2JTLW6Bcjs.cameraBoxOfView.call(void 0, to, stage);
1574
1574
  const zoom = from.zoom * Math.pow(to.zoom / from.zoom, k);
1575
1575
  const cx = lerp(a.x + a.width / 2, b.x + b.width / 2, k);
1576
1576
  const cy = lerp(a.y + a.height / 2, b.y + b.height / 2, k);
@@ -1602,7 +1602,7 @@ function motionCameraViewAt(scene, time, provider) {
1602
1602
  return retaken ? null : before.after;
1603
1603
  }
1604
1604
  function motionCameraBox(view, scene) {
1605
- return _chunkISIO63Q5cjs.cameraBoxOfView.call(void 0, view, motionCameraStage(scene));
1605
+ return _chunkL2JTLW6Bcjs.cameraBoxOfView.call(void 0, view, motionCameraStage(scene));
1606
1606
  }
1607
1607
  function motionCameraTransform(view, width) {
1608
1608
  if (!view || !view.tilt && !view.turn) return "";
@@ -1643,7 +1643,7 @@ async function checkMotionCameraMoves(scene, provider) {
1643
1643
  const spec = shot.camera[field];
1644
1644
  if (spec === void 0) continue;
1645
1645
  try {
1646
- await _chunkISIO63Q5cjs.resolveCameraMoveSpec.call(void 0, provider, spec, role);
1646
+ await _chunkL2JTLW6Bcjs.resolveCameraMoveSpec.call(void 0, provider, spec, role);
1647
1647
  } catch (error) {
1648
1648
  const code = error.code === "invalid_values" || /preset/.test(error.message) ? "camera-move-values" : "camera-move-unknown";
1649
1649
  issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, code, error.message, { shot: shot.id }));
@@ -1671,7 +1671,7 @@ function motionLayerSpans(scene) {
1671
1671
  (_nullishCoalesce(shot.layers, () => ( []))).forEach((layer, index) => {
1672
1672
  const start = at(shotIndex2, layer.from, shots[shotIndex2].start);
1673
1673
  const end = Math.max(start, at(shotIndex2, _nullishCoalesce(layer.until, () => ( { shots: 1 })), total));
1674
- out.push({ layer, shot: shot.id, shotIndex: shotIndex2, index, role: _chunkZ3BRH4JKcjs.effectLayerRole.call(void 0, layer), start, end });
1674
+ out.push({ layer, shot: shot.id, shotIndex: shotIndex2, index, role: _chunk557BDWKOcjs.effectLayerRole.call(void 0, layer), start, end });
1675
1675
  });
1676
1676
  });
1677
1677
  spanCache.set(scene, out);
@@ -1766,7 +1766,7 @@ var MotionLayersEngine = (_class = class {
1766
1766
  __init6() {this.generation = 0}
1767
1767
 
1768
1768
  constructor(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);
1769
- this.slot = _nullishCoalesce(options.slot, () => ( new (0, _chunkISIO63Q5cjs.EffectsSlot)()));
1769
+ this.slot = _nullishCoalesce(options.slot, () => ( new (0, _chunkL2JTLW6Bcjs.EffectsSlot)()));
1770
1770
  this.host = _nullishCoalesce(options.host, () => ( null));
1771
1771
  this.frameSvg = options.frameSvg;
1772
1772
  this.options = { scale: _nullishCoalesce(options.scale, () => ( 1)), maxSize: _nullishCoalesce(options.maxSize, () => ( 1280)), fps: _nullishCoalesce(options.fps, () => ( 30)), maxEntries: _nullishCoalesce(options.maxEntries, () => ( 160)) };
@@ -1886,8 +1886,8 @@ var MotionLayersEngine = (_class = class {
1886
1886
  const effect = layer.effect;
1887
1887
  this.request(key, { layer: layer.id }, async () => {
1888
1888
  const d = await this.describe(effect);
1889
- const values = _chunkISIO63Q5cjs.resolveEffectValues.call(void 0, d, layer.values);
1890
- const input = _chunkZ3BRH4JKcjs.effectLayerInput.call(void 0, d);
1889
+ const values = _chunkL2JTLW6Bcjs.resolveEffectValues.call(void 0, d, layer.values);
1890
+ const input = _chunk557BDWKOcjs.effectLayerInput.call(void 0, d);
1891
1891
  const inputs = layer.subject && input ? { [input]: await this.subjectPixels(layer.subject, size) } : void 0;
1892
1892
  const pixels = await this.pool.render(provider, effect, size, { time, values, ...inputs ? { inputs } : {} });
1893
1893
  return host.encode(pixels);
@@ -1908,7 +1908,7 @@ var MotionLayersEngine = (_class = class {
1908
1908
  const fromKey = `from:${sceneId(scene)}:${active.shot}:${size.width}x${size.height}`;
1909
1909
  this.request(key, { transition: active.shot }, async () => {
1910
1910
  const d = await this.describe(t.effect);
1911
- const values = _chunkISIO63Q5cjs.resolveEffectValues.call(void 0, d, t.values);
1911
+ const values = _chunkL2JTLW6Bcjs.resolveEffectValues.call(void 0, d, t.values);
1912
1912
  frameSvg(scene, Math.max(0, active.start - 1e-3));
1913
1913
  frameSvg(scene, at);
1914
1914
  await this.idleExcept(key);
@@ -3325,7 +3325,7 @@ function toFlairSvgMotion(scene, options = {}) {
3325
3325
  if (w) return `${w.move}@${w.shot}`;
3326
3326
  const seg = camera.find((x) => t >= x.start - 1e-6 && t < Math.max(x.end, x.start + 1e-3));
3327
3327
  const shot = seg ? scene.shots.find((x) => x.id === seg.shot) : void 0;
3328
- const move = _optionalChain([_chunkISIO63Q5cjs.cameraMoveParts.call(void 0, _optionalChain([shot, 'optionalAccess', _155 => _155.camera, 'optionalAccess', _156 => _156.in])), 'optionalAccess', _157 => _157.move]);
3328
+ const move = _optionalChain([_chunkL2JTLW6Bcjs.cameraMoveParts.call(void 0, _optionalChain([shot, 'optionalAccess', _155 => _155.camera, 'optionalAccess', _156 => _156.in])), 'optionalAccess', _157 => _157.move]);
3329
3329
  return move && shot ? `${move}@${shot.id}` : void 0;
3330
3330
  };
3331
3331
  const unplayed = scene.shots.filter((x) => x.camera && (x.camera.in !== void 0 || x.camera.during !== void 0) && !windows.some((w) => w.shot === x.id));
@@ -4429,7 +4429,7 @@ var kindHint = (d) => {
4429
4429
  function withPreset(d, preset2, values) {
4430
4430
  if (preset2 === void 0) return values;
4431
4431
  const p = d.presets.find((x) => x.name === preset2);
4432
- if (!p) throw new (0, _chunkISIO63Q5cjs.EffectUnsupportedError)(`"${d.id}" has no preset "${preset2}"; ${d.presets.length ? `its presets are ${d.presets.map((x) => `"${x.name}"`).join(", ")}` : "it has no presets"}.`, d.id);
4432
+ if (!p) throw new (0, _chunkL2JTLW6Bcjs.EffectUnsupportedError)(`"${d.id}" has no preset "${preset2}"; ${d.presets.length ? `its presets are ${d.presets.map((x) => `"${x.name}"`).join(", ")}` : "it has no presets"}.`, d.id);
4433
4433
  return { ...p.values, ..._nullishCoalesce(values, () => ( {})) };
4434
4434
  }
4435
4435
  async function resolveMotionShotLayer(provider, layer) {
@@ -4439,27 +4439,27 @@ async function resolveMotionShotLayer(provider, layer) {
4439
4439
  const { provider: _p, name: _n, ...rest } = layer;
4440
4440
  return rest;
4441
4441
  }
4442
- if (!provider) throw new (0, _chunkISIO63Q5cjs.NoEffectsProviderError)();
4442
+ if (!provider) throw new (0, _chunkL2JTLW6Bcjs.NoEffectsProviderError)();
4443
4443
  const d = await provider.describe(layer.effect);
4444
- if (!_chunkZ3BRH4JKcjs.effectLayerOffered.call(void 0, d)) throw new (0, _chunkISIO63Q5cjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) cannot be a layer; ${kindHint(d)}. A layer's effect is a generator, filter, overlay or title (search_effects with use "generator", "filter", "overlay" or "title").`, d.id);
4445
- const input = _chunkZ3BRH4JKcjs.effectLayerInput.call(void 0, d);
4446
- if (layer.subject && !input) throw new (0, _chunkISIO63Q5cjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) draws its own picture and takes no subject; leave "subject" out, or pick a filter to change the subject.`, d.id);
4447
- if (!layer.subject && input && _optionalChain([d, 'access', _198 => _198.inputs, 'access', _199 => _199.find, 'call', _200 => _200((i) => i.name === input), 'optionalAccess', _201 => _201.required])) throw new (0, _chunkISIO63Q5cjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) changes a picture; give a "subject": { "image": url }, { "svg": markup }, { "text": words } or { "slot": name }.`, d.id);
4444
+ if (!_chunk557BDWKOcjs.effectLayerOffered.call(void 0, d)) throw new (0, _chunkL2JTLW6Bcjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) cannot be a layer; ${kindHint(d)}. A layer's effect is a generator, filter, overlay or title (search_effects with use "generator", "filter", "overlay" or "title").`, d.id);
4445
+ const input = _chunk557BDWKOcjs.effectLayerInput.call(void 0, d);
4446
+ if (layer.subject && !input) throw new (0, _chunkL2JTLW6Bcjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) draws its own picture and takes no subject; leave "subject" out, or pick a filter to change the subject.`, d.id);
4447
+ if (!layer.subject && input && _optionalChain([d, 'access', _198 => _198.inputs, 'access', _199 => _199.find, 'call', _200 => _200((i) => i.name === input), 'optionalAccess', _201 => _201.required])) throw new (0, _chunkL2JTLW6Bcjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) changes a picture; give a "subject": { "image": url }, { "svg": markup }, { "text": words } or { "slot": name }.`, d.id);
4448
4448
  const values = withPreset(d, layer.preset, layer.values);
4449
- _chunkISIO63Q5cjs.resolveEffectValues.call(void 0, d, values);
4449
+ _chunkL2JTLW6Bcjs.resolveEffectValues.call(void 0, d, values);
4450
4450
  const out = { ...layer, provider: provider.id, name: d.name };
4451
4451
  if (values && Object.keys(values).length) out.values = values;
4452
4452
  else delete out.values;
4453
4453
  return out;
4454
4454
  }
4455
4455
  async function resolveMotionShotTransition(provider, transition) {
4456
- if (!provider) throw new (0, _chunkISIO63Q5cjs.NoEffectsProviderError)();
4456
+ if (!provider) throw new (0, _chunkL2JTLW6Bcjs.NoEffectsProviderError)();
4457
4457
  const d = await provider.describe(transition.effect);
4458
- if (!d.uses.includes("transition") || d.kind === "camera") throw new (0, _chunkISIO63Q5cjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) is not a transition (${kindHint(d)}); search_effects with use "transition" lists them.`, d.id);
4458
+ if (!d.uses.includes("transition") || d.kind === "camera") throw new (0, _chunkL2JTLW6Bcjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) is not a transition (${kindHint(d)}); search_effects with use "transition" lists them.`, d.id);
4459
4459
  if (transition.ms !== void 0 && !(transition.ms > 0 && transition.ms <= 6e4)) throw new Error(`A transition's "ms" is from 1 to 60000; got ${transition.ms}.`);
4460
4460
  const values = withPreset(d, transition.preset, transition.values);
4461
- _chunkISIO63Q5cjs.resolveEffectValues.call(void 0, d, values);
4462
- const out = { ...transition, ms: _nullishCoalesce(transition.ms, () => ( _chunkISIO63Q5cjs.EFFECT_TRANSITION_MS)), provider: provider.id, name: d.name };
4461
+ _chunkL2JTLW6Bcjs.resolveEffectValues.call(void 0, d, values);
4462
+ const out = { ...transition, ms: _nullishCoalesce(transition.ms, () => ( _chunkL2JTLW6Bcjs.EFFECT_TRANSITION_MS)), provider: provider.id, name: d.name };
4463
4463
  if (values && Object.keys(values).length) out.values = values;
4464
4464
  else delete out.values;
4465
4465
  return out;
@@ -4654,7 +4654,7 @@ var AnimationEditorController = (_class2 = class {
4654
4654
 
4655
4655
 
4656
4656
  /** The effects provider slot (see `setEffectsProvider`). */
4657
- __init9() {this.effects = new (0, _chunkISIO63Q5cjs.EffectsSlot)()}
4657
+ __init9() {this.effects = new (0, _chunkL2JTLW6Bcjs.EffectsSlot)()}
4658
4658
  /** Renders shot layer and transition pictures for frames (see `MotionLayersEngine`). */
4659
4659
 
4660
4660
 
@@ -4848,7 +4848,7 @@ var AnimationEditorController = (_class2 = class {
4848
4848
  try {
4849
4849
  plan = await work();
4850
4850
  } catch (error) {
4851
- if (_chunkISIO63Q5cjs.isEffectError.call(void 0, error) || error instanceof Error) return this.refuse(error.message);
4851
+ if (_chunkL2JTLW6Bcjs.isEffectError.call(void 0, error) || error instanceof Error) return this.refuse(error.message);
4852
4852
  throw error;
4853
4853
  }
4854
4854
  const r = this.apply({ op: "replace_scene", scene: plan.scene }, plan.label, { merge });
@@ -4859,11 +4859,11 @@ var AnimationEditorController = (_class2 = class {
4859
4859
  }
4860
4860
  async describeFor(effect, kinds) {
4861
4861
  const provider = this.effects.require();
4862
- if (!provider.applyVectorEffect) throw new (0, _chunkISIO63Q5cjs.EffectUnsupportedError)(`${provider.name} has no vector or motion effects.`);
4862
+ if (!provider.applyVectorEffect) throw new (0, _chunkL2JTLW6Bcjs.EffectUnsupportedError)(`${provider.name} has no vector or motion effects.`);
4863
4863
  const d = await provider.describe(effect);
4864
4864
  if (!kinds.includes(d.kind)) {
4865
4865
  const hint = d.kind === "motion" ? "apply it to nodes with apply_effect" : d.kind === "vector" ? "add it with add_vector_effect" : "it draws pixels, which scenes cannot hold";
4866
- throw new (0, _chunkISIO63Q5cjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) is a ${d.kind} effect: ${hint}.`, d.id);
4866
+ throw new (0, _chunkL2JTLW6Bcjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) is a ${d.kind} effect: ${hint}.`, d.id);
4867
4867
  }
4868
4868
  return d;
4869
4869
  }
@@ -4884,7 +4884,7 @@ var AnimationEditorController = (_class2 = class {
4884
4884
  return this.effectEdit(async () => {
4885
4885
  this.knownNodes(targets);
4886
4886
  const d = await this.describeFor(effect, ["motion"]);
4887
- const values = _chunkISIO63Q5cjs.resolveEffectValues.call(void 0, d, options.values);
4887
+ const values = _chunkL2JTLW6Bcjs.resolveEffectValues.call(void 0, d, options.values);
4888
4888
  const instance = _nullishCoalesce(options.instance, () => ( nextMotionEffectInstance(this.scene)));
4889
4889
  const motion = await this.effects.require().applyVectorEffect(d.id, { svg: motionArtworkSvg(this.scene.artwork), nodeIds: [...targets] }, values);
4890
4890
  const source = { instance, effect: d.id, provider: this.effects.require().id, name: d.name, values };
@@ -4909,7 +4909,7 @@ var AnimationEditorController = (_class2 = class {
4909
4909
  let given = options.values;
4910
4910
  let place = options.box;
4911
4911
  if (options.path) {
4912
- const controls = _chunkISIO63Q5cjs.pathControlsOf.call(void 0, d);
4912
+ const controls = _chunkL2JTLW6Bcjs.pathControlsOf.call(void 0, d);
4913
4913
  if (!controls.length) throw new Error(`"${d.id}" does not draw along a path. Find one that does with search_effects accepts ["path"] (kind vector).`);
4914
4914
  const control = _nullishCoalesce(options.control, () => ( (controls.length === 1 ? controls[0] : void 0)));
4915
4915
  if (!control) throw new Error(`"${d.id}" has several path controls (${controls.join(", ")}); pass control.`);
@@ -4929,7 +4929,7 @@ var AnimationEditorController = (_class2 = class {
4929
4929
  }
4930
4930
  given = { ..._nullishCoalesce(given, () => ( {})), [control]: value };
4931
4931
  }
4932
- const values = _chunkISIO63Q5cjs.resolveEffectValues.call(void 0, d, given);
4932
+ const values = _chunkL2JTLW6Bcjs.resolveEffectValues.call(void 0, d, given);
4933
4933
  const instance = _nullishCoalesce(options.instance, () => ( nextMotionEffectInstance(this.scene)));
4934
4934
  const motion = await this.effects.require().applyVectorEffect(d.id, motionArtworkSvg(this.scene.artwork), values);
4935
4935
  const source = { instance, effect: d.id, provider: this.effects.require().id, name: d.name, values };
@@ -4953,7 +4953,7 @@ var AnimationEditorController = (_class2 = class {
4953
4953
  else if (k in d.controls.properties) base[k] = d.defaults[k];
4954
4954
  else throw new Error(`"${k}" is not a control of "${d.id}"; its controls are ${Object.keys(d.controls.properties).join(", ")}.`);
4955
4955
  }
4956
- const merged = { ...base, ..._chunkISIO63Q5cjs.resolveEffectValues.call(void 0, { ...d, defaults: base }, given) };
4956
+ const merged = { ...base, ..._chunkL2JTLW6Bcjs.resolveEffectValues.call(void 0, { ...d, defaults: base }, given) };
4957
4957
  const { scene: bare, place } = withoutMotionEffect(this.scene, instance);
4958
4958
  const provider = this.effects.require();
4959
4959
  const source = { instance, effect: d.id, provider: provider.id, name: d.name, values: merged };
@@ -5008,12 +5008,12 @@ var AnimationEditorController = (_class2 = class {
5008
5008
  if (!found) return this.refuse(`There is no shot "${shot}"; the shots are ${this.scene.shots.map((s) => `"${s.id}"`).join(", ") || "none yet"}.`);
5009
5009
  let resolved;
5010
5010
  try {
5011
- resolved = await _chunkISIO63Q5cjs.resolveCameraMoveSpec.call(void 0, this.effects.require(), spec, role);
5011
+ resolved = await _chunkL2JTLW6Bcjs.resolveCameraMoveSpec.call(void 0, this.effects.require(), spec, role);
5012
5012
  } catch (error) {
5013
- if (_chunkISIO63Q5cjs.isEffectError.call(void 0, error) || error instanceof Error) return this.refuse(error.message);
5013
+ if (_chunkL2JTLW6Bcjs.isEffectError.call(void 0, error) || error instanceof Error) return this.refuse(error.message);
5014
5014
  throw error;
5015
5015
  }
5016
- const field = _chunkISIO63Q5cjs.cameraMoveField.call(void 0, role);
5016
+ const field = _chunkL2JTLW6Bcjs.cameraMoveField.call(void 0, role);
5017
5017
  const current = this.scene.shots.find((s) => s.id === shot);
5018
5018
  if (!current) return this.refuse(`Shot "${shot}" was removed while the move was checked.`);
5019
5019
  const parts = role === "transition" && resolved.spec.ms === void 0 ? { ...resolved.spec, ms: resolved.description.camera.durationMs } : resolved.spec;
@@ -5027,7 +5027,7 @@ var AnimationEditorController = (_class2 = class {
5027
5027
  clearShotCameraMove(shot, role) {
5028
5028
  const found = this.scene.shots.find((s) => s.id === shot);
5029
5029
  if (!found) return this.refuse(`There is no shot "${shot}"; the shots are ${this.scene.shots.map((s) => `"${s.id}"`).join(", ") || "none yet"}.`);
5030
- const field = _chunkISIO63Q5cjs.cameraMoveField.call(void 0, role);
5030
+ const field = _chunkL2JTLW6Bcjs.cameraMoveField.call(void 0, role);
5031
5031
  if (_optionalChain([found, 'access', _232 => _232.camera, 'optionalAccess', _233 => _233[field]]) === void 0) return this.refuse(`Shot "${shot}" has no camera "${field}" move to clear${_optionalChain([found, 'access', _234 => _234.camera, 'optionalAccess', _235 => _235[field === "in" ? "during" : "in"]]) !== void 0 ? `; it has a "${field === "in" ? "during" : "in"}" move (role ${role === "hold" ? "transition" : "hold"})` : ""}.`);
5032
5032
  const rest = { ...found.camera };
5033
5033
  delete rest[field];
@@ -5169,7 +5169,7 @@ var AnimationEditorController = (_class2 = class {
5169
5169
  try {
5170
5170
  plan = await work();
5171
5171
  } catch (error) {
5172
- if (_chunkISIO63Q5cjs.isEffectError.call(void 0, error) || error instanceof Error) return this.refuse(error.message);
5172
+ if (_chunkL2JTLW6Bcjs.isEffectError.call(void 0, error) || error instanceof Error) return this.refuse(error.message);
5173
5173
  throw error;
5174
5174
  }
5175
5175
  const id = this.scene.shots[plan.shot].id;
@@ -5251,7 +5251,7 @@ var AnimationEditorController = (_class2 = class {
5251
5251
  const { provider: _p, name: _n, ...given } = transition;
5252
5252
  resolved = await resolveMotionShotTransition(this.effects.provider, given);
5253
5253
  } catch (error) {
5254
- if (_chunkISIO63Q5cjs.isEffectError.call(void 0, error) || error instanceof Error) return this.refuse(error.message);
5254
+ if (_chunkL2JTLW6Bcjs.isEffectError.call(void 0, error) || error instanceof Error) return this.refuse(error.message);
5255
5255
  throw error;
5256
5256
  }
5257
5257
  if (!this.scene.shots.some((s) => s.id === shot)) return this.refuse(`Shot "${shot}" was removed while the transition was checked.`);
@@ -5283,9 +5283,9 @@ var AnimationEditorController = (_class2 = class {
5283
5283
  const mode = _nullishCoalesce(options.mode, () => ( "replace"));
5284
5284
  let filled;
5285
5285
  try {
5286
- filled = await _chunkZ3BRH4JKcjs.fillEffectTemplate.call(void 0, this.effects.provider, template, values, options.name ? { name: options.name } : {});
5286
+ filled = await _chunk557BDWKOcjs.fillEffectTemplate.call(void 0, this.effects.provider, template, values, options.name ? { name: options.name } : {});
5287
5287
  } catch (error) {
5288
- if (_chunkISIO63Q5cjs.isEffectError.call(void 0, error) || error instanceof Error) return this.refuse(error.message);
5288
+ if (_chunkL2JTLW6Bcjs.isEffectError.call(void 0, error) || error instanceof Error) return this.refuse(error.message);
5289
5289
  throw error;
5290
5290
  }
5291
5291
  const provider = this.effects.provider.id;
@@ -6985,7 +6985,7 @@ function animationCameraMoveTools(controller, ctx) {
6985
6985
  shot: { type: "string", minLength: 1, description: "A shot id from get_animation." },
6986
6986
  role: ROLE,
6987
6987
  move: { type: "string", minLength: 1, description: "A camera move id of that role from list_camera_moves." },
6988
- ms: { type: "number", minimum: 0, maximum: _chunkISIO63Q5cjs.CAMERA_MOVE_MAX_MS, description: `How long a transition takes, ms (0\u2013${_chunkISIO63Q5cjs.CAMERA_MOVE_MAX_MS}; default the move's durationMs). Not for a hold, which plays across the shot's hold.` },
6988
+ ms: { type: "number", minimum: 0, maximum: _chunkL2JTLW6Bcjs.CAMERA_MOVE_MAX_MS, description: `How long a transition takes, ms (0\u2013${_chunkL2JTLW6Bcjs.CAMERA_MOVE_MAX_MS}; default the move's durationMs). Not for a hold, which plays across the shot's hold.` },
6989
6989
  preset: { type: "string", minLength: 1, description: "A preset of the move by name, from describe_effect; its values are written out on the shot." },
6990
6990
  values: ctx.valuesSchema("Control values over the move's defaults (and the preset's), from describe_effect")
6991
6991
  },
@@ -7058,7 +7058,7 @@ var fields = (ctx) => ({
7058
7058
  preset: { type: "string", minLength: 1, description: "A preset of the effect by name, from describe_effect; its values are written out on the layer." },
7059
7059
  values: ctx.valuesSchema("Control values over the effect's defaults (and the preset's), from describe_effect"),
7060
7060
  subject: SUBJECT,
7061
- role: { type: "string", enum: [..._chunkISIO63Q5cjs.EFFECT_LAYER_ROLES], description: "background: under the artwork; subject: over the artwork; overlay: over everything. Default subject with a rect, else background." },
7061
+ role: { type: "string", enum: [..._chunkL2JTLW6Bcjs.EFFECT_LAYER_ROLES], description: "background: under the artwork; subject: over the artwork; overlay: over everything. Default subject with a rect, else background." },
7062
7062
  rect: RECT,
7063
7063
  from: TIME("starts", "0: as its shot starts"),
7064
7064
  until: TIME("stops", '{ "shots": 1 }: as the next shot starts'),
@@ -7148,7 +7148,7 @@ function animationShotLayerTools(controller, ctx) {
7148
7148
  properties: {
7149
7149
  shot: { ...SHOT2, description: "A shot id from get_animation (not the first shot)." },
7150
7150
  effect: { ...ctx.effectIdSchema(["transition"]), description: "A transition effect id from search_effects (use transition)." },
7151
- ms: { type: "number", exclusiveMinimum: 0, maximum: 6e4, description: `How long it takes, ms (default ${_chunkISIO63Q5cjs.EFFECT_TRANSITION_MS}).` },
7151
+ ms: { type: "number", exclusiveMinimum: 0, maximum: 6e4, description: `How long it takes, ms (default ${_chunkL2JTLW6Bcjs.EFFECT_TRANSITION_MS}).` },
7152
7152
  preset: { type: "string", minLength: 1, description: "A preset of the transition by name, from describe_effect." },
7153
7153
  values: ctx.valuesSchema("Control values over the transition's defaults (and the preset's)")
7154
7154
  },
@@ -7184,7 +7184,7 @@ function createAnimationEffectsToolProvider(controller, options = {}) {
7184
7184
  if (!r.ok) throw new Error(r.error);
7185
7185
  return { target, effect: info(r.effect), effects: controller.appliedEffects().filter((e) => e.targets.includes(target)).map(info), ...r.notes.length ? { notes: r.notes } : {} };
7186
7186
  };
7187
- return withAnimationPlayback(_chunkISIO63Q5cjs.createEffectToolProvider.call(void 0, {
7187
+ return withAnimationPlayback(_chunkL2JTLW6Bcjs.createEffectToolProvider.call(void 0, {
7188
7188
  id: _nullishCoalesce(options.id, () => ( "animation-effects")),
7189
7189
  namespace: options.namespace,
7190
7190
  title: "Animation effects",
@@ -7219,7 +7219,7 @@ function createAnimationEffectsToolProvider(controller, options = {}) {
7219
7219
  extraTools: (ctx) => [
7220
7220
  ...animationCameraMoveTools(controller, ctx),
7221
7221
  ...animationShotLayerTools(controller, ctx),
7222
- ..._chunkZ3BRH4JKcjs.effectTemplateTools.call(void 0,
7222
+ ..._chunk557BDWKOcjs.effectTemplateTools.call(void 0,
7223
7223
  {
7224
7224
  provider: () => controller.effectsProvider,
7225
7225
  fillDetails: "mode replace (default) makes a new scene from the template's shots, the size of its stage; mode append adds its shots after the current ones, scaled to the artwork. Each shot keeps its framing and camera moves, its layers and its transition; its title and caption become overlay word layers. Returns { template, mode, shots (new shot ids), layers (new layer ids), openSlots, notes }. Check the result with render_animation_frame.",
@@ -16,7 +16,7 @@ import {
16
16
  effectSubjectKind,
17
17
  effectTemplateTools,
18
18
  fillEffectTemplate
19
- } from "../chunk-226QDRO3.js";
19
+ } from "../chunk-DHQPRKXF.js";
20
20
  import {
21
21
  IDENTITY_MATRIX,
22
22
  applyMatrix,
@@ -71,7 +71,7 @@ import {
71
71
  pathControlsOf,
72
72
  resolveCameraMoveSpec,
73
73
  resolveEffectValues
74
- } from "../chunk-JA57JIT5.js";
74
+ } from "../chunk-MV7CYFMJ.js";
75
75
  import "../chunk-V2ZCJRDM.js";
76
76
  import "../chunk-TXFWZ5ZJ.js";
77
77
  import "../chunk-GCVFEPJG.js";
@@ -39,6 +39,12 @@ export interface CanvasChange<TNode extends CanvasNode = CanvasNode, TEdge exten
39
39
  };
40
40
  /** A node is being dragged or resized: more changes of the same gesture will follow. */
41
41
  inGesture: boolean;
42
+ /**
43
+ * The drag or resize that earlier changes reported (`inGesture`) has ended. Set on the first
44
+ * change after it, which the feed reports even when nothing else changed: a drop often only
45
+ * clears the runtime `dragging` flag, and followers need to know the gesture is over.
46
+ */
47
+ gestureEnded?: true;
42
48
  /** Milliseconds since the epoch. */
43
49
  at: number;
44
50
  }
@@ -39,6 +39,12 @@ export interface CanvasChange<TNode extends CanvasNode = CanvasNode, TEdge exten
39
39
  };
40
40
  /** A node is being dragged or resized: more changes of the same gesture will follow. */
41
41
  inGesture: boolean;
42
+ /**
43
+ * The drag or resize that earlier changes reported (`inGesture`) has ended. Set on the first
44
+ * change after it, which the feed reports even when nothing else changed: a drop often only
45
+ * clears the runtime `dragging` flag, and followers need to know the gesture is over.
46
+ */
47
+ gestureEnded?: true;
42
48
  /** Milliseconds since the epoch. */
43
49
  at: number;
44
50
  }
@@ -223,7 +223,7 @@ var _chunkPY4GWSE2cjs = require('../chunk-PY4GWSE2.cjs');
223
223
 
224
224
 
225
225
 
226
- var _chunkISIO63Q5cjs = require('../chunk-ISIO63Q5.cjs');
226
+ var _chunkL2JTLW6Bcjs = require('../chunk-L2JTLW6B.cjs');
227
227
  require('../chunk-WOFXZ3XU.cjs');
228
228
 
229
229
 
@@ -11428,14 +11428,14 @@ function canvasEffectErrorReason(effect, error) {
11428
11428
  }
11429
11429
  var CANVAS_EFFECT_MISSING_PATTERN = "repeating-linear-gradient(45deg, rgba(128,128,128,0.28) 0 2px, transparent 2px 8px)";
11430
11430
  function createCanvasEffectsController(options) {
11431
- const slot = new (0, _chunkISIO63Q5cjs.EffectsSlot)();
11431
+ const slot = new (0, _chunkL2JTLW6Bcjs.EffectsSlot)();
11432
11432
  if (options.effects) slot.set(options.effects);
11433
11433
  const describe = async (effect, place2) => {
11434
11434
  const d = await slot.require().describe(effect);
11435
11435
  const allowed = CANVAS_EFFECT_PLACES[place2];
11436
11436
  if (d.kind === "motion" || !d.uses.some((u) => allowed.includes(u))) {
11437
11437
  const where = place2 === "transition" ? "a scene transition" : place2 === "fill" ? "a fill" : "an overlay";
11438
- throw new (0, _chunkISIO63Q5cjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) is a ${d.uses.join(" / ")} effect and cannot be ${where}, which takes ${allowed.join(" or ")} effects; search_effects with use "${allowed[0]}" lists ones that fit.`, d.id);
11438
+ throw new (0, _chunkL2JTLW6Bcjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) is a ${d.uses.join(" / ")} effect and cannot be ${where}, which takes ${allowed.join(" or ")} effects; search_effects with use "${allowed[0]}" lists ones that fit.`, d.id);
11439
11439
  }
11440
11440
  return d;
11441
11441
  };
@@ -11466,7 +11466,7 @@ function createCanvasEffectsController(options) {
11466
11466
  else if (k in d.controls.properties) base[k] = d.defaults[k];
11467
11467
  else throw new Error(`"${k}" is not a control of "${d.id}"; its controls are ${Object.keys(d.controls.properties).join(", ")}.`);
11468
11468
  }
11469
- return { ...base, ..._chunkISIO63Q5cjs.resolveEffectValues.call(void 0, { ...d, defaults: base }, given) };
11469
+ return { ...base, ..._chunkL2JTLW6Bcjs.resolveEffectValues.call(void 0, { ...d, defaults: base }, given) };
11470
11470
  };
11471
11471
  return {
11472
11472
  /** The provider slot. */
@@ -11500,7 +11500,7 @@ function createCanvasEffectsController(options) {
11500
11500
  const mode = _nullishCoalesce(opts.mode, () => ( "fill"));
11501
11501
  const d = await describe(effect, mode);
11502
11502
  if (opts.opacity !== void 0 && !(opts.opacity >= 0 && opts.opacity <= 1)) throw new Error("opacity must be from 0 to 1.");
11503
- const next = { effect: d.id, provider: slot.require().id, name: d.name, values: _chunkISIO63Q5cjs.resolveEffectValues.call(void 0, d, opts.values), mode, ...opts.opacity !== void 0 && opts.opacity !== 1 ? { opacity: opts.opacity } : {} };
11503
+ const next = { effect: d.id, provider: slot.require().id, name: d.name, values: _chunkL2JTLW6Bcjs.resolveEffectValues.call(void 0, d, opts.values), mode, ...opts.opacity !== void 0 && opts.opacity !== 1 ? { opacity: opts.opacity } : {} };
11504
11504
  commit(`Add effect ${d.name}`, withEffect(nodeId, next));
11505
11505
  return next;
11506
11506
  },
@@ -11557,7 +11557,7 @@ function createCanvasEffectsController(options) {
11557
11557
  const d = await describe(effect, "transition");
11558
11558
  const duration = _nullishCoalesce(_nullishCoalesce(opts.duration, () => ( d.duration)), () => ( 600));
11559
11559
  if (!(duration > 0)) throw new Error("duration must be a number of ms above 0.");
11560
- const transition = { effect: d.id, provider: slot.require().id, name: d.name, values: _chunkISIO63Q5cjs.resolveEffectValues.call(void 0, d, opts.values), duration };
11560
+ const transition = { effect: d.id, provider: slot.require().id, name: d.name, values: _chunkL2JTLW6Bcjs.resolveEffectValues.call(void 0, d, opts.values), duration };
11561
11561
  scenes2.update(sceneId, { transition });
11562
11562
  return transition;
11563
11563
  }
@@ -11701,7 +11701,7 @@ function createCanvasEffectsToolProvider(controller, options = {}) {
11701
11701
  const once = (instance) => {
11702
11702
  if (instance !== "effect") throw new Error('A shape node has one effect, "effect".');
11703
11703
  };
11704
- return _chunkISIO63Q5cjs.createEffectToolProvider.call(void 0, {
11704
+ return _chunkL2JTLW6Bcjs.createEffectToolProvider.call(void 0, {
11705
11705
  id: _nullishCoalesce(options.id, () => ( "canvas-effects")),
11706
11706
  namespace: options.namespace,
11707
11707
  title: "Canvas effects",
@@ -12440,6 +12440,8 @@ function createCanvasChangeFeed(options) {
12440
12440
  let edges = options.getEdges();
12441
12441
  let seq = 0;
12442
12442
  let pending;
12443
+ const gesturing = (list) => list.some((n3) => n3.dragging || n3.resizing);
12444
+ let wasInGesture = gesturing(nodes);
12443
12445
  const check = () => {
12444
12446
  const nextNodes = options.getNodes();
12445
12447
  const nextEdges = options.getEdges();
@@ -12471,8 +12473,11 @@ function createCanvasChangeFeed(options) {
12471
12473
  const selectionChanged = selBefore.length !== selAfter.length || selBefore.some((id, i) => id !== selAfter[i]);
12472
12474
  nodes = nextNodes;
12473
12475
  edges = nextEdges;
12476
+ const inGesture = gesturing(nextNodes);
12477
+ const gestureEnded = wasInGesture && !inGesture;
12478
+ wasInGesture = inGesture;
12474
12479
  const edited = addedNodes.length + removedNodes.length + changedNodes.length + addedEdges.length + removedEdges.length + changedEdges.length > 0;
12475
- if (!edited && !selectionChanged) return null;
12480
+ if (!edited && !selectionChanged && !gestureEnded) return null;
12476
12481
  const commit = edited ? pending : void 0;
12477
12482
  if (edited) pending = void 0;
12478
12483
  const change = {
@@ -12486,7 +12491,8 @@ function createCanvasChangeFeed(options) {
12486
12491
  removedEdges,
12487
12492
  changedEdges,
12488
12493
  ...selectionChanged ? { selection: { before: selBefore, after: selAfter } } : {},
12489
- inGesture: nextNodes.some((n3) => n3.dragging || n3.resizing),
12494
+ inGesture,
12495
+ ...gestureEnded ? { gestureEnded: true } : {},
12490
12496
  at: now()
12491
12497
  };
12492
12498
  listeners.forEach((l) => l(change));
@@ -12511,6 +12517,7 @@ function createCanvasChangeFeed(options) {
12511
12517
  nodes = options.getNodes();
12512
12518
  edges = options.getEdges();
12513
12519
  pending = void 0;
12520
+ wasInGesture = gesturing(nodes);
12514
12521
  },
12515
12522
  noteCommit,
12516
12523
  dispose: () => {
@@ -223,7 +223,7 @@ import {
223
223
  EffectsSlot,
224
224
  createEffectToolProvider,
225
225
  resolveEffectValues
226
- } from "../chunk-JA57JIT5.js";
226
+ } from "../chunk-MV7CYFMJ.js";
227
227
  import "../chunk-V2ZCJRDM.js";
228
228
  import {
229
229
  brushstrokeScaleBucket
@@ -12440,6 +12440,8 @@ function createCanvasChangeFeed(options) {
12440
12440
  let edges = options.getEdges();
12441
12441
  let seq = 0;
12442
12442
  let pending;
12443
+ const gesturing = (list) => list.some((n3) => n3.dragging || n3.resizing);
12444
+ let wasInGesture = gesturing(nodes);
12443
12445
  const check = () => {
12444
12446
  const nextNodes = options.getNodes();
12445
12447
  const nextEdges = options.getEdges();
@@ -12471,8 +12473,11 @@ function createCanvasChangeFeed(options) {
12471
12473
  const selectionChanged = selBefore.length !== selAfter.length || selBefore.some((id, i) => id !== selAfter[i]);
12472
12474
  nodes = nextNodes;
12473
12475
  edges = nextEdges;
12476
+ const inGesture = gesturing(nextNodes);
12477
+ const gestureEnded = wasInGesture && !inGesture;
12478
+ wasInGesture = inGesture;
12474
12479
  const edited = addedNodes.length + removedNodes.length + changedNodes.length + addedEdges.length + removedEdges.length + changedEdges.length > 0;
12475
- if (!edited && !selectionChanged) return null;
12480
+ if (!edited && !selectionChanged && !gestureEnded) return null;
12476
12481
  const commit = edited ? pending : void 0;
12477
12482
  if (edited) pending = void 0;
12478
12483
  const change = {
@@ -12486,7 +12491,8 @@ function createCanvasChangeFeed(options) {
12486
12491
  removedEdges,
12487
12492
  changedEdges,
12488
12493
  ...selectionChanged ? { selection: { before: selBefore, after: selAfter } } : {},
12489
- inGesture: nextNodes.some((n3) => n3.dragging || n3.resizing),
12494
+ inGesture,
12495
+ ...gestureEnded ? { gestureEnded: true } : {},
12490
12496
  at: now()
12491
12497
  };
12492
12498
  listeners.forEach((l) => l(change));
@@ -12511,6 +12517,7 @@ function createCanvasChangeFeed(options) {
12511
12517
  nodes = options.getNodes();
12512
12518
  edges = options.getEdges();
12513
12519
  pending = void 0;
12520
+ wasInGesture = gesturing(nodes);
12514
12521
  },
12515
12522
  noteCommit,
12516
12523
  dispose: () => {