@pixerate/editor 0.232.0 → 0.233.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 (47) hide show
  1. package/dist/animation-editor/compile.d.cts +3 -0
  2. package/dist/animation-editor/compile.d.ts +3 -0
  3. package/dist/animation-editor/controller.d.cts +67 -2
  4. package/dist/animation-editor/controller.d.ts +67 -2
  5. package/dist/animation-editor/edit.d.cts +1 -1
  6. package/dist/animation-editor/edit.d.ts +1 -1
  7. package/dist/animation-editor/evaluate.d.cts +8 -1
  8. package/dist/animation-editor/evaluate.d.ts +8 -1
  9. package/dist/animation-editor/index.cjs +1202 -371
  10. package/dist/animation-editor/index.d.cts +3 -0
  11. package/dist/animation-editor/index.d.ts +3 -0
  12. package/dist/animation-editor/index.js +1032 -201
  13. package/dist/animation-editor/issues.d.cts +28 -0
  14. package/dist/animation-editor/issues.d.ts +28 -0
  15. package/dist/animation-editor/layer-edit.d.cts +55 -0
  16. package/dist/animation-editor/layer-edit.d.ts +55 -0
  17. package/dist/animation-editor/layer-tools.d.cts +11 -0
  18. package/dist/animation-editor/layer-tools.d.ts +11 -0
  19. package/dist/animation-editor/layers.d.cts +166 -0
  20. package/dist/animation-editor/layers.d.ts +166 -0
  21. package/dist/animation-editor/parse.d.cts +2 -0
  22. package/dist/animation-editor/parse.d.ts +2 -0
  23. package/dist/animation-editor/types.d.cts +44 -1
  24. package/dist/animation-editor/types.d.ts +44 -1
  25. package/dist/canvas/index.cjs +10 -10
  26. package/dist/canvas/index.js +4 -4
  27. package/dist/chunk-6RZVFYGJ.cjs +255 -0
  28. package/dist/{chunk-RG4O6K2A.cjs → chunk-A7PQNIBX.cjs} +14 -14
  29. package/dist/{chunk-ERJA5CGT.cjs → chunk-DGWOGQJ5.cjs} +8 -1
  30. package/dist/{chunk-TYCJGFCF.js → chunk-EUKY4KFX.js} +8 -1
  31. package/dist/chunk-I277KJLQ.js +255 -0
  32. package/dist/{chunk-2K5TELHV.cjs → chunk-JAOSL334.cjs} +10 -10
  33. package/dist/{chunk-MUSZYMBX.js → chunk-TEFQISBY.js} +1 -1
  34. package/dist/{chunk-6QLTUGMO.js → chunk-WDDUPWVG.js} +1 -1
  35. package/dist/{chunk-A653N65Q.cjs → chunk-ZQIWBJGH.cjs} +7 -7
  36. package/dist/effects/index.cjs +114 -322
  37. package/dist/effects/index.js +23 -231
  38. package/dist/image-editor/index.cjs +16 -16
  39. package/dist/image-editor/index.js +4 -4
  40. package/dist/paint-editor/index.cjs +10 -10
  41. package/dist/paint-editor/index.js +5 -5
  42. package/dist/vector/index.cjs +6 -6
  43. package/dist/vector/index.js +1 -1
  44. package/dist/video-editor/index.cjs +339 -339
  45. package/dist/video-editor/index.js +5 -5
  46. package/package.json +1 -1
  47. package/dist/{chunk-PQYDJ2AV.js → chunk-SIK3LT6M.js} +16 -16
@@ -1,4 +1,10 @@
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;
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;
2
+
3
+
4
+
5
+
6
+ var _chunk6RZVFYGJcjs = require('../chunk-6RZVFYGJ.cjs');
7
+
2
8
 
3
9
  var _chunkGXLZZVR5cjs = require('../chunk-GXLZZVR5.cjs');
4
10
 
@@ -8,7 +14,7 @@ var _chunkGXLZZVR5cjs = require('../chunk-GXLZZVR5.cjs');
8
14
 
9
15
 
10
16
 
11
- var _chunkERJA5CGTcjs = require('../chunk-ERJA5CGT.cjs');
17
+ var _chunkDGWOGQJ5cjs = require('../chunk-DGWOGQJ5.cjs');
12
18
 
13
19
 
14
20
 
@@ -44,6 +50,10 @@ var _chunkGF2RBSDMcjs = require('../chunk-GF2RBSDM.cjs');
44
50
  require('../chunk-NM3G4VQT.cjs');
45
51
 
46
52
 
53
+ var _chunkPY4GWSE2cjs = require('../chunk-PY4GWSE2.cjs');
54
+
55
+
56
+
47
57
 
48
58
 
49
59
 
@@ -54,7 +64,9 @@ require('../chunk-NM3G4VQT.cjs');
54
64
 
55
65
 
56
66
 
57
- var _chunk2K5TELHVcjs = require('../chunk-2K5TELHV.cjs');
67
+
68
+
69
+ var _chunkJAOSL334cjs = require('../chunk-JAOSL334.cjs');
58
70
  require('../chunk-WOFXZ3XU.cjs');
59
71
  require('../chunk-PQ3H2AAN.cjs');
60
72
  require('../chunk-YQGCPL5X.cjs');
@@ -196,7 +208,10 @@ ${problems.map((p) => `- ${p}`).join("\n")}`);
196
208
  }
197
209
  };
198
210
  var SCENE_KEYS = ["version", "name", "description", "artwork", "shots", "clips", "drivers", "motionBlur", "loop", "trigger", "fps", "steps", "beat", "narration", "target", "controls", "presets"];
199
- var SHOT_KEYS = ["id", "label", "camera", "at", "run", "hold", "speed"];
211
+ var SHOT_KEYS = ["id", "label", "camera", "at", "run", "hold", "speed", "layers", "transition"];
212
+ var LAYER_KEYS = ["id", "effect", "preset", "values", "subject", "role", "rect", "from", "until", "opacity", "fade", "provider", "name"];
213
+ var TRANSITION_KEYS = ["effect", "ms", "preset", "values", "provider", "name"];
214
+ var LAYER_ID = /^[A-Za-z_][\w.-]*$/;
200
215
  var CAMERA_KEYS = ["x", "y", "width", "height", "frame", "padding", "ease", "ms", "geometric", "in", "during"];
201
216
  var CAMERA_MOVE_KEYS = ["move", "ms", "preset", "values"];
202
217
  var BASE_KEYS = ["type", "targets", "duration", "delay", "stagger", "with", "ease", "note", "repeat", "repeatMode", "steps", "split", "cascade", "source"];
@@ -228,14 +243,14 @@ var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
228
243
  var isNum = (v) => typeof v === "number" && Number.isFinite(v);
229
244
  var isPoint = (v) => isObject(v) && isNum(v.x) && isNum(v.y);
230
245
  function unknownKeys(value, known, where, problems) {
231
- for (const key of Object.keys(value)) if (!known.includes(key)) problems.push(`${where}: unknown field ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, key, known)}.`);
246
+ for (const key of Object.keys(value)) if (!known.includes(key)) problems.push(`${where}: unknown field ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, key, known)}.`);
232
247
  }
233
248
  function commandShapeProblems(command3, where) {
234
249
  const problems = [];
235
250
  if (!isObject(command3)) return [`${where} must be an object.`];
236
251
  const type = command3.type;
237
252
  if (typeof type !== "string" || !MOTION_COMMAND_TYPES.includes(type)) {
238
- return [`${where}: unknown type ${typeof type === "string" ? _chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, type, MOTION_COMMAND_TYPES) : String(type)}; use one of ${MOTION_COMMAND_TYPES.join(", ")}.`];
253
+ return [`${where}: unknown type ${typeof type === "string" ? _chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, type, MOTION_COMMAND_TYPES) : String(type)}; use one of ${MOTION_COMMAND_TYPES.join(", ")}.`];
239
254
  }
240
255
  unknownKeys(command3, COMMAND_KEYS[type], `${where} (${type})`, problems);
241
256
  const p = (msg) => problems.push(`${where} (${type}): ${msg}`);
@@ -287,7 +302,7 @@ function commandShapeProblems(command3, where) {
287
302
  case "exit": {
288
303
  const styles = type === "enter" ? MOTION_ENTER_STYLES : MOTION_EXIT_STYLES;
289
304
  if (command3.style !== void 0 && !(typeof command3.style === "string" && styles.includes(command3.style)))
290
- p(`"style" must be one of ${styles.join(", ")}${typeof command3.style === "string" ? ` (got ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, command3.style, styles)})` : ""}.`);
305
+ p(`"style" must be one of ${styles.join(", ")}${typeof command3.style === "string" ? ` (got ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, command3.style, styles)})` : ""}.`);
291
306
  if (command3.direction !== void 0 && !MOTION_DIRECTIONS.includes(command3.direction)) p(`"direction" must be one of ${MOTION_DIRECTIONS.join(", ")}.`);
292
307
  optionalNumber("distance");
293
308
  if (command3.scale !== void 0 && !(isNum(command3.scale) && command3.scale >= 0)) p(`"scale" must be a number \u2265 0 (the starting scale for pop and zoom).`);
@@ -466,6 +481,50 @@ function shotShapeProblems(shot, where) {
466
481
  }
467
482
  if (!Array.isArray(shot.run)) problems.push(`${name}: "run" must be a list of commands (it may be empty).`);
468
483
  else shot.run.forEach((command3, i) => problems.push(...commandShapeProblems(command3, `${name}, command ${i + 1}`)));
484
+ problems.push(...shotLayersShapeProblems(shot, name));
485
+ return problems;
486
+ }
487
+ var isLayerTime = (t) => isNum(t) && t >= 0 || isObject(t) && Object.keys(t).length === 1 && Number.isInteger(t.shots) && t.shots >= 1;
488
+ function shotLayersShapeProblems(shot, name) {
489
+ const problems = [];
490
+ if (shot.layers !== void 0) {
491
+ if (!Array.isArray(shot.layers)) problems.push(`${name}: "layers" must be a list of layers.`);
492
+ else
493
+ shot.layers.forEach((layer, i) => {
494
+ const where = `${name}, layer ${i + 1}`;
495
+ if (!isObject(layer)) return void problems.push(`${where} must be an object.`);
496
+ unknownKeys(layer, LAYER_KEYS, where, problems);
497
+ if (typeof layer.id !== "string" || !LAYER_ID.test(layer.id)) problems.push(`${where}: "id" must be letters, digits, "_", "-" or "." (e.g. "layer1").`);
498
+ if (layer.effect === void 0 && layer.subject === void 0) problems.push(`${where} needs an "effect", a "subject" or both.`);
499
+ if (layer.effect !== void 0 && (typeof layer.effect !== "string" || !layer.effect)) problems.push(`${where}: "effect" must be an effect id.`);
500
+ if (layer.subject !== void 0) {
501
+ const kind = _chunk6RZVFYGJcjs.effectSubjectKind.call(void 0, layer.subject);
502
+ if (kind === "video") problems.push(`${where}: an animation layer cannot play a video; use { "image" }, { "svg" }, { "text" } or { "slot" } (the video editor plays video).`);
503
+ else if (!kind || !String(layer.subject[kind]).trim()) problems.push(`${where}: "subject" must be { "image": url }, { "svg": markup }, { "text": words } or { "slot": name }.`);
504
+ }
505
+ if (layer.role !== void 0 && !_chunkJAOSL334cjs.EFFECT_LAYER_ROLES.includes(layer.role)) problems.push(`${where}: "role" must be one of ${_chunkJAOSL334cjs.EFFECT_LAYER_ROLES.join(", ")}.`);
506
+ if (layer.rect !== void 0) {
507
+ const r = layer.rect;
508
+ 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.`);
509
+ }
510
+ for (const k of ["from", "until"]) if (layer[k] !== void 0 && !isLayerTime(layer[k])) problems.push(`${where}: "${k}" must be ms \u2265 0 after the shot starts, or { "shots": n } (n \u2265 1: when the n-th shot after this one starts).`);
511
+ if (layer.opacity !== void 0 && !(isNum(layer.opacity) && layer.opacity >= 0 && layer.opacity <= 1)) problems.push(`${where}: "opacity" must be a number from 0 to 1.`);
512
+ if (layer.fade !== void 0 && !(isNum(layer.fade) && layer.fade >= 0)) problems.push(`${where}: "fade" must be ms \u2265 0.`);
513
+ if (layer.preset !== void 0 && typeof layer.preset !== "string") problems.push(`${where}: "preset" is a preset's name.`);
514
+ if (layer.values !== void 0 && !isObject(layer.values)) problems.push(`${where}: "values" is an object of the effect's control values.`);
515
+ for (const k of ["provider", "name"]) if (layer[k] !== void 0 && typeof layer[k] !== "string") problems.push(`${where}: "${k}" must be a string.`);
516
+ });
517
+ }
518
+ if (shot.transition !== void 0) {
519
+ const t = shot.transition;
520
+ if (!isObject(t) || typeof t.effect !== "string" || !t.effect) problems.push(`${name}: "transition" is { "effect": id, "ms"?, "preset"?, "values"? }.`);
521
+ else {
522
+ unknownKeys(t, TRANSITION_KEYS, `${name} transition`, problems);
523
+ if (t.ms !== void 0 && !(isNum(t.ms) && t.ms > 0 && t.ms <= 6e4)) problems.push(`${name} transition: "ms" must be a number from 1 to 60000.`);
524
+ if (t.preset !== void 0 && typeof t.preset !== "string") problems.push(`${name} transition: "preset" is a preset's name.`);
525
+ if (t.values !== void 0 && !isObject(t.values)) problems.push(`${name} transition: "values" is an object of the effect's control values.`);
526
+ }
527
+ }
469
528
  return problems;
470
529
  }
471
530
  function motionSceneShapeProblems(input) {
@@ -494,8 +553,8 @@ function motionSceneShapeProblems(input) {
494
553
  problems.push('Scene: "beat" must be { "bpm": 1\u20131000, "offset"?: ms, "meter"?: beats a bar }.');
495
554
  }
496
555
  if (input.narration !== void 0) {
497
- const n = input.narration;
498
- if (!isObject(n) || !Array.isArray(n.words) || !n.words.every((w, i, all) => isObject(w) && typeof w.text === "string" && isNum(w.start) && w.start >= 0 && (w.end === void 0 || isNum(w.end) && w.end >= w.start) && (i === 0 || w.start >= (_nullishCoalesce(all[i - 1].start, () => ( 0))))))
556
+ const n2 = input.narration;
557
+ if (!isObject(n2) || !Array.isArray(n2.words) || !n2.words.every((w, i, all) => isObject(w) && typeof w.text === "string" && isNum(w.start) && w.start >= 0 && (w.end === void 0 || isNum(w.end) && w.end >= w.start) && (i === 0 || w.start >= (_nullishCoalesce(all[i - 1].start, () => ( 0))))))
499
558
  problems.push('Scene: "narration" must be { "words": [{ "text", "start": ms, "end"?: ms }, \u2026] } in time order.');
500
559
  }
501
560
  if (input.steps !== void 0 && !(isNum(input.steps) && input.steps > 0 && input.steps <= 120)) problems.push('Scene: "steps" must be frames per second from above 0 to 120.');
@@ -513,6 +572,17 @@ function motionSceneShapeProblems(input) {
513
572
  problems.push('Scene: "presets" must be a list of { name, values }.');
514
573
  if (!Array.isArray(input.shots)) problems.push('Scene: "shots" must be a list.');
515
574
  else input.shots.forEach((shot, i) => problems.push(...shotShapeProblems(shot, `Shot ${i + 1}`)));
575
+ if (Array.isArray(input.shots)) {
576
+ const seen = /* @__PURE__ */ new Set();
577
+ for (const shot of input.shots)
578
+ if (isObject(shot) && Array.isArray(shot.layers)) {
579
+ for (const layer of shot.layers)
580
+ if (isObject(layer) && typeof layer.id === "string") {
581
+ if (seen.has(layer.id)) problems.push(`Layer id "${layer.id}" is used twice; layer ids are unique in the scene.`);
582
+ seen.add(layer.id);
583
+ }
584
+ }
585
+ }
516
586
  return problems;
517
587
  }
518
588
  function parseMotionScene(input) {
@@ -556,7 +626,7 @@ function matchMotionNodes(from, into) {
556
626
  for (const x of [...left]) {
557
627
  const k = key(x, a);
558
628
  if (!k) continue;
559
- const y = [...right].find((n) => key(n, b) === k);
629
+ const y = [...right].find((n2) => key(n2, b) === k);
560
630
  if (!y) continue;
561
631
  left.delete(x);
562
632
  right.delete(y);
@@ -564,18 +634,18 @@ function matchMotionNodes(from, into) {
564
634
  else out.pairs.push([x.id, y.id]);
565
635
  }
566
636
  };
567
- pair((n) => _optionalChain([n, 'access', _2 => _2.name, 'optionalAccess', _3 => _3.trim, 'call', _4 => _4()]) || void 0);
568
- pair((n, g) => localId(n.id, g.id) || void 0);
637
+ pair((n2) => _optionalChain([n2, 'access', _2 => _2.name, 'optionalAccess', _3 => _3.trim, 'call', _4 => _4()]) || void 0);
638
+ pair((n2, g) => localId(n2.id, g.id) || void 0);
569
639
  for (const x of [...left]) {
570
- const y = [...right].find((n) => n.type === x.type);
640
+ const y = [...right].find((n2) => n2.type === x.type);
571
641
  if (!y) continue;
572
642
  left.delete(x);
573
643
  right.delete(y);
574
644
  if (x.type === "group" && y.type === "group") visit(x, y);
575
645
  else out.pairs.push([x.id, y.id]);
576
646
  }
577
- out.unpairedFrom.push(...[...left].map((n) => n.id));
578
- out.unpairedInto.push(...[...right].map((n) => n.id));
647
+ out.unpairedFrom.push(...[...left].map((n2) => n2.id));
648
+ out.unpairedInto.push(...[...right].map((n2) => n2.id));
579
649
  };
580
650
  visit(from, into);
581
651
  return out;
@@ -583,10 +653,10 @@ function matchMotionNodes(from, into) {
583
653
 
584
654
  // src/animation-editor/smooth.ts
585
655
  function smoothMotionLegs(times, values) {
586
- const n = values.length;
587
- const slope = new Array(n).fill(0);
656
+ const n2 = values.length;
657
+ const slope = new Array(n2).fill(0);
588
658
  const secant = (i) => (values[i + 1] - values[i]) / Math.max(1e-9, times[i + 1] - times[i]);
589
- for (let i = 1; i < n - 1; i++) {
659
+ for (let i = 1; i < n2 - 1; i++) {
590
660
  const a = secant(i - 1);
591
661
  const b = secant(i);
592
662
  if (a * b <= 0) continue;
@@ -595,7 +665,7 @@ function smoothMotionLegs(times, values) {
595
665
  slope[i] = 3 * (ha + hb) / ((2 * hb + ha) / a + (hb + 2 * ha) / b);
596
666
  }
597
667
  const legs = [];
598
- for (let i = 0; i + 1 < n; i++) {
668
+ for (let i = 0; i + 1 < n2; i++) {
599
669
  const dt = times[i + 1] - times[i];
600
670
  const dv = values[i + 1] - values[i];
601
671
  if (Math.abs(dv) < 1e-9) {
@@ -658,8 +728,8 @@ function motionTextUnits(layout, by) {
658
728
  return layout.lines.flatMap((l) => l.words.flatMap((w) => w.letters.map((id) => [id])));
659
729
  }
660
730
  function motionCascade(units, cascade = "forward", seed = 1) {
661
- const n = units.length;
662
- const mid = (n - 1) / 2;
731
+ const n2 = units.length;
732
+ const mid = (n2 - 1) / 2;
663
733
  const indexed = units.map((u, i) => ({ u, i }));
664
734
  if (cascade === "backward") return [...units].reverse();
665
735
  if (cascade === "center") return indexed.sort((a, b) => Math.abs(a.i - mid) - Math.abs(b.i - mid) || a.i - b.i).map((x) => x.u);
@@ -674,7 +744,7 @@ function motionCascade(units, cascade = "forward", seed = 1) {
674
744
  return ((t ^ t >>> 14) >>> 0) / 4294967296;
675
745
  };
676
746
  const out = [...units];
677
- for (let i = n - 1; i > 0; i--) {
747
+ for (let i = n2 - 1; i > 0; i--) {
678
748
  const j = Math.floor(rand() * (i + 1));
679
749
  [out[i], out[j]] = [out[j], out[i]];
680
750
  }
@@ -739,12 +809,12 @@ function strokeReach(node) {
739
809
  return reach;
740
810
  }
741
811
  function motionRevealPadding(entry) {
742
- const box = entry.box;
743
- if (!box || !(box.width > 0) || !(box.height > 0)) return null;
812
+ const box2 = entry.box;
813
+ if (!box2 || !(box2.width > 0) || !(box2.height > 0)) return null;
744
814
  const s = strokeReach(entry.node);
745
- return [Math.min(1, s / box.width + 0.02), Math.min(1, s / box.height + 0.02)];
815
+ return [Math.min(1, s / box2.width + 0.02), Math.min(1, s / box2.height + 0.02)];
746
816
  }
747
- var clipRound = (n) => Math.round(n * 1e4) / 1e4 || 0;
817
+ var clipRound = (n2) => Math.round(n2 * 1e4) / 1e4 || 0;
748
818
  function revealFull(property, [px, py]) {
749
819
  return (property === "clipRect" ? [-px, -py, 1 + 2 * px, 1 + 2 * py] : [0.5, 0.5, Math.hypot(0.5 + px, 0.5 + py)]).map(clipRound);
750
820
  }
@@ -978,8 +1048,8 @@ function motionDriverSources(timeline, node, properties, seen = /* @__PURE__ */
978
1048
  }
979
1049
  function motionCommandRepeat(command3) {
980
1050
  if (command3.type === "wait" || command3.type === "enter" || command3.type === "exit") return 1;
981
- const n = _nullishCoalesce(command3.repeat, () => ( 1));
982
- return Number.isInteger(n) && n >= 1 ? n : 1;
1051
+ const n2 = _nullishCoalesce(command3.repeat, () => ( 1));
1052
+ return Number.isInteger(n2) && n2 >= 1 ? n2 : 1;
983
1053
  }
984
1054
  function motionCommandDuration(command3, scene, target) {
985
1055
  return motionCycleDuration(command3, scene, target) * motionCommandRepeat(command3);
@@ -1053,14 +1123,15 @@ function compileMotionScene(scene) {
1053
1123
  const start = t;
1054
1124
  let cursor = start;
1055
1125
  const f = motionShotTimeScale(shot);
1056
- const moveIn = shot.camera ? _chunk2K5TELHVcjs.cameraMoveParts.call(void 0, shot.camera.in) : void 0;
1057
- const moveDuring = shot.camera ? _chunk2K5TELHVcjs.cameraMoveParts.call(void 0, shot.camera.during) : void 0;
1126
+ const moveIn = shot.camera ? _chunkJAOSL334cjs.cameraMoveParts.call(void 0, shot.camera.in) : void 0;
1127
+ const transitionMs = index > 0 && shot.transition ? (_nullishCoalesce(shot.transition.ms, () => ( _chunkJAOSL334cjs.EFFECT_TRANSITION_MS))) * f : 0;
1128
+ const moveDuring = shot.camera ? _chunkJAOSL334cjs.cameraMoveParts.call(void 0, shot.camera.during) : void 0;
1058
1129
  let playsIn = false;
1059
1130
  if (shot.camera) {
1060
1131
  const framed = motionCameraFramed(shot.camera);
1061
1132
  const rect = framed ? resolveMotionCamera(shot.camera, scene) : null;
1062
1133
  if (rect && rect.width > 0 && rect.height > 0 || !framed && moveIn) {
1063
- const ms2 = index === 0 ? 0 : (_nullishCoalesce(_nullishCoalesce(_optionalChain([moveIn, 'optionalAccess', _37 => _37.ms]), () => ( shot.camera.ms)), () => ( MOTION_CAMERA_MS))) * f;
1134
+ const ms2 = index === 0 || transitionMs > 0 && !moveIn ? 0 : (_nullishCoalesce(_nullishCoalesce(_optionalChain([moveIn, 'optionalAccess', _37 => _37.ms]), () => ( shot.camera.ms)), () => ( MOTION_CAMERA_MS))) * f;
1064
1135
  cameraMoves.push({ shot: shot.id, start, end: start + ms2, to: rect, ease: _nullishCoalesce(shot.camera.ease, () => ( MOTION_EASE_IN_OUT)), geometric: shot.camera.geometric === true });
1065
1136
  cursor = start + ms2;
1066
1137
  playsIn = !!moveIn && index > 0;
@@ -1081,7 +1152,7 @@ function compileMotionScene(scene) {
1081
1152
  prevStart = begin;
1082
1153
  runEnd = Math.max(runEnd, end2);
1083
1154
  });
1084
- const end = runEnd + (_nullishCoalesce(shot.hold, () => ( MOTION_DEFAULT_HOLD))) * f;
1155
+ const end = Math.max(runEnd, start + transitionMs) + (_nullishCoalesce(shot.hold, () => ( MOTION_DEFAULT_HOLD))) * f;
1085
1156
  shots.push({
1086
1157
  id: shot.id,
1087
1158
  index,
@@ -1093,7 +1164,8 @@ function compileMotionScene(scene) {
1093
1164
  ...cue !== void 0 ? { cue } : {},
1094
1165
  ...missedBy !== void 0 ? { missedBy } : {},
1095
1166
  ...playsIn ? { cameraIn: moveIn.move } : {},
1096
- ...moveDuring ? { cameraDuring: moveDuring.move } : {}
1167
+ ...moveDuring ? { cameraDuring: moveDuring.move } : {},
1168
+ ...transitionMs > 0 ? { transition: shot.transition.effect, transitionEnd: start + transitionMs } : {}
1097
1169
  });
1098
1170
  t = end;
1099
1171
  });
@@ -1157,14 +1229,14 @@ function compileMotionScene(scene) {
1157
1229
  const made = segments.slice(first);
1158
1230
  const steps = _nullishCoalesce(command3.steps, () => ( scene.steps));
1159
1231
  if (steps && steps > 0) for (const s of made) s.steps = steps;
1160
- const n = motionCommandRepeat(command3);
1232
+ const n2 = motionCommandRepeat(command3);
1161
1233
  const mode = _nullishCoalesce(command3.repeatMode, () => ( "loop"));
1162
1234
  const shift = (v, by, k) => {
1163
1235
  if (typeof v === "number" && typeof by === "number") return v + by * k;
1164
1236
  if (Array.isArray(v) && Array.isArray(by) && v.length === by.length) return v.map((x, i) => x + by[i] * k);
1165
1237
  return v;
1166
1238
  };
1167
- for (let k = 1; k < n; k++) {
1239
+ for (let k = 1; k < n2; k++) {
1168
1240
  const at = start + k * cycle;
1169
1241
  for (const s of made) {
1170
1242
  if (mode === "mirror" && k % 2 === 1) {
@@ -1422,7 +1494,7 @@ function motionShotAt(timeline, time) {
1422
1494
  // src/animation-editor/camera.ts
1423
1495
  var windowCache = /* @__PURE__ */ new WeakMap();
1424
1496
  function motionShotCameraMove(shot, role) {
1425
- return _chunk2K5TELHVcjs.cameraMoveParts.call(void 0, _optionalChain([shot, 'optionalAccess', _55 => _55.camera, 'optionalAccess', _56 => _56[role === "transition" ? "in" : "during"]]));
1497
+ return _chunkJAOSL334cjs.cameraMoveParts.call(void 0, _optionalChain([shot, 'optionalAccess', _55 => _55.camera, 'optionalAccess', _56 => _56[role === "transition" ? "in" : "during"]]));
1426
1498
  }
1427
1499
  var motionCameraStage = (scene) => ({ width: scene.artwork.width, height: scene.artwork.height });
1428
1500
  var effectsPlayCamera = (provider) => !!provider && provider.capabilities.supportsCamera === true && typeof provider.cameraView === "function";
@@ -1444,7 +1516,7 @@ function motionCameraWindows(scene, provider) {
1444
1516
  const track = timeline.tracks.get(motionTrackKey("", "camera"));
1445
1517
  const boxView = (t) => {
1446
1518
  const b = trackValue(track, t);
1447
- return _chunk2K5TELHVcjs.cameraViewOfBox.call(void 0, Array.isArray(b) ? { x: b[0], y: b[1], width: b[2], height: b[3] } : full, stage);
1519
+ return _chunkJAOSL334cjs.cameraViewOfBox.call(void 0, Array.isArray(b) ? { x: b[0], y: b[1], width: b[2], height: b[3] } : full, stage);
1448
1520
  };
1449
1521
  const view = (move, values, from, to, u, ms2) => {
1450
1522
  try {
@@ -1460,7 +1532,7 @@ function motionCameraWindows(scene, provider) {
1460
1532
  const eases = timing.commandsStart > timing.start && !!_optionalChain([track, 'optionalAccess', _57 => _57.some, 'call', _58 => _58((seg) => seg.shot === timing.id)]);
1461
1533
  const framed = !!_optionalChain([track, 'optionalAccess', _59 => _59.some, 'call', _60 => _60((seg) => seg.shot === timing.id)]);
1462
1534
  if (timing.cameraIn && timing.commandsStart > timing.start) {
1463
- const spec = _chunk2K5TELHVcjs.cameraMoveParts.call(void 0, _optionalChain([shot, 'access', _61 => _61.camera, 'optionalAccess', _62 => _62.in]));
1535
+ const spec = _chunkJAOSL334cjs.cameraMoveParts.call(void 0, _optionalChain([shot, 'access', _61 => _61.camera, 'optionalAccess', _62 => _62.in]));
1464
1536
  const from = _nullishCoalesce(carry, () => ( boxView(timing.start)));
1465
1537
  const to = boxView(timing.commandsStart);
1466
1538
  if (view(spec.move, spec.values, from, to, 0, timing.commandsStart - timing.start)) {
@@ -1472,7 +1544,7 @@ function motionCameraWindows(scene, provider) {
1472
1544
  carry = null;
1473
1545
  } else if (framed) carry = null;
1474
1546
  if (timing.cameraDuring && timing.end > timing.commandsEnd) {
1475
- const spec = _chunk2K5TELHVcjs.cameraMoveParts.call(void 0, _optionalChain([shot, 'access', _63 => _63.camera, 'optionalAccess', _64 => _64.during]));
1547
+ const spec = _chunkJAOSL334cjs.cameraMoveParts.call(void 0, _optionalChain([shot, 'access', _63 => _63.camera, 'optionalAccess', _64 => _64.during]));
1476
1548
  const from = _nullishCoalesce(carry, () => ( boxView(timing.commandsEnd)));
1477
1549
  const ms2 = timing.end - timing.commandsEnd;
1478
1550
  const last = view(spec.move, spec.values, from, from, 1, ms2);
@@ -1489,8 +1561,8 @@ var lerp = (a, b, t) => a + (b - a) * t;
1489
1561
  var smooth = (t) => t * t * (3 - 2 * t);
1490
1562
  function easeViews(from, to, u, stage) {
1491
1563
  const k = smooth(Math.min(1, Math.max(0, u)));
1492
- const a = _chunk2K5TELHVcjs.cameraBoxOfView.call(void 0, from, stage);
1493
- const b = _chunk2K5TELHVcjs.cameraBoxOfView.call(void 0, to, stage);
1564
+ const a = _chunkJAOSL334cjs.cameraBoxOfView.call(void 0, from, stage);
1565
+ const b = _chunkJAOSL334cjs.cameraBoxOfView.call(void 0, to, stage);
1494
1566
  const zoom = from.zoom * Math.pow(to.zoom / from.zoom, k);
1495
1567
  const cx = lerp(a.x + a.width / 2, b.x + b.width / 2, k);
1496
1568
  const cy = lerp(a.y + a.height / 2, b.y + b.height / 2, k);
@@ -1522,15 +1594,15 @@ function motionCameraViewAt(scene, time, provider) {
1522
1594
  return retaken ? null : before.after;
1523
1595
  }
1524
1596
  function motionCameraBox(view, scene) {
1525
- return _chunk2K5TELHVcjs.cameraBoxOfView.call(void 0, view, motionCameraStage(scene));
1597
+ return _chunkJAOSL334cjs.cameraBoxOfView.call(void 0, view, motionCameraStage(scene));
1526
1598
  }
1527
1599
  function motionCameraTransform(view, width) {
1528
1600
  if (!view || !view.tilt && !view.turn) return "";
1529
1601
  const distance = (_nullishCoalesce(view.perspective, () => ( 2))) * width;
1530
1602
  return `perspective(${Math.round(distance)}px)${view.tilt ? ` rotateX(${round(view.tilt)}deg)` : ""}${view.turn ? ` rotateY(${round(-view.turn)}deg)` : ""}`;
1531
1603
  }
1532
- var round = (n) => Math.round(n * 1e3) / 1e3;
1533
- function withCameraViewEffects(svg, view, box, idPrefix = "vg-") {
1604
+ var round = (n2) => Math.round(n2 * 1e3) / 1e3;
1605
+ function withCameraViewEffects(svg, view, box2, idPrefix = "vg-") {
1534
1606
  if (!view || !view.roll && !(view.blur && view.blur > 0)) return svg;
1535
1607
  const open = svg.match(/<svg\b[^>]*>/);
1536
1608
  const close = svg.lastIndexOf("</svg>");
@@ -1538,7 +1610,7 @@ function withCameraViewEffects(svg, view, box, idPrefix = "vg-") {
1538
1610
  const startBody = open.index + open[0].length;
1539
1611
  const attrs = [];
1540
1612
  let defs = "";
1541
- if (view.roll) attrs.push(`transform="rotate(${round(view.roll)} ${round(box.x + box.width / 2)} ${round(box.y + box.height / 2)})"`);
1613
+ if (view.roll) attrs.push(`transform="rotate(${round(view.roll)} ${round(box2.x + box2.width / 2)} ${round(box2.y + box2.height / 2)})"`);
1542
1614
  if (view.blur && view.blur > 0) {
1543
1615
  const s = view.blur / view.zoom / 2;
1544
1616
  const a = (_nullishCoalesce(view.blurAngle, () => ( 0))) * Math.PI / 180;
@@ -1555,7 +1627,7 @@ async function checkMotionCameraMoves(scene, provider) {
1555
1627
  if (!named.length) return issues;
1556
1628
  if (!effectsPlayCamera(provider)) {
1557
1629
  const ids = named.map((s) => `"${s.id}"`).join(", ");
1558
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "camera-move-not-played", `Shots ${ids} name camera moves, but ${provider ? `${provider.name} has no camera moves` : "no effects provider is set"}, so the camera eases between framings instead; set a provider with camera moves to play them.`));
1630
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "camera-move-not-played", `Shots ${ids} name camera moves, but ${provider ? `${provider.name} has no camera moves` : "no effects provider is set"}, so the camera eases between framings instead; set a provider with camera moves to play them.`));
1559
1631
  return issues;
1560
1632
  }
1561
1633
  for (const shot of named) {
@@ -1563,22 +1635,345 @@ async function checkMotionCameraMoves(scene, provider) {
1563
1635
  const spec = shot.camera[field];
1564
1636
  if (spec === void 0) continue;
1565
1637
  try {
1566
- await _chunk2K5TELHVcjs.resolveCameraMoveSpec.call(void 0, provider, spec, role);
1638
+ await _chunkJAOSL334cjs.resolveCameraMoveSpec.call(void 0, provider, spec, role);
1567
1639
  } catch (error) {
1568
1640
  const code = error.code === "invalid_values" || /preset/.test(error.message) ? "camera-move-values" : "camera-move-unknown";
1569
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, code, error.message, { shot: shot.id }));
1641
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, code, error.message, { shot: shot.id }));
1570
1642
  }
1571
1643
  }
1572
1644
  }
1573
1645
  return issues;
1574
1646
  }
1575
1647
 
1648
+ // src/animation-editor/layers.ts
1649
+ var spanCache = /* @__PURE__ */ new WeakMap();
1650
+ function motionLayerSpans(scene) {
1651
+ const hit = spanCache.get(scene);
1652
+ if (hit) return hit;
1653
+ const timeline = compileMotionScene(scene);
1654
+ const shots = timeline.shots;
1655
+ const total = timeline.duration;
1656
+ const at = (shotIndex2, t, fallback) => {
1657
+ if (t === void 0) return fallback;
1658
+ if (typeof t === "number") return shots[shotIndex2].start + t;
1659
+ return _nullishCoalesce(_optionalChain([shots, 'access', _68 => _68[shotIndex2 + t.shots], 'optionalAccess', _69 => _69.start]), () => ( total));
1660
+ };
1661
+ const out = [];
1662
+ scene.shots.forEach((shot, shotIndex2) => {
1663
+ (_nullishCoalesce(shot.layers, () => ( []))).forEach((layer, index) => {
1664
+ const start = at(shotIndex2, layer.from, shots[shotIndex2].start);
1665
+ const end = Math.max(start, at(shotIndex2, _nullishCoalesce(layer.until, () => ( { shots: 1 })), total));
1666
+ out.push({ layer, shot: shot.id, shotIndex: shotIndex2, index, role: _chunk6RZVFYGJcjs.effectLayerRole.call(void 0, layer), start, end });
1667
+ });
1668
+ });
1669
+ spanCache.set(scene, out);
1670
+ return out;
1671
+ }
1672
+ function motionLayersAt(scene, time) {
1673
+ if (!scene.shots.some((s) => _optionalChain([s, 'access', _70 => _70.layers, 'optionalAccess', _71 => _71.length]))) return [];
1674
+ const total = compileMotionScene(scene).duration;
1675
+ const out = [];
1676
+ for (const span of motionLayerSpans(scene)) {
1677
+ if (time < span.start || time > span.end || time === span.end && span.end < total) continue;
1678
+ const fade = _nullishCoalesce(span.layer.fade, () => ( 0));
1679
+ const k = fade > 0 ? Math.min(1, (time - span.start) / fade, (span.end - time) / fade) : 1;
1680
+ const opacity = Math.max(0, Math.min(1, (_nullishCoalesce(span.layer.opacity, () => ( 1))) * k));
1681
+ if (opacity <= 0) continue;
1682
+ out.push({ ...span, time: time - span.start, opacity });
1683
+ }
1684
+ return out;
1685
+ }
1686
+ function motionTransitionAt(scene, time) {
1687
+ if (!scene.shots.some((s) => s.transition)) return null;
1688
+ for (const s of compileMotionScene(scene).shots) {
1689
+ if (s.transition && s.transitionEnd !== void 0 && time >= s.start && time < s.transitionEnd)
1690
+ return { shot: s.id, shotIndex: s.index, effect: s.transition, start: s.start, end: s.transitionEnd, progress: (time - s.start) / (s.transitionEnd - s.start) };
1691
+ }
1692
+ return null;
1693
+ }
1694
+ var motionSceneHasLayers = (scene) => scene.shots.some((s) => _optionalChain([s, 'access', _72 => _72.layers, 'optionalAccess', _73 => _73.length]) || s.transition);
1695
+ var esc = (s) => s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1696
+ var n = (v) => String(Math.round(v * 1e3) / 1e3);
1697
+ var box = (b) => `x="${n(b.x)}" y="${n(b.y)}" width="${n(b.width)}" height="${n(b.height)}"`;
1698
+ var svgDataUrl = (svg) => `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
1699
+ function subjectSvg(subject, b, opacity, pictures) {
1700
+ const o = opacity < 1 ? ` opacity="${n(opacity)}"` : "";
1701
+ if ("image" in subject) {
1702
+ const href = _nullishCoalesce(_optionalChain([pictures, 'optionalAccess', _74 => _74.subject, 'call', _75 => _75(subject)]), () => ( subject.image));
1703
+ return `<image href="${esc(href)}" ${box(b)} preserveAspectRatio="xMidYMid meet"${o}/>`;
1704
+ }
1705
+ if ("svg" in subject) {
1706
+ const href = subject.svg.trimStart().startsWith("<") ? svgDataUrl(subject.svg) : subject.svg;
1707
+ return `<image href="${esc(href)}" ${box(b)} preserveAspectRatio="xMidYMid meet"${o}/>`;
1708
+ }
1709
+ if ("text" in subject) {
1710
+ const size = Math.max(1, Math.min(b.height * 0.6, b.width / Math.max(1, subject.text.length) * 1.6));
1711
+ return `<text x="${n(b.x + b.width / 2)}" y="${n(b.y + b.height / 2)}" text-anchor="middle" dominant-baseline="central" font-family="${esc(_nullishCoalesce(subject.font, () => ( "system-ui, sans-serif")))}" font-weight="${esc(String(_nullishCoalesce(subject.weight, () => ( 700))))}" font-size="${n(size)}" fill="#ffffff"${o}>${esc(subject.text)}</text>`;
1712
+ }
1713
+ return "";
1714
+ }
1715
+ function layerSvg(scene, active, camera, pictures) {
1716
+ const b = _nullishCoalesce(active.layer.rect, () => ( camera));
1717
+ if (!active.layer.effect) return active.layer.subject ? subjectSvg(active.layer.subject, b, active.opacity, pictures) : "";
1718
+ const href = _optionalChain([pictures, 'optionalAccess', _76 => _76.layer, 'call', _77 => _77(scene, active, b)]);
1719
+ if (!href) return "";
1720
+ return `<image href="${esc(href)}" ${box(b)} preserveAspectRatio="none" data-layer="${esc(active.layer.id)}"${active.opacity < 1 ? ` opacity="${n(active.opacity)}"` : ""}/>`;
1721
+ }
1722
+ function withMotionShotLayers(svg, scene, time, camera, pictures, background) {
1723
+ const active = motionLayersAt(scene, time);
1724
+ const open = svg.indexOf(">") + 1;
1725
+ let body = svg.slice(open, svg.lastIndexOf("</svg>"));
1726
+ const defs = _nullishCoalesce(_optionalChain([/^\s*<defs>[\s\S]*?<\/defs>/, 'access', _78 => _78.exec, 'call', _79 => _79(body), 'optionalAccess', _80 => _80[0]]), () => ( ""));
1727
+ body = body.slice(defs.length);
1728
+ const bg = background && scene.artwork.background ? `<rect width="${n(scene.artwork.width)}" height="${n(scene.artwork.height)}" fill="${esc(scene.artwork.background)}"/>` : "";
1729
+ const draw = (roles) => active.filter((a) => roles.includes(a.role)).map((a) => layerSvg(scene, a, camera, pictures)).join("");
1730
+ return `${svg.slice(0, open)}${defs}${bg}${draw(["background"])}${body}${draw(["subject"])}${draw(["overlay"])}</svg>`;
1731
+ }
1732
+ function motionTransitionSvg(svg, from, active, camera, picture) {
1733
+ const open = svg.indexOf(">") + 1;
1734
+ if (picture) return `${svg.slice(0, open)}<image href="${esc(picture)}" ${box(camera)} preserveAspectRatio="none" data-transition="${esc(active.effect)}"/></svg>`;
1735
+ const inner = svg.slice(open, svg.lastIndexOf("</svg>"));
1736
+ const fromView = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _81 => _81.exec, 'call', _82 => _82(from), 'optionalAccess', _83 => _83[1]]), () => ( ""));
1737
+ const fromInner = from.slice(from.indexOf(">") + 1, from.lastIndexOf("</svg>"));
1738
+ return `${svg.slice(0, open)}<svg ${box(camera)} viewBox="${fromView}" preserveAspectRatio="none" data-transition-from="">${fromInner}</svg><g opacity="${n(active.progress)}" data-transition-to="">${inner}</g></svg>`;
1739
+ }
1740
+ var sceneIds = /* @__PURE__ */ new WeakMap();
1741
+ var nextSceneId = 1;
1742
+ var sceneId = (scene) => {
1743
+ let id = sceneIds.get(scene);
1744
+ if (!id) sceneIds.set(scene, id = nextSceneId++);
1745
+ return id;
1746
+ };
1747
+ var MotionLayersEngine = (_class = class {
1748
+
1749
+
1750
+
1751
+ __init() {this.cache = /* @__PURE__ */ new Map()}
1752
+ __init2() {this.pending = /* @__PURE__ */ new Map()}
1753
+ __init3() {this.failures = /* @__PURE__ */ new Map()}
1754
+ __init4() {this.descriptions = /* @__PURE__ */ new Map()}
1755
+ __init5() {this.listeners = /* @__PURE__ */ new Set()}
1756
+
1757
+
1758
+ __init6() {this.generation = 0}
1759
+
1760
+ 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);
1761
+ this.slot = _nullishCoalesce(options.slot, () => ( new (0, _chunkJAOSL334cjs.EffectsSlot)()));
1762
+ this.host = _nullishCoalesce(options.host, () => ( null));
1763
+ this.frameSvg = options.frameSvg;
1764
+ this.options = { scale: _nullishCoalesce(options.scale, () => ( 1)), maxSize: _nullishCoalesce(options.maxSize, () => ( 1280)), fps: _nullishCoalesce(options.fps, () => ( 30)), maxEntries: _nullishCoalesce(options.maxEntries, () => ( 160)) };
1765
+ this.pool = new (0, _chunkPY4GWSE2cjs.EffectRendererPool)({ maxRenderers: options.maxRenderers });
1766
+ this.detach = this.slot.subscribe(() => {
1767
+ this.reset();
1768
+ this.notify();
1769
+ });
1770
+ }
1771
+ get provider() {
1772
+ return this.slot.provider;
1773
+ }
1774
+ /** Called when a picture is ready (draw the frame again). */
1775
+ subscribe(listener) {
1776
+ this.listeners.add(listener);
1777
+ return () => this.listeners.delete(listener);
1778
+ }
1779
+ /** Why pictures failed, newest last. */
1780
+ failuresList() {
1781
+ return [...this.failures.values()];
1782
+ }
1783
+ /** True while any picture is being made. */
1784
+ get busy() {
1785
+ return this.pending.size > 0;
1786
+ }
1787
+ /** Resolves once every picture asked for so far is made (or failed). */
1788
+ async idle() {
1789
+ while (this.pending.size) await Promise.all([...this.pending.values()]);
1790
+ }
1791
+ /** Forgets every picture and failure (a new provider, or the host changed). */
1792
+ reset() {
1793
+ this.generation++;
1794
+ this.cache.clear();
1795
+ this.pending.clear();
1796
+ this.failures.clear();
1797
+ this.descriptions.clear();
1798
+ this.pool.clear();
1799
+ }
1800
+ dispose() {
1801
+ this.detach();
1802
+ this.reset();
1803
+ this.listeners.clear();
1804
+ }
1805
+ notify() {
1806
+ this.listeners.forEach((l) => l());
1807
+ }
1808
+ size(b) {
1809
+ const k = Math.min(this.options.scale, this.options.maxSize / Math.max(b.width, b.height));
1810
+ return { width: Math.max(1, Math.round(b.width * k)), height: Math.max(1, Math.round(b.height * k)) };
1811
+ }
1812
+ snap(t) {
1813
+ const step = 1e3 / this.options.fps;
1814
+ return Math.round(Math.round(t / step) * step * 1e3) / 1e3;
1815
+ }
1816
+ get(key) {
1817
+ const v = this.cache.get(key);
1818
+ if (v === void 0) return void 0;
1819
+ this.cache.delete(key);
1820
+ this.cache.set(key, v);
1821
+ return v;
1822
+ }
1823
+ request(key, info2, work) {
1824
+ if (this.cache.has(key) || this.pending.has(key) || this.failures.has(key)) return;
1825
+ const generation = this.generation;
1826
+ const run = work().then((url) => {
1827
+ if (generation !== this.generation) return;
1828
+ this.cache.set(key, url);
1829
+ while (this.cache.size > this.options.maxEntries) this.cache.delete(this.cache.keys().next().value);
1830
+ }).catch((error) => {
1831
+ if (generation !== this.generation) return;
1832
+ this.failures.set(key, { key, ...info2, error: error instanceof Error ? error : new Error(String(error)) });
1833
+ }).finally(() => {
1834
+ if (generation !== this.generation) return;
1835
+ this.pending.delete(key);
1836
+ this.notify();
1837
+ });
1838
+ this.pending.set(key, run);
1839
+ }
1840
+ describe(id) {
1841
+ const provider = this.slot.require();
1842
+ let d = this.descriptions.get(id);
1843
+ if (!d) {
1844
+ d = Promise.resolve(provider.describe(id));
1845
+ this.descriptions.set(id, d);
1846
+ d.catch(() => this.descriptions.delete(id));
1847
+ }
1848
+ return d;
1849
+ }
1850
+ subject(subject) {
1851
+ if (subject.image.startsWith("data:")) return subject.image;
1852
+ const host = this.host;
1853
+ if (!host) return void 0;
1854
+ const key = `img:${subject.image}`;
1855
+ this.request(key, {}, () => host.loadImage(subject.image));
1856
+ return this.get(key);
1857
+ }
1858
+ /** The pixels a layer's subject feeds its effect, at `size`. */
1859
+ async subjectPixels(subject, size) {
1860
+ const host = this.host;
1861
+ const b = { x: 0, y: 0, ...size };
1862
+ let s = subject;
1863
+ if ("image" in s && !s.image.startsWith("data:")) s = { image: await host.loadImage(s.image) };
1864
+ return host.rasterize(`<svg xmlns="http://www.w3.org/2000/svg" width="${size.width}" height="${size.height}" viewBox="0 0 ${size.width} ${size.height}">${subjectSvg(s, b, 1, void 0)}</svg>`, size.width, size.height);
1865
+ }
1866
+ layer(scene, active, b) {
1867
+ const provider = this.slot.provider;
1868
+ const host = this.host;
1869
+ const layer = active.layer;
1870
+ if (!provider || !host || !layer.effect) return void 0;
1871
+ if (layer.subject && "slot" in layer.subject) return void 0;
1872
+ const size = this.size(b);
1873
+ const time = this.snap(active.time);
1874
+ const key = `layer:${provider.id}:${layer.effect}:${JSON.stringify(_nullishCoalesce(layer.values, () => ( {})))}:${JSON.stringify(_nullishCoalesce(layer.subject, () => ( null)))}:${size.width}x${size.height}@${time}`;
1875
+ const effect = layer.effect;
1876
+ this.request(key, { layer: layer.id }, async () => {
1877
+ const d = await this.describe(effect);
1878
+ const values = _chunkJAOSL334cjs.resolveEffectValues.call(void 0, d, layer.values);
1879
+ const input = _chunk6RZVFYGJcjs.effectLayerInput.call(void 0, d);
1880
+ const inputs = layer.subject && input ? { [input]: await this.subjectPixels(layer.subject, size) } : void 0;
1881
+ const pixels = await this.pool.render(provider, effect, size, { time, values, ...inputs ? { inputs } : {} });
1882
+ return host.encode(pixels);
1883
+ });
1884
+ return this.get(key);
1885
+ }
1886
+ transition(scene, time, active) {
1887
+ const provider = this.slot.provider;
1888
+ const host = this.host;
1889
+ const frameSvg = this.frameSvg;
1890
+ if (!provider || !host || !frameSvg) return void 0;
1891
+ const shot = scene.shots[active.shotIndex];
1892
+ const t = shot.transition;
1893
+ const size = this.size({ x: 0, y: 0, width: scene.artwork.width, height: scene.artwork.height });
1894
+ const at = Math.min(active.end - 1e-3, this.snap(time));
1895
+ const progress = (at - active.start) / (active.end - active.start);
1896
+ const key = `transition:${sceneId(scene)}:${provider.id}:${t.effect}:${JSON.stringify(_nullishCoalesce(t.values, () => ( {})))}:${size.width}x${size.height}@${at}`;
1897
+ const fromKey = `from:${sceneId(scene)}:${active.shot}:${size.width}x${size.height}`;
1898
+ this.request(key, { transition: active.shot }, async () => {
1899
+ const d = await this.describe(t.effect);
1900
+ const values = _chunkJAOSL334cjs.resolveEffectValues.call(void 0, d, t.values);
1901
+ frameSvg(scene, Math.max(0, active.start - 1e-3));
1902
+ frameSvg(scene, at);
1903
+ await this.idleExcept(key);
1904
+ const fromSvg = frameSvg(scene, Math.max(0, active.start - 1e-3));
1905
+ const toSvg = frameSvg(scene, at);
1906
+ let from = this.fromPixels.get(fromKey);
1907
+ if (!from) {
1908
+ from = host.rasterize(fromSvg, size.width, size.height);
1909
+ this.fromPixels.set(fromKey, from);
1910
+ if (this.fromPixels.size > 8) this.fromPixels.delete(this.fromPixels.keys().next().value);
1911
+ }
1912
+ const to = await host.rasterize(toSvg, size.width, size.height);
1913
+ const pixels = await this.pool.render(provider, t.effect, size, { time: at - active.start, progress, values, inputs: { from: await from, to } });
1914
+ return host.encode(pixels);
1915
+ });
1916
+ return this.get(key);
1917
+ }
1918
+ __init7() {this.fromPixels = /* @__PURE__ */ new Map()}
1919
+ async idleExcept(key) {
1920
+ for (; ; ) {
1921
+ const others = [...this.pending].filter(([k]) => k !== key && !k.startsWith("transition:")).map(([, p]) => p);
1922
+ if (!others.length) return;
1923
+ await Promise.all(others);
1924
+ }
1925
+ }
1926
+ }, _class);
1927
+ function browserMotionLayerHost(doc = globalThis.document) {
1928
+ const view = _optionalChain([doc, 'optionalAccess', _84 => _84.defaultView]);
1929
+ if (!doc || !_optionalChain([view, 'optionalAccess', _85 => _85.Image]) || typeof doc.createElement !== "function") return null;
1930
+ const canvas = (width, height) => {
1931
+ const c = doc.createElement("canvas");
1932
+ c.width = width;
1933
+ c.height = height;
1934
+ const ctx = c.getContext("2d");
1935
+ if (!ctx) throw new Error("This browser cannot draw layer pictures (no 2D canvas).");
1936
+ return { c, ctx };
1937
+ };
1938
+ const load = (src, cors) => new Promise((resolve2, reject) => {
1939
+ const image = new view.Image();
1940
+ if (cors) image.crossOrigin = "anonymous";
1941
+ image.onload = () => resolve2(image);
1942
+ image.onerror = () => reject(new Error(`The picture ${src.startsWith("data:") ? "(a data: URL)" : `"${src}"`} could not be loaded; check the URL and that its server allows cross-origin use.`));
1943
+ image.src = src;
1944
+ });
1945
+ return {
1946
+ async rasterize(svg, width, height) {
1947
+ const image = await load(svgDataUrl(svg), false);
1948
+ const { ctx } = canvas(width, height);
1949
+ ctx.drawImage(image, 0, 0, width, height);
1950
+ const data = ctx.getImageData(0, 0, width, height);
1951
+ return { width, height, data: data.data };
1952
+ },
1953
+ encode(pixels) {
1954
+ const { c, ctx } = canvas(pixels.width, pixels.height);
1955
+ const image = ctx.createImageData(pixels.width, pixels.height);
1956
+ image.data.set(pixels.data);
1957
+ ctx.putImageData(image, 0, 0);
1958
+ return c.toDataURL("image/png");
1959
+ },
1960
+ async loadImage(url) {
1961
+ if (url.startsWith("data:")) return url;
1962
+ const image = await load(url, true);
1963
+ const { c, ctx } = canvas(image.naturalWidth || 1, image.naturalHeight || 1);
1964
+ ctx.drawImage(image, 0, 0);
1965
+ return c.toDataURL("image/png");
1966
+ }
1967
+ };
1968
+ }
1969
+ var motionLayerSlot = (layer) => layer.subject && "slot" in layer.subject ? layer.subject.slot : void 0;
1970
+
1576
1971
  // src/animation-editor/evaluate.ts
1577
1972
  var MOTION_DRAW_EFFECT_ID = "~draw";
1578
1973
  var TRANSFORM_PROPS = ["x", "y", "rotation", "scaleX", "scaleY", "follow"];
1579
1974
  var tidy = (v) => Math.round(v * 1e6) / 1e6 || 0;
1580
1975
  function applyMotionControlValues(scene, values) {
1581
- if (!values || !_optionalChain([scene, 'access', _68 => _68.controls, 'optionalAccess', _69 => _69.length])) return scene;
1976
+ if (!values || !_optionalChain([scene, 'access', _86 => _86.controls, 'optionalAccess', _87 => _87.length])) return scene;
1582
1977
  const map = /* @__PURE__ */ new Map();
1583
1978
  for (const control of scene.controls) {
1584
1979
  const value = values[control.name];
@@ -1625,7 +2020,7 @@ function shapeIn(entry, shape) {
1625
2020
  }
1626
2021
  function shapeAt(entry, timeline, nodes, time) {
1627
2022
  const track = timeline.tracks.get(motionTrackKey(entry.node.id, "shape"));
1628
- if (!_optionalChain([track, 'optionalAccess', _70 => _70.length])) return null;
2023
+ if (!_optionalChain([track, 'optionalAccess', _88 => _88.length])) return null;
1629
2024
  const s = activeSegment(track, time);
1630
2025
  const fromId = String(s ? s.from : track[0].from);
1631
2026
  if (!s) return fromId === entry.node.id ? null : nodes.get(fromId) ? shapeIn(entry, nodes.get(fromId)) : null;
@@ -1648,8 +2043,8 @@ function motionFollowRoute(entry, path) {
1648
2043
  }
1649
2044
  function followAt(entry, timeline, nodes, time) {
1650
2045
  const track = timeline.tracks.get(motionTrackKey(entry.node.id, "follow"));
1651
- const s = _nullishCoalesce(activeSegment(track, time), () => ( _optionalChain([track, 'optionalAccess', _71 => _71[0]])));
1652
- if (!_optionalChain([s, 'optionalAccess', _72 => _72.path])) return null;
2046
+ const s = _nullishCoalesce(activeSegment(track, time), () => ( _optionalChain([track, 'optionalAccess', _89 => _89[0]])));
2047
+ if (!_optionalChain([s, 'optionalAccess', _90 => _90.path])) return null;
1653
2048
  const route = motionFollowRoute(entry, nodes.get(s.path));
1654
2049
  const progress = Number(trackValue(track, time));
1655
2050
  if (!route || !(progress >= 0)) return null;
@@ -1667,7 +2062,7 @@ function motionNodeOffset(entry, timeline, time, nodes = indexMotionNodes([entry
1667
2062
  const layered = withMotionLayers(timeline, entry.node.id, p, typeof v === "number" ? v : rest, time, raw);
1668
2063
  return p === "follow" ? layered : applyMotionDrivers(timeline, entry.node.id, p, layered, time, raw);
1669
2064
  };
1670
- const driven = _optionalChain([timeline, 'access', _73 => _73.drivers, 'optionalAccess', _74 => _74.some, 'call', _75 => _75((d) => d.target === entry.node.id && d.property !== "opacity")]);
2065
+ const driven = _optionalChain([timeline, 'access', _91 => _91.drivers, 'optionalAccess', _92 => _92.some, 'call', _93 => _93((d) => d.target === entry.node.id && d.property !== "opacity")]);
1671
2066
  if (!driven && !TRANSFORM_PROPS.some((p) => (follow || p !== "follow") && (timeline.tracks.has(motionTrackKey(entry.node.id, p)) || timeline.tracks.has(motionAddTrackKey(entry.node.id, p))))) return null;
1672
2067
  const dx = read("x", 0);
1673
2068
  const dy = read("y", 0);
@@ -1711,7 +2106,7 @@ function motionFrameAt(scene, time, options = {}) {
1711
2106
  if (out.visible === void 0) delete out.visible;
1712
2107
  }
1713
2108
  const effectTracks = [...timeline.tracks.keys()].filter((k) => k.startsWith(`${id}\0effect:`));
1714
- if (effectTracks.length && _optionalChain([out, 'access', _76 => _76.effects, 'optionalAccess', _77 => _77.length])) {
2109
+ if (effectTracks.length && _optionalChain([out, 'access', _94 => _94.effects, 'optionalAccess', _95 => _95.length])) {
1715
2110
  out = {
1716
2111
  ...out,
1717
2112
  effects: out.effects.map((effect) => {
@@ -1751,7 +2146,7 @@ function motionFrameAt(scene, time, options = {}) {
1751
2146
  return p;
1752
2147
  });
1753
2148
  }
1754
- const opacity = _optionalChain([timeline, 'access', _78 => _78.drivers, 'optionalAccess', _79 => _79.some, 'call', _80 => _80((d) => d.target === id && d.property === "opacity")]) ? motionPropertyValue(timeline, id, "opacity", time) : value(id, "opacity");
2149
+ const opacity = _optionalChain([timeline, 'access', _96 => _96.drivers, 'optionalAccess', _97 => _97.some, 'call', _98 => _98((d) => d.target === id && d.property === "opacity")]) ? motionPropertyValue(timeline, id, "opacity", time) : value(id, "opacity");
1755
2150
  if (typeof opacity === "number") {
1756
2151
  const o = Math.min(1, Math.max(0, tidy(opacity)));
1757
2152
  out = { ...out, opacity: o };
@@ -1767,8 +2162,8 @@ function motionFrameAt(scene, time, options = {}) {
1767
2162
  const document = { ...s.artwork, nodes: s.artwork.nodes.map(visit) };
1768
2163
  const cam = trackValue(timeline.tracks.get(motionTrackKey("", "camera")), time);
1769
2164
  const view = motionCameraViewAt(s, time, options.effects);
1770
- const box = view ? (({ x, y, width, height }) => [x, y, width, height])(motionCameraBox(view, s)) : Array.isArray(cam) ? cam : [0, 0, s.artwork.width, s.artwork.height];
1771
- return { time, document, camera: { x: tidy(box[0]), y: tidy(box[1]), width: tidy(box[2]), height: tidy(box[3]) }, ...view ? { view } : {}, clips };
2165
+ const box2 = view ? (({ x, y, width, height }) => [x, y, width, height])(motionCameraBox(view, s)) : Array.isArray(cam) ? cam : [0, 0, s.artwork.width, s.artwork.height];
2166
+ return { time, document, camera: { x: tidy(box2[0]), y: tidy(box2[1]), width: tidy(box2[2]), height: tidy(box2[3]) }, ...view ? { view } : {}, clips };
1772
2167
  }
1773
2168
  function motionClipAttrs(clip) {
1774
2169
  const { shape: _shape, ...attrs } = clip;
@@ -1780,11 +2175,20 @@ function motionFrameToSvg(scene, time, options = {}) {
1780
2175
  const wrap = { ...options.wrap };
1781
2176
  for (const [id, clips] of Object.entries(frame.clips))
1782
2177
  wrap[id] = [..._nullishCoalesce(wrap[id], () => ( [])), ...clips.map((clip, i) => ({ clip: { id: `${prefix}reveal-${id}-${i}`, shape: clip.shape, attrs: motionClipAttrs(clip) } }))];
1783
- return withCameraViewEffects(withViewBox(_chunkBQE3Y6ZOcjs.vectorDocumentToSvg.call(void 0, frame.document, { ...options, wrap }), frame.camera), frame.view, frame.camera, prefix);
2178
+ if (!motionSceneHasLayers(scene)) return withCameraViewEffects(withViewBox(_chunkBQE3Y6ZOcjs.vectorDocumentToSvg.call(void 0, frame.document, { ...options, wrap }), frame.camera), frame.view, frame.camera, prefix);
2179
+ const art = _chunkBQE3Y6ZOcjs.vectorDocumentToSvg.call(void 0, frame.document, { ...options, wrap, background: false });
2180
+ let svg = withViewBox(withMotionShotLayers(art, scene, time, frame.camera, options.layers, options.background !== false), frame.camera);
2181
+ const transition = options.transitions === false ? null : motionTransitionAt(scene, time);
2182
+ if (transition) {
2183
+ const picture = _optionalChain([options, 'access', _99 => _99.layers, 'optionalAccess', _100 => _100.transition, 'call', _101 => _101(scene, time, transition)]);
2184
+ const from = picture ? "" : motionFrameToSvg(scene, Math.max(0, transition.start - 1e-3), { ...options, transitions: false, idPrefix: `${prefix}from-` });
2185
+ svg = motionTransitionSvg(svg, from, transition, frame.camera, picture);
2186
+ }
2187
+ return withCameraViewEffects(svg, frame.view, frame.camera, prefix);
1784
2188
  }
1785
- function withViewBox(svg, box) {
1786
- const f = (n) => String(Math.round(n * 1e3) / 1e3);
1787
- return svg.replace(/viewBox="[^"]*"/, `viewBox="${f(box.x)} ${f(box.y)} ${f(box.width)} ${f(box.height)}"`);
2189
+ function withViewBox(svg, box2) {
2190
+ const f = (n2) => String(Math.round(n2 * 1e3) / 1e3);
2191
+ return svg.replace(/viewBox="[^"]*"/, `viewBox="${f(box2.x)} ${f(box2.y)} ${f(box2.width)} ${f(box2.height)}"`);
1788
2192
  }
1789
2193
 
1790
2194
  // src/animation-editor/onion.ts
@@ -1822,10 +2226,10 @@ function motionPath(scene, id, options = {}) {
1822
2226
  const to = Math.min(timeline.duration, _nullishCoalesce(options.to, () => ( timeline.duration)));
1823
2227
  let step = options.step && options.step > 0 ? options.step : 1e3 / 30;
1824
2228
  if ((to - from) / step > 400) step = (to - from) / 400;
1825
- const r = (n) => Math.round(n * 100) / 100;
2229
+ const r = (n2) => Math.round(n2 * 100) / 100;
1826
2230
  const centreAt = (t) => {
1827
2231
  const node = indexMotionNodes(motionFrameAtCache(scene, t)).get(id);
1828
- const b = _optionalChain([node, 'optionalAccess', _81 => _81.bounds]);
2232
+ const b = _optionalChain([node, 'optionalAccess', _102 => _102.bounds]);
1829
2233
  return b ? { t: r(t), x: r(b.x + b.width / 2), y: r(b.y + b.height / 2) } : null;
1830
2234
  };
1831
2235
  const points = [];
@@ -1843,7 +2247,7 @@ function motionFrameAtCache(scene, t) {
1843
2247
  }
1844
2248
  function motionPathsSvg(paths, camera, color = "#f43f5e") {
1845
2249
  const w = Math.max(camera.width, camera.height) / 400;
1846
- const f = (n) => String(Math.round(n * 100) / 100);
2250
+ const f = (n2) => String(Math.round(n2 * 100) / 100);
1847
2251
  return paths.filter((p) => p.points.length > 1).map(
1848
2252
  (p) => `<g data-motion-path="${p.id.replace(/"/g, "&quot;")}" fill="none" stroke="${color}" stroke-width="${f(w)}" stroke-linecap="round" stroke-linejoin="round" opacity="0.8"><polyline points="${p.points.map((q) => `${f(q.x)},${f(q.y)}`).join(" ")}" stroke-dasharray="${f(w * 3)} ${f(w * 2)}"/>${p.keys.map((k) => `<circle cx="${f(k.x)}" cy="${f(k.y)}" r="${f(w * 2.5)}" fill="${color}" stroke="none"/>`).join("")}</g>`
1849
2253
  ).join("");
@@ -1859,22 +2263,22 @@ function motionBlurTimes(scene, time, blur = {}) {
1859
2263
  const b = indexMotionNodes(motionFrameAt(scene, time + span / 2).document.nodes);
1860
2264
  let travel = 0;
1861
2265
  for (const id of ids) {
1862
- const p = _optionalChain([a, 'access', _82 => _82.get, 'call', _83 => _83(id), 'optionalAccess', _84 => _84.bounds]);
1863
- const q = _optionalChain([b, 'access', _85 => _85.get, 'call', _86 => _86(id), 'optionalAccess', _87 => _87.bounds]);
2266
+ const p = _optionalChain([a, 'access', _103 => _103.get, 'call', _104 => _104(id), 'optionalAccess', _105 => _105.bounds]);
2267
+ const q = _optionalChain([b, 'access', _106 => _106.get, 'call', _107 => _107(id), 'optionalAccess', _108 => _108.bounds]);
1864
2268
  if (p && q) travel = Math.max(travel, Math.abs(p.x - q.x), Math.abs(p.y - q.y), Math.abs(p.x + p.width - q.x - q.width), Math.abs(p.y + p.height - q.y - q.height));
1865
2269
  }
1866
- const n = Math.min(most, Math.ceil(travel / 1.5));
1867
- if (n <= 1) return [time];
1868
- return Array.from({ length: n }, (_, k) => time - span / 2 + span * (k + 0.5) / n);
2270
+ const n2 = Math.min(most, Math.ceil(travel / 1.5));
2271
+ if (n2 <= 1) return [time];
2272
+ return Array.from({ length: n2 }, (_, k) => time - span / 2 + span * (k + 0.5) / n2);
1869
2273
  }
1870
2274
  function averagedFrames(scene, times, options, prefix) {
1871
- const n = times.length;
1872
- return `<g data-motion-blur="${n}" style="isolation:isolate">${times.map((t, k) => {
2275
+ const n2 = times.length;
2276
+ return `<g data-motion-blur="${n2}" style="isolation:isolate">${times.map((t, k) => {
1873
2277
  const own = `${prefix}blur${k}-`;
1874
2278
  const frame = motionFrameToSvg(scene, t, { ...options, idPrefix: own, background: false });
1875
- const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _88 => _88.exec, 'call', _89 => _89(frame), 'optionalAccess', _90 => _90[1]]), () => ( ""));
1876
- const inner = k === n - 1 ? innerSvg(frame) : innerSvg(frame).replace(new RegExp(` id="(?!${own.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
1877
- return `<svg viewBox="${view}" preserveAspectRatio="none" style="mix-blend-mode:plus-lighter" opacity="${Math.round(1e4 / n) / 1e4}">${inner}</svg>`;
2279
+ const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _109 => _109.exec, 'call', _110 => _110(frame), 'optionalAccess', _111 => _111[1]]), () => ( ""));
2280
+ const inner = k === n2 - 1 ? innerSvg(frame) : innerSvg(frame).replace(new RegExp(` id="(?!${own.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
2281
+ return `<svg viewBox="${view}" preserveAspectRatio="none" style="mix-blend-mode:plus-lighter" opacity="${Math.round(1e4 / n2) / 1e4}">${inner}</svg>`;
1878
2282
  }).join("")}</g>`;
1879
2283
  }
1880
2284
  var innerSvg = (svg) => svg.slice(svg.indexOf(">") + 1, svg.lastIndexOf("</svg>"));
@@ -1887,7 +2291,7 @@ function motionStageSvg(scene, time, options = {}) {
1887
2291
  if (blurTimes.length > 1) {
1888
2292
  const open2 = current.indexOf(">") + 1;
1889
2293
  const body2 = current.slice(open2, current.lastIndexOf("</svg>"));
1890
- const bg2 = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _91 => _91.exec, 'call', _92 => _92(body2), 'optionalAccess', _93 => _93[0]]), () => ( "")) : "";
2294
+ const bg2 = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _112 => _112.exec, 'call', _113 => _113(body2), 'optionalAccess', _114 => _114[0]]), () => ( "")) : "";
1891
2295
  current = `${current.slice(0, open2)}${bg2}${averagedFrames(scene, blurTimes, svgOptions, prefix)}</svg>`;
1892
2296
  }
1893
2297
  if (!onionSkin && !motionPaths) return current;
@@ -1902,14 +2306,14 @@ function motionStageSvg(scene, time, options = {}) {
1902
2306
  const open = current.indexOf(">") + 1;
1903
2307
  const close = current.lastIndexOf("</svg>");
1904
2308
  const body = current.slice(open, close);
1905
- const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _94 => _94.exec, 'call', _95 => _95(body), 'optionalAccess', _96 => _96[0]]), () => ( "")) : "";
2309
+ const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _115 => _115.exec, 'call', _116 => _116(body), 'optionalAccess', _117 => _117[0]]), () => ( "")) : "";
1906
2310
  return `${current.slice(0, open)}${bg}${ghosts.join("")}${body.slice(bg.length)}${paths}</svg>`;
1907
2311
  }
1908
2312
  function motionContactSheetSvg(scene, times, options = {}) {
1909
2313
  const { columns, labels = true, ...frameOptions } = options;
1910
- const n = Math.max(1, times.length);
1911
- const cols = Math.max(1, Math.min(n, Math.floor(_nullishCoalesce(columns, () => ( Math.ceil(Math.sqrt(n)))))));
1912
- const rows = Math.ceil(n / cols);
2314
+ const n2 = Math.max(1, times.length);
2315
+ const cols = Math.max(1, Math.min(n2, Math.floor(_nullishCoalesce(columns, () => ( Math.ceil(Math.sqrt(n2)))))));
2316
+ const rows = Math.ceil(n2 / cols);
1913
2317
  const { width: w, height: h } = scene.artwork;
1914
2318
  const gap = Math.round(Math.max(w, h) * 0.04);
1915
2319
  const label = labels ? Math.round(Math.max(12, h * 0.08)) : 0;
@@ -1921,7 +2325,7 @@ function motionContactSheetSvg(scene, times, options = {}) {
1921
2325
  const x = gap + i % cols * (w + gap);
1922
2326
  const y = gap + Math.floor(i / cols) * (h + label + gap);
1923
2327
  const frame = motionStageSvg(scene, t, { ...frameOptions, idPrefix: `${prefix}f${i}-` });
1924
- const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _97 => _97.exec, 'call', _98 => _98(frame), 'optionalAccess', _99 => _99[1]]), () => ( `0 0 ${w} ${h}`));
2328
+ const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _118 => _118.exec, 'call', _119 => _119(frame), 'optionalAccess', _120 => _120[1]]), () => ( `0 0 ${w} ${h}`));
1925
2329
  const inner = innerSvg(frame).replace(new RegExp(` id="(?!${`${prefix}f${i}-`.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
1926
2330
  const shot = _nullishCoalesce(timeline.shots.find((s) => t >= s.start && t < s.end), () => ( timeline.shots[timeline.shots.length - 1]));
1927
2331
  const caption = labels ? `<text x="${x + w / 2}" y="${y + h + label * 0.8}" font-family="system-ui, sans-serif" font-size="${Math.round(label * 0.7)}" text-anchor="middle" fill="#64748b">${(t / 1e3).toFixed(2)} s${shot ? ` \xB7 ${shot.id.replace(/[<&]/g, "")}` : ""}</text>` : "";
@@ -1933,7 +2337,7 @@ function motionContactSheetSvg(scene, times, options = {}) {
1933
2337
  // src/animation-editor/check.ts
1934
2338
  var MOTION_MIN_CHANGE_MS = 120;
1935
2339
  var NUMERIC_PARAMS = (node, effectId) => {
1936
- const effect = _optionalChain([node, 'access', _100 => _100.effects, 'optionalAccess', _101 => _101.find, 'call', _102 => _102((e) => e.id === effectId)]);
2340
+ const effect = _optionalChain([node, 'access', _121 => _121.effects, 'optionalAccess', _122 => _122.find, 'call', _123 => _123((e) => e.id === effectId)]);
1937
2341
  if (!effect) return null;
1938
2342
  return Object.entries(effect.params).filter(([, v]) => typeof v === "number").map(([k]) => k);
1939
2343
  };
@@ -1976,97 +2380,97 @@ function checkMotionScene(scene) {
1976
2380
  const ids = [...nodes.keys()];
1977
2381
  const timeline = compileMotionScene(scene);
1978
2382
  const artAspect = scene.artwork.width / scene.artwork.height;
1979
- if (!scene.shots.length) issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "no-shots", "The scene has no shots yet; add one with a list of commands."));
2383
+ if (!scene.shots.length) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "no-shots", "The scene has no shots yet; add one with a list of commands."));
1980
2384
  const seen = /* @__PURE__ */ new Set();
1981
2385
  for (const shot of scene.shots) {
1982
- if (seen.has(shot.id)) issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "duplicate-shot-id", `Shot id "${shot.id}" is used twice; give each shot its own id.`, { shot: shot.id }));
2386
+ if (seen.has(shot.id)) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "duplicate-shot-id", `Shot id "${shot.id}" is used twice; give each shot its own id.`, { shot: shot.id }));
1983
2387
  seen.add(shot.id);
1984
2388
  }
1985
2389
  const known = (id, code, what, where) => {
1986
2390
  if (nodes.has(id)) return true;
1987
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, code, `There is no ${what} ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, id, ids)} in the artwork${ids.length ? "" : " (it has no nodes yet)"}.`, { ...where, ids: [id] }));
2391
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, code, `There is no ${what} ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, id, ids)} in the artwork${ids.length ? "" : " (it has no nodes yet)"}.`, { ...where, ids: [id] }));
1988
2392
  return false;
1989
2393
  };
1990
2394
  scene.shots.forEach((shot, shotIndex2) => {
1991
2395
  if (shot.camera && "frame" in shot.camera && typeof shot.camera.frame === "string") known(shot.camera.frame, "unknown-frame", "node to frame", { shot: shot.id });
1992
2396
  else if (shot.camera && motionCameraFramed(shot.camera)) {
1993
2397
  const c = shot.camera;
1994
- if (!(c.width > 0 && c.height > 0)) issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "invalid-camera", `The camera needs a positive width and height (got ${c.width} \xD7 ${c.height}).`, { shot: shot.id }));
2398
+ if (!(c.width > 0 && c.height > 0)) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "invalid-camera", `The camera needs a positive width and height (got ${c.width} \xD7 ${c.height}).`, { shot: shot.id }));
1995
2399
  else if (Math.abs(c.width / c.height / artAspect - 1) > 0.02)
1996
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "camera-aspect", `The camera is ${c.width} \xD7 ${c.height}, a different shape from the ${scene.artwork.width} \xD7 ${scene.artwork.height} artwork, so it shows bands at the sides; frame a node instead to match.`, { shot: shot.id }));
2400
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "camera-aspect", `The camera is ${c.width} \xD7 ${c.height}, a different shape from the ${scene.artwork.width} \xD7 ${scene.artwork.height} artwork, so it shows bands at the sides; frame a node instead to match.`, { shot: shot.id }));
1997
2401
  }
1998
- if (shotIndex2 === 0 && _optionalChain([shot, 'access', _103 => _103.camera, 'optionalAccess', _104 => _104.in]) !== void 0) issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "camera-move-first-shot", 'The first shot cuts to its framing (there is none before it to move from), so its camera "in" does not play; put the move on the next shot, or use a "during" move here.', { shot: shot.id }));
1999
- if (!shot.run.length && !shot.camera && shotIndex2 > 0) issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "empty-shot", "This shot only holds; add commands or a camera move, or remove it.", { shot: shot.id }));
2402
+ if (shotIndex2 === 0 && _optionalChain([shot, 'access', _124 => _124.camera, 'optionalAccess', _125 => _125.in]) !== void 0) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "camera-move-first-shot", 'The first shot cuts to its framing (there is none before it to move from), so its camera "in" does not play; put the move on the next shot, or use a "during" move here.', { shot: shot.id }));
2403
+ if (!shot.run.length && !shot.camera && shotIndex2 > 0) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "empty-shot", "This shot only holds; add commands or a camera move, or remove it.", { shot: shot.id }));
2000
2404
  shot.run.forEach((command3, i) => {
2001
2405
  const where = { shot: shot.id, command: i };
2002
2406
  if (command3.type === "wait") return;
2003
2407
  const targets = command3.targets.filter((id) => known(id, "unknown-target", "node", where));
2004
2408
  const timing = timeline.commands.find((c) => c.shot === shot.id && c.command === i);
2005
2409
  if (timing && timing.duration > 0 && timing.duration < MOTION_MIN_CHANGE_MS)
2006
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "too-fast", `This ${command3.type} lasts ${timing.duration} ms, too quick to follow; make it at least ${MOTION_MIN_CHANGE_MS} ms.`, { ...where, measured: timing.duration, needed: MOTION_MIN_CHANGE_MS, unit: "ms", ids: targets }));
2410
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "too-fast", `This ${command3.type} lasts ${timing.duration} ms, too quick to follow; make it at least ${MOTION_MIN_CHANGE_MS} ms.`, { ...where, measured: timing.duration, needed: MOTION_MIN_CHANGE_MS, unit: "ms", ids: targets }));
2007
2411
  if (command3.duration !== void 0 && command3.ease !== void 0) {
2008
2412
  const ease = _chunkDWJJI6RTcjs.resolveEasing.call(void 0, command3.ease);
2009
2413
  if (ease !== "hold" && ease.type === "spring") {
2010
2414
  const settle = _chunkDWJJI6RTcjs.springSettleMs.call(void 0, ease);
2011
2415
  if (command3.duration < settle * 0.9)
2012
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "spring-cut-short", `This ${command3.type}'s spring takes ${settle} ms to settle but lasts ${command3.duration} ms, so it jumps to its end; leave "duration" out (the spring sets it) or make it at least ${settle} ms.`, { ...where, measured: command3.duration, needed: settle, unit: "ms", ids: targets }));
2416
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "spring-cut-short", `This ${command3.type}'s spring takes ${settle} ms to settle but lasts ${command3.duration} ms, so it jumps to its end; leave "duration" out (the spring sets it) or make it at least ${settle} ms.`, { ...where, measured: command3.duration, needed: settle, unit: "ms", ids: targets }));
2013
2417
  }
2014
2418
  }
2015
2419
  if (command3.type === "follow" && known(command3.path, "unknown-path", "path", where) && nodes.get(command3.path).node.type !== "path")
2016
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "not-a-path", `"${command3.path}" is a group; follow a single path node.`, { ...where, ids: [command3.path] }));
2420
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "not-a-path", `"${command3.path}" is a group; follow a single path node.`, { ...where, ids: [command3.path] }));
2017
2421
  if (command3.type === "morph") known(command3.into, "unknown-shape", "shape", where);
2018
2422
  if (command3.type !== "play" && command3.split) {
2019
2423
  for (const id of targets)
2020
2424
  if (!motionTextLayout(nodes, id))
2021
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "split-not-text", `"${id}" is not a group of letter paths, so "split": "${command3.split}" animates it as a whole; convert the text to outlines and group the letters (get_text_layout shows how they read).`, { ...where, ids: [id] }));
2425
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "split-not-text", `"${id}" is not a group of letter paths, so "split": "${command3.split}" animates it as a whole; convert the text to outlines and group the letters (get_text_layout shows how they read).`, { ...where, ids: [id] }));
2022
2426
  }
2023
- if (command3.type === "play" && !_optionalChain([scene, 'access', _105 => _105.clips, 'optionalAccess', _106 => _106[command3.clip]]))
2024
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "unknown-clip", `There is no clip ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, command3.clip, Object.keys(_nullishCoalesce(scene.clips, () => ( {}))))}; define it with set_animation_clip.`, where));
2427
+ if (command3.type === "play" && !_optionalChain([scene, 'access', _126 => _126.clips, 'optionalAccess', _127 => _127[command3.clip]]))
2428
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "unknown-clip", `There is no clip ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, command3.clip, Object.keys(_nullishCoalesce(scene.clips, () => ( {}))))}; define it with set_animation_clip.`, where));
2025
2429
  if (command3.type === "match" && known(command3.into, "unknown-shape", "group", where)) {
2026
2430
  const into = nodes.get(command3.into);
2027
2431
  for (const id of targets) {
2028
2432
  const entry = nodes.get(id);
2029
2433
  if (id === command3.into || into.ancestors.includes(id) || entry.ancestors.includes(command3.into)) {
2030
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "match-self", `"${id}" cannot match "${command3.into}": one is the other or holds it. Match two separate groups (the second usually hidden).`, { ...where, ids: [id, command3.into] }));
2434
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "match-self", `"${id}" cannot match "${command3.into}": one is the other or holds it. Match two separate groups (the second usually hidden).`, { ...where, ids: [id, command3.into] }));
2031
2435
  continue;
2032
2436
  }
2033
2437
  const m = matchMotionNodes(entry.node, into.node);
2034
2438
  if (m.unpairedInto.length)
2035
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "match-unpaired", `${m.unpairedInto.map((x) => `"${x}"`).join(", ")} in "${command3.into}" ${m.unpairedInto.length > 1 ? "have" : "has"} no partner in "${id}", so ${m.unpairedInto.length > 1 ? "they do" : "it does"} not appear; add a part with the same name or id to "${id}" (it can start hidden or tiny).`, { ...where, ids: m.unpairedInto }));
2439
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "match-unpaired", `${m.unpairedInto.map((x) => `"${x}"`).join(", ")} in "${command3.into}" ${m.unpairedInto.length > 1 ? "have" : "has"} no partner in "${id}", so ${m.unpairedInto.length > 1 ? "they do" : "it does"} not appear; add a part with the same name or id to "${id}" (it can start hidden or tiny).`, { ...where, ids: m.unpairedInto }));
2036
2440
  }
2037
2441
  }
2038
2442
  const style = command3.type === "enter" || command3.type === "exit" ? command3.style : void 0;
2039
2443
  if (style === "wipe" || style === "iris" || style === "split") {
2040
2444
  for (const id of targets)
2041
2445
  if (!motionRevealPadding(nodes.get(id)))
2042
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "reveal-no-box", `"${id}" has no width or height (a straight line), so a ${style} cannot clip it and it fades instead; use style "${command3.type === "enter" ? "draw" : "undraw"}" for a line.`, { ...where, ids: [id] }));
2446
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "reveal-no-box", `"${id}" has no width or height (a straight line), so a ${style} cannot clip it and it fades instead; use style "${command3.type === "enter" ? "draw" : "undraw"}" for a line.`, { ...where, ids: [id] }));
2043
2447
  }
2044
2448
  if (command3.type === "style") {
2045
2449
  for (const key of ["fill", "stroke"]) {
2046
2450
  const color = command3[key];
2047
2451
  if (color === void 0) continue;
2048
2452
  if (_chunkA6QWKBJPcjs.normalizeTokenColor.call(void 0, color) === null) {
2049
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "invalid-color", `${key} "${color}" is not a colour this editor reads; use hex ("#0ea5e9"), rgb() or rgba().`, where));
2453
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "invalid-color", `${key} "${color}" is not a colour this editor reads; use hex ("#0ea5e9"), rgb() or rgba().`, where));
2050
2454
  continue;
2051
2455
  }
2052
2456
  for (const id of targets) {
2053
2457
  const node = nodes.get(id).node;
2054
2458
  const paths = node.type === "path" ? [node] : _chunkBQE3Y6ZOcjs.collectPathNodes.call(void 0, node);
2055
2459
  if (paths.some((p) => p[key] !== void 0 && p[key] !== null && typeof p[key] !== "string"))
2056
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "paint-not-color", `"${id}" has a gradient ${key}; it switches to ${color} at the end instead of blending.`, { ...where, ids: [id] }));
2460
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "paint-not-color", `"${id}" has a gradient ${key}; it switches to ${color} at the end instead of blending.`, { ...where, ids: [id] }));
2057
2461
  }
2058
2462
  }
2059
2463
  }
2060
2464
  if (command3.type === "color-pulse") {
2061
2465
  const key = _nullishCoalesce(command3.paint, () => ( "fill"));
2062
2466
  const color = _nullishCoalesce(command3.color, () => ( "#f59e0b"));
2063
- if (_chunkA6QWKBJPcjs.normalizeTokenColor.call(void 0, color) === null) issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "invalid-color", `color "${color}" is not a colour this editor reads; use hex ("#0ea5e9"), rgb() or rgba().`, where));
2467
+ if (_chunkA6QWKBJPcjs.normalizeTokenColor.call(void 0, color) === null) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "invalid-color", `color "${color}" is not a colour this editor reads; use hex ("#0ea5e9"), rgb() or rgba().`, where));
2064
2468
  else
2065
2469
  for (const id of targets) {
2066
2470
  const node = nodes.get(id).node;
2067
2471
  const paths = node.type === "path" ? [node] : _chunkBQE3Y6ZOcjs.collectPathNodes.call(void 0, node);
2068
2472
  if (paths.some((p) => typeof p[key] !== "string"))
2069
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "paint-not-color", `"${id}" has a gradient or no ${key}, so a colour pulse cannot tint it; give it a solid ${key} or pulse the ${key === "fill" ? "stroke" : "fill"}.`, { ...where, ids: [id] }));
2473
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "paint-not-color", `"${id}" has a gradient or no ${key}, so a colour pulse cannot tint it; give it a solid ${key} or pulse the ${key === "fill" ? "stroke" : "fill"}.`, { ...where, ids: [id] }));
2070
2474
  }
2071
2475
  }
2072
2476
  if (command3.type === "effect") {
@@ -2075,9 +2479,9 @@ function checkMotionScene(scene) {
2075
2479
  const params = NUMERIC_PARAMS(node, command3.effect);
2076
2480
  const effects = (_nullishCoalesce(node.effects, () => ( []))).map((e) => e.id);
2077
2481
  if (!params)
2078
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "unknown-effect", `"${id}" has no effect ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, command3.effect, effects)}${effects.length ? `; its effects are ${effects.map((e) => `"${e}"`).join(", ")}` : "; it has no live effects"}.`, { ...where, ids: [id] }));
2482
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "unknown-effect", `"${id}" has no effect ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, command3.effect, effects)}${effects.length ? `; its effects are ${effects.map((e) => `"${e}"`).join(", ")}` : "; it has no live effects"}.`, { ...where, ids: [id] }));
2079
2483
  else if (!params.includes(command3.param))
2080
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "unknown-effect-param", `Effect "${command3.effect}" has no numeric parameter ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, command3.param, params)}; use one of ${params.join(", ")}.`, { ...where, ids: [id] }));
2484
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "unknown-effect-param", `Effect "${command3.effect}" has no numeric parameter ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, command3.param, params)}; use one of ${params.join(", ")}.`, { ...where, ids: [id] }));
2081
2485
  }
2082
2486
  }
2083
2487
  if (timing) {
@@ -2086,10 +2490,10 @@ function checkMotionScene(scene) {
2086
2490
  const before = Math.max(0, timing.start - 0.5);
2087
2491
  const visible = timing.start > 0 ? visibleAt(timeline, id, before, node) : node.visible !== false;
2088
2492
  if (command3.type === "enter" && visible && timing.start > 0)
2089
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "enter-visible", `"${id}" is already showing when it enters, so it blinks out first; remove the enter or exit it earlier.`, { ...where, ids: [id] }));
2493
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "enter-visible", `"${id}" is already showing when it enters, so it blinks out first; remove the enter or exit it earlier.`, { ...where, ids: [id] }));
2090
2494
  const probe = command3.type === "exit" ? timing.start + 0.5 : (timing.start + timing.end) / 2;
2091
2495
  if (command3.type !== "enter" && command3.type !== "draw" && !visibleAt(timeline, id, probe, node))
2092
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "exit-hidden", `"${id}" is hidden when this ${command3.type} starts, so it is not seen; enter it first.`, { ...where, ids: [id] }));
2496
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "exit-hidden", `"${id}" is hidden when this ${command3.type} starts, so it is not seen; enter it first.`, { ...where, ids: [id] }));
2093
2497
  }
2094
2498
  }
2095
2499
  });
@@ -2097,10 +2501,10 @@ function checkMotionScene(scene) {
2097
2501
  const drivers = _nullishCoalesce(scene.drivers, () => ( []));
2098
2502
  drivers.forEach((d, i) => {
2099
2503
  for (const id of [d.target, d.source.node])
2100
- if (!nodes.has(id)) issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "unknown-target", `Driver ${i + 1} names ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, id, [...nodes.keys()])}, which the artwork does not have.`, { ids: [id] }));
2504
+ if (!nodes.has(id)) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "unknown-target", `Driver ${i + 1} names ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, id, [...nodes.keys()])}, which the artwork does not have.`, { ids: [id] }));
2101
2505
  });
2102
2506
  {
2103
- const key = (n, p) => `${n}.${p}`;
2507
+ const key = (n2, p) => `${n2}.${p}`;
2104
2508
  const edges = /* @__PURE__ */ new Map();
2105
2509
  for (const d of drivers) edges.set(key(d.target, d.property), [..._nullishCoalesce(edges.get(key(d.target, d.property)), () => ( [])), key(d.source.node, d.source.property)]);
2106
2510
  const state = /* @__PURE__ */ new Map();
@@ -2109,25 +2513,25 @@ function checkMotionScene(scene) {
2109
2513
  if (state.get(k) === 2) return false;
2110
2514
  if (state.get(k) === 1) return loop.push(...trail.slice(trail.indexOf(k)), k), true;
2111
2515
  state.set(k, 1);
2112
- for (const n of _nullishCoalesce(edges.get(k), () => ( []))) if (visit(n, [...trail, k])) return true;
2516
+ for (const n2 of _nullishCoalesce(edges.get(k), () => ( []))) if (visit(n2, [...trail, k])) return true;
2113
2517
  state.set(k, 2);
2114
2518
  return false;
2115
2519
  };
2116
2520
  for (const k of edges.keys()) if (visit(k, [])) break;
2117
- if (loop.length) issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "driver-cycle", `Drivers follow each other round in a loop (${loop.join(" \u2190 ")}); remove one of them.`, { ids: [...new Set(loop.map((k) => k.split(".")[0]))] }));
2521
+ if (loop.length) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "driver-cycle", `Drivers follow each other round in a loop (${loop.join(" \u2190 ")}); remove one of them.`, { ids: [...new Set(loop.map((k) => k.split(".")[0]))] }));
2118
2522
  }
2119
2523
  for (const [name, clip] of Object.entries(_nullishCoalesce(scene.clips, () => ( {}))))
2120
2524
  for (const command3 of clip.run)
2121
2525
  if (command3.type !== "wait") {
2122
2526
  for (const id of command3.targets)
2123
- if (id !== MOTION_CLIP_TARGET && !nodes.has(id)) issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "unknown-target", `Clip "${name}" names ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, id, [...nodes.keys()])}, which the artwork does not have; use "$" for the node the clip is played on.`, { ids: [id] }));
2527
+ if (id !== MOTION_CLIP_TARGET && !nodes.has(id)) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "unknown-target", `Clip "${name}" names ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, id, [...nodes.keys()])}, which the artwork does not have; use "$" for the node the clip is played on.`, { ids: [id] }));
2124
2528
  }
2125
2529
  if (scene.trigger) {
2126
- if ((_nullishCoalesce(scene.loop, () => ( "none"))) !== "none") issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "trigger-loop", `The scene plays on ${scene.trigger}, so its "${scene.loop}" loop does not apply; set loop to "none" or remove the trigger.`, {}));
2530
+ if ((_nullishCoalesce(scene.loop, () => ( "none"))) !== "none") issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "trigger-loop", `The scene plays on ${scene.trigger}, so its "${scene.loop}" loop does not apply; set loop to "none" or remove the trigger.`, {}));
2127
2531
  if (scene.trigger === "click") {
2128
- if (scene.shots.length === 1) issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "click-one-shot", "The scene plays on click but has one shot, so every click replays it from the start; split it into shots (each click plays the next one).", { shot: scene.shots[0].id }));
2532
+ if (scene.shots.length === 1) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "click-one-shot", "The scene plays on click but has one shot, so every click replays it from the start; split it into shots (each click plays the next one).", { shot: scene.shots[0].id }));
2129
2533
  scene.shots.forEach((shot, i) => {
2130
- if (i > 0 && shot.at) issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "click-cue", `Shot "${shot.id}" is cued, but on click a shot starts when it is clicked to; remove its "at" or the trigger.`, { shot: shot.id }));
2534
+ if (i > 0 && shot.at) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "click-cue", `Shot "${shot.id}" is cued, but on click a shot starts when it is clicked to; remove its "at" or the trigger.`, { shot: shot.id }));
2131
2535
  });
2132
2536
  }
2133
2537
  }
@@ -2135,11 +2539,11 @@ function checkMotionScene(scene) {
2135
2539
  if (!shot.at) return;
2136
2540
  const timing = timeline.shots[i];
2137
2541
  if (timing.cue === void 0) {
2138
- const what = shot.at.beat !== void 0 ? `beat ${shot.at.beat}, but the scene has no beat grid; set one with set_animation_settings ({ "beat": { "bpm": \u2026 } })` : `word ${shot.at.word}, but the narration has ${_nullishCoalesce(_optionalChain([scene, 'access', _107 => _107.narration, 'optionalAccess', _108 => _108.words, 'access', _109 => _109.length]), () => ( 0))} words (indexes from 0)`;
2139
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "unknown-cue", `Shot "${shot.id}" is cued to ${what}.`, { shot: shot.id }));
2542
+ const what = shot.at.beat !== void 0 ? `beat ${shot.at.beat}, but the scene has no beat grid; set one with set_animation_settings ({ "beat": { "bpm": \u2026 } })` : `word ${shot.at.word}, but the narration has ${_nullishCoalesce(_optionalChain([scene, 'access', _128 => _128.narration, 'optionalAccess', _129 => _129.words, 'access', _130 => _130.length]), () => ( 0))} words (indexes from 0)`;
2543
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "unknown-cue", `Shot "${shot.id}" is cued to ${what}.`, { shot: shot.id }));
2140
2544
  } else if (timing.missedBy !== void 0) {
2141
2545
  const late = Math.round(timing.missedBy);
2142
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "cue-missed", `Shot "${shot.id}" is cued at ${Math.round(timing.cue)} ms but starts ${late} ms late, because the shot before runs until ${Math.round(timing.start)} ms; shorten that shot (its hold, durations or speed) or cue this one later.`, { shot: shot.id, measured: late, needed: 0, unit: "ms" }));
2546
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "cue-missed", `Shot "${shot.id}" is cued at ${Math.round(timing.cue)} ms but starts ${late} ms late, because the shot before runs until ${Math.round(timing.start)} ms; shorten that shot (its hold, durations or speed) or cue this one later.`, { shot: shot.id, measured: late, needed: 0, unit: "ms" }));
2143
2547
  }
2144
2548
  });
2145
2549
  for (const [key, track] of timeline.tracks) {
@@ -2151,7 +2555,7 @@ function checkMotionScene(scene) {
2151
2555
  const property = key.split("\0")[1];
2152
2556
  if (!a.node) continue;
2153
2557
  issues.push(
2154
- _chunkERJA5CGTcjs.motionIssue.call(void 0, "overlap", `"${a.node}" ${property} is changed by two commands at once (shot "${a.shot}" command ${a.command + 1} and shot "${b.shot}" command ${b.command + 1}); the later one takes over. Start it after the first ends, or use with: "after".`, {
2558
+ _chunkDGWOGQJ5cjs.motionIssue.call(void 0, "overlap", `"${a.node}" ${property} is changed by two commands at once (shot "${a.shot}" command ${a.command + 1} and shot "${b.shot}" command ${b.command + 1}); the later one takes over. Start it after the first ends, or use with: "after".`, {
2155
2559
  shot: b.shot,
2156
2560
  command: b.command,
2157
2561
  ids: [a.node]
@@ -2168,26 +2572,26 @@ function checkMotionScene(scene) {
2168
2572
  const cam = frame.camera;
2169
2573
  const docIndex = indexMotionNodes(frame.document.nodes);
2170
2574
  for (const id of command3.targets) {
2171
- const b = _optionalChain([docIndex, 'access', _110 => _110.get, 'call', _111 => _111(id), 'optionalAccess', _112 => _112.bounds]);
2575
+ const b = _optionalChain([docIndex, 'access', _131 => _131.get, 'call', _132 => _132(id), 'optionalAccess', _133 => _133.bounds]);
2172
2576
  if (!b || reported.has(id) || !visibleAt(timeline, id, timing.end, nodes.get(id).node)) continue;
2173
2577
  const outside = b.x + b.width < cam.x || b.y + b.height < cam.y || b.x > cam.x + cam.width || b.y > cam.y + cam.height;
2174
2578
  if (outside) {
2175
2579
  reported.add(id);
2176
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "out-of-frame", `"${id}" is outside the camera when this ${command3.type} ends, so it is not seen; move it into frame or move the camera.`, { shot: timing.shot, command: timing.command, ids: [id] }));
2580
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "out-of-frame", `"${id}" is outside the camera when this ${command3.type} ends, so it is not seen; move it into frame or move the camera.`, { shot: timing.shot, command: timing.command, ids: [id] }));
2177
2581
  }
2178
2582
  }
2179
2583
  }
2180
2584
  if (scene.controls) {
2181
2585
  for (const control of scene.controls) {
2182
2586
  if (!usesColor(scene, control.default))
2183
- issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "unknown-control-color", `Control "${control.name}" stands for ${control.default}, which the scene never uses; set its default to a colour in the artwork.`));
2587
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "unknown-control-color", `Control "${control.name}" stands for ${control.default}, which the scene never uses; set its default to a colour in the artwork.`));
2184
2588
  }
2185
2589
  }
2186
2590
  if (scene.target && scene.shots.length) {
2187
2591
  const seconds = Math.round(timeline.duration / 100) / 10;
2188
2592
  const [min, max] = scene.target;
2189
- if (seconds > max) issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "scene-too-long", `The scene runs ${seconds} s, past its ${max} s target; shorten holds or durations, or fit it (fit_animation).`, { measured: seconds, needed: max, unit: "s" }));
2190
- if (seconds < min) issues.push(_chunkERJA5CGTcjs.motionIssue.call(void 0, "scene-too-short", `The scene runs ${seconds} s, under its ${min} s target; hold longer, add shots, or fit it (fit_animation).`, { measured: seconds, needed: min, unit: "s" }));
2593
+ if (seconds > max) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "scene-too-long", `The scene runs ${seconds} s, past its ${max} s target; shorten holds or durations, or fit it (fit_animation).`, { measured: seconds, needed: max, unit: "s" }));
2594
+ if (seconds < min) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "scene-too-short", `The scene runs ${seconds} s, under its ${min} s target; hold longer, add shots, or fit it (fit_animation).`, { measured: seconds, needed: min, unit: "s" }));
2191
2595
  }
2192
2596
  return issues;
2193
2597
  }
@@ -2216,7 +2620,7 @@ var fail = (message) => {
2216
2620
  };
2217
2621
  function shotIndex(scene, id) {
2218
2622
  const i = scene.shots.findIndex((s) => s.id === id);
2219
- if (i < 0) fail(`There is no shot ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, id, scene.shots.map((s) => s.id))}; the shots are ${scene.shots.map((s) => `"${s.id}"`).join(", ") || "none yet"}.`);
2623
+ if (i < 0) fail(`There is no shot ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, id, scene.shots.map((s) => s.id))}; the shots are ${scene.shots.map((s) => `"${s.id}"`).join(", ") || "none yet"}.`);
2220
2624
  return i;
2221
2625
  }
2222
2626
  function position(at, length, what) {
@@ -2228,9 +2632,9 @@ var replaceShot = (scene, index, shot) => ({ ...scene, shots: scene.shots.map((s
2228
2632
  function freshShotId(scene, base = "shot") {
2229
2633
  const used = new Set(scene.shots.map((s) => s.id));
2230
2634
  if (!used.has(base)) return base;
2231
- let n = 2;
2232
- while (used.has(`${base}-${n}`)) n++;
2233
- return `${base}-${n}`;
2635
+ let n2 = 2;
2636
+ while (used.has(`${base}-${n2}`)) n2++;
2637
+ return `${base}-${n2}`;
2234
2638
  }
2235
2639
  function applyOne(scene, edit) {
2236
2640
  switch (edit.op) {
@@ -2274,7 +2678,7 @@ function applyOne(scene, edit) {
2274
2678
  if (typeof edit.name !== "string" || !edit.name) fail('Give the clip a "name".');
2275
2679
  const clips = { ..._nullishCoalesce(scene.clips, () => ( {})) };
2276
2680
  if (edit.clip === null) {
2277
- if (!clips[edit.name]) fail(`There is no clip ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, edit.name, Object.keys(clips))} to delete.`);
2681
+ if (!clips[edit.name]) fail(`There is no clip ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, edit.name, Object.keys(clips))} to delete.`);
2278
2682
  const playing = scene.shots.filter((s) => s.run.some((c) => c.type === "play" && c.clip === edit.name)).map((s) => `"${s.id}"`);
2279
2683
  if (playing.length) fail(`Clip "${edit.name}" is played in shot${playing.length > 1 ? "s" : ""} ${playing.join(", ")}; remove those play commands first.`);
2280
2684
  delete clips[edit.name];
@@ -2380,7 +2784,7 @@ function applyOne(scene, edit) {
2380
2784
  const names = new Set(edit.controls.map((c) => c.name));
2381
2785
  for (const preset2 of _nullishCoalesce(next.presets, () => ( [])))
2382
2786
  for (const key of Object.keys(preset2.values))
2383
- if (!names.has(key)) fail(`Preset "${preset2.name}" sets ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, key, names)}, which is not a control.`);
2787
+ if (!names.has(key)) fail(`Preset "${preset2.name}" sets ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, key, names)}, which is not a control.`);
2384
2788
  return next;
2385
2789
  }
2386
2790
  case "replace_scene":
@@ -2388,11 +2792,11 @@ function applyOne(scene, edit) {
2388
2792
  }
2389
2793
  }
2390
2794
  function errorKeys(issues) {
2391
- return new Set(issues.filter((i) => i.severity === "error").map(_chunkERJA5CGTcjs.motionIssueKey));
2795
+ return new Set(issues.filter((i) => i.severity === "error").map(_chunkDGWOGQJ5cjs.motionIssueKey));
2392
2796
  }
2393
2797
  function editHints(before, after) {
2394
- const old = new Set(before.map(_chunkERJA5CGTcjs.motionIssueKey));
2395
- return after.filter((i) => i.code === "overlap" && !old.has(_chunkERJA5CGTcjs.motionIssueKey.call(void 0, i))).map(_chunkERJA5CGTcjs.motionIssueLine);
2798
+ const old = new Set(before.map(_chunkDGWOGQJ5cjs.motionIssueKey));
2799
+ return after.filter((i) => i.code === "overlap" && !old.has(_chunkDGWOGQJ5cjs.motionIssueKey.call(void 0, i))).map(_chunkDGWOGQJ5cjs.motionIssueLine);
2396
2800
  }
2397
2801
  function applyMotionEdits(scene, edits) {
2398
2802
  const list = Array.isArray(edits) ? edits : [edits];
@@ -2400,9 +2804,9 @@ function applyMotionEdits(scene, edits) {
2400
2804
  let next = scene;
2401
2805
  for (let i = 0; i < list.length; i++) {
2402
2806
  const edit = list[i];
2403
- const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _113 => _113.op])}): ` : "";
2807
+ const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _134 => _134.op])}): ` : "";
2404
2808
  if (!edit || !MOTION_EDIT_OPS.includes(edit.op))
2405
- return { ok: false, error: `${prefix}Unknown op ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, String(_optionalChain([edit, 'optionalAccess', _114 => _114.op])), MOTION_EDIT_OPS)}. Nothing was changed.` };
2809
+ return { ok: false, error: `${prefix}Unknown op ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, String(_optionalChain([edit, 'optionalAccess', _135 => _135.op])), MOTION_EDIT_OPS)}. Nothing was changed.` };
2406
2810
  try {
2407
2811
  next = applyOne(next, edit);
2408
2812
  } catch (error) {
@@ -2414,8 +2818,8 @@ function applyMotionEdits(scene, edits) {
2414
2818
  }
2415
2819
  const issues = checkMotionScene(next);
2416
2820
  const had = errorKeys(before);
2417
- const added = issues.filter((i) => i.severity === "error" && !had.has(_chunkERJA5CGTcjs.motionIssueKey.call(void 0, i)));
2418
- if (added.length) return { ok: false, error: `Nothing was changed: ${added.map(_chunkERJA5CGTcjs.motionIssueLine).join(" ")}` };
2821
+ const added = issues.filter((i) => i.severity === "error" && !had.has(_chunkDGWOGQJ5cjs.motionIssueKey.call(void 0, i)));
2822
+ if (added.length) return { ok: false, error: `Nothing was changed: ${added.map(_chunkDGWOGQJ5cjs.motionIssueLine).join(" ")}` };
2419
2823
  return { ok: true, scene: next, issues, hints: editHints(before, issues) };
2420
2824
  }
2421
2825
  var REF = /^\$(\d+|last)((?:\.[A-Za-z_]\w*|\[\d+\])+)$/;
@@ -2424,21 +2828,21 @@ function resolveMotionEditRefs(scene, edits) {
2424
2828
  const resolved = [];
2425
2829
  let next = scene;
2426
2830
  for (let i = 0; i < edits.length; i++) {
2427
- const where = `op ${i + 1} (${_optionalChain([edits, 'access', _115 => _115[i], 'optionalAccess', _116 => _116.op])})`;
2831
+ const where = `op ${i + 1} (${_optionalChain([edits, 'access', _136 => _136[i], 'optionalAccess', _137 => _137.op])})`;
2428
2832
  let problem = null;
2429
2833
  const walk = (v) => {
2430
2834
  if (typeof v === "string") {
2431
2835
  if (v.startsWith("$$")) return v.slice(1);
2432
2836
  const m = REF.exec(v);
2433
2837
  if (!m) return v;
2434
- const n = m[1] === "last" ? i - 1 : Number(m[1]);
2435
- if (!(n >= 0 && n < i)) {
2436
- problem = `${where}: "${v}" refers to op ${n + 1}, but only ops before this one (1 to ${i}) can be referred to.`;
2838
+ const n2 = m[1] === "last" ? i - 1 : Number(m[1]);
2839
+ if (!(n2 >= 0 && n2 < i)) {
2840
+ problem = `${where}: "${v}" refers to op ${n2 + 1}, but only ops before this one (1 to ${i}) can be referred to.`;
2437
2841
  return v;
2438
2842
  }
2439
- let value = outcomes[n];
2843
+ let value = outcomes[n2];
2440
2844
  for (const part of _nullishCoalesce(m[2].match(/\.[A-Za-z_]\w*|\[\d+\]/g), () => ( []))) value = value == null ? void 0 : value[part.startsWith(".") ? part.slice(1) : part.slice(1, -1)];
2441
- if (value === void 0) problem = `${where}: "${v}" has no value; op ${n + 1} made ${JSON.stringify(outcomes[n])}.`;
2845
+ if (value === void 0) problem = `${where}: "${v}" has no value; op ${n2 + 1} made ${JSON.stringify(outcomes[n2])}.`;
2442
2846
  return value;
2443
2847
  }
2444
2848
  if (Array.isArray(v)) return v.map(walk);
@@ -2457,9 +2861,9 @@ function resolveMotionEditRefs(scene, edits) {
2457
2861
  resolved.push(edit);
2458
2862
  const out = { op: edit.op };
2459
2863
  if (edit.op === "insert_shots") out.shots = edit.shots.map((s) => s.id);
2460
- if ("shot" in edit && typeof edit.shot === "string") out.shot = edit.op === "update_shot" && _optionalChain([edit, 'access', _117 => _117.set, 'optionalAccess', _118 => _118.id]) ? edit.set.id : edit.shot;
2864
+ if ("shot" in edit && typeof edit.shot === "string") out.shot = edit.op === "update_shot" && _optionalChain([edit, 'access', _138 => _138.set, 'optionalAccess', _139 => _139.id]) ? edit.set.id : edit.shot;
2461
2865
  if (edit.op === "add_commands") {
2462
- const at = _nullishCoalesce(_nullishCoalesce(edit.at, () => ( _optionalChain([before, 'access', _119 => _119.shots, 'access', _120 => _120.find, 'call', _121 => _121((s) => s.id === edit.shot), 'optionalAccess', _122 => _122.run, 'access', _123 => _123.length]))), () => ( 0));
2866
+ const at = _nullishCoalesce(_nullishCoalesce(edit.at, () => ( _optionalChain([before, 'access', _140 => _140.shots, 'access', _141 => _141.find, 'call', _142 => _142((s) => s.id === edit.shot), 'optionalAccess', _143 => _143.run, 'access', _144 => _144.length]))), () => ( 0));
2463
2867
  out.indexes = edit.commands.map((_, k) => at + k);
2464
2868
  }
2465
2869
  if (edit.op === "move_command") out.indexes = [edit.to];
@@ -2478,7 +2882,7 @@ function fitMotionScene(scene, ms2, options = {}) {
2478
2882
  if (!(Number.isFinite(ms2) && ms2 > 0)) throw new Error("Give the length to fit as a number of milliseconds above 0.");
2479
2883
  const ids = scene.shots.map((s) => s.id);
2480
2884
  const keep = new Set(_nullishCoalesce(options.keep, () => ( [])));
2481
- for (const id of keep) if (!ids.includes(id)) throw new Error(`There is no shot ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, id, ids)} to keep.`);
2885
+ for (const id of keep) if (!ids.includes(id)) throw new Error(`There is no shot ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, id, ids)} to keep.`);
2482
2886
  const timeline = compileMotionScene(scene);
2483
2887
  let kept = 0;
2484
2888
  let rest = 0;
@@ -2506,7 +2910,7 @@ var COLOR_GAP = 80;
2506
2910
  var MAX_VIA = 64;
2507
2911
  function motionNodePose(scene, id, time) {
2508
2912
  const entry = indexMotionNodes(scene.artwork.nodes).get(id);
2509
- if (!entry) throw new Error(`There is no element ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, id, [...indexMotionNodes(scene.artwork.nodes).keys()])} in the artwork.`);
2913
+ if (!entry) throw new Error(`There is no element ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, id, [...indexMotionNodes(scene.artwork.nodes).keys()])} in the artwork.`);
2510
2914
  const timeline = compileMotionScene(scene);
2511
2915
  const drawn = indexMotionNodes(motionFrameAt(scene, time).document.nodes).get(id);
2512
2916
  const centre = _chunkBQE3Y6ZOcjs.applyMatrix.call(void 0, entry.parentWorld, { x: entry.center.x + motionPropertyValue(timeline, id, "x", time), y: entry.center.y + motionPropertyValue(timeline, id, "y", time) });
@@ -2524,15 +2928,15 @@ function motionNodePose(scene, id, time) {
2524
2928
  ...typeof fill === "string" && fill !== "transparent" ? { fill } : {},
2525
2929
  ...typeof stroke === "string" && stroke !== "transparent" ? { stroke } : {},
2526
2930
  ...typeof width === "number" ? { strokeWidth: width } : {},
2527
- bounds: _nullishCoalesce(_optionalChain([drawn, 'optionalAccess', _124 => _124.bounds]), () => ( null))
2931
+ bounds: _nullishCoalesce(_optionalChain([drawn, 'optionalAccess', _145 => _145.bounds]), () => ( null))
2528
2932
  };
2529
2933
  }
2530
2934
  function simplifyTimedSamples(times, values, tol) {
2531
- const n = times.length;
2532
- if (n <= 2) return [...Array(n).keys()];
2533
- const keep = new Uint8Array(n);
2534
- keep[0] = keep[n - 1] = 1;
2535
- const stack = [[0, n - 1]];
2935
+ const n2 = times.length;
2936
+ if (n2 <= 2) return [...Array(n2).keys()];
2937
+ const keep = new Uint8Array(n2);
2938
+ keep[0] = keep[n2 - 1] = 1;
2939
+ const stack = [[0, n2 - 1]];
2536
2940
  while (stack.length) {
2537
2941
  const [a, b] = stack.pop();
2538
2942
  let worst = -1;
@@ -2559,8 +2963,8 @@ function simplifyTimedSamples(times, values, tol) {
2559
2963
  keep.forEach((k, i) => k && out.push(i));
2560
2964
  return out;
2561
2965
  }
2562
- var round2 = (n, places = 2) => Math.round(n * 10 ** places) / 10 ** places || 0;
2563
- var ms = (n) => Math.max(0, Math.round(n));
2966
+ var round2 = (n2, places = 2) => Math.round(n2 * 10 ** places) / 10 ** places || 0;
2967
+ var ms = (n2) => Math.max(0, Math.round(n2));
2564
2968
  function motionTakeProblems(scene, take) {
2565
2969
  const problems = [];
2566
2970
  const nodes = indexMotionNodes(scene.artwork.nodes);
@@ -2569,7 +2973,7 @@ function motionTakeProblems(scene, take) {
2569
2973
  take.samples.forEach((s, i) => {
2570
2974
  const where = `Sample ${i + 1}`;
2571
2975
  if (!s || typeof s !== "object") return problems.push(`${where} must be an object.`);
2572
- if (typeof s.node !== "string" || !nodes.has(s.node)) problems.push(`${where}: the artwork has no element ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, String(s.node), [...nodes.keys()])}; get_animation lists the element ids.`);
2976
+ if (typeof s.node !== "string" || !nodes.has(s.node)) problems.push(`${where}: the artwork has no element ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, String(s.node), [...nodes.keys()])}; get_animation lists the element ids.`);
2573
2977
  if (!num2(s.t) || s.t < 0) problems.push(`${where}: "t" must be ms from the start of the take, \u2265 0.`);
2574
2978
  if (s.x === void 0 !== (s.y === void 0)) problems.push(`${where}: give "x" and "y" together (the centre, document units).`);
2575
2979
  for (const k of ["x", "y", "scaleX", "scaleY", "rotation", "opacity", "strokeWidth"]) if (s[k] !== void 0 && !num2(s[k])) problems.push(`${where}: "${k}" must be a number.`);
@@ -2648,7 +3052,7 @@ function channelCommands(node, channel, pose, samples, tol, kept) {
2648
3052
  const via = [];
2649
3053
  for (const k of keep.slice(1, -1)) {
2650
3054
  const f = round2((list[k].t - first.t) / span, 6);
2651
- if (f > (_nullishCoalesce(_optionalChain([via, 'access', _125 => _125.at, 'call', _126 => _126(-1), 'optionalAccess', _127 => _127.f]), () => ( 0))) && f < 1) via.push({ s: list[k], f });
3055
+ if (f > (_nullishCoalesce(_optionalChain([via, 'access', _146 => _146.at, 'call', _147 => _147(-1), 'optionalAccess', _148 => _148.f]), () => ( 0))) && f < 1) via.push({ s: list[k], f });
2652
3056
  }
2653
3057
  const extra = via.length ? { via: via.map(({ s }) => ({ x: round2(s.v[0]), y: round2(s.v[1]) })), times: via.map(({ f }) => f) } : {};
2654
3058
  out.push({ start: first.t, command: make(end.v, span, "linear", extra) });
@@ -2701,10 +3105,10 @@ function motionTakeShot(scene, take, options = {}) {
2701
3105
  if (!run.length) Object.assign(shot, { run: [{ type: "wait", duration: ms(Math.max(1, length)) }], hold: 0 });
2702
3106
  return { shot, at, start, samples: { given: take.samples.length, kept: kept.n } };
2703
3107
  }
2704
- function motionClientToDocument(camera, box, clientX, clientY) {
2705
- const k = Math.min(box.width / camera.width, box.height / camera.height) || 1;
2706
- const ox = box.left + (box.width - camera.width * k) / 2;
2707
- const oy = box.top + (box.height - camera.height * k) / 2;
3108
+ function motionClientToDocument(camera, box2, clientX, clientY) {
3109
+ const k = Math.min(box2.width / camera.width, box2.height / camera.height) || 1;
3110
+ const ox = box2.left + (box2.width - camera.width * k) / 2;
3111
+ const oy = box2.top + (box2.height - camera.height * k) / 2;
2708
3112
  return { x: camera.x + (clientX - ox) / k, y: camera.y + (clientY - oy) / k };
2709
3113
  }
2710
3114
 
@@ -2716,8 +3120,8 @@ var FLAIR_EASES = {
2716
3120
  };
2717
3121
  var FLAIR_EXPORT_VERSION = "0.39";
2718
3122
  function flairAtLeast(version, min) {
2719
- const a = version.split(".").map((n) => parseInt(n, 10) || 0);
2720
- const b = min.split(".").map((n) => parseInt(n, 10) || 0);
3123
+ const a = version.split(".").map((n2) => parseInt(n2, 10) || 0);
3124
+ const b = min.split(".").map((n2) => parseInt(n2, 10) || 0);
2721
3125
  for (let i = 0; i < Math.max(a.length, b.length); i++) if ((_nullishCoalesce(a[i], () => ( 0))) !== (_nullishCoalesce(b[i], () => ( 0)))) return (_nullishCoalesce(a[i], () => ( 0))) > (_nullishCoalesce(b[i], () => ( 0)));
2722
3126
  return true;
2723
3127
  }
@@ -2767,8 +3171,8 @@ function toFlairSvgMotion(scene, options = {}) {
2767
3171
  return back && curves ? flairCurveFor(back, curves) : void 0;
2768
3172
  };
2769
3173
  const plays = (s) => (!s.behavior || behaviors) && (!s.steps || strobes) && !s.geometric;
2770
- const round4 = (n) => Math.round(n * 10 ** precision) / 10 ** precision;
2771
- const f = (n) => String(round4(n));
3174
+ const round4 = (n2) => Math.round(n2 * 10 ** precision) / 10 ** precision;
3175
+ const f = (n2) => String(round4(n2));
2772
3176
  const timeline = compileMotionScene(scene);
2773
3177
  const nodes = indexMotionNodes(scene.artwork.nodes);
2774
3178
  const notes = [];
@@ -2793,14 +3197,14 @@ function toFlairSvgMotion(scene, options = {}) {
2793
3197
  const usedIds = new Set(nodes.keys());
2794
3198
  const freshId = (base) => {
2795
3199
  let id = base;
2796
- for (let n = 2; usedIds.has(id); n++) id = `${base}-${n}`;
3200
+ for (let n2 = 2; usedIds.has(id); n2++) id = `${base}-${n2}`;
2797
3201
  usedIds.add(id);
2798
3202
  return id;
2799
3203
  };
2800
3204
  for (const id of animatedNodes) {
2801
3205
  const entry = nodes.get(id);
2802
3206
  const node = entry.node;
2803
- const drawn = [...entry.ancestors.map((a) => nodes.get(a).node), node].every((n) => n.visible !== false);
3207
+ const drawn = [...entry.ancestors.map((a) => nodes.get(a).node), node].every((n2) => n2.visible !== false);
2804
3208
  for (const [p, name] of [["clipRect", "reveal"], ["clipCircle", "iris"]]) {
2805
3209
  if (!drawn || !timeline.tracks.has(motionTrackKey(id, p))) continue;
2806
3210
  const wrapper = freshId(`${id}--${name}`);
@@ -2826,7 +3230,7 @@ function toFlairSvgMotion(scene, options = {}) {
2826
3230
  const wrapper = freshId(`${id}--follow`);
2827
3231
  const centre = freshId(`${id}--follow-centre`);
2828
3232
  const c = entry.center;
2829
- const at = _optionalChain([motionNodeOffset, 'call', _128 => _128(entry, timeline, 0, nodes), 'optionalAccess', _129 => _129.follow]);
3233
+ const at = _optionalChain([motionNodeOffset, 'call', _149 => _149(entry, timeline, 0, nodes), 'optionalAccess', _150 => _150.follow]);
2830
3234
  const placed = at ? `translate(${f(at.x)} ${f(at.y)})${at.angle ? ` rotate(${f(at.angle)})` : ""}` : void 0;
2831
3235
  (wrap[id] ??= []).push({ id: wrapper, ...placed ? { transform: placed } : {} }, { id: centre, transform: `translate(${f(-c.x)} ${f(-c.y)})` });
2832
3236
  follows.push({ id, wrapper, routes: ids, segments: followSegments.filter((seg) => seg.path && ids.has(seg.path)) });
@@ -2887,7 +3291,7 @@ function toFlairSvgMotion(scene, options = {}) {
2887
3291
  exact: () => false,
2888
3292
  value: (t) => {
2889
3293
  const frame = motionFrameAt(scene, t, { draw: false, offsets: false });
2890
- const now = _chunkBQE3Y6ZOcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _130 => _130(frame.document.nodes), 'access', _131 => _131.get, 'call', _132 => _132(id), 'optionalAccess', _133 => _133.node]));
3294
+ const now = _chunkBQE3Y6ZOcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _151 => _151(frame.document.nodes), 'access', _152 => _152.get, 'call', _153 => _153(id), 'optionalAccess', _154 => _154.node]));
2891
3295
  if (!now || now.type !== "path") return "";
2892
3296
  const bezier = _chunkGF2RBSDMcjs.transformBezierPath.call(void 0, _chunkBQE3Y6ZOcjs.vectorPathWorldBezier.call(void 0, now, _chunkBQE3Y6ZOcjs.IDENTITY_MATRIX, { effects: false }), inverse);
2893
3297
  return _chunkGF2RBSDMcjs.bezierPathToSvg.call(void 0, bezier, { precision: Math.min(precision, 2) });
@@ -2901,8 +3305,8 @@ function toFlairSvgMotion(scene, options = {}) {
2901
3305
  const moveSegments = windows.map((w) => ({ node: "", property: "camera", start: w.start, end: w.end, from: [], to: [], ease: "linear", shot: w.shot, command: -1 }));
2902
3306
  const sampledMove = new WeakSet(moveSegments);
2903
3307
  const cameraValue = windows.length ? (t) => {
2904
- const box = motionFrameAt(scene, t, { draw: false, offsets: false, effects: options.effects }).camera;
2905
- return [box.x, box.y, box.width, box.height].map(round4);
3308
+ const box2 = motionFrameAt(scene, t, { draw: false, offsets: false, effects: options.effects }).camera;
3309
+ return [box2.x, box2.y, box2.width, box2.height].map(round4);
2906
3310
  } : (t) => trackValue(camera, t).map(round4);
2907
3311
  if (camera.length || windows.length) channels.push({ id: "", kind: "view", segments: [...camera, ...moveSegments].sort((a, b) => a.start - b.start), exact: (seg) => !sampledMove.has(seg), overshoot: true, value: cameraValue });
2908
3312
  const cameraTag = (t) => {
@@ -2910,7 +3314,7 @@ function toFlairSvgMotion(scene, options = {}) {
2910
3314
  if (w) return `${w.move}@${w.shot}`;
2911
3315
  const seg = camera.find((x) => t >= x.start - 1e-6 && t < Math.max(x.end, x.start + 1e-3));
2912
3316
  const shot = seg ? scene.shots.find((x) => x.id === seg.shot) : void 0;
2913
- const move = _optionalChain([_chunk2K5TELHVcjs.cameraMoveParts.call(void 0, _optionalChain([shot, 'optionalAccess', _134 => _134.camera, 'optionalAccess', _135 => _135.in])), 'optionalAccess', _136 => _136.move]);
3317
+ const move = _optionalChain([_chunkJAOSL334cjs.cameraMoveParts.call(void 0, _optionalChain([shot, 'optionalAccess', _155 => _155.camera, 'optionalAccess', _156 => _156.in])), 'optionalAccess', _157 => _157.move]);
2914
3318
  return move && shot ? `${move}@${shot.id}` : void 0;
2915
3319
  };
2916
3320
  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));
@@ -2927,8 +3331,8 @@ function toFlairSvgMotion(scene, options = {}) {
2927
3331
  ...delay > 0 ? { delay: round4(delay) } : {},
2928
3332
  ms: round4(ms2),
2929
3333
  ease: written,
2930
- ..._optionalChain([segment, 'optionalAccess', _137 => _137.behavior]) ? { behavior: segment.behavior } : {},
2931
- ..._optionalChain([segment, 'optionalAccess', _138 => _138.steps]) ? { strobe: segment.steps } : {}
3334
+ ..._optionalChain([segment, 'optionalAccess', _158 => _158.behavior]) ? { behavior: segment.behavior } : {},
3335
+ ..._optionalChain([segment, 'optionalAccess', _159 => _159.steps]) ? { strobe: segment.steps } : {}
2932
3336
  };
2933
3337
  if (channel.kind === "view") {
2934
3338
  const tag2 = cameraTag(delay);
@@ -2965,11 +3369,11 @@ function toFlairSvgMotion(scene, options = {}) {
2965
3369
  if (!same(from, to) || channel.kind === "view") emit(channel, a, b - a, from, to, ease, first);
2966
3370
  continue;
2967
3371
  }
2968
- const n = Math.max(1, Math.ceil((b - a) * fps / 1e3));
3372
+ const n2 = Math.max(1, Math.ceil((b - a) * fps / 1e3));
2969
3373
  let prev = channel.value(a);
2970
- for (let k = 1; k <= n; k++) {
2971
- const t0 = a + (b - a) * (k - 1) / n;
2972
- const t1 = a + (b - a) * k / n;
3374
+ for (let k = 1; k <= n2; k++) {
3375
+ const t0 = a + (b - a) * (k - 1) / n2;
3376
+ const t1 = a + (b - a) * k / n2;
2973
3377
  const next = channel.value(t1);
2974
3378
  const jump = channel.kind === "view" && active.some((x) => sampledMove.has(x)) ? viewJump(channel.value, t0, t1) : void 0;
2975
3379
  if (jump) {
@@ -3001,10 +3405,10 @@ function toFlairSvgMotion(scene, options = {}) {
3001
3405
  continue;
3002
3406
  }
3003
3407
  const until = Math.min(seg.end, ...starts.filter((t) => t > seg.start));
3004
- const n = Math.max(1, Math.ceil((until - seg.start) * fps / 1e3));
3005
- for (let k = 1; k <= n; k++) {
3006
- const t0 = seg.start + (until - seg.start) * (k - 1) / n;
3007
- const t1 = seg.start + (until - seg.start) * k / n;
3408
+ const n2 = Math.max(1, Math.ceil((until - seg.start) * fps / 1e3));
3409
+ for (let k = 1; k <= n2; k++) {
3410
+ const t0 = seg.start + (until - seg.start) * (k - 1) / n2;
3411
+ const t1 = seg.start + (until - seg.start) * k / n2;
3008
3412
  animate.push({ ...along, ...t0 > 0 ? { delay: round4(t0) } : {}, ms: round4(t1 - t0), ease: "linear", at: [progress(t0), progress(t1)] });
3009
3413
  sampled++;
3010
3414
  }
@@ -3032,7 +3436,7 @@ function toFlairSvgMotion(scene, options = {}) {
3032
3436
  let svg = _chunkBQE3Y6ZOcjs.vectorDocumentToSvg.call(void 0, { ...scene.artwork, nodes: _chunkBQE3Y6ZOcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) }, { pretty: false, precision, wrap });
3033
3437
  if (routes.length) svg = svg.replace(/^(<svg[^>]*>)/, `$1<defs>${routes.join("")}</defs>`);
3034
3438
  let motion = { svg, animate };
3035
- if (_optionalChain([scene, 'access', _139 => _139.controls, 'optionalAccess', _140 => _140.length])) {
3439
+ if (_optionalChain([scene, 'access', _160 => _160.controls, 'optionalAccess', _161 => _161.length])) {
3036
3440
  const declarations = scene.controls.map((c) => `{{${c.name}: color default=${c.default.toUpperCase()}${c.label ? ` label='${c.label.replace(/'/g, "\u2019")}'` : ""}}}`).join(" ");
3037
3441
  const swap = (text) => scene.controls.reduce((s, c) => s.replace(new RegExp(`${c.default}(?![0-9a-fA-F])`, "gi"), `{{${c.name}}}`), text);
3038
3442
  svg = svg.replace(/^(<svg[^>]*>)/, `$1<!-- ${declarations} -->`);
@@ -3054,7 +3458,7 @@ function toFlairVectorEffect(scene, options = {}) {
3054
3458
  tags: _nullishCoalesce(options.tags, () => ( [])),
3055
3459
  motion: out.motion,
3056
3460
  hold: out.hold,
3057
- ..._optionalChain([scene, 'access', _141 => _141.presets, 'optionalAccess', _142 => _142.length]) ? { presets: scene.presets } : {},
3461
+ ..._optionalChain([scene, 'access', _162 => _162.presets, 'optionalAccess', _163 => _163.length]) ? { presets: scene.presets } : {},
3058
3462
  ...out.trigger ? { trigger: out.trigger } : {}
3059
3463
  };
3060
3464
  }
@@ -3073,17 +3477,17 @@ function toFlairEffectDocument(scene, options = {}) {
3073
3477
  };
3074
3478
  }
3075
3479
  function viewJump(value, t0, t1) {
3076
- const box = (t) => value(t);
3480
+ const box2 = (t) => value(t);
3077
3481
  const apart = (a2, b2) => Math.hypot(a2[0] + a2[2] / 2 - b2[0] - b2[2] / 2, a2[1] + a2[3] / 2 - b2[1] - b2[3] / 2, a2[2] - b2[2]);
3078
3482
  let lo = t0;
3079
3483
  let hi = t1;
3080
3484
  for (let i = 0; i < 40 && hi - lo > 1e-4; i++) {
3081
3485
  const mid = (lo + hi) / 2;
3082
- if (apart(box(lo), box(mid)) >= apart(box(mid), box(hi))) hi = mid;
3486
+ if (apart(box2(lo), box2(mid)) >= apart(box2(mid), box2(hi))) hi = mid;
3083
3487
  else lo = mid;
3084
3488
  }
3085
- const a = box(lo);
3086
- const b = box(hi);
3489
+ const a = box2(lo);
3490
+ const b = box2(hi);
3087
3491
  return apart(a, b) > Math.min(a[2], b[2]) / 20 ? hi : void 0;
3088
3492
  }
3089
3493
 
@@ -3112,7 +3516,7 @@ function resolvePlaceholders(motion, notes) {
3112
3516
  const filled = text.replace(PLACEHOLDER, (_, name) => {
3113
3517
  const d = declared.get(name);
3114
3518
  if (!d) unknown.add(name);
3115
- return (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _143 => _143.value]), () => ( ""))).replace(/"/g, '\\"');
3519
+ return (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _164 => _164.value]), () => ( ""))).replace(/"/g, '\\"');
3116
3520
  });
3117
3521
  for (const name of unknown) notes.push(`Placeholder "{{${name}}}" has no declaration, so it was left empty.`);
3118
3522
  return { motion: JSON.parse(filled), controls };
@@ -3138,7 +3542,7 @@ function poseOf(value, entry) {
3138
3542
  return { dx: moved.x - c.x, dy: moved.y - c.y, rotation: Math.atan2(o[1], o[0]) * 180 / Math.PI, sx, sy };
3139
3543
  }
3140
3544
  var near = (a, b, eps = 1e-6) => Math.abs(a - b) < eps;
3141
- var r3 = (n) => Math.round(n * 1e3) / 1e3;
3545
+ var r3 = (n2) => Math.round(n2 * 1e3) / 1e3;
3142
3546
  function commandsFor(anim, nodes, entered, notes) {
3143
3547
  const id = anim.id;
3144
3548
  const entry = nodes.get(id);
@@ -3166,7 +3570,7 @@ function commandsFor(anim, nodes, entered, notes) {
3166
3570
  if (anim.along) {
3167
3571
  const path = nodes.get(anim.along);
3168
3572
  if (!path || path.node.type !== "path") notes.push(`${where} follows "${anim.along}", which is not a path in the artwork; the follow was skipped.`);
3169
- else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access', _144 => _144.at, 'optionalAccess', _145 => _145[0]]), () => ( 0)), to: _nullishCoalesce(_optionalChain([anim, 'access', _146 => _146.at, 'optionalAccess', _147 => _147[1]]), () => ( 1)), ...anim.orient ? { rotate: true } : {}, ...timing, ...ease(MOTION_EASE_IN_OUT) });
3573
+ else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access', _165 => _165.at, 'optionalAccess', _166 => _166[0]]), () => ( 0)), to: _nullishCoalesce(_optionalChain([anim, 'access', _167 => _167.at, 'optionalAccess', _168 => _168[1]]), () => ( 1)), ...anim.orient ? { rotate: true } : {}, ...timing, ...ease(MOTION_EASE_IN_OUT) });
3170
3574
  }
3171
3575
  for (const [attr, [a, b]] of Object.entries(_nullishCoalesce(anim.attrs, () => ( {})))) {
3172
3576
  if (attr === "opacity") {
@@ -3177,7 +3581,7 @@ function commandsFor(anim, nodes, entered, notes) {
3177
3581
  out.push({ type: "enter", targets, style: "fade", ...timing, ...ease(MOTION_EASE_OUT) });
3178
3582
  } else out.push({ type: "fade", targets, to: Math.min(1, Math.max(0, to)), ...timing, ...ease(MOTION_EASE_IN_OUT) });
3179
3583
  } else if (attr === "fill" || attr === "stroke") {
3180
- if (_optionalChain([behavior, 'optionalAccess', _148 => _148.type]) === "pulse") out.push({ type: "color-pulse", targets, color: String(b), paint: attr, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...ease("linear") });
3584
+ if (_optionalChain([behavior, 'optionalAccess', _169 => _169.type]) === "pulse") out.push({ type: "color-pulse", targets, color: String(b), paint: attr, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...ease("linear") });
3181
3585
  else {
3182
3586
  if (behavior) notes.push(`${where}: a ${attr} ${behavior.type} became a plain colour change.`);
3183
3587
  out.push({ type: "style", targets, [attr]: String(b), ...timing, ...ease(MOTION_EASE_IN_OUT) });
@@ -3269,17 +3673,17 @@ ${problems.join("\n")}`);
3269
3673
  }
3270
3674
  function presetColor(value) {
3271
3675
  if (typeof value === "string") return /^#[0-9a-f]{6}$/i.test(value) ? value.toLowerCase() : value;
3272
- if (Array.isArray(value) && value.length >= 3 && value.slice(0, 3).every((n) => typeof n === "number" && Number.isFinite(n)))
3273
- return `#${value.slice(0, 3).map((n) => Math.round(Math.min(1, Math.max(0, n)) * 255).toString(16).padStart(2, "0")).join("")}`;
3676
+ if (Array.isArray(value) && value.length >= 3 && value.slice(0, 3).every((n2) => typeof n2 === "number" && Number.isFinite(n2)))
3677
+ return `#${value.slice(0, 3).map((n2) => Math.round(Math.min(1, Math.max(0, n2)) * 255).toString(16).padStart(2, "0")).join("")}`;
3274
3678
  return void 0;
3275
3679
  }
3276
3680
  function fromFlairVectorEffect(effect, options = {}) {
3277
3681
  if (!effect || effect.kind !== "vector" || !effect.motion) throw new Error('Give a Flair vector effect: { kind: "vector", id, name, motion: { svg, animate } }.');
3278
- const result = fromFlairSvgMotion(effect.motion, { name: _nullishCoalesce(_nullishCoalesce(options.name, () => ( effect.id)), () => ( effect.name)), hold: _nullishCoalesce(options.hold, () => ( effect.hold)), ...effect.trigger === "click" ? { clickSteps: true } : {} });
3279
- const names = new Set((_nullishCoalesce(result.scene.controls, () => ( []))).map((c) => c.name));
3682
+ const result2 = fromFlairSvgMotion(effect.motion, { name: _nullishCoalesce(_nullishCoalesce(options.name, () => ( effect.id)), () => ( effect.name)), hold: _nullishCoalesce(options.hold, () => ( effect.hold)), ...effect.trigger === "click" ? { clickSteps: true } : {} });
3683
+ const names = new Set((_nullishCoalesce(result2.scene.controls, () => ( []))).map((c) => c.name));
3280
3684
  const presets = (_nullishCoalesce(effect.presets, () => ( []))).map((p) => ({ name: p.name, values: Object.fromEntries(Object.entries(p.values).flatMap(([k, v]) => names.has(k) && presetColor(v) ? [[k, presetColor(v)]] : [])) })).filter((p) => Object.keys(p.values).length);
3281
- const scene = { ...result.scene, ...effect.description ? { description: effect.description } : {}, ...presets.length ? { presets } : {}, ...effect.trigger ? { trigger: effect.trigger } : {} };
3282
- const notes = [...result.notes];
3685
+ const scene = { ...result2.scene, ...effect.description ? { description: effect.description } : {}, ...presets.length ? { presets } : {}, ...effect.trigger ? { trigger: effect.trigger } : {} };
3686
+ const notes = [...result2.notes];
3283
3687
  return { scene, notes, issues: checkMotionScene(scene) };
3284
3688
  }
3285
3689
 
@@ -3297,8 +3701,8 @@ var STATIC_TR = () => ({
3297
3701
  function rgba(color) {
3298
3702
  const hex = typeof color === "string" ? _chunkA6QWKBJPcjs.normalizeTokenColor.call(void 0, color) : null;
3299
3703
  if (!hex) return null;
3300
- const n = (i) => parseInt(hex.slice(i, i + 2), 16) / 255;
3301
- return [n(1), n(3), n(5), hex.length >= 9 ? parseInt(hex.slice(7, 9), 16) / 255 : 1];
3704
+ const n2 = (i) => parseInt(hex.slice(i, i + 2), 16) / 255;
3705
+ return [n2(1), n2(3), n2(5), hex.length >= 9 ? parseInt(hex.slice(7, 9), 16) / 255 : 1];
3302
3706
  }
3303
3707
  function lottieContours(path, round4) {
3304
3708
  const out = [];
@@ -3329,9 +3733,9 @@ function lottieContours(path, round4) {
3329
3733
  add(cmd.x, cmd.y, cmd.x2, cmd.y2);
3330
3734
  } else if (cmd.type === "Z") {
3331
3735
  cur.c = true;
3332
- const n = cur.v.length;
3333
- if (n > 1 && Math.abs(cur.v[n - 1][0] - cur.v[0][0]) < 1e-9 && Math.abs(cur.v[n - 1][1] - cur.v[0][1]) < 1e-9) {
3334
- cur.i[0] = cur.i[n - 1];
3736
+ const n2 = cur.v.length;
3737
+ if (n2 > 1 && Math.abs(cur.v[n2 - 1][0] - cur.v[0][0]) < 1e-9 && Math.abs(cur.v[n2 - 1][1] - cur.v[0][1]) < 1e-9) {
3738
+ cur.i[0] = cur.i[n2 - 1];
3335
3739
  cur.v.pop();
3336
3740
  cur.i.pop();
3337
3741
  cur.o.pop();
@@ -3353,7 +3757,7 @@ function staticParts(m) {
3353
3757
  function toLottie(scene, options = {}) {
3354
3758
  const fps = _nullishCoalesce(_nullishCoalesce(options.fps, () => ( scene.fps)), () => ( 30));
3355
3759
  const precision = _nullishCoalesce(options.precision, () => ( 3));
3356
- const round4 = (n) => Math.round(n * 10 ** precision) / 10 ** precision;
3760
+ const round4 = (n2) => Math.round(n2 * 10 ** precision) / 10 ** precision;
3357
3761
  const frame = (ms2) => round4(ms2 * fps / 1e3);
3358
3762
  const timeline = compileMotionScene(scene);
3359
3763
  const nodes = indexMotionNodes(scene.artwork.nodes);
@@ -3386,9 +3790,9 @@ function toLottie(scene, options = {}) {
3386
3790
  else keys.push(k);
3387
3791
  };
3388
3792
  const times = [.../* @__PURE__ */ new Set([0, ...segments.flatMap((s) => [s.start, s.end]), end])].filter((t) => t >= 0 && t <= end).sort((a, b) => a - b);
3389
- for (let n = 0; n + 1 < times.length; n++) {
3390
- const a = times[n];
3391
- const b = times[n + 1];
3793
+ for (let n2 = 0; n2 + 1 < times.length; n2++) {
3794
+ const a = times[n2];
3795
+ const b = times[n2 + 1];
3392
3796
  const active = segments.filter((s) => s.start < b && s.end > a);
3393
3797
  if (!active.length) {
3394
3798
  key(a, value(a), "hold");
@@ -3467,11 +3871,11 @@ function toLottie(scene, options = {}) {
3467
3871
  const shapeSegments = [...timeline.tracks.entries()].filter(([k]) => k.startsWith(`${path.id}\0`) && (k.endsWith("\0shape") || k.includes("\0effect:"))).flatMap(([, v]) => v);
3468
3872
  const geometryAt = (t) => {
3469
3873
  if (!shapeSegments.length) return _chunkBQE3Y6ZOcjs.vectorPathWorldBezier.call(void 0, path);
3470
- const now = _optionalChain([indexMotionNodes, 'call', _149 => _149(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes), 'access', _150 => _150.get, 'call', _151 => _151(path.id), 'optionalAccess', _152 => _152.node]);
3874
+ const now = _optionalChain([indexMotionNodes, 'call', _170 => _170(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes), 'access', _171 => _171.get, 'call', _172 => _172(path.id), 'optionalAccess', _173 => _173.node]);
3471
3875
  return now && now.type === "path" ? _chunkBQE3Y6ZOcjs.vectorPathWorldBezier.call(void 0, _chunkBQE3Y6ZOcjs.expandVectorAppearances.call(void 0, [now])[0]) : _chunkBQE3Y6ZOcjs.vectorPathWorldBezier.call(void 0, path);
3472
3876
  };
3473
3877
  const rest = lottieContours(_chunkGF2RBSDMcjs.toBezierPath.call(void 0, geometryAt(0)), round4);
3474
- rest.forEach((contour, n) => {
3878
+ rest.forEach((contour, n2) => {
3475
3879
  let ks = { a: 0, k: contour };
3476
3880
  if (shapeSegments.length) {
3477
3881
  const keys = [];
@@ -3479,7 +3883,7 @@ function toLottie(scene, options = {}) {
3479
3883
  const count = Math.max(1, Math.ceil(end * fps / 1e3));
3480
3884
  for (let k = 0; k <= count && fits; k++) {
3481
3885
  const t = end * k / count;
3482
- const c = lottieContours(_chunkGF2RBSDMcjs.toBezierPath.call(void 0, geometryAt(t)), round4)[n];
3886
+ const c = lottieContours(_chunkGF2RBSDMcjs.toBezierPath.call(void 0, geometryAt(t)), round4)[n2];
3483
3887
  if (!c || c.v.length !== contour.v.length || c.c !== contour.c) fits = false;
3484
3888
  else keys.push({ t: frame(t), s: [c], ...k < count ? { o: { x: [0], y: [0] }, i: { x: [1], y: [1] } } : {} });
3485
3889
  }
@@ -3488,7 +3892,7 @@ function toLottie(scene, options = {}) {
3488
3892
  sampled += keys.length;
3489
3893
  } else notes.push(`"${path.id}": its morph changes how many points the outline has, which Lottie cannot tween; it shows its starting shape.`);
3490
3894
  }
3491
- items.push({ ty: "sh", nm: `${path.id} ${n + 1}`, ks });
3895
+ items.push({ ty: "sh", nm: `${path.id} ${n2 + 1}`, ks });
3492
3896
  });
3493
3897
  const fillFrom = owner(path, "fill");
3494
3898
  const strokeFrom = owner(path, "stroke");
@@ -3515,7 +3919,7 @@ function toLottie(scene, options = {}) {
3515
3919
  lj: path.strokeLinejoin === "round" ? 2 : path.strokeLinejoin === "bevel" ? 3 : 1,
3516
3920
  ml: 4
3517
3921
  });
3518
- if (_optionalChain([path, 'access', _153 => _153.strokeDasharray, 'optionalAccess', _154 => _154.length])) notes.push(`"${path.id}": its dashes are not exported; the stroke is solid.`);
3922
+ if (_optionalChain([path, 'access', _174 => _174.strokeDasharray, 'optionalAccess', _175 => _175.length])) notes.push(`"${path.id}": its dashes are not exported; the stroke is solid.`);
3519
3923
  if (path.strokeAlign && path.strokeAlign !== "center") notes.push(`"${path.id}": its stroke is drawn centred (Lottie has no inside or outside strokes).`);
3520
3924
  }
3521
3925
  const fill = paintOf(path.fill, "fill", path.id);
@@ -3532,7 +3936,7 @@ function toLottie(scene, options = {}) {
3532
3936
  }
3533
3937
  return items;
3534
3938
  };
3535
- const shown = (list) => list.filter((n) => n.visible !== false);
3939
+ const shown = (list) => list.filter((n2) => n2.visible !== false);
3536
3940
  const nodeGroup = (node) => {
3537
3941
  const entry = nodes.get(node.id);
3538
3942
  if (timeline.tracks.has(motionTrackKey(node.id, "clipRect")) || timeline.tracks.has(motionTrackKey(node.id, "clipCircle")))
@@ -3567,9 +3971,9 @@ function toLottie(scene, options = {}) {
3567
3971
  const camera = track("", "camera");
3568
3972
  const root = STATIC_TR();
3569
3973
  if (camera.length) {
3570
- const box = (t) => _nullishCoalesce(trackValue(camera, t), () => ( [0, 0, artwork.width, artwork.height]));
3571
- root.a = animate(camera, (t) => box(t).slice(0, 2), false);
3572
- root.s = animate(camera, (t) => [artwork.width / box(t)[2] * 100, artwork.height / box(t)[3] * 100], false);
3974
+ const box2 = (t) => _nullishCoalesce(trackValue(camera, t), () => ( [0, 0, artwork.width, artwork.height]));
3975
+ root.a = animate(camera, (t) => box2(t).slice(0, 2), false);
3976
+ root.s = animate(camera, (t) => [artwork.width / box2(t)[2] * 100, artwork.height / box2(t)[3] * 100], false);
3573
3977
  }
3574
3978
  const op = Math.max(1, Math.ceil(end * fps / 1e3));
3575
3979
  const lottie = {
@@ -3609,9 +4013,9 @@ var kids = (el, name) => el ? Array.from(el.children).filter((c) => c.localName
3609
4013
  var kid = (el, name) => _nullishCoalesce(kids(el, name)[0], () => ( null));
3610
4014
  var deep = (el, name) => el ? Array.from(el.getElementsByTagNameNS("*", name)) : [];
3611
4015
  var num = (el, attr, fallback = 0) => {
3612
- const v = _optionalChain([el, 'optionalAccess', _155 => _155.getAttribute, 'call', _156 => _156(attr)]);
3613
- const n = v === null || v === void 0 ? NaN : Number(v);
3614
- return Number.isFinite(n) ? n : fallback;
4016
+ const v = _optionalChain([el, 'optionalAccess', _176 => _176.getAttribute, 'call', _177 => _177(attr)]);
4017
+ const n2 = v === null || v === void 0 ? NaN : Number(v);
4018
+ return Number.isFinite(n2) ? n2 : fallback;
3615
4019
  };
3616
4020
  var dirname = (p) => p.slice(0, p.lastIndexOf("/") + 1);
3617
4021
  function resolve(base, target) {
@@ -3717,8 +4121,8 @@ function placement(xfrm, map) {
3717
4121
  const ext = kid(xfrm, "ext");
3718
4122
  const [x, y, w, h] = map(num(off, "x"), num(off, "y"), num(ext, "cx"), num(ext, "cy"));
3719
4123
  const rot = num(xfrm, "rot") / 6e4 * (Math.PI / 180);
3720
- const fx = _optionalChain([xfrm, 'optionalAccess', _157 => _157.getAttribute, 'call', _158 => _158("flipH")]) === "1" ? -1 : 1;
3721
- const fy = _optionalChain([xfrm, 'optionalAccess', _159 => _159.getAttribute, 'call', _160 => _160("flipV")]) === "1" ? -1 : 1;
4124
+ const fx = _optionalChain([xfrm, 'optionalAccess', _178 => _178.getAttribute, 'call', _179 => _179("flipH")]) === "1" ? -1 : 1;
4125
+ const fy = _optionalChain([xfrm, 'optionalAccess', _180 => _180.getAttribute, 'call', _181 => _181("flipV")]) === "1" ? -1 : 1;
3722
4126
  const cx = x + w / 2;
3723
4127
  const cy = y + h / 2;
3724
4128
  const cos = Math.cos(rot);
@@ -3745,7 +4149,7 @@ function shapes(ctx, tree, map) {
3745
4149
  const nv = el.firstElementChild;
3746
4150
  const cNvPr = kid(nv, "cNvPr");
3747
4151
  const spid = num(cNvPr, "id", -1);
3748
- const name = _nullishCoalesce(_optionalChain([cNvPr, 'optionalAccess', _161 => _161.getAttribute, 'call', _162 => _162("name")]), () => ( ""));
4152
+ const name = _nullishCoalesce(_optionalChain([cNvPr, 'optionalAccess', _182 => _182.getAttribute, 'call', _183 => _183("name")]), () => ( ""));
3749
4153
  if (el.localName === "grpSp") {
3750
4154
  const xfrm = kid(kid(el, "grpSpPr"), "xfrm");
3751
4155
  const off = kid(xfrm, "off");
@@ -3766,7 +4170,7 @@ function shapes(ctx, tree, map) {
3766
4170
  if (el.localName !== "sp" && el.localName !== "cxnSp") continue;
3767
4171
  const spPr = kid(el, "spPr");
3768
4172
  const { w, h, transform } = placement(kid(spPr, "xfrm"), map);
3769
- const prst = _optionalChain([kid, 'call', _163 => _163(spPr, "prstGeom"), 'optionalAccess', _164 => _164.getAttribute, 'call', _165 => _165("prst")]);
4173
+ const prst = _optionalChain([kid, 'call', _184 => _184(spPr, "prstGeom"), 'optionalAccess', _185 => _185.getAttribute, 'call', _186 => _186("prst")]);
3770
4174
  const cust = kid(spPr, "custGeom");
3771
4175
  let subpaths = cust ? custom(cust, w, h) : prst ? preset(prst, w, h) : null;
3772
4176
  if (!subpaths) {
@@ -3880,13 +4284,13 @@ function command(ctx, ctn, scene, artwork, target, hidden) {
3880
4284
  }
3881
4285
  if (cls === "path") {
3882
4286
  const motion = deep(ctn, "animMotion")[0];
3883
- const d = _optionalChain([motion, 'optionalAccess', _166 => _166.getAttribute, 'call', _167 => _167("path")]);
4287
+ const d = _optionalChain([motion, 'optionalAccess', _187 => _187.getAttribute, 'call', _188 => _188("path")]);
3884
4288
  if (!d) return null;
3885
- const entry = artwork.flatMap(function all(n) {
3886
- return n.type === "group" ? [n, ...n.children.flatMap(all)] : [n];
3887
- }).find((n) => n.id === target);
3888
- const t = _nullishCoalesce(_optionalChain([entry, 'optionalAccess', _168 => _168.transform]), () => ( [1, 0, 0, 1, 0, 0]));
3889
- const p = _optionalChain([entry, 'optionalAccess', _169 => _169.type]) === "path" ? _optionalChain([entry, 'access', _170 => _170.subpaths, 'access', _171 => _171[0], 'optionalAccess', _172 => _172.anchors]) : void 0;
4289
+ const entry = artwork.flatMap(function all(n2) {
4290
+ return n2.type === "group" ? [n2, ...n2.children.flatMap(all)] : [n2];
4291
+ }).find((n2) => n2.id === target);
4292
+ const t = _nullishCoalesce(_optionalChain([entry, 'optionalAccess', _189 => _189.transform]), () => ( [1, 0, 0, 1, 0, 0]));
4293
+ const p = _optionalChain([entry, 'optionalAccess', _190 => _190.type]) === "path" ? _optionalChain([entry, 'access', _191 => _191.subpaths, 'access', _192 => _192[0], 'optionalAccess', _193 => _193.anchors]) : void 0;
3890
4294
  const xs = (_nullishCoalesce(p, () => ( []))).map((a) => a.x);
3891
4295
  const ys = (_nullishCoalesce(p, () => ( []))).map((a) => a.y);
3892
4296
  const lx = xs.length ? (Math.min(...xs) + Math.max(...xs)) / 2 : 0;
@@ -3929,8 +4333,8 @@ async function fromPptxAnimation(data, options = {}) {
3929
4333
  if (!pres) throw new Error("This is not a PowerPoint file (.pptx): it has no ppt/presentation.xml.");
3930
4334
  const presRels = await rels(ctx, "ppt/presentation.xml");
3931
4335
  const slidePaths = deep(pres, "sldId").map((s) => _nullishCoalesce(presRels.get(_nullishCoalesce(_nullishCoalesce(s.getAttributeNS("http://schemas.openxmlformats.org/officeDocument/2006/relationships", "id"), () => ( s.getAttribute("r:id"))), () => ( ""))), () => ( ""))).filter(Boolean);
3932
- const n = _nullishCoalesce(options.slide, () => ( 1));
3933
- if (!(Number.isInteger(n) && n >= 1 && n <= slidePaths.length)) throw new Error(`There is no slide ${n}; the file has ${slidePaths.length} slide${slidePaths.length === 1 ? "" : "s"}.`);
4336
+ const n2 = _nullishCoalesce(options.slide, () => ( 1));
4337
+ if (!(Number.isInteger(n2) && n2 >= 1 && n2 <= slidePaths.length)) throw new Error(`There is no slide ${n2}; the file has ${slidePaths.length} slide${slidePaths.length === 1 ? "" : "s"}.`);
3934
4338
  const size = deep(pres, "sldSz")[0];
3935
4339
  const width = Math.round(num(size, "cx", 12192e3) / EMU);
3936
4340
  const height = Math.round(num(size, "cy", 6858e3) / EMU);
@@ -3938,15 +4342,15 @@ async function fromPptxAnimation(data, options = {}) {
3938
4342
  if (!path.includes("theme/")) continue;
3939
4343
  const theme = await xml(ctx, path);
3940
4344
  const scheme = deep(theme, "clrScheme")[0];
3941
- for (const c of Array.from(_nullishCoalesce(_optionalChain([scheme, 'optionalAccess', _173 => _173.children]), () => ( [])))) {
4345
+ for (const c of Array.from(_nullishCoalesce(_optionalChain([scheme, 'optionalAccess', _194 => _194.children]), () => ( [])))) {
3942
4346
  const v = c.firstElementChild;
3943
- const hex = _optionalChain([v, 'optionalAccess', _174 => _174.localName]) === "srgbClr" ? v.getAttribute("val") : _optionalChain([v, 'optionalAccess', _175 => _175.getAttribute, 'call', _176 => _176("lastClr")]);
4347
+ const hex = _optionalChain([v, 'optionalAccess', _195 => _195.localName]) === "srgbClr" ? v.getAttribute("val") : _optionalChain([v, 'optionalAccess', _196 => _196.getAttribute, 'call', _197 => _197("lastClr")]);
3944
4348
  if (hex) ctx.theme.set(c.localName, `#${hex.toLowerCase()}`);
3945
4349
  }
3946
4350
  break;
3947
4351
  }
3948
- const slide = await xml(ctx, slidePaths[n - 1]);
3949
- if (!slide) throw new Error(`Slide ${n} cannot be read.`);
4352
+ const slide = await xml(ctx, slidePaths[n2 - 1]);
4353
+ if (!slide) throw new Error(`Slide ${n2} cannot be read.`);
3950
4354
  const tree = deep(slide, "spTree")[0];
3951
4355
  const toPx = (x, y, w, h) => [x / EMU, y / EMU, w / EMU, h / EMU];
3952
4356
  const nodes = tree ? shapes(ctx, tree, toPx) : [];
@@ -3973,18 +4377,118 @@ async function fromPptxAnimation(data, options = {}) {
3973
4377
  }
3974
4378
  if (effects.length && !shots.length) ctx.notes.push("The slide's effects could not be read.");
3975
4379
  const artwork = { width, height, background: bg, nodes: [...nodes, ...hidden] };
3976
- const scene = { version: 1, name: `slide-${n}`, artwork, shots, ...shots.length > 1 ? { trigger: "click" } : {} };
4380
+ const scene = { version: 1, name: `slide-${n2}`, artwork, shots, ...shots.length > 1 ? { trigger: "click" } : {} };
3977
4381
  const problems = motionSceneShapeProblems(scene);
3978
4382
  if (problems.length) throw new Error(`The imported scene is not well formed:
3979
4383
  ${problems.join("\n")}`);
3980
4384
  return { scene, notes: [...new Set(ctx.notes)], issues: checkMotionScene(scene), slides: slidePaths.length };
3981
4385
  }
3982
4386
 
4387
+ // src/animation-editor/layer-edit.ts
4388
+ function motionShotLayersInfo(scene, shot) {
4389
+ const spans = motionLayerSpans(scene);
4390
+ const timings = compileMotionScene(scene).shots;
4391
+ return scene.shots.filter((s) => shot === void 0 || s.id === shot).map((s) => {
4392
+ const timing = timings.find((t) => t.id === s.id);
4393
+ return {
4394
+ shot: s.id,
4395
+ layers: spans.filter((sp) => sp.shot === s.id).map((sp) => ({ ...sp.layer, shot: s.id, index: sp.index, role: sp.role, start: sp.start, end: sp.end })),
4396
+ transition: s.transition && timing.transitionEnd !== void 0 ? { ...s.transition, start: timing.start, end: timing.transitionEnd } : null
4397
+ };
4398
+ });
4399
+ }
4400
+ function findMotionShotLayer(scene, id) {
4401
+ for (let shot = 0; shot < scene.shots.length; shot++) {
4402
+ const index = (_nullishCoalesce(scene.shots[shot].layers, () => ( []))).findIndex((l) => l.id === id);
4403
+ if (index >= 0) return { shot, index };
4404
+ }
4405
+ return null;
4406
+ }
4407
+ var motionLayerIds = (scene) => scene.shots.flatMap((s) => (_nullishCoalesce(s.layers, () => ( []))).map((l) => l.id));
4408
+ function freshMotionLayerId(scene, base = "layer") {
4409
+ const used = new Set(motionLayerIds(scene));
4410
+ for (let i = 1; ; i++) if (!used.has(`${base}${i}`)) return `${base}${i}`;
4411
+ }
4412
+ var kindHint = (d) => {
4413
+ if (d.uses.includes("transition")) return "it is a transition: set it between shots with set_shot_transition";
4414
+ if (d.kind === "camera") return "it is a camera move: set it with set_shot_camera_move";
4415
+ if (d.kind === "motion") return "it animates artwork nodes: apply it with apply_effect";
4416
+ return `its uses are ${d.uses.join(", ")}`;
4417
+ };
4418
+ function withPreset(d, preset2, values) {
4419
+ if (preset2 === void 0) return values;
4420
+ const p = d.presets.find((x) => x.name === preset2);
4421
+ if (!p) throw new (0, _chunkJAOSL334cjs.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);
4422
+ return { ...p.values, ..._nullishCoalesce(values, () => ( {})) };
4423
+ }
4424
+ async function resolveMotionShotLayer(provider, layer) {
4425
+ if (!layer.effect) {
4426
+ if (!layer.subject) throw new Error('A layer needs an "effect", a "subject" or both.');
4427
+ if (layer.preset !== void 0 || layer.values !== void 0) throw new Error('A bare subject has no controls; leave out "preset" and "values", or give an "effect".');
4428
+ const { provider: _p, name: _n, ...rest } = layer;
4429
+ return rest;
4430
+ }
4431
+ if (!provider) throw new (0, _chunkJAOSL334cjs.NoEffectsProviderError)();
4432
+ const d = await provider.describe(layer.effect);
4433
+ if (!_chunk6RZVFYGJcjs.effectLayerOffered.call(void 0, d)) throw new (0, _chunkJAOSL334cjs.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);
4434
+ const input = _chunk6RZVFYGJcjs.effectLayerInput.call(void 0, d);
4435
+ if (layer.subject && !input) throw new (0, _chunkJAOSL334cjs.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);
4436
+ 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, _chunkJAOSL334cjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) changes a picture; give a "subject": { "image": url }, { "svg": markup }, { "text": words } or { "slot": name }.`, d.id);
4437
+ const values = withPreset(d, layer.preset, layer.values);
4438
+ _chunkJAOSL334cjs.resolveEffectValues.call(void 0, d, values);
4439
+ const out = { ...layer, provider: provider.id, name: d.name };
4440
+ if (values && Object.keys(values).length) out.values = values;
4441
+ else delete out.values;
4442
+ return out;
4443
+ }
4444
+ async function resolveMotionShotTransition(provider, transition) {
4445
+ if (!provider) throw new (0, _chunkJAOSL334cjs.NoEffectsProviderError)();
4446
+ const d = await provider.describe(transition.effect);
4447
+ if (!d.uses.includes("transition") || d.kind === "camera") throw new (0, _chunkJAOSL334cjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) is not a transition (${kindHint(d)}); search_effects with use "transition" lists them.`, d.id);
4448
+ 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}.`);
4449
+ const values = withPreset(d, transition.preset, transition.values);
4450
+ _chunkJAOSL334cjs.resolveEffectValues.call(void 0, d, values);
4451
+ const out = { ...transition, ms: _nullishCoalesce(transition.ms, () => ( _chunkJAOSL334cjs.EFFECT_TRANSITION_MS)), provider: provider.id, name: d.name };
4452
+ if (values && Object.keys(values).length) out.values = values;
4453
+ else delete out.values;
4454
+ return out;
4455
+ }
4456
+ async function checkMotionShotLayers(scene, provider) {
4457
+ const issues = [];
4458
+ if (_optionalChain([scene, 'access', _202 => _202.shots, 'access', _203 => _203[0], 'optionalAccess', _204 => _204.transition])) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "transition-first-shot", `Shot "${scene.shots[0].id}" is the first shot, so its transition does not play; move it to a later shot or clear it.`, { shot: scene.shots[0].id }));
4459
+ for (const shot of scene.shots)
4460
+ for (const layer of _nullishCoalesce(shot.layers, () => ( [])))
4461
+ if (layer.subject && "slot" in layer.subject) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "layer-slot-empty", `Layer "${layer.id}" shows slot "${layer.subject.slot}", which is empty; set its "subject" to a picture with update_shot_layer.`, { shot: shot.id, ids: [layer.id] }));
4462
+ const effectLayers = scene.shots.flatMap((s) => (_nullishCoalesce(s.layers, () => ( []))).filter((l) => l.effect).map((l) => ({ shot: s.id, layer: l })));
4463
+ const transitions = scene.shots.slice(1).filter((s) => s.transition);
4464
+ if (!provider) {
4465
+ if (effectLayers.length) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "layer-not-drawn", `Layers ${effectLayers.map((e) => `"${e.layer.id}"`).join(", ")} name provider effects, but no effects provider is set, so they are left out of frames; set a provider to draw them.`, { ids: effectLayers.map((e) => e.layer.id) }));
4466
+ if (transitions.length) issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, "transition-not-played", `Shots ${transitions.map((s) => `"${s.id}"`).join(", ")} name transitions, but no effects provider is set, so they crossfade; set a provider to play them.`));
4467
+ return issues;
4468
+ }
4469
+ for (const { shot, layer } of effectLayers) {
4470
+ try {
4471
+ await resolveMotionShotLayer(provider, layer);
4472
+ } catch (error) {
4473
+ const code = error.code === "invalid_values" || /preset/.test(error.message) ? "layer-effect-values" : "layer-effect-unknown";
4474
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, code, `Layer "${layer.id}": ${error.message}`, { shot, ids: [layer.id] }));
4475
+ }
4476
+ }
4477
+ for (const shot of transitions) {
4478
+ try {
4479
+ await resolveMotionShotTransition(provider, shot.transition);
4480
+ } catch (error) {
4481
+ issues.push(_chunkDGWOGQJ5cjs.motionIssue.call(void 0, error.code === "invalid_values" ? "layer-effect-values" : "transition-unknown", error.message, { shot: shot.id }));
4482
+ }
4483
+ }
4484
+ return issues;
4485
+ }
4486
+
3983
4487
  // src/animation-editor/effects.ts
3984
4488
  var sourceOf = (c) => c.source;
3985
4489
  function motionSceneEffects(scene) {
3986
4490
  const out = /* @__PURE__ */ new Map();
3987
- const groups = new Set(scene.artwork.nodes.filter((n) => n.type === "group").map((n) => n.id));
4491
+ const groups = new Set(scene.artwork.nodes.filter((n2) => n2.type === "group").map((n2) => n2.id));
3988
4492
  for (const shot of scene.shots) {
3989
4493
  for (const c of shot.run) {
3990
4494
  const s = sourceOf(c);
@@ -4005,7 +4509,7 @@ function motionSceneEffects(scene) {
4005
4509
  }
4006
4510
  function nextMotionEffectInstance(scene) {
4007
4511
  const used = /* @__PURE__ */ new Set([...motionSceneEffects(scene).map((e) => e.instance), ...scene.shots.map((s) => s.id)]);
4008
- const walk = (nodes) => nodes.forEach((n) => (used.add(n.id), n.type === "group" && walk(n.children)));
4512
+ const walk = (nodes) => nodes.forEach((n2) => (used.add(n2.id), n2.type === "group" && walk(n2.children)));
4009
4513
  walk(scene.artwork.nodes);
4010
4514
  for (let i = 1; ; i++) if (!used.has(`fx${i}`)) return `fx${i}`;
4011
4515
  }
@@ -4021,10 +4525,10 @@ function motionCommandsFromEffect(artwork, motion, source) {
4021
4525
  }
4022
4526
  function prefixed(nodes, shots, prefix) {
4023
4527
  const map = /* @__PURE__ */ new Map();
4024
- const rename = (list) => list.map((n) => {
4025
- map.set(n.id, prefix + n.id);
4026
- const next = { ...n, id: prefix + n.id };
4027
- if (next.type === "group") next.children = rename(n.children);
4528
+ const rename = (list) => list.map((n2) => {
4529
+ map.set(n2.id, prefix + n2.id);
4530
+ const next = { ...n2, id: prefix + n2.id };
4531
+ if (next.type === "group") next.children = rename(n2.children);
4028
4532
  return next;
4029
4533
  });
4030
4534
  const out = rename(nodes);
@@ -4040,14 +4544,14 @@ function prefixed(nodes, shots, prefix) {
4040
4544
  }));
4041
4545
  return { nodes: out, shots: remapped };
4042
4546
  }
4043
- function motionPartsFromVectorEffect(artwork, motion, source, box = { x: 0, y: 0, width: artwork.width, height: artwork.height }) {
4547
+ function motionPartsFromVectorEffect(artwork, motion, source, box2 = { x: 0, y: 0, width: artwork.width, height: artwork.height }) {
4044
4548
  const imported = fromFlairSvgMotion({ svg: motion.svg, animate: [..._nullishCoalesce(motion.animate, () => ( []))] }, { hold: 0 });
4045
4549
  const art = imported.scene.artwork;
4046
4550
  const prefix = `${source.instance}-`;
4047
4551
  const { nodes, shots } = prefixed(art.nodes, imported.scene.shots, prefix);
4048
- const k = Math.min(box.width / art.width, box.height / art.height);
4049
- const dx = box.x + (box.width - art.width * k) / 2;
4050
- const dy = box.y + (box.height - art.height * k) / 2;
4552
+ const k = Math.min(box2.width / art.width, box2.height / art.height);
4553
+ const dx = box2.x + (box2.width - art.width * k) / 2;
4554
+ const dy = box2.y + (box2.height - art.height * k) / 2;
4051
4555
  const group = { type: "group", id: source.instance, name: _nullishCoalesce(source.name, () => ( source.effect)), children: nodes, transform: [k, 0, 0, k, dx, dy] };
4052
4556
  const tagged = shots.map((s, i) => ({ ...s, id: i === 0 ? source.instance + "-shot" : `${source.instance}-shot-${i + 1}`, run: tag(s.run, source) }));
4053
4557
  return { group, shots: tagged.map((s) => s.hold === 0 ? (({ hold: _h, ...rest }) => rest)(s) : s), notes: imported.notes };
@@ -4058,7 +4562,7 @@ function withoutMotionEffect(scene, instance) {
4058
4562
  const shots = [];
4059
4563
  for (const shot of scene.shots) {
4060
4564
  const run = shot.run.filter((c, i) => {
4061
- const mine = _optionalChain([sourceOf, 'call', _177 => _177(c), 'optionalAccess', _178 => _178.instance]) === instance;
4565
+ const mine = _optionalChain([sourceOf, 'call', _205 => _205(c), 'optionalAccess', _206 => _206.instance]) === instance;
4062
4566
  if (mine && !place) place = { shot: shot.id, index: i };
4063
4567
  return !mine;
4064
4568
  });
@@ -4066,7 +4570,7 @@ function withoutMotionEffect(scene, instance) {
4066
4570
  else if (run.length === 0) removedShots.push(shot.id);
4067
4571
  else shots.push({ ...shot, run });
4068
4572
  }
4069
- const artwork = { ...scene.artwork, nodes: scene.artwork.nodes.filter((n) => n.id !== instance) };
4573
+ const artwork = { ...scene.artwork, nodes: scene.artwork.nodes.filter((n2) => n2.id !== instance) };
4070
4574
  return { scene: { ...scene, shots, artwork }, place, removedShots };
4071
4575
  }
4072
4576
 
@@ -4074,30 +4578,37 @@ function withoutMotionEffect(scene, instance) {
4074
4578
  var MOTION_RECORD_GESTURE_MODES = ["move", "scale", "rotate"];
4075
4579
  var MOTION_MAX_TAKE_MS = 10 * 6e4;
4076
4580
  var BLANK = { width: 800, height: 450, background: "#ffffff", nodes: [] };
4077
- var AnimationEditorController = (_class = class {
4581
+ var AnimationEditorController = (_class2 = class {
4078
4582
 
4079
4583
 
4080
4584
  /** The effects provider slot (see `setEffectsProvider`). */
4081
- __init() {this.effects = new (0, _chunk2K5TELHVcjs.EffectsSlot)()}
4585
+ __init8() {this.effects = new (0, _chunkJAOSL334cjs.EffectsSlot)()}
4586
+ /** Renders shot layer and transition pictures for frames (see `MotionLayersEngine`). */
4587
+
4082
4588
 
4083
4589
 
4084
- __init2() {this.selection = null}
4590
+ __init9() {this.selection = null}
4085
4591
 
4086
- __init3() {this.error = null}
4087
- __init4() {this.revision = 0}
4088
- __init5() {this.applying = false}
4592
+ __init10() {this.error = null}
4593
+ __init11() {this.revision = 0}
4594
+ __init12() {this.applying = false}
4089
4595
 
4090
- __init6() {this.listeners = /* @__PURE__ */ new Set()}
4091
- __init7() {this.unsubscribers = []}
4596
+ __init13() {this.listeners = /* @__PURE__ */ new Set()}
4597
+ __init14() {this.unsubscribers = []}
4092
4598
 
4093
- __init8() {this.recording = null}
4094
- 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);_class.prototype.__init9.call(this);_class.prototype.__init10.call(this);
4599
+ __init15() {this.recording = null}
4600
+ constructor(options = {}) {;_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);
4095
4601
  this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkFJ2MSI43cjs.HistoryManager)()));
4096
4602
  this.scene = options.scene ? parseMotionScene(options.scene) : createMotionScene(BLANK);
4097
4603
  this.issues = checkMotionScene(this.scene);
4098
4604
  this.readOnly = _nullishCoalesce(options.readOnly, () => ( false));
4099
4605
  this.onChange = options.onChange;
4100
4606
  if (options.effects) this.effects.set(options.effects);
4607
+ this.layers = new MotionLayersEngine({
4608
+ slot: this.effects,
4609
+ host: options.layerHost === void 0 ? browserMotionLayerHost() : options.layerHost,
4610
+ frameSvg: (scene, time) => motionFrameToSvg(scene, time, { effects: this.effects.provider, layers: this.layers, transitions: false, pretty: false })
4611
+ });
4101
4612
  this.player = new (0, _chunkM5GLQQWGcjs.AnimationPlayer)({ clock: options.clock, duration: () => compileMotionScene(this.scene).duration, loop: _nullishCoalesce(this.scene.loop, () => ( "none")) });
4102
4613
  this.state = this.computeState();
4103
4614
  this.connect();
@@ -4109,7 +4620,8 @@ var AnimationEditorController = (_class = class {
4109
4620
  this.history.subscribe(() => {
4110
4621
  if (!this.applying) this.notify();
4111
4622
  }),
4112
- this.effects.subscribe(() => this.notify())
4623
+ this.effects.subscribe(() => this.notify()),
4624
+ this.layers.subscribe(() => this.notify())
4113
4625
  );
4114
4626
  }
4115
4627
  /**
@@ -4121,8 +4633,8 @@ var AnimationEditorController = (_class = class {
4121
4633
  this.player.pause();
4122
4634
  this.unsubscribers.splice(0).forEach((u) => u());
4123
4635
  }
4124
- __init9() {this.getState = () => this.state}
4125
- __init10() {this.subscribe = (listener) => {
4636
+ __init16() {this.getState = () => this.state}
4637
+ __init17() {this.subscribe = (listener) => {
4126
4638
  this.connect();
4127
4639
  this.listeners.add(listener);
4128
4640
  return () => {
@@ -4145,7 +4657,7 @@ var AnimationEditorController = (_class = class {
4145
4657
  time: p.time,
4146
4658
  playing: p.playing,
4147
4659
  loop: p.loop,
4148
- currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _179 => _179(timeline, p.time), 'optionalAccess', _180 => _180.id]), () => ( null)),
4660
+ currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _207 => _207(timeline, p.time), 'optionalAccess', _208 => _208.id]), () => ( null)),
4149
4661
  selection: this.selection,
4150
4662
  readOnly: this.readOnly,
4151
4663
  error: this.error,
@@ -4193,7 +4705,7 @@ var AnimationEditorController = (_class = class {
4193
4705
  this.applying = false;
4194
4706
  }
4195
4707
  this.notify();
4196
- _optionalChain([this, 'access', _181 => _181.onChange, 'optionalCall', _182 => _182(scene)]);
4708
+ _optionalChain([this, 'access', _209 => _209.onChange, 'optionalCall', _210 => _210(scene)]);
4197
4709
  }
4198
4710
  commit(label, next, merge) {
4199
4711
  const before = this.scene;
@@ -4228,10 +4740,10 @@ var AnimationEditorController = (_class = class {
4228
4740
  apply(edits, label = "Edit animation", options = {}) {
4229
4741
  const reason = _nullishCoalesce(this.readOnlyReason(), () => ( (this.recording ? "A take is being recorded; stop or cancel the recording first." : null)));
4230
4742
  if (reason) return this.refuse(reason);
4231
- const result = applyMotionEdits(this.scene, edits);
4232
- if (!result.ok) return options.dryRun ? result : this.refuse(result.error);
4233
- if (!options.dryRun) this.commit(label, result.scene, options.merge);
4234
- return result;
4743
+ const result2 = applyMotionEdits(this.scene, edits);
4744
+ if (!result2.ok) return options.dryRun ? result2 : this.refuse(result2.error);
4745
+ if (!options.dryRun) this.commit(label, result2.scene, options.merge);
4746
+ return result2;
4235
4747
  }
4236
4748
  /** Replaces the artwork (e.g. from a vector editor's `onChange`), as one undo step. */
4237
4749
  setArtwork(artwork, label = "Change artwork") {
@@ -4263,7 +4775,7 @@ var AnimationEditorController = (_class = class {
4263
4775
  try {
4264
4776
  plan = await work();
4265
4777
  } catch (error) {
4266
- if (_chunk2K5TELHVcjs.isEffectError.call(void 0, error) || error instanceof Error) return this.refuse(error.message);
4778
+ if (_chunkJAOSL334cjs.isEffectError.call(void 0, error) || error instanceof Error) return this.refuse(error.message);
4267
4779
  throw error;
4268
4780
  }
4269
4781
  const r = this.apply({ op: "replace_scene", scene: plan.scene }, plan.label, { merge });
@@ -4274,17 +4786,17 @@ var AnimationEditorController = (_class = class {
4274
4786
  }
4275
4787
  async describeFor(effect, kinds) {
4276
4788
  const provider = this.effects.require();
4277
- if (!provider.applyVectorEffect) throw new (0, _chunk2K5TELHVcjs.EffectUnsupportedError)(`${provider.name} has no vector or motion effects.`);
4789
+ if (!provider.applyVectorEffect) throw new (0, _chunkJAOSL334cjs.EffectUnsupportedError)(`${provider.name} has no vector or motion effects.`);
4278
4790
  const d = await provider.describe(effect);
4279
4791
  if (!kinds.includes(d.kind)) {
4280
4792
  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";
4281
- throw new (0, _chunk2K5TELHVcjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) is a ${d.kind} effect: ${hint}.`, d.id);
4793
+ throw new (0, _chunkJAOSL334cjs.EffectUnsupportedError)(`"${d.id}" (${d.name}) is a ${d.kind} effect: ${hint}.`, d.id);
4282
4794
  }
4283
4795
  return d;
4284
4796
  }
4285
4797
  knownNodes(ids) {
4286
4798
  const all = /* @__PURE__ */ new Set();
4287
- const walk = (nodes) => nodes.forEach((n) => (all.add(n.id), n.type === "group" && walk(n.children)));
4799
+ const walk = (nodes) => nodes.forEach((n2) => (all.add(n2.id), n2.type === "group" && walk(n2.children)));
4288
4800
  walk(this.scene.artwork.nodes);
4289
4801
  const missing = ids.filter((id) => !all.has(id));
4290
4802
  if (missing.length) throw new Error(`The artwork has no node ${missing.map((m) => `"${m}"`).join(", ")}; get_animation lists the node ids.`);
@@ -4299,7 +4811,7 @@ var AnimationEditorController = (_class = class {
4299
4811
  return this.effectEdit(async () => {
4300
4812
  this.knownNodes(targets);
4301
4813
  const d = await this.describeFor(effect, ["motion"]);
4302
- const values = _chunk2K5TELHVcjs.resolveEffectValues.call(void 0, d, options.values);
4814
+ const values = _chunkJAOSL334cjs.resolveEffectValues.call(void 0, d, options.values);
4303
4815
  const instance = _nullishCoalesce(options.instance, () => ( nextMotionEffectInstance(this.scene)));
4304
4816
  const motion = await this.effects.require().applyVectorEffect(d.id, { svg: motionArtworkSvg(this.scene.artwork), nodeIds: [...targets] }, values);
4305
4817
  const source = { instance, effect: d.id, provider: this.effects.require().id, name: d.name, values };
@@ -4321,7 +4833,7 @@ var AnimationEditorController = (_class = class {
4321
4833
  addVectorEffect(effect, options = {}) {
4322
4834
  return this.effectEdit(async () => {
4323
4835
  const d = await this.describeFor(effect, ["vector"]);
4324
- const values = _chunk2K5TELHVcjs.resolveEffectValues.call(void 0, d, options.values);
4836
+ const values = _chunkJAOSL334cjs.resolveEffectValues.call(void 0, d, options.values);
4325
4837
  const instance = _nullishCoalesce(options.instance, () => ( nextMotionEffectInstance(this.scene)));
4326
4838
  const motion = await this.effects.require().applyVectorEffect(d.id, motionArtworkSvg(this.scene.artwork), values);
4327
4839
  const source = { instance, effect: d.id, provider: this.effects.require().id, name: d.name, values };
@@ -4345,17 +4857,17 @@ var AnimationEditorController = (_class = class {
4345
4857
  else if (k in d.controls.properties) base[k] = d.defaults[k];
4346
4858
  else throw new Error(`"${k}" is not a control of "${d.id}"; its controls are ${Object.keys(d.controls.properties).join(", ")}.`);
4347
4859
  }
4348
- const merged = { ...base, ..._chunk2K5TELHVcjs.resolveEffectValues.call(void 0, { ...d, defaults: base }, given) };
4860
+ const merged = { ...base, ..._chunkJAOSL334cjs.resolveEffectValues.call(void 0, { ...d, defaults: base }, given) };
4349
4861
  const { scene: bare, place } = withoutMotionEffect(this.scene, instance);
4350
4862
  const provider = this.effects.require();
4351
4863
  const source = { instance, effect: d.id, provider: provider.id, name: d.name, values: merged };
4352
4864
  if (was.kind === "vector") {
4353
- const old = this.scene.artwork.nodes.find((n) => n.id === instance);
4865
+ const old = this.scene.artwork.nodes.find((n2) => n2.id === instance);
4354
4866
  const motion2 = await provider.applyVectorEffect(d.id, motionArtworkSvg(bare.artwork), merged);
4355
4867
  const { group, shots: shots2, notes: notes2 } = motionPartsFromVectorEffect(bare.artwork, motion2, source);
4356
- const index = this.scene.artwork.nodes.findIndex((n) => n.id === instance);
4868
+ const index = this.scene.artwork.nodes.findIndex((n2) => n2.id === instance);
4357
4869
  const nodes = [...bare.artwork.nodes];
4358
- nodes.splice(Math.max(0, index), 0, _optionalChain([old, 'optionalAccess', _183 => _183.transform]) ? { ...group, transform: old.transform } : group);
4870
+ nodes.splice(Math.max(0, index), 0, _optionalChain([old, 'optionalAccess', _211 => _211.transform]) ? { ...group, transform: old.transform } : group);
4359
4871
  const at = this.scene.shots.findIndex((s) => was.shots.includes(s.id));
4360
4872
  const kept = bare.shots;
4361
4873
  const nextShots = [...kept.slice(0, Math.max(0, at)), ...shots2, ...kept.slice(Math.max(0, at))];
@@ -4366,7 +4878,7 @@ var AnimationEditorController = (_class = class {
4366
4878
  let shots = bare.shots;
4367
4879
  if (place && shots.some((s) => s.id === place.shot)) shots = shots.map((s) => s.id === place.shot ? { ...s, run: [...s.run.slice(0, place.index), ...commands, ...s.run.slice(place.index)] } : s);
4368
4880
  else {
4369
- const at = this.scene.shots.findIndex((s) => s.id === _optionalChain([place, 'optionalAccess', _184 => _184.shot]));
4881
+ const at = this.scene.shots.findIndex((s) => s.id === _optionalChain([place, 'optionalAccess', _212 => _212.shot]));
4370
4882
  shots = [...shots.slice(0, Math.max(0, at)), { id: instance, label: d.name, run: commands }, ...shots.slice(Math.max(0, at))];
4371
4883
  }
4372
4884
  return { scene: { ...bare, shots }, label: `Change ${d.name}`, instance, notes };
@@ -4400,12 +4912,12 @@ var AnimationEditorController = (_class = class {
4400
4912
  if (!found) return this.refuse(`There is no shot "${shot}"; the shots are ${this.scene.shots.map((s) => `"${s.id}"`).join(", ") || "none yet"}.`);
4401
4913
  let resolved;
4402
4914
  try {
4403
- resolved = await _chunk2K5TELHVcjs.resolveCameraMoveSpec.call(void 0, this.effects.require(), spec, role);
4915
+ resolved = await _chunkJAOSL334cjs.resolveCameraMoveSpec.call(void 0, this.effects.require(), spec, role);
4404
4916
  } catch (error) {
4405
- if (_chunk2K5TELHVcjs.isEffectError.call(void 0, error) || error instanceof Error) return this.refuse(error.message);
4917
+ if (_chunkJAOSL334cjs.isEffectError.call(void 0, error) || error instanceof Error) return this.refuse(error.message);
4406
4918
  throw error;
4407
4919
  }
4408
- const field = _chunk2K5TELHVcjs.cameraMoveField.call(void 0, role);
4920
+ const field = _chunkJAOSL334cjs.cameraMoveField.call(void 0, role);
4409
4921
  const current = this.scene.shots.find((s) => s.id === shot);
4410
4922
  if (!current) return this.refuse(`Shot "${shot}" was removed while the move was checked.`);
4411
4923
  const parts = role === "transition" && resolved.spec.ms === void 0 ? { ...resolved.spec, ms: resolved.description.camera.durationMs } : resolved.spec;
@@ -4419,8 +4931,8 @@ var AnimationEditorController = (_class = class {
4419
4931
  clearShotCameraMove(shot, role) {
4420
4932
  const found = this.scene.shots.find((s) => s.id === shot);
4421
4933
  if (!found) return this.refuse(`There is no shot "${shot}"; the shots are ${this.scene.shots.map((s) => `"${s.id}"`).join(", ") || "none yet"}.`);
4422
- const field = _chunk2K5TELHVcjs.cameraMoveField.call(void 0, role);
4423
- if (_optionalChain([found, 'access', _185 => _185.camera, 'optionalAccess', _186 => _186[field]]) === void 0) return this.refuse(`Shot "${shot}" has no camera "${field}" move to clear${_optionalChain([found, 'access', _187 => _187.camera, 'optionalAccess', _188 => _188[field === "in" ? "during" : "in"]]) !== void 0 ? `; it has a "${field === "in" ? "during" : "in"}" move (role ${role === "hold" ? "transition" : "hold"})` : ""}.`);
4934
+ const field = _chunkJAOSL334cjs.cameraMoveField.call(void 0, role);
4935
+ if (_optionalChain([found, 'access', _213 => _213.camera, 'optionalAccess', _214 => _214[field]]) === void 0) return this.refuse(`Shot "${shot}" has no camera "${field}" move to clear${_optionalChain([found, 'access', _215 => _215.camera, 'optionalAccess', _216 => _216[field === "in" ? "during" : "in"]]) !== void 0 ? `; it has a "${field === "in" ? "during" : "in"}" move (role ${role === "hold" ? "transition" : "hold"})` : ""}.`);
4424
4936
  const rest = { ...found.camera };
4425
4937
  delete rest[field];
4426
4938
  const keep = motionCameraFramed(rest) || rest.in !== void 0 || rest.during !== void 0;
@@ -4430,7 +4942,7 @@ var AnimationEditorController = (_class = class {
4430
4942
  }
4431
4943
  cameraMoveResult(shot, role, move) {
4432
4944
  const timing = compileMotionScene(this.scene).shots.find((s) => s.id === shot);
4433
- const camera = _nullishCoalesce(_optionalChain([this, 'access', _189 => _189.scene, 'access', _190 => _190.shots, 'access', _191 => _191.find, 'call', _192 => _192((s) => s.id === shot), 'optionalAccess', _193 => _193.camera]), () => ( null));
4945
+ const camera = _nullishCoalesce(_optionalChain([this, 'access', _217 => _217.scene, 'access', _218 => _218.shots, 'access', _219 => _219.find, 'call', _220 => _220((s) => s.id === shot), 'optionalAccess', _221 => _221.camera]), () => ( null));
4434
4946
  const window = role === "transition" ? { start: timing.start, end: timing.commandsStart } : { start: timing.commandsEnd, end: timing.end };
4435
4947
  const notes = [];
4436
4948
  if (move && role === "transition" && timing.index === 0) notes.push('This is the first shot, which cuts to its framing, so its "in" move does not play; it plays from the second shot on.');
@@ -4501,7 +5013,7 @@ var AnimationEditorController = (_class = class {
4501
5013
  viewScene() {
4502
5014
  const r = this.recording;
4503
5015
  if (!r) return this.scene;
4504
- if (_optionalChain([r, 'access', _194 => _194.preview, 'optionalAccess', _195 => _195.count]) === r.samples.length) return r.preview.scene;
5016
+ if (_optionalChain([r, 'access', _222 => _222.preview, 'optionalAccess', _223 => _223.count]) === r.samples.length) return r.preview.scene;
4505
5017
  let scene = this.scene;
4506
5018
  if (r.samples.length) {
4507
5019
  try {
@@ -4517,9 +5029,151 @@ var AnimationEditorController = (_class = class {
4517
5029
  frame(time = this.player.currentTime, options = {}) {
4518
5030
  return motionFrameAt(this.viewScene(), time, { effects: this.effects.provider, ...options });
4519
5031
  }
4520
- /** The scene at `time` (default: the playhead) as SVG. */
5032
+ /**
5033
+ * The scene at `time` (default: the playhead) as SVG. Shot layer and transition pictures that
5034
+ * are not rendered yet are left out (a transition crossfades) and come in on a later frame;
5035
+ * `toSvgExact` waits for them.
5036
+ */
4521
5037
  toSvg(time = this.player.currentTime, options = {}) {
4522
- return motionStageSvg(this.viewScene(), time, { effects: this.effects.provider, ...options });
5038
+ return motionStageSvg(this.viewScene(), time, { effects: this.effects.provider, layers: this.layers, ...options });
5039
+ }
5040
+ /** Like `toSvg`, once every shot layer and transition picture of the frame is rendered (or has failed). */
5041
+ async toSvgExact(time = this.player.currentTime, options = {}) {
5042
+ let svg = this.toSvg(time, options);
5043
+ for (let pass = 0; pass < 8 && this.layers.busy; pass++) {
5044
+ await this.layers.idle();
5045
+ svg = this.toSvg(time, options);
5046
+ }
5047
+ return svg;
5048
+ }
5049
+ // ---------------------------------------------------------------------------------------
5050
+ // Shot layers and transitions
5051
+ // ---------------------------------------------------------------------------------------
5052
+ /** Every shot's layers and transition (or one shot's). Throws for an unknown shot, naming the shots. */
5053
+ shotLayers(shot) {
5054
+ if (shot !== void 0 && !this.scene.shots.some((s) => s.id === shot)) throw new Error(this.noShot(shot));
5055
+ return motionShotLayersInfo(this.scene, shot);
5056
+ }
5057
+ noShot(shot) {
5058
+ return `There is no shot "${shot}"; the shots are ${this.scene.shots.map((s) => `"${s.id}"`).join(", ") || "none yet"}.`;
5059
+ }
5060
+ noLayer(layer) {
5061
+ const ids = this.scene.shots.flatMap((s) => (_nullishCoalesce(s.layers, () => ( []))).map((l) => `"${l.id}"`));
5062
+ return `There is no layer "${layer}"; ${ids.length ? `the layers are ${ids.join(", ")}` : "the scene has no layers yet (add_shot_layer adds one)"}.`;
5063
+ }
5064
+ layerResult(shot, layerId, notes = []) {
5065
+ const info2 = motionShotLayersInfo(this.scene, shot)[0];
5066
+ const layer = layerId ? info2.layers.find((l) => l.id === layerId) : void 0;
5067
+ return { ok: true, ...info2, ...layer ? { layer } : {}, notes };
5068
+ }
5069
+ async layerEdit(work, merge) {
5070
+ const reason = this.readOnlyReason();
5071
+ if (reason) return this.refuse(reason);
5072
+ let plan;
5073
+ try {
5074
+ plan = await work();
5075
+ } catch (error) {
5076
+ if (_chunkJAOSL334cjs.isEffectError.call(void 0, error) || error instanceof Error) return this.refuse(error.message);
5077
+ throw error;
5078
+ }
5079
+ const id = this.scene.shots[plan.shot].id;
5080
+ const r = this.apply(plan.layers.length ? { op: "update_shot", shot: id, set: { layers: plan.layers } } : { op: "update_shot", shot: id, remove: ["layers"] }, plan.label, { merge });
5081
+ if (!r.ok) return r;
5082
+ return this.layerResult(id, plan.layer, plan.notes);
5083
+ }
5084
+ /**
5085
+ * Adds a layer to a shot: a provider effect (a generator, filter, overlay or title), optionally
5086
+ * applied to a `subject`, or a bare subject. Checked against the provider first; at `index`
5087
+ * (default the top). One undo step. Returns the layer (with its id) and the shot's layers.
5088
+ */
5089
+ addShotLayer(shot, layer, options = {}) {
5090
+ return this.layerEdit(async () => {
5091
+ const at = this.scene.shots.findIndex((s) => s.id === shot);
5092
+ if (at < 0) throw new Error(this.noShot(shot));
5093
+ const id = _nullishCoalesce(layer.id, () => ( freshMotionLayerId(this.scene)));
5094
+ if (findMotionShotLayer(this.scene, id)) throw new Error(`Layer id "${id}" is already used; choose another (e.g. "${freshMotionLayerId(this.scene)}").`);
5095
+ const resolved = await resolveMotionShotLayer(this.effects.provider, { ...layer, id });
5096
+ const layers = [..._nullishCoalesce(this.scene.shots[at].layers, () => ( []))];
5097
+ const index = _nullishCoalesce(options.index, () => ( layers.length));
5098
+ if (!Number.isInteger(index) || index < 0 || index > layers.length) throw new Error(`"index" must be a whole number from 0 to ${layers.length} (got ${index}).`);
5099
+ layers.splice(index, 0, resolved);
5100
+ const notes = resolved.subject && "slot" in resolved.subject ? [`The subject is slot "${resolved.subject.slot}", which draws nothing until it is filled; set "subject" with update_shot_layer.`] : [];
5101
+ return { shot: at, layers, label: `Add layer ${_nullishCoalesce(resolved.name, () => ( id))}`, layer: id, notes };
5102
+ });
5103
+ }
5104
+ /** Changes a layer's fields (`null` clears an optional one); the result is checked against the provider. One undo step (`merge` folds a slider drag into one). */
5105
+ updateShotLayer(layer, changes, options = {}) {
5106
+ return this.layerEdit(async () => {
5107
+ const found = findMotionShotLayer(this.scene, layer);
5108
+ if (!found) throw new Error(this.noLayer(layer));
5109
+ const was = this.scene.shots[found.shot].layers[found.index];
5110
+ const next = { ...was };
5111
+ for (const [k, v] of Object.entries(changes)) {
5112
+ if (v === null) delete next[k];
5113
+ else if (v !== void 0) next[k] = v;
5114
+ }
5115
+ if (changes.effect !== void 0 && changes.effect !== was.effect && changes.values === void 0) delete next.values;
5116
+ const resolved = await resolveMotionShotLayer(this.effects.provider, next);
5117
+ const layers = this.scene.shots[found.shot].layers.map((l, i) => i === found.index ? resolved : l);
5118
+ return { shot: found.shot, layers, label: `Change layer ${_nullishCoalesce(resolved.name, () => ( layer))}`, layer };
5119
+ }, options.merge);
5120
+ }
5121
+ /** Removes a layer. One undo step. Works without a provider. */
5122
+ removeShotLayer(layer) {
5123
+ return this.layerEdit(async () => {
5124
+ const found = findMotionShotLayer(this.scene, layer);
5125
+ if (!found) throw new Error(this.noLayer(layer));
5126
+ const layers = this.scene.shots[found.shot].layers.filter((_, i) => i !== found.index);
5127
+ return { shot: found.shot, layers, label: "Remove layer" };
5128
+ });
5129
+ }
5130
+ /** Moves a layer to `index` in its shot (0 = bottom). One undo step. */
5131
+ moveShotLayer(layer, index) {
5132
+ return this.layerEdit(async () => {
5133
+ const found = findMotionShotLayer(this.scene, layer);
5134
+ if (!found) throw new Error(this.noLayer(layer));
5135
+ const layers = [...this.scene.shots[found.shot].layers];
5136
+ if (!Number.isInteger(index) || index < 0 || index >= layers.length) throw new Error(`"index" must be a whole number from 0 to ${layers.length - 1} (got ${index}).`);
5137
+ const [moved] = layers.splice(found.index, 1);
5138
+ layers.splice(index, 0, moved);
5139
+ return { shot: found.shot, layers, label: "Move layer", layer };
5140
+ });
5141
+ }
5142
+ /**
5143
+ * Sets the provider transition into a shot from the shot before (not the first shot). Checked
5144
+ * against the provider (its use, preset and values; its length is written out). With no camera
5145
+ * move in, the camera cuts. One undo step (`merge` folds a slider drag into one).
5146
+ */
5147
+ async setShotTransition(shot, transition, options = {}) {
5148
+ const reason = this.readOnlyReason();
5149
+ if (reason) return this.refuse(reason);
5150
+ const at = this.scene.shots.findIndex((s) => s.id === shot);
5151
+ if (at < 0) return this.refuse(this.noShot(shot));
5152
+ if (at === 0) return this.refuse(`Shot "${shot}" is the first shot, so there is no shot before it to transition from; set the transition on a later shot${this.scene.shots[1] ? ` (e.g. "${this.scene.shots[1].id}")` : ""}.`);
5153
+ let resolved;
5154
+ try {
5155
+ const { provider: _p, name: _n, ...given } = transition;
5156
+ resolved = await resolveMotionShotTransition(this.effects.provider, given);
5157
+ } catch (error) {
5158
+ if (_chunkJAOSL334cjs.isEffectError.call(void 0, error) || error instanceof Error) return this.refuse(error.message);
5159
+ throw error;
5160
+ }
5161
+ if (!this.scene.shots.some((s) => s.id === shot)) return this.refuse(`Shot "${shot}" was removed while the transition was checked.`);
5162
+ const r = this.apply({ op: "update_shot", shot, set: { transition: resolved } }, `Transition ${_nullishCoalesce(resolved.name, () => ( resolved.effect))}`, { merge: options.merge });
5163
+ if (!r.ok) return r;
5164
+ const camera = this.scene.shots.find((s) => s.id === shot).camera;
5165
+ return this.layerResult(shot, void 0, _optionalChain([camera, 'optionalAccess', _224 => _224.in]) === void 0 ? ["With no camera move in, the camera cuts to this shot's framing and the transition does the move."] : []);
5166
+ }
5167
+ /** Removes a shot's transition. One undo step. Works without a provider. */
5168
+ clearShotTransition(shot) {
5169
+ const reason = this.readOnlyReason();
5170
+ if (reason) return this.refuse(reason);
5171
+ const found = this.scene.shots.find((s) => s.id === shot);
5172
+ if (!found) return this.refuse(this.noShot(shot));
5173
+ if (!found.transition) return this.refuse(`Shot "${shot}" has no transition to clear.`);
5174
+ const r = this.apply({ op: "update_shot", shot, remove: ["transition"] }, "Clear transition");
5175
+ if (!r.ok) return r;
5176
+ return this.layerResult(shot, void 0);
4523
5177
  }
4524
5178
  // ---------------------------------------------------------------------------------------
4525
5179
  // Recording
@@ -4568,8 +5222,8 @@ var AnimationEditorController = (_class = class {
4568
5222
  return motionNodePose(this.viewScene(), node, time);
4569
5223
  }
4570
5224
  /** A point on a stage element (client pixels, e.g. a pointer event) in document units, through the camera now. */
4571
- clientToDocument(box, clientX, clientY) {
4572
- return motionClientToDocument(this.frame().camera, box, clientX, clientY);
5225
+ clientToDocument(box2, clientX, clientY) {
5226
+ return motionClientToDocument(this.frame().camera, box2, clientX, clientY);
4573
5227
  }
4574
5228
  /**
4575
5229
  * Starts a stage drag while recording: from `point` (document units), `move` drags the node,
@@ -4586,13 +5240,13 @@ var AnimationEditorController = (_class = class {
4586
5240
  }
4587
5241
  /** Records the drag's pose with the pointer at `point` (document units). */
4588
5242
  updateRecordGesture(point) {
4589
- const g = _optionalChain([this, 'access', _196 => _196.recording, 'optionalAccess', _197 => _197.gesture]);
5243
+ const g = _optionalChain([this, 'access', _225 => _225.recording, 'optionalAccess', _226 => _226.gesture]);
4590
5244
  if (g) this.recordGesture(g.pose, point);
4591
5245
  }
4592
5246
  /** Ends the stage drag (the take keeps recording). */
4593
5247
  endRecordGesture() {
4594
5248
  const r = this.recording;
4595
- if (!_optionalChain([r, 'optionalAccess', _198 => _198.gesture])) return;
5249
+ if (!_optionalChain([r, 'optionalAccess', _227 => _227.gesture])) return;
4596
5250
  r.gesture = null;
4597
5251
  this.notify();
4598
5252
  }
@@ -4606,7 +5260,7 @@ var AnimationEditorController = (_class = class {
4606
5260
  this.recordSample(g.node, { scaleX: Math.max(0.01, pose.scaleX * f), scaleY: Math.max(0.01, pose.scaleY * f) });
4607
5261
  } else {
4608
5262
  const turn = (Math.atan2(point.y - c.y, point.x - c.x) - Math.atan2(g.from.y - c.y, g.from.x - c.x)) * (180 / Math.PI);
4609
- const was = _nullishCoalesce(_optionalChain([this, 'access', _199 => _199.recording, 'access', _200 => _200.samples, 'access', _201 => _201.filter, 'call', _202 => _202((s) => s.node === g.node && s.rotation !== void 0), 'access', _203 => _203.at, 'call', _204 => _204(-1), 'optionalAccess', _205 => _205.rotation]), () => ( pose.rotation));
5263
+ const was = _nullishCoalesce(_optionalChain([this, 'access', _228 => _228.recording, 'access', _229 => _229.samples, 'access', _230 => _230.filter, 'call', _231 => _231((s) => s.node === g.node && s.rotation !== void 0), 'access', _232 => _232.at, 'call', _233 => _233(-1), 'optionalAccess', _234 => _234.rotation]), () => ( pose.rotation));
4610
5264
  let next = pose.rotation + turn;
4611
5265
  next += 360 * Math.round((was - next) / 360);
4612
5266
  this.recordSample(g.node, { rotation: next });
@@ -4633,9 +5287,9 @@ var AnimationEditorController = (_class = class {
4633
5287
  this.player.seek(r.start);
4634
5288
  return this.refuse("Nothing was recorded, so no shot was added: drag an element on the stage (or change its paint) while recording.");
4635
5289
  }
4636
- const result = this.recordTake({ samples: r.samples, length }, r.options);
5290
+ const result2 = this.recordTake({ samples: r.samples, length }, r.options);
4637
5291
  this.player.seek(r.start);
4638
- return result;
5292
+ return result2;
4639
5293
  }
4640
5294
  /** Stops recording and throws the take away. */
4641
5295
  cancelRecording() {
@@ -4655,10 +5309,10 @@ var AnimationEditorController = (_class = class {
4655
5309
  } catch (error) {
4656
5310
  return options.dryRun ? { ok: false, error: error.message } : this.refuse(error.message);
4657
5311
  }
4658
- const result = this.apply({ op: "insert_shots", shots: [made.shot], at: made.at }, "Record take", { dryRun: options.dryRun });
4659
- if (!result.ok) return result;
5312
+ const result2 = this.apply({ op: "insert_shots", shots: [made.shot], at: made.at }, "Record take", { dryRun: options.dryRun });
5313
+ if (!result2.ok) return result2;
4660
5314
  if (!options.dryRun) this.select({ shot: made.shot.id });
4661
- return { ok: true, ...made, issues: result.issues };
5315
+ return { ok: true, ...made, issues: result2.issues };
4662
5316
  }
4663
5317
  /** The scene as a Flair `SvgMotion`. */
4664
5318
  toFlairMotion(options = {}) {
@@ -4672,7 +5326,7 @@ var AnimationEditorController = (_class = class {
4672
5326
  toFlairDocument(options = {}) {
4673
5327
  return toFlairEffectDocument(this.scene, { effects: this.effects.provider, ...options });
4674
5328
  }
4675
- }, _class);
5329
+ }, _class2);
4676
5330
 
4677
5331
  // src/animation-editor/playback.ts
4678
5332
  var str = (v) => typeof v === "string" && v.length > 0;
@@ -4700,7 +5354,7 @@ function elementsOf(args) {
4700
5354
  if (Array.isArray(args.ops)) {
4701
5355
  for (const op of args.ops) {
4702
5356
  fromShot(op);
4703
- if (Array.isArray(_optionalChain([op, 'optionalAccess', _206 => _206.shots]))) op.shots.forEach(fromShot);
5357
+ if (Array.isArray(_optionalChain([op, 'optionalAccess', _235 => _235.shots]))) op.shots.forEach(fromShot);
4704
5358
  }
4705
5359
  }
4706
5360
  return out.slice(0, 4);
@@ -4882,7 +5536,7 @@ var MOTION_COMMAND_SCHEMA = {
4882
5536
  oneOf: MOTION_COMMAND_TYPES.map((type) => MOTION_COMMAND_TYPE_SCHEMAS[type])
4883
5537
  };
4884
5538
  var REF2 = (name, description) => ({ $ref: `#/$defs/${name}`, ...description ? { description } : {} });
4885
- var firstSentence = (text) => _nullishCoalesce(_optionalChain([/^.*?[.;](?=\s|$)/, 'access', _207 => _207.exec, 'call', _208 => _208(text), 'optionalAccess', _209 => _209[0]]), () => ( text));
5539
+ var firstSentence = (text) => _nullishCoalesce(_optionalChain([/^.*?[.;](?=\s|$)/, 'access', _236 => _236.exec, 'call', _237 => _237(text), 'optionalAccess', _238 => _238[0]]), () => ( text));
4886
5540
  var COMMAND_TYPE_LINES = MOTION_COMMAND_TYPES.map((type) => `${type}: ${firstSentence(_nullishCoalesce(MOTION_COMMAND_TYPE_SCHEMAS[type].description, () => ( "")))}`);
4887
5541
  var EASE_COMPACT = {
4888
5542
  description: `Easing: a preset, or a curve { "type": "cubic-bezier", x1, y1, x2, y2 }, { "type": "spring", stiffness, damping, mass, velocity } (spring presets are Figma's; with no "duration" it lasts until it settles) or { "type": "elastic", amplitude, period, mode }. Default: ease-out for entrances, ease-in-out for moves.`,
@@ -5019,7 +5673,7 @@ function withDefs(schema) {
5019
5673
  };
5020
5674
  visit(schema);
5021
5675
  if (!used.size) return schema;
5022
- return { ...schema, $defs: Object.fromEntries(Object.keys(DEFS).filter((n) => used.has(n)).map((n) => [n, DEFS[n]])) };
5676
+ return { ...schema, $defs: Object.fromEntries(Object.keys(DEFS).filter((n2) => used.has(n2)).map((n2) => [n2, DEFS[n2]])) };
5023
5677
  }
5024
5678
  var SETTINGS = {
5025
5679
  name: { type: "string", minLength: 1, description: 'Short name, e.g. "logo-reveal".' },
@@ -5084,7 +5738,7 @@ function describeElement(node, ancestors) {
5084
5738
  ...effects.length ? { effects } : {}
5085
5739
  };
5086
5740
  }
5087
- var round3 = (n) => Math.round(n * 10) / 10;
5741
+ var round3 = (n2) => Math.round(n2 * 10) / 10;
5088
5742
  function describeMotionElements(scene) {
5089
5743
  return [...indexMotionNodes(scene.artwork.nodes).values()].map((e) => ({
5090
5744
  ...describeElement(e.node, e.ancestors),
@@ -5116,7 +5770,7 @@ var issueSummary = (issues) => ({
5116
5770
  errors: issues.filter((i) => i.severity === "error").length,
5117
5771
  warnings: issues.filter((i) => i.severity === "warning").length,
5118
5772
  notes: issues.filter((i) => i.severity === "note").length,
5119
- lines: issues.map((i) => `${i.severity}: ${_chunkERJA5CGTcjs.motionIssueLine.call(void 0, i)}`)
5773
+ lines: issues.map((i) => `${i.severity}: ${_chunkDGWOGQJ5cjs.motionIssueLine.call(void 0, i)}`)
5120
5774
  });
5121
5775
  function createAnimationEditorToolProvider(controller, options = {}) {
5122
5776
  const readOnlyReason = () => {
@@ -5125,20 +5779,20 @@ function createAnimationEditorToolProvider(controller, options = {}) {
5125
5779
  return controller.readOnlyReason();
5126
5780
  };
5127
5781
  const scene = () => controller.getScene();
5128
- const outcome = (result, extra = {}) => {
5129
- if (!result.ok) throw new Error(result.error);
5130
- const timeline = compileMotionScene(result.scene);
5782
+ const outcome = (result2, extra = {}) => {
5783
+ if (!result2.ok) throw new Error(result2.error);
5784
+ const timeline = compileMotionScene(result2.scene);
5131
5785
  return {
5132
5786
  ...extra,
5133
5787
  duration: timeline.duration,
5134
5788
  shots: timeline.shots.map((s) => ({ id: s.id, start: s.start, end: s.end })),
5135
- issues: issueSummary(result.issues),
5136
- ...result.hints.length ? { hints: result.hints } : {}
5789
+ issues: issueSummary(result2.issues),
5790
+ ...result2.hints.length ? { hints: result2.hints } : {}
5137
5791
  };
5138
5792
  };
5139
5793
  const apply = (edits, label, extra) => {
5140
- const result = controller.apply(edits, label);
5141
- return outcome(result, result.ok && extra ? extra(result.scene) : {});
5794
+ const result2 = controller.apply(edits, label);
5795
+ return outcome(result2, result2.ok && extra ? extra(result2.scene) : {});
5142
5796
  };
5143
5797
  const getAnimation = {
5144
5798
  name: "get_animation",
@@ -5180,7 +5834,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
5180
5834
  annotations: { title: "Describe command types", ...READ },
5181
5835
  handler: ({ types }) => {
5182
5836
  for (const type of types)
5183
- if (!MOTION_COMMAND_TYPES.includes(type)) throw new Error(`There is no command type ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, type, MOTION_COMMAND_TYPES)}; use one of ${MOTION_COMMAND_TYPES.join(", ")}.`);
5837
+ if (!MOTION_COMMAND_TYPES.includes(type)) throw new Error(`There is no command type ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, type, MOTION_COMMAND_TYPES)}; use one of ${MOTION_COMMAND_TYPES.join(", ")}.`);
5184
5838
  return { commands: types.map((type) => ({ type, schema: MOTION_COMMAND_TYPE_SCHEMAS[type] })) };
5185
5839
  }
5186
5840
  };
@@ -5253,7 +5907,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
5253
5907
  }
5254
5908
  if (Array.isArray(motion_paths)) {
5255
5909
  const known = new Set(describeMotionElements(s).map((e) => e.id));
5256
- for (const id of motion_paths) if (!known.has(id)) throw new Error(`There is no element ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, id, known)} to draw a motion path for.`);
5910
+ for (const id of motion_paths) if (!known.has(id)) throw new Error(`There is no element ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, id, known)} to draw a motion path for.`);
5257
5911
  }
5258
5912
  const overlays = { ...onion_skin ? { onionSkin: onion_skin } : {}, ...motion_paths ? { motionPaths: motion_paths } : {} };
5259
5913
  if (times) {
@@ -5266,7 +5920,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
5266
5920
  }
5267
5921
  return { times, svg: sheet };
5268
5922
  }
5269
- const svg = controller.toSvg(t, { pretty: false, ...overlays });
5923
+ const svg = await controller.toSvgExact(t, { pretty: false, ...overlays });
5270
5924
  if (format === "png") {
5271
5925
  if (!options.rasterize) throw new Error('This host cannot make PNG images; call render_animation_frame with format "svg".');
5272
5926
  const image = await options.rasterize(svg, { width: s.artwork.width, height: s.artwork.height });
@@ -5291,9 +5945,9 @@ function createAnimationEditorToolProvider(controller, options = {}) {
5291
5945
  handler: ({ from = 0, to, limit = 200 }) => {
5292
5946
  const s = scene();
5293
5947
  const timeline = compileMotionScene(s);
5294
- const words = _nullishCoalesce(_optionalChain([s, 'access', _210 => _210.narration, 'optionalAccess', _211 => _211.words]), () => ( []));
5948
+ const words = _nullishCoalesce(_optionalChain([s, 'access', _239 => _239.narration, 'optionalAccess', _240 => _240.words]), () => ( []));
5295
5949
  const end = _nullishCoalesce(to, () => ( Math.max(timeline.duration, words.length ? _nullishCoalesce(words[words.length - 1].end, () => ( words[words.length - 1].start)) : 0)));
5296
- const shotAt = (ms2) => _optionalChain([timeline, 'access', _212 => _212.shots, 'access', _213 => _213.find, 'call', _214 => _214((x) => ms2 >= x.start && ms2 < x.end), 'optionalAccess', _215 => _215.id]);
5950
+ const shotAt = (ms2) => _optionalChain([timeline, 'access', _241 => _241.shots, 'access', _242 => _242.find, 'call', _243 => _243((x) => ms2 >= x.start && ms2 < x.end), 'optionalAccess', _244 => _244.id]);
5297
5951
  const beats = [];
5298
5952
  if (s.beat && s.beat.bpm > 0) {
5299
5953
  const first = Math.max(0, Math.ceil(motionBeatAt(s.beat, from).beat - 1e-9));
@@ -5323,10 +5977,10 @@ function createAnimationEditorToolProvider(controller, options = {}) {
5323
5977
  handler: ({ group }) => {
5324
5978
  const s = scene();
5325
5979
  const nodes = indexMotionNodes(s.artwork.nodes);
5326
- if (!nodes.has(group)) throw new Error(`There is no element ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, group, [...nodes.keys()])}.`);
5980
+ if (!nodes.has(group)) throw new Error(`There is no element ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, group, [...nodes.keys()])}.`);
5327
5981
  const layout = motionTextLayout(nodes, group);
5328
5982
  if (!layout) throw new Error(`"${group}" is not a group of paths; convert the text to outlines (one path a letter) and group it.`);
5329
- return { group, letters: layout.lines.reduce((n, l) => n + l.words.reduce((m, w) => m + w.letters.length, 0), 0), ...layout };
5983
+ return { group, letters: layout.lines.reduce((n2, l) => n2 + l.words.reduce((m, w) => m + w.letters.length, 0), 0), ...layout };
5330
5984
  }
5331
5985
  };
5332
5986
  const getMotionPath = {
@@ -5343,7 +5997,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
5343
5997
  handler: ({ node, from, to, step }) => {
5344
5998
  const s = scene();
5345
5999
  const ids = describeMotionElements(s).map((e) => e.id);
5346
- if (!ids.includes(node)) throw new Error(`There is no element ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, node, ids)}.`);
6000
+ if (!ids.includes(node)) throw new Error(`There is no element ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, node, ids)}.`);
5347
6001
  return motionPath(s, node, { from, to, step });
5348
6002
  }
5349
6003
  };
@@ -5415,17 +6069,17 @@ function createAnimationEditorToolProvider(controller, options = {}) {
5415
6069
  ),
5416
6070
  annotations: { title: "Import a PowerPoint slide's animation", ...DESTRUCTIVE },
5417
6071
  handler: async ({ data, slide, dry_run = false }) => {
5418
- let result;
6072
+ let result2;
5419
6073
  try {
5420
6074
  const bytes = Uint8Array.from(atob(data.replace(/\s+/g, "")), (c) => c.charCodeAt(0));
5421
- result = await fromPptxAnimation(bytes, slide ? { slide } : {});
6075
+ result2 = await fromPptxAnimation(bytes, slide ? { slide } : {});
5422
6076
  } catch (error) {
5423
6077
  throw new Error(`The slide could not be imported: ${error.message} Nothing was changed.`);
5424
6078
  }
5425
- const report = { slides: result.slides, notes: result.notes, issues: issueSummary(result.issues), shots: describeMotionShots(result.scene).map(({ id, start, end, commands }) => ({ id, start, end, commands: commands.length })) };
6079
+ const report = { slides: result2.slides, notes: result2.notes, issues: issueSummary(result2.issues), shots: describeMotionShots(result2.scene).map(({ id, start, end, commands }) => ({ id, start, end, commands: commands.length })) };
5426
6080
  if (dry_run) return { dry_run: true, ...report, hint: "Nothing was replaced; call again without dry_run to import." };
5427
- const applied = controller.apply({ op: "replace_scene", scene: result.scene }, "Import PowerPoint slide");
5428
- return outcome(applied, applied.ok ? { ...report, elements: describeMotionElements(result.scene) } : {});
6081
+ const applied = controller.apply({ op: "replace_scene", scene: result2.scene }, "Import PowerPoint slide");
6082
+ return outcome(applied, applied.ok ? { ...report, elements: describeMotionElements(result2.scene) } : {});
5429
6083
  }
5430
6084
  };
5431
6085
  const importFlairEffect = {
@@ -5444,16 +6098,16 @@ function createAnimationEditorToolProvider(controller, options = {}) {
5444
6098
  annotations: { title: "Import a Flair effect", ...DESTRUCTIVE },
5445
6099
  handler: ({ effect, motion, name, dry_run = false }) => {
5446
6100
  if (effect === void 0 === (motion === void 0)) throw new Error('Give exactly one of "effect" (a Flair vector effect) or "motion" (a Flair SvgMotion). Nothing was changed.');
5447
- let result;
6101
+ let result2;
5448
6102
  try {
5449
- result = effect !== void 0 ? fromFlairVectorEffect(effect, name ? { name } : {}) : fromFlairSvgMotion(motion, name ? { name } : {});
6103
+ result2 = effect !== void 0 ? fromFlairVectorEffect(effect, name ? { name } : {}) : fromFlairSvgMotion(motion, name ? { name } : {});
5450
6104
  } catch (error) {
5451
6105
  throw new Error(`The Flair effect could not be imported: ${error.message} Nothing was changed.`);
5452
6106
  }
5453
- const report = { notes: result.notes, issues: issueSummary(result.issues), shots: describeMotionShots(result.scene).map(({ id, start, end, commands }) => ({ id, start, end, commands: commands.length })) };
6107
+ const report = { notes: result2.notes, issues: issueSummary(result2.issues), shots: describeMotionShots(result2.scene).map(({ id, start, end, commands }) => ({ id, start, end, commands: commands.length })) };
5454
6108
  if (dry_run) return { dry_run: true, ...report, hint: "Nothing was replaced; call again without dry_run to import." };
5455
- const applied = controller.apply({ op: "replace_scene", scene: result.scene }, "Import Flair effect");
5456
- return outcome(applied, applied.ok ? { ...report, elements: describeMotionElements(result.scene) } : {});
6109
+ const applied = controller.apply({ op: "replace_scene", scene: result2.scene }, "Import Flair effect");
6110
+ return outcome(applied, applied.ok ? { ...report, elements: describeMotionElements(result2.scene) } : {});
5457
6111
  }
5458
6112
  };
5459
6113
  const fitAnimation = {
@@ -5516,18 +6170,18 @@ function createAnimationEditorToolProvider(controller, options = {}) {
5516
6170
  ),
5517
6171
  annotations: { title: "Record a performed take", ...WRITE },
5518
6172
  handler: ({ samples, length, id, label, at, tolerance, dry_run = false }) => {
5519
- const result = controller.recordTake({ samples, length }, { id, label, at, tolerance, dryRun: dry_run });
5520
- if (!result.ok) throw new Error(result.error);
6173
+ const result2 = controller.recordTake({ samples, length }, { id, label, at, tolerance, dryRun: dry_run });
6174
+ if (!result2.ok) throw new Error(result2.error);
5521
6175
  const s = dry_run ? scene() : controller.getScene();
5522
- const report = { shot: result.shot.id, at: result.at, start: Math.round(result.start), samples: result.samples };
5523
- if (dry_run) return { dry_run: true, ...report, run: result.shot.run, hold: result.shot.hold, hint: "Nothing was changed; call again without dry_run to add the shot." };
6176
+ const report = { shot: result2.shot.id, at: result2.at, start: Math.round(result2.start), samples: result2.samples };
6177
+ if (dry_run) return { dry_run: true, ...report, run: result2.shot.run, hold: result2.shot.hold, hint: "Nothing was changed; call again without dry_run to add the shot." };
5524
6178
  const timeline = compileMotionScene(s);
5525
- const described = describeMotionShots(s).find((x) => x.id === result.shot.id);
6179
+ const described = describeMotionShots(s).find((x) => x.id === result2.shot.id);
5526
6180
  return {
5527
6181
  ...report,
5528
6182
  commands: described.commands.map((c) => ({ index: c.index, type: c.type, targets: "targets" in c ? c.targets : [], start: c.start, end: c.end })),
5529
6183
  duration: timeline.duration,
5530
- issues: issueSummary(result.issues)
6184
+ issues: issueSummary(result2.issues)
5531
6185
  };
5532
6186
  }
5533
6187
  };
@@ -5595,27 +6249,27 @@ function createAnimationEditorToolProvider(controller, options = {}) {
5595
6249
  handler: ({ from, to, shots, stagger }) => {
5596
6250
  const s = scene();
5597
6251
  const ids = describeMotionElements(s).map((e) => e.id);
5598
- for (const id of [from, ...to]) if (!ids.includes(id)) throw new Error(`There is no element ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, id, ids)}. Nothing was changed.`);
6252
+ for (const id of [from, ...to]) if (!ids.includes(id)) throw new Error(`There is no element ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, id, ids)}. Nothing was changed.`);
5599
6253
  if (to.includes(from)) throw new Error(`"to" includes "${from}" itself; give other elements. Nothing was changed.`);
5600
6254
  const only = shots ? new Set(shots) : null;
5601
6255
  if (only) {
5602
- for (const id of only) if (!s.shots.some((x) => x.id === id)) throw new Error(`There is no shot ${_chunkERJA5CGTcjs.namedWithSuggestion.call(void 0, id, s.shots.map((x) => x.id))}. Nothing was changed.`);
6256
+ for (const id of only) if (!s.shots.some((x) => x.id === id)) throw new Error(`There is no shot ${_chunkDGWOGQJ5cjs.namedWithSuggestion.call(void 0, id, s.shots.map((x) => x.id))}. Nothing was changed.`);
5603
6257
  }
5604
6258
  const copied = {};
5605
6259
  const edits = [];
5606
6260
  for (const shot of s.shots) {
5607
6261
  if (only && !only.has(shot.id)) continue;
5608
6262
  const run = [];
5609
- let n = 0;
6263
+ let n2 = 0;
5610
6264
  for (const c of shot.run) {
5611
6265
  run.push(c);
5612
6266
  if (c.type === "wait" || !c.targets.includes(from)) continue;
5613
6267
  const { delay: _delay, ...rest } = c;
5614
6268
  run.push({ ...rest, targets: to, with: "previous", ...stagger ? { stagger } : {} });
5615
- n++;
6269
+ n2++;
5616
6270
  }
5617
- if (n) {
5618
- copied[shot.id] = n;
6271
+ if (n2) {
6272
+ copied[shot.id] = n2;
5619
6273
  edits.push({ op: "update_shot", shot: shot.id, set: { run } });
5620
6274
  }
5621
6275
  }
@@ -5669,7 +6323,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
5669
6323
  inputSchema: object({ shot: SHOT_ID, commands: { type: "array", items: REF2("command"), minItems: 1, description: "Commands to add." }, at: { type: "integer", minimum: 0, description: "Index in the run (default: the end)." } }, ["shot", "commands"]),
5670
6324
  annotations: { title: "Add commands to a shot", ...WRITE },
5671
6325
  handler: ({ shot, commands, at }) => {
5672
- const before = _nullishCoalesce(_optionalChain([scene, 'call', _216 => _216(), 'access', _217 => _217.shots, 'access', _218 => _218.find, 'call', _219 => _219((s) => s.id === shot), 'optionalAccess', _220 => _220.run, 'access', _221 => _221.length]), () => ( 0));
6326
+ const before = _nullishCoalesce(_optionalChain([scene, 'call', _245 => _245(), 'access', _246 => _246.shots, 'access', _247 => _247.find, 'call', _248 => _248((s) => s.id === shot), 'optionalAccess', _249 => _249.run, 'access', _250 => _250.length]), () => ( 0));
5673
6327
  const first = _nullishCoalesce(at, () => ( before));
5674
6328
  return apply({ op: "add_commands", shot, commands, at }, "Add commands", (s) => {
5675
6329
  const shots = describeMotionShots(s);
@@ -5742,8 +6396,8 @@ function createAnimationEditorToolProvider(controller, options = {}) {
5742
6396
  handler: ({ ops, dry_run = false }) => {
5743
6397
  const refs = resolveMotionEditRefs(controller.getScene(), ops);
5744
6398
  if (!refs.ok) throw new Error(refs.error);
5745
- const result = controller.apply(refs.edits, "Edit animation", { dryRun: dry_run });
5746
- return outcome(result, { outcomes: refs.outcomes, ...dry_run ? { dry_run: true, hint: "Nothing was applied; call again without dry_run to apply." } : {} });
6399
+ const result2 = controller.apply(refs.edits, "Edit animation", { dryRun: dry_run });
6400
+ return outcome(result2, { outcomes: refs.outcomes, ...dry_run ? { dry_run: true, hint: "Nothing was applied; call again without dry_run to apply." } : {} });
5747
6401
  }
5748
6402
  };
5749
6403
  const writeAnimation = {
@@ -5870,7 +6524,7 @@ var TIMING = /* @__PURE__ */ new Set(["duration", "delay", "stagger", "with", "e
5870
6524
  var COLORS = /* @__PURE__ */ new Set(["fill", "stroke", "color"]);
5871
6525
  var NODES = /* @__PURE__ */ new Set(["into", "path"]);
5872
6526
  function branch(type) {
5873
- return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access', _222 => _222.properties, 'optionalAccess', _223 => _223.type, 'optionalAccess', _224 => _224.const]) === type);
6527
+ return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access', _251 => _251.properties, 'optionalAccess', _252 => _252.type, 'optionalAccess', _253 => _253.const]) === type);
5874
6528
  }
5875
6529
  function unitOf(type, name) {
5876
6530
  if (name === "duration" || name === "delay" || name === "stagger") return "ms";
@@ -5905,9 +6559,9 @@ function kindOf(name, schema) {
5905
6559
  }
5906
6560
  function motionCommandFields(type) {
5907
6561
  const schema = branch(type);
5908
- if (!_optionalChain([schema, 'optionalAccess', _225 => _225.properties])) return [];
6562
+ if (!_optionalChain([schema, 'optionalAccess', _254 => _254.properties])) return [];
5909
6563
  const required = new Set(_nullishCoalesce(schema.required, () => ( [])));
5910
- const fields = Object.entries(schema.properties).filter(([name]) => name !== "type").map(([name, s]) => {
6564
+ const fields2 = Object.entries(schema.properties).filter(([name]) => name !== "type").map(([name, s]) => {
5911
6565
  const kind = kindOf(name, s);
5912
6566
  const min = typeof s.minimum === "number" ? s.minimum : typeof s.exclusiveMinimum === "number" ? s.exclusiveMinimum : void 0;
5913
6567
  return {
@@ -5923,7 +6577,7 @@ function motionCommandFields(type) {
5923
6577
  group: TIMING.has(name) ? "timing" : "main"
5924
6578
  };
5925
6579
  });
5926
- return [...fields.filter((f) => f.group === "main"), ...fields.filter((f) => f.group === "timing")];
6580
+ return [...fields2.filter((f) => f.group === "main"), ...fields2.filter((f) => f.group === "timing")];
5927
6581
  }
5928
6582
  function parseMotionFieldInput(field, raw) {
5929
6583
  if (field.kind === "boolean") return raw === true ? { set: true } : { remove: true };
@@ -5942,12 +6596,12 @@ function parseMotionFieldInput(field, raw) {
5942
6596
  const text = String(raw).trim();
5943
6597
  if (text === "") return field.required ? { error: `${field.label} is required.` } : { remove: true };
5944
6598
  if (field.kind === "number" || field.kind === "integer" || field.kind === "scale") {
5945
- const n = Number(text);
5946
- if (!Number.isFinite(n)) return { error: `${field.label} must be a number${field.unit ? ` (${field.unit})` : ""}.` };
5947
- if (field.kind === "integer" && !Number.isInteger(n)) return { error: `${field.label} must be a whole number.` };
5948
- if (field.min !== void 0 && n < field.min) return { error: `${field.label} must be at least ${field.min}.` };
5949
- if (field.max !== void 0 && n > field.max) return { error: `${field.label} must be at most ${field.max}.` };
5950
- return { set: n };
6599
+ const n2 = Number(text);
6600
+ if (!Number.isFinite(n2)) return { error: `${field.label} must be a number${field.unit ? ` (${field.unit})` : ""}.` };
6601
+ if (field.kind === "integer" && !Number.isInteger(n2)) return { error: `${field.label} must be a whole number.` };
6602
+ if (field.min !== void 0 && n2 < field.min) return { error: `${field.label} must be at least ${field.min}.` };
6603
+ if (field.max !== void 0 && n2 > field.max) return { error: `${field.label} must be at most ${field.max}.` };
6604
+ return { set: n2 };
5951
6605
  }
5952
6606
  if (field.kind === "enum" && field.options && !field.options.includes(text)) return { error: `${field.label} must be one of ${field.options.join(", ")}.` };
5953
6607
  return { set: text };
@@ -5962,7 +6616,7 @@ function defaultMotionCommand(type, targets, scene) {
5962
6616
  if (type === "wait") return { type: "wait", duration: 500 };
5963
6617
  if (!targets.length) return { error: "Pick a target first: click an element on the stage." };
5964
6618
  const nodes = [...indexMotionNodes(scene.artwork.nodes).values()];
5965
- const other = (pred) => _optionalChain([nodes, 'access', _226 => _226.find, 'call', _227 => _227((e) => !targets.includes(e.node.id) && pred(e)), 'optionalAccess', _228 => _228.node, 'access', _229 => _229.id]);
6619
+ const other = (pred) => _optionalChain([nodes, 'access', _255 => _255.find, 'call', _256 => _256((e) => !targets.includes(e.node.id) && pred(e)), 'optionalAccess', _257 => _257.node, 'access', _258 => _258.id]);
5966
6620
  const base = { targets: [...targets] };
5967
6621
  switch (type) {
5968
6622
  case "scale":
@@ -5993,8 +6647,8 @@ function defaultMotionCommand(type, targets, scene) {
5993
6647
  }
5994
6648
  case "effect": {
5995
6649
  for (const id of targets) {
5996
- const node = _optionalChain([nodes, 'access', _230 => _230.find, 'call', _231 => _231((e) => e.node.id === id), 'optionalAccess', _232 => _232.node]);
5997
- for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess', _233 => _233.effects]), () => ( []))) {
6650
+ const node = _optionalChain([nodes, 'access', _259 => _259.find, 'call', _260 => _260((e) => e.node.id === id), 'optionalAccess', _261 => _261.node]);
6651
+ for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess', _262 => _262.effects]), () => ( []))) {
5998
6652
  const param = Object.entries(effect.params).find(([, v]) => typeof v === "number");
5999
6653
  if (param) return { type, ...base, effect: effect.id, param: param[0], to: param[1] + 1 };
6000
6654
  }
@@ -6019,11 +6673,11 @@ function motionCommandRows(scene, shotId) {
6019
6673
  return scene.shots[index].run.map((command3, i) => {
6020
6674
  const t = timeline.commands.find((c) => c.shotIndex === index && c.command === i);
6021
6675
  const primitive = motionFlairPrimitive(command3);
6022
- return { index: i, command: command3, summary: motionCommandSummary(command3), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _234 => _234.start]), () => ( 0)), end: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _235 => _235.end]), () => ( 0)), ...primitive ? { flairPrimitive: primitive } : {} };
6676
+ return { index: i, command: command3, summary: motionCommandSummary(command3), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _263 => _263.start]), () => ( 0)), end: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _264 => _264.end]), () => ( 0)), ...primitive ? { flairPrimitive: primitive } : {} };
6023
6677
  });
6024
6678
  }
6025
6679
  function duplicateMotionCommandEdit(scene, shot, command3) {
6026
- const run = _nullishCoalesce(_optionalChain([scene, 'access', _236 => _236.shots, 'access', _237 => _237.find, 'call', _238 => _238((s) => s.id === shot), 'optionalAccess', _239 => _239.run]), () => ( []));
6680
+ const run = _nullishCoalesce(_optionalChain([scene, 'access', _265 => _265.shots, 'access', _266 => _266.find, 'call', _267 => _267((s) => s.id === shot), 'optionalAccess', _268 => _268.run]), () => ( []));
6027
6681
  return { op: "add_commands", shot, commands: run[command3] ? [structuredClone(run[command3])] : [], at: command3 + 1 };
6028
6682
  }
6029
6683
  var MOTION_EASE_CHOICES = [
@@ -6037,18 +6691,18 @@ function motionEaseKey(ease) {
6037
6691
  function motionEasingCurve(ease, samples = 48, durationMs = 1e3) {
6038
6692
  const resolved = _chunkDWJJI6RTcjs.resolveEasing.call(void 0, ease);
6039
6693
  const ms2 = resolved !== "hold" && resolved.type === "spring" ? _chunkDWJJI6RTcjs.springSettleMs.call(void 0, resolved) : durationMs;
6040
- const n = Math.max(2, Math.floor(samples));
6694
+ const n2 = Math.max(2, Math.floor(samples));
6041
6695
  const points = [];
6042
- for (let i = 0; i <= n; i++) {
6043
- const u = i / n;
6044
- points.push({ u, v: resolved === "hold" ? i === n ? 1 : 0 : _chunkDWJJI6RTcjs.evaluateEasing.call(void 0, ease, u, ms2) });
6696
+ for (let i = 0; i <= n2; i++) {
6697
+ const u = i / n2;
6698
+ points.push({ u, v: resolved === "hold" ? i === n2 ? 1 : 0 : _chunkDWJJI6RTcjs.evaluateEasing.call(void 0, ease, u, ms2) });
6045
6699
  }
6046
6700
  const values = points.map((p) => p.v);
6047
6701
  return { points, min: Math.min(0, ...values), max: Math.max(1, ...values), durationMs: ms2 };
6048
6702
  }
6049
6703
  function motionEasingPath(curve, width, height, pad = 4) {
6050
6704
  const span = curve.max - curve.min || 1;
6051
- const f = (n) => String(Math.round(n * 100) / 100);
6705
+ const f = (n2) => String(Math.round(n2 * 100) / 100);
6052
6706
  return curve.points.map((p, i) => `${i ? "L" : "M"}${f(pad + p.u * (width - 2 * pad))} ${f(pad + (1 - (p.v - curve.min) / span) * (height - 2 * pad))}`).join(" ");
6053
6707
  }
6054
6708
  function motionEasingGuides(curve, height, pad = 4) {
@@ -6108,7 +6762,7 @@ function motionCueFromKey(key) {
6108
6762
  function motionCueChoices(scene, limit = 256) {
6109
6763
  const choices = [];
6110
6764
  const s = (ms2) => `${(ms2 / 1e3).toFixed(2)} s`;
6111
- const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access', _240 => _240.narration, 'optionalAccess', _241 => _241.words, 'access', _242 => _242.map, 'call', _243 => _243((w) => _nullishCoalesce(w.end, () => ( w.start)))]), () => ( [0]))) + 2e3;
6765
+ const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access', _269 => _269.narration, 'optionalAccess', _270 => _270.words, 'access', _271 => _271.map, 'call', _272 => _272((w) => _nullishCoalesce(w.end, () => ( w.start)))]), () => ( [0]))) + 2e3;
6112
6766
  if (scene.beat && scene.beat.bpm > 0) {
6113
6767
  for (let beat = 0; choices.length < limit; beat++) {
6114
6768
  const time = motionBeatTime(scene.beat, beat);
@@ -6118,7 +6772,7 @@ function motionCueChoices(scene, limit = 256) {
6118
6772
  choices.push({ value: `beat:${beat}`, label: `Beat ${beat} (bar ${at.bar}.${at.beatInBar}) \xB7 ${s(time)}`, time });
6119
6773
  }
6120
6774
  }
6121
- _optionalChain([scene, 'access', _244 => _244.narration, 'optionalAccess', _245 => _245.words, 'access', _246 => _246.forEach, 'call', _247 => _247((w, i) => {
6775
+ _optionalChain([scene, 'access', _273 => _273.narration, 'optionalAccess', _274 => _274.words, 'access', _275 => _275.forEach, 'call', _276 => _276((w, i) => {
6122
6776
  if (choices.length < limit * 2) choices.push({ value: `word:${i}`, label: `Word ${i} \u201C${w.text}\u201D \xB7 ${s(w.start)}`, time: w.start });
6123
6777
  })]);
6124
6778
  return choices;
@@ -6127,7 +6781,7 @@ function motionShotCueTime(scene, cue) {
6127
6781
  return cue ? motionCueTime(scene, cue) : void 0;
6128
6782
  }
6129
6783
  function formatMotionNarration(narration) {
6130
- return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess', _248 => _248.words]), () => ( []))).map((w) => `${w.start} ${w.text}`).join("\n");
6784
+ return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess', _277 => _277.words]), () => ( []))).map((w) => `${w.start} ${w.text}`).join("\n");
6131
6785
  }
6132
6786
  function parseMotionNarration(text) {
6133
6787
  const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
@@ -6158,7 +6812,7 @@ function parseMotionDrivers(text) {
6158
6812
  if (factor !== void 0) d.factor = Number(factor);
6159
6813
  if (offset !== void 0) d.offset = (sign === "-" ? -1 : 1) * Number(offset);
6160
6814
  if (min !== void 0) d.clamp = [Number(min), Number(max)];
6161
- if ([d.factor, d.offset, ..._nullishCoalesce(d.clamp, () => ( []))].some((n) => n !== void 0 && !Number.isFinite(n))) return { error: `Line ${i + 1} has a number that cannot be read.` };
6815
+ if ([d.factor, d.offset, ..._nullishCoalesce(d.clamp, () => ( []))].some((n2) => n2 !== void 0 && !Number.isFinite(n2))) return { error: `Line ${i + 1} has a number that cannot be read.` };
6162
6816
  drivers.push(d);
6163
6817
  }
6164
6818
  return { drivers };
@@ -6170,7 +6824,7 @@ var ROLE = {
6170
6824
  enum: ["transition", "hold"],
6171
6825
  description: `transition: the move the camera gets into the shot with (its camera "in": a glide, whip pan, punch zoom, cut). hold: the move it makes while the shot holds (its camera "during": a Ken Burns drift, a crash zoom), played across the shot's hold.`
6172
6826
  };
6173
- var summary = (e) => ({ id: e.id, name: e.name, role: e.camera.role, durationMs: e.camera.durationMs, ...e.camera.cut !== void 0 ? { cut: e.camera.cut } : {}, ...e.description ? { description: e.description } : {}, ..._optionalChain([e, 'access', _249 => _249.tags, 'optionalAccess', _250 => _250.length]) ? { tags: e.tags } : {} });
6827
+ var summary = (e) => ({ id: e.id, name: e.name, role: e.camera.role, durationMs: e.camera.durationMs, ...e.camera.cut !== void 0 ? { cut: e.camera.cut } : {}, ...e.description ? { description: e.description } : {}, ..._optionalChain([e, 'access', _278 => _278.tags, 'optionalAccess', _279 => _279.length]) ? { tags: e.tags } : {} });
6174
6828
  var done = (r) => {
6175
6829
  if (!r.ok) throw new Error(r.error);
6176
6830
  return { shot: r.shot, camera: r.camera, ...r.window ? { window: r.window } : {}, ...r.notes.length ? { notes: r.notes } : {} };
@@ -6199,7 +6853,7 @@ function animationCameraMoveTools(controller, ctx) {
6199
6853
  shot: { type: "string", minLength: 1, description: "A shot id from get_animation." },
6200
6854
  role: ROLE,
6201
6855
  move: { type: "string", minLength: 1, description: "A camera move id of that role from list_camera_moves." },
6202
- ms: { type: "number", minimum: 0, maximum: _chunk2K5TELHVcjs.CAMERA_MOVE_MAX_MS, description: `How long a transition takes, ms (0\u2013${_chunk2K5TELHVcjs.CAMERA_MOVE_MAX_MS}; default the move's durationMs). Not for a hold, which plays across the shot's hold.` },
6856
+ ms: { type: "number", minimum: 0, maximum: _chunkJAOSL334cjs.CAMERA_MOVE_MAX_MS, description: `How long a transition takes, ms (0\u2013${_chunkJAOSL334cjs.CAMERA_MOVE_MAX_MS}; default the move's durationMs). Not for a hold, which plays across the shot's hold.` },
6203
6857
  preset: { type: "string", minLength: 1, description: "A preset of the move by name, from describe_effect; its values are written out on the shot." },
6204
6858
  values: ctx.valuesSchema("Control values over the move's defaults (and the preset's), from describe_effect")
6205
6859
  },
@@ -6229,6 +6883,163 @@ function animationCameraMoveTools(controller, ctx) {
6229
6883
  ];
6230
6884
  }
6231
6885
 
6886
+ // src/animation-editor/layer-tools.ts
6887
+ var LAYER_USES = ["generator", "filter", "overlay", "title"];
6888
+ var SHOT2 = { type: "string", minLength: 1, description: "A shot id from get_animation." };
6889
+ var LAYER = { type: "string", minLength: 1, description: 'A layer id from list_shot_layers (e.g. "layer1").' };
6890
+ var SUBJECT = {
6891
+ anyOf: [
6892
+ { type: "object", properties: { image: { type: "string", minLength: 1, description: "A picture URL or data: URL." } }, required: ["image"], additionalProperties: false, description: '{ "image": url }' },
6893
+ { type: "object", properties: { svg: { type: "string", minLength: 1, description: "SVG markup or an SVG URL." } }, required: ["svg"], additionalProperties: false, description: '{ "svg": markup }' },
6894
+ {
6895
+ type: "object",
6896
+ properties: { text: { type: "string", minLength: 1, description: "The words." }, font: { type: "string", description: "A CSS font family (default system-ui)." }, weight: { anyOf: [{ type: "number" }, { type: "string" }], description: "A CSS font weight (default 700)." } },
6897
+ required: ["text"],
6898
+ additionalProperties: false,
6899
+ description: '{ "text": words }'
6900
+ },
6901
+ { type: "object", properties: { slot: { type: "string", minLength: 1, description: "A slot name the host fills later." } }, required: ["slot"], additionalProperties: false, description: '{ "slot": name }: draws nothing until filled.' }
6902
+ ],
6903
+ description: "What the layer shows, or feeds its effect (a filter changes it): a picture, SVG, words, or a slot to fill later. Leave out for a generator, overlay or title that draws its own picture."
6904
+ };
6905
+ var RECT = {
6906
+ type: "object",
6907
+ properties: {
6908
+ x: { type: "number", description: "Left edge, artwork units." },
6909
+ y: { type: "number", description: "Top edge, artwork units." },
6910
+ width: { type: "number", exclusiveMinimum: 0, description: "Width, artwork units." },
6911
+ height: { type: "number", exclusiveMinimum: 0, description: "Height, artwork units." }
6912
+ },
6913
+ required: ["x", "y", "width", "height"],
6914
+ additionalProperties: false,
6915
+ description: "Where the layer sits on the artwork (it moves with the camera). Leave out to fill the screen."
6916
+ };
6917
+ var TIME = (what, fallback) => ({
6918
+ anyOf: [
6919
+ { type: "number", minimum: 0, description: "Ms after the shot starts." },
6920
+ { type: "object", properties: { shots: { type: "integer", minimum: 1, description: "1: when the next shot starts; 2: the one after\u2026" } }, required: ["shots"], additionalProperties: false, description: '{ "shots": n }' }
6921
+ ],
6922
+ description: `When the layer ${what}: ms after its shot starts, or { "shots": n } for when the n-th shot after its own starts (default ${fallback}).`
6923
+ });
6924
+ var fields = (ctx) => ({
6925
+ effect: { ...ctx.effectIdSchema(LAYER_USES), description: "A generator, filter, overlay or title effect id from search_effects. Leave out for a bare subject." },
6926
+ preset: { type: "string", minLength: 1, description: "A preset of the effect by name, from describe_effect; its values are written out on the layer." },
6927
+ values: ctx.valuesSchema("Control values over the effect's defaults (and the preset's), from describe_effect"),
6928
+ subject: SUBJECT,
6929
+ role: { type: "string", enum: [..._chunkJAOSL334cjs.EFFECT_LAYER_ROLES], description: "background: under the artwork; subject: over the artwork; overlay: over everything. Default subject with a rect, else background." },
6930
+ rect: RECT,
6931
+ from: TIME("starts", "0: as its shot starts"),
6932
+ until: TIME("stops", '{ "shots": 1 }: as the next shot starts'),
6933
+ opacity: { type: "number", minimum: 0, maximum: 1, description: "Opacity 0\u20131 (default 1)." },
6934
+ fade: { type: "number", minimum: 0, maximum: 6e4, description: "Ms it fades in and out over (default 0: it cuts)." }
6935
+ });
6936
+ var result = (r) => {
6937
+ if (!r.ok) throw new Error(r.error);
6938
+ const { ok: _ok, notes, ...rest } = r;
6939
+ return { ...rest, ...notes.length ? { notes } : {} };
6940
+ };
6941
+ function animationShotLayerTools(controller, ctx) {
6942
+ const f = fields(ctx);
6943
+ return [
6944
+ {
6945
+ name: "list_shot_layers",
6946
+ title: "List shot layers",
6947
+ description: "Lists the layers and transition of every shot, or of one: [{ shot, layers: [{ id, effect, name, subject, role, rect, from, until, opacity, fade, values, index (0 = bottom), start, end (ms when it shows) }], transition: { effect, ms, values, start, end } or null }]. Use the layer ids with update_shot_layer, move_shot_layer and remove_shot_layer. Works without a provider.",
6948
+ inputSchema: { type: "object", properties: { shot: { ...SHOT2, description: "Only this shot (default every shot)." } }, additionalProperties: false },
6949
+ annotations: { title: "List shot layers", ...ctx.annotations.read },
6950
+ handler: async (args) => ({ shots: controller.shotLayers(args.shot) })
6951
+ },
6952
+ {
6953
+ name: "add_shot_layer",
6954
+ title: "Add a shot layer",
6955
+ description: "Adds a layer to a shot: a provider effect (a generator such as a gradient background, a filter applied to a subject, an overlay or title) or a bare subject (a picture, SVG or words). It shows from its shot's start until the next shot starts unless from / until say otherwise, fills the screen unless given a rect, and goes on top unless given an index. Checked first: an effect that cannot be a layer (a transition, a camera move, a motion effect) is refused with the tool to use instead, as are a subject the effect cannot take, a filter without a subject, an unknown preset and values outside its controls. One undo step. Returns { shot, layer (with its id, start and end), layers (the shot's, bottom to top), transition, notes }. Check it with render_animation_frame.",
6956
+ inputSchema: {
6957
+ type: "object",
6958
+ properties: { shot: SHOT2, ...f, id: { type: "string", pattern: "^[A-Za-z_][\\w.-]*$", description: "The layer's id (default layer1, layer2\u2026); unique in the scene." }, index: { type: "integer", minimum: 0, description: "Its place in the shot, 0 = bottom (default the top)." } },
6959
+ required: ["shot"],
6960
+ additionalProperties: false
6961
+ },
6962
+ annotations: { title: "Add a shot layer", ...ctx.annotations.write },
6963
+ handler: async ({ shot, index, ...layer }) => {
6964
+ if (layer.effect !== void 0) ctx.requireProvider();
6965
+ if (layer.effect === void 0 && layer.subject === void 0) throw new Error('Give an "effect" (search_effects with use generator, filter, overlay or title), a "subject" (a picture, SVG or words), or both.');
6966
+ return result(await controller.addShotLayer(shot, layer, { index }));
6967
+ }
6968
+ },
6969
+ {
6970
+ name: "update_shot_layer",
6971
+ title: "Change a shot layer",
6972
+ description: `Changes a layer's fields: give the ones to change, and list in "clear" the optional ones to remove (back to their defaults). A new effect starts from its own defaults unless values are given. Checked against the provider like add_shot_layer. One undo step. Returns { shot, layer, layers, transition }.`,
6973
+ inputSchema: {
6974
+ type: "object",
6975
+ properties: {
6976
+ layer: LAYER,
6977
+ ...f,
6978
+ clear: { type: "array", items: { type: "string", enum: ["effect", "preset", "values", "subject", "role", "rect", "from", "until", "opacity", "fade"] }, uniqueItems: true, description: "Fields to remove." }
6979
+ },
6980
+ required: ["layer"],
6981
+ additionalProperties: false
6982
+ },
6983
+ annotations: { title: "Change a shot layer", ...ctx.annotations.writeIdempotent },
6984
+ handler: async ({ layer, clear, ...set }) => {
6985
+ const changes = { ...set };
6986
+ for (const k of _nullishCoalesce(clear, () => ( []))) {
6987
+ if (k in set) throw new Error(`"${k}" is both given and cleared; do one or the other.`);
6988
+ changes[k] = null;
6989
+ }
6990
+ if (!Object.keys(changes).length) throw new Error('Give at least one field to change, or a field in "clear".');
6991
+ return result(await controller.updateShotLayer(layer, changes));
6992
+ }
6993
+ },
6994
+ {
6995
+ name: "move_shot_layer",
6996
+ title: "Restack a shot layer",
6997
+ description: "Moves a layer to another place in its shot's stack (0 = bottom; within a role, later layers draw over earlier ones). One undo step. Returns { shot, layer, layers }.",
6998
+ inputSchema: { type: "object", properties: { layer: LAYER, index: { type: "integer", minimum: 0, description: "Its new place in the shot, 0 = bottom." } }, required: ["layer", "index"], additionalProperties: false },
6999
+ annotations: { title: "Restack a shot layer", ...ctx.annotations.writeIdempotent },
7000
+ handler: async ({ layer, index }) => result(await controller.moveShotLayer(layer, index))
7001
+ },
7002
+ {
7003
+ name: "remove_shot_layer",
7004
+ title: "Remove a shot layer",
7005
+ description: "Removes a layer from its shot. Works without a provider. One undo step. Returns the shot's remaining layers.",
7006
+ inputSchema: { type: "object", properties: { layer: LAYER }, required: ["layer"], additionalProperties: false },
7007
+ annotations: { title: "Remove a shot layer", ...ctx.annotations.destructive },
7008
+ handler: async ({ layer }) => result(await controller.removeShotLayer(layer))
7009
+ },
7010
+ {
7011
+ name: "set_shot_transition",
7012
+ title: "Set a shot's transition",
7013
+ description: `Sets the transition into a shot: a provider effect with the use transition (search_effects with use "transition") blends the last frame of the shot before into this shot over ms. Not on the first shot (there is no shot before it). With no camera move in (set_shot_camera_move, role transition), the camera cuts and the transition does the move; with one, both play together. Checked first: an effect that is not a transition, an unknown preset or values outside its controls are refused. One undo step. Returns { shot, layers, transition: { effect, ms, values, start, end }, notes }.`,
7014
+ inputSchema: {
7015
+ type: "object",
7016
+ properties: {
7017
+ shot: { ...SHOT2, description: "A shot id from get_animation (not the first shot)." },
7018
+ effect: { ...ctx.effectIdSchema(["transition"]), description: "A transition effect id from search_effects (use transition)." },
7019
+ ms: { type: "number", exclusiveMinimum: 0, maximum: 6e4, description: `How long it takes, ms (default ${_chunkJAOSL334cjs.EFFECT_TRANSITION_MS}).` },
7020
+ preset: { type: "string", minLength: 1, description: "A preset of the transition by name, from describe_effect." },
7021
+ values: ctx.valuesSchema("Control values over the transition's defaults (and the preset's)")
7022
+ },
7023
+ required: ["shot", "effect"],
7024
+ additionalProperties: false
7025
+ },
7026
+ annotations: { title: "Set a shot's transition", ...ctx.annotations.writeIdempotent },
7027
+ handler: async ({ shot, effect, ms: ms2, preset: preset2, values }) => {
7028
+ ctx.requireProvider();
7029
+ return result(await controller.setShotTransition(shot, { effect, ...ms2 !== void 0 ? { ms: ms2 } : {}, ...preset2 !== void 0 ? { preset: preset2 } : {}, ...values !== void 0 ? { values } : {} }));
7030
+ }
7031
+ },
7032
+ {
7033
+ name: "clear_shot_transition",
7034
+ title: "Clear a shot's transition",
7035
+ description: "Removes the transition into a shot, so it cuts (or the camera eases) from the shot before. Works without a provider. One undo step. Returns { shot, layers, transition: null }.",
7036
+ inputSchema: { type: "object", properties: { shot: SHOT2 }, required: ["shot"], additionalProperties: false },
7037
+ annotations: { title: "Clear a shot's transition", ...ctx.annotations.destructive },
7038
+ handler: async ({ shot }) => result(controller.clearShotTransition(shot))
7039
+ }
7040
+ ];
7041
+ }
7042
+
6232
7043
  // src/animation-editor/effect-tools.ts
6233
7044
  var info = (e) => ({ id: e.instance, effect: e.effect, ...e.name ? { name: e.name } : {}, ...e.provider ? { provider: e.provider } : {}, role: e.kind === "vector" ? "vector picture" : "motion", enabled: true, values: e.values, targets: e.targets, shots: e.shots });
6234
7045
  function createAnimationEffectsToolProvider(controller, options = {}) {
@@ -6241,7 +7052,7 @@ function createAnimationEffectsToolProvider(controller, options = {}) {
6241
7052
  if (!r.ok) throw new Error(r.error);
6242
7053
  return { target, effect: info(r.effect), effects: controller.appliedEffects().filter((e) => e.targets.includes(target)).map(info), ...r.notes.length ? { notes: r.notes } : {} };
6243
7054
  };
6244
- return withAnimationPlayback(_chunk2K5TELHVcjs.createEffectToolProvider.call(void 0, {
7055
+ return withAnimationPlayback(_chunkJAOSL334cjs.createEffectToolProvider.call(void 0, {
6245
7056
  id: _nullishCoalesce(options.id, () => ( "animation-effects")),
6246
7057
  namespace: options.namespace,
6247
7058
  title: "Animation effects",
@@ -6275,6 +7086,7 @@ function createAnimationEffectsToolProvider(controller, options = {}) {
6275
7086
  subscribe: (notify) => controller.subscribe(() => notify()),
6276
7087
  extraTools: (ctx) => [
6277
7088
  ...animationCameraMoveTools(controller, ctx),
7089
+ ...animationShotLayerTools(controller, ctx),
6278
7090
  {
6279
7091
  name: "add_vector_effect",
6280
7092
  title: "Add a vector effect",
@@ -6318,8 +7130,8 @@ function createAnimationEditorDocument(controller) {
6318
7130
  kind: "animation",
6319
7131
  get: () => controller.getScene(),
6320
7132
  load: (document, { history }) => {
6321
- const result = controller.load(document, { clearHistory: history === "reset" });
6322
- if (!result.ok) throw new Error(result.error);
7133
+ const result2 = controller.load(document, { clearHistory: history === "reset" });
7134
+ if (!result2.ok) throw new Error(result2.error);
6323
7135
  },
6324
7136
  subscribe: (listener) => {
6325
7137
  let scene = controller.getScene();
@@ -6512,4 +7324,23 @@ function createAnimationEditorDocument(controller) {
6512
7324
 
6513
7325
 
6514
7326
 
6515
- exports.ANIMATION_EDITOR_AGENT_INSTRUCTIONS = ANIMATION_EDITOR_AGENT_INSTRUCTIONS; exports.AnimationEditorController = AnimationEditorController; exports.FLAIR_EASES = FLAIR_EASES; exports.FLAIR_EXPORT_VERSION = FLAIR_EXPORT_VERSION; exports.MOTION_ADDITIVE_PROPERTIES = MOTION_ADDITIVE_PROPERTIES; exports.MOTION_BLENDS = MOTION_BLENDS; exports.MOTION_CAMERA_MS = MOTION_CAMERA_MS; exports.MOTION_CASCADES = MOTION_CASCADES; exports.MOTION_CLIP_TARGET = MOTION_CLIP_TARGET; exports.MOTION_COMMAND_FIELDS = MOTION_COMMAND_FIELDS; exports.MOTION_COMMAND_SCHEMA = MOTION_COMMAND_SCHEMA; exports.MOTION_COMMAND_TYPES = MOTION_COMMAND_TYPES; exports.MOTION_COMMAND_TYPE_SCHEMAS = MOTION_COMMAND_TYPE_SCHEMAS; exports.MOTION_DEFAULT_DURATIONS = MOTION_DEFAULT_DURATIONS; exports.MOTION_DEFAULT_HOLD = MOTION_DEFAULT_HOLD; exports.MOTION_DIRECTIONS = MOTION_DIRECTIONS; exports.MOTION_DRAW_EFFECT_ID = MOTION_DRAW_EFFECT_ID; exports.MOTION_DRAW_MS_PER_UNIT = MOTION_DRAW_MS_PER_UNIT; exports.MOTION_DRAW_RANGE = MOTION_DRAW_RANGE; exports.MOTION_DRIVEN_PROPERTIES = MOTION_DRIVEN_PROPERTIES; exports.MOTION_EASE_CHOICES = MOTION_EASE_CHOICES; exports.MOTION_EASE_IN_OUT = MOTION_EASE_IN_OUT; exports.MOTION_EASE_OUT = MOTION_EASE_OUT; exports.MOTION_EDIT_OPS = MOTION_EDIT_OPS; exports.MOTION_ENTER_STYLES = MOTION_ENTER_STYLES; exports.MOTION_EXIT_STYLES = MOTION_EXIT_STYLES; exports.MOTION_IRIS_SHAPES = MOTION_IRIS_SHAPES; exports.MOTION_ISSUE_CODES = _chunkERJA5CGTcjs.MOTION_ISSUE_CODES; exports.MOTION_MAX_TAKE_MS = MOTION_MAX_TAKE_MS; exports.MOTION_MIN_CHANGE_MS = MOTION_MIN_CHANGE_MS; exports.MOTION_RECORD_GESTURE_MODES = MOTION_RECORD_GESTURE_MODES; exports.MOTION_REPEAT_MODES = MOTION_REPEAT_MODES; exports.MOTION_SPEED_RANGE = MOTION_SPEED_RANGE; exports.MOTION_SPLIT_ORIENTATIONS = MOTION_SPLIT_ORIENTATIONS; exports.MOTION_TEXT_STAGGER = MOTION_TEXT_STAGGER; exports.MOTION_TEXT_UNITS = MOTION_TEXT_UNITS; exports.MOTION_TRIGGERS = MOTION_TRIGGERS; exports.MotionSceneFormatError = MotionSceneFormatError; exports.activeSegment = activeSegment; exports.animationCameraMoveTools = animationCameraMoveTools; exports.applyMotionControlValues = applyMotionControlValues; exports.applyMotionDrivers = applyMotionDrivers; exports.applyMotionEdits = applyMotionEdits; exports.checkMotionCameraMoves = checkMotionCameraMoves; exports.checkMotionScene = checkMotionScene; exports.clipsShapeProblems = clipsShapeProblems; exports.commandShapeProblems = commandShapeProblems; exports.compileMotionScene = compileMotionScene; exports.createAnimationEditorDocument = createAnimationEditorDocument; exports.createAnimationEditorToolProvider = createAnimationEditorToolProvider; exports.createAnimationEffectsToolProvider = createAnimationEffectsToolProvider; exports.createMotionScene = createMotionScene; exports.defaultMotionCommand = defaultMotionCommand; exports.describeMotionElements = describeMotionElements; exports.describeMotionShots = describeMotionShots; exports.driversShapeProblems = driversShapeProblems; exports.duplicateMotionCommandEdit = duplicateMotionCommandEdit; exports.effectsPlayCamera = effectsPlayCamera; exports.fitMotionRect = fitMotionRect; exports.fitMotionScene = fitMotionScene; exports.fitMotionSceneEdits = fitMotionSceneEdits; exports.flairAtLeast = flairAtLeast; exports.flairCurveFor = flairCurveFor; exports.flairEaseFor = flairEaseFor; exports.formatMotionDrivers = formatMotionDrivers; exports.formatMotionNarration = formatMotionNarration; exports.freshShotId = freshShotId; exports.fromFlairSvgMotion = fromFlairSvgMotion; exports.fromFlairVectorEffect = fromFlairVectorEffect; exports.fromPptxAnimation = fromPptxAnimation; exports.highlightMotionSvg = highlightMotionSvg; exports.indexMotionNodes = indexMotionNodes; exports.interpolateMotionValue = interpolateMotionValue; exports.isMotionCommand = isMotionCommand; exports.isMotionShot = isMotionShot; exports.matchMotionNodes = matchMotionNodes; exports.motionAddTrackKey = motionAddTrackKey; exports.motionArtworkSvg = motionArtworkSvg; exports.motionBeatAt = motionBeatAt; exports.motionBeatTime = motionBeatTime; exports.motionBlurTimes = motionBlurTimes; exports.motionCameraBox = motionCameraBox; exports.motionCameraFramed = motionCameraFramed; exports.motionCameraMoves = motionCameraMoves; exports.motionCameraStage = motionCameraStage; exports.motionCameraTransform = motionCameraTransform; exports.motionCameraViewAt = motionCameraViewAt; exports.motionCameraWindows = motionCameraWindows; exports.motionCascade = motionCascade; exports.motionClientToDocument = motionClientToDocument; exports.motionClipAttrs = motionClipAttrs; exports.motionClipLayout = motionClipLayout; exports.motionCommandDuration = motionCommandDuration; exports.motionCommandFields = motionCommandFields; exports.motionCommandRepeat = motionCommandRepeat; exports.motionCommandRows = motionCommandRows; exports.motionCommandSlots = motionCommandSlots; exports.motionCommandSummary = motionCommandSummary; exports.motionCommandsFromEffect = motionCommandsFromEffect; exports.motionContactSheetSvg = motionContactSheetSvg; exports.motionCueChoices = motionCueChoices; exports.motionCueFromKey = motionCueFromKey; exports.motionCueKey = motionCueKey; exports.motionCueTime = motionCueTime; exports.motionCycleDuration = motionCycleDuration; exports.motionDrivenNodes = motionDrivenNodes; exports.motionDriverSources = motionDriverSources; exports.motionEaseKey = motionEaseKey; exports.motionEasingCurve = motionEasingCurve; exports.motionEasingGuides = motionEasingGuides; exports.motionEasingPath = motionEasingPath; exports.motionFieldEdit = motionFieldEdit; exports.motionFieldLabel = motionFieldLabel; exports.motionFlairPrimitive = motionFlairPrimitive; exports.motionFollowRoute = motionFollowRoute; exports.motionFrameAt = motionFrameAt; exports.motionFrameToSvg = motionFrameToSvg; exports.motionGhosts = motionGhosts; exports.motionIssue = _chunkERJA5CGTcjs.motionIssue; exports.motionIssueKey = _chunkERJA5CGTcjs.motionIssueKey; exports.motionIssueLine = _chunkERJA5CGTcjs.motionIssueLine; exports.motionMovingIds = motionMovingIds; exports.motionNodeAtElement = motionNodeAtElement; exports.motionNodeClips = motionNodeClips; exports.motionNodeOffset = motionNodeOffset; exports.motionNodePose = motionNodePose; exports.motionPartsFromVectorEffect = motionPartsFromVectorEffect; exports.motionPath = motionPath; exports.motionPathLength = motionPathLength; exports.motionPathsSvg = motionPathsSvg; exports.motionPropertyValue = motionPropertyValue; exports.motionRestValue = motionRestValue; exports.motionRevealClip = motionRevealClip; exports.motionRevealPadding = motionRevealPadding; exports.motionRulerTicks = motionRulerTicks; exports.motionSceneEffects = motionSceneEffects; exports.motionSceneShapeProblems = motionSceneShapeProblems; exports.motionSceneToJson = motionSceneToJson; exports.motionShotAt = motionShotAt; exports.motionShotCameraMove = motionShotCameraMove; exports.motionShotCueTime = motionShotCueTime; exports.motionShotTimeScale = motionShotTimeScale; exports.motionStageSvg = motionStageSvg; exports.motionTakeProblems = motionTakeProblems; exports.motionTakeShot = motionTakeShot; exports.motionTextLayout = motionTextLayout; exports.motionTextUnits = motionTextUnits; exports.motionTrackKey = motionTrackKey; exports.namedWithSuggestion = _chunkERJA5CGTcjs.namedWithSuggestion; exports.nextMotionEffectInstance = nextMotionEffectInstance; exports.parseMotionDrivers = parseMotionDrivers; exports.parseMotionFieldInput = parseMotionFieldInput; exports.parseMotionNarration = parseMotionNarration; exports.parseMotionScene = parseMotionScene; exports.planAnimationToolPlayback = planAnimationToolPlayback; exports.resolveMotionCamera = resolveMotionCamera; exports.resolveMotionEditRefs = resolveMotionEditRefs; exports.segmentProgress = segmentProgress; exports.segmentValue = segmentValue; exports.shotShapeProblems = shotShapeProblems; exports.simplifyTimedSamples = simplifyTimedSamples; exports.smoothMotionLegs = smoothMotionLegs; exports.smoothMotionTimes = smoothMotionTimes; exports.suggestName = _chunkERJA5CGTcjs.suggestName; exports.toFlairEffectDocument = toFlairEffectDocument; exports.toFlairSvgMotion = toFlairSvgMotion; exports.toFlairVectorEffect = toFlairVectorEffect; exports.toLottie = toLottie; exports.trackValue = trackValue; exports.withAnimationPlayback = withAnimationPlayback; exports.withCameraViewEffects = withCameraViewEffects; exports.withMotionLayers = withMotionLayers; exports.withViewBox = withViewBox; exports.withoutMotionEffect = withoutMotionEffect;
7327
+
7328
+
7329
+
7330
+
7331
+
7332
+
7333
+
7334
+
7335
+
7336
+
7337
+
7338
+
7339
+
7340
+
7341
+
7342
+
7343
+
7344
+
7345
+
7346
+ exports.ANIMATION_EDITOR_AGENT_INSTRUCTIONS = ANIMATION_EDITOR_AGENT_INSTRUCTIONS; exports.AnimationEditorController = AnimationEditorController; exports.FLAIR_EASES = FLAIR_EASES; exports.FLAIR_EXPORT_VERSION = FLAIR_EXPORT_VERSION; exports.MOTION_ADDITIVE_PROPERTIES = MOTION_ADDITIVE_PROPERTIES; exports.MOTION_BLENDS = MOTION_BLENDS; exports.MOTION_CAMERA_MS = MOTION_CAMERA_MS; exports.MOTION_CASCADES = MOTION_CASCADES; exports.MOTION_CLIP_TARGET = MOTION_CLIP_TARGET; exports.MOTION_COMMAND_FIELDS = MOTION_COMMAND_FIELDS; exports.MOTION_COMMAND_SCHEMA = MOTION_COMMAND_SCHEMA; exports.MOTION_COMMAND_TYPES = MOTION_COMMAND_TYPES; exports.MOTION_COMMAND_TYPE_SCHEMAS = MOTION_COMMAND_TYPE_SCHEMAS; exports.MOTION_DEFAULT_DURATIONS = MOTION_DEFAULT_DURATIONS; exports.MOTION_DEFAULT_HOLD = MOTION_DEFAULT_HOLD; exports.MOTION_DIRECTIONS = MOTION_DIRECTIONS; exports.MOTION_DRAW_EFFECT_ID = MOTION_DRAW_EFFECT_ID; exports.MOTION_DRAW_MS_PER_UNIT = MOTION_DRAW_MS_PER_UNIT; exports.MOTION_DRAW_RANGE = MOTION_DRAW_RANGE; exports.MOTION_DRIVEN_PROPERTIES = MOTION_DRIVEN_PROPERTIES; exports.MOTION_EASE_CHOICES = MOTION_EASE_CHOICES; exports.MOTION_EASE_IN_OUT = MOTION_EASE_IN_OUT; exports.MOTION_EASE_OUT = MOTION_EASE_OUT; exports.MOTION_EDIT_OPS = MOTION_EDIT_OPS; exports.MOTION_ENTER_STYLES = MOTION_ENTER_STYLES; exports.MOTION_EXIT_STYLES = MOTION_EXIT_STYLES; exports.MOTION_IRIS_SHAPES = MOTION_IRIS_SHAPES; exports.MOTION_ISSUE_CODES = _chunkDGWOGQJ5cjs.MOTION_ISSUE_CODES; exports.MOTION_MAX_TAKE_MS = MOTION_MAX_TAKE_MS; exports.MOTION_MIN_CHANGE_MS = MOTION_MIN_CHANGE_MS; exports.MOTION_RECORD_GESTURE_MODES = MOTION_RECORD_GESTURE_MODES; exports.MOTION_REPEAT_MODES = MOTION_REPEAT_MODES; exports.MOTION_SPEED_RANGE = MOTION_SPEED_RANGE; exports.MOTION_SPLIT_ORIENTATIONS = MOTION_SPLIT_ORIENTATIONS; exports.MOTION_TEXT_STAGGER = MOTION_TEXT_STAGGER; exports.MOTION_TEXT_UNITS = MOTION_TEXT_UNITS; exports.MOTION_TRIGGERS = MOTION_TRIGGERS; exports.MotionLayersEngine = MotionLayersEngine; exports.MotionSceneFormatError = MotionSceneFormatError; exports.activeSegment = activeSegment; exports.animationCameraMoveTools = animationCameraMoveTools; exports.animationShotLayerTools = animationShotLayerTools; exports.applyMotionControlValues = applyMotionControlValues; exports.applyMotionDrivers = applyMotionDrivers; exports.applyMotionEdits = applyMotionEdits; exports.browserMotionLayerHost = browserMotionLayerHost; exports.checkMotionCameraMoves = checkMotionCameraMoves; exports.checkMotionScene = checkMotionScene; exports.checkMotionShotLayers = checkMotionShotLayers; exports.clipsShapeProblems = clipsShapeProblems; exports.commandShapeProblems = commandShapeProblems; exports.compileMotionScene = compileMotionScene; exports.createAnimationEditorDocument = createAnimationEditorDocument; exports.createAnimationEditorToolProvider = createAnimationEditorToolProvider; exports.createAnimationEffectsToolProvider = createAnimationEffectsToolProvider; exports.createMotionScene = createMotionScene; exports.defaultMotionCommand = defaultMotionCommand; exports.describeMotionElements = describeMotionElements; exports.describeMotionShots = describeMotionShots; exports.driversShapeProblems = driversShapeProblems; exports.duplicateMotionCommandEdit = duplicateMotionCommandEdit; exports.effectsPlayCamera = effectsPlayCamera; exports.findMotionShotLayer = findMotionShotLayer; exports.fitMotionRect = fitMotionRect; exports.fitMotionScene = fitMotionScene; exports.fitMotionSceneEdits = fitMotionSceneEdits; exports.flairAtLeast = flairAtLeast; exports.flairCurveFor = flairCurveFor; exports.flairEaseFor = flairEaseFor; exports.formatMotionDrivers = formatMotionDrivers; exports.formatMotionNarration = formatMotionNarration; exports.freshMotionLayerId = freshMotionLayerId; exports.freshShotId = freshShotId; exports.fromFlairSvgMotion = fromFlairSvgMotion; exports.fromFlairVectorEffect = fromFlairVectorEffect; exports.fromPptxAnimation = fromPptxAnimation; exports.highlightMotionSvg = highlightMotionSvg; exports.indexMotionNodes = indexMotionNodes; exports.interpolateMotionValue = interpolateMotionValue; exports.isMotionCommand = isMotionCommand; exports.isMotionShot = isMotionShot; exports.matchMotionNodes = matchMotionNodes; exports.motionAddTrackKey = motionAddTrackKey; exports.motionArtworkSvg = motionArtworkSvg; exports.motionBeatAt = motionBeatAt; exports.motionBeatTime = motionBeatTime; exports.motionBlurTimes = motionBlurTimes; exports.motionCameraBox = motionCameraBox; exports.motionCameraFramed = motionCameraFramed; exports.motionCameraMoves = motionCameraMoves; exports.motionCameraStage = motionCameraStage; exports.motionCameraTransform = motionCameraTransform; exports.motionCameraViewAt = motionCameraViewAt; exports.motionCameraWindows = motionCameraWindows; exports.motionCascade = motionCascade; exports.motionClientToDocument = motionClientToDocument; exports.motionClipAttrs = motionClipAttrs; exports.motionClipLayout = motionClipLayout; exports.motionCommandDuration = motionCommandDuration; exports.motionCommandFields = motionCommandFields; exports.motionCommandRepeat = motionCommandRepeat; exports.motionCommandRows = motionCommandRows; exports.motionCommandSlots = motionCommandSlots; exports.motionCommandSummary = motionCommandSummary; exports.motionCommandsFromEffect = motionCommandsFromEffect; exports.motionContactSheetSvg = motionContactSheetSvg; exports.motionCueChoices = motionCueChoices; exports.motionCueFromKey = motionCueFromKey; exports.motionCueKey = motionCueKey; exports.motionCueTime = motionCueTime; exports.motionCycleDuration = motionCycleDuration; exports.motionDrivenNodes = motionDrivenNodes; exports.motionDriverSources = motionDriverSources; exports.motionEaseKey = motionEaseKey; exports.motionEasingCurve = motionEasingCurve; exports.motionEasingGuides = motionEasingGuides; exports.motionEasingPath = motionEasingPath; exports.motionFieldEdit = motionFieldEdit; exports.motionFieldLabel = motionFieldLabel; exports.motionFlairPrimitive = motionFlairPrimitive; exports.motionFollowRoute = motionFollowRoute; exports.motionFrameAt = motionFrameAt; exports.motionFrameToSvg = motionFrameToSvg; exports.motionGhosts = motionGhosts; exports.motionIssue = _chunkDGWOGQJ5cjs.motionIssue; exports.motionIssueKey = _chunkDGWOGQJ5cjs.motionIssueKey; exports.motionIssueLine = _chunkDGWOGQJ5cjs.motionIssueLine; exports.motionLayerIds = motionLayerIds; exports.motionLayerSlot = motionLayerSlot; exports.motionLayerSpans = motionLayerSpans; exports.motionLayersAt = motionLayersAt; exports.motionMovingIds = motionMovingIds; exports.motionNodeAtElement = motionNodeAtElement; exports.motionNodeClips = motionNodeClips; exports.motionNodeOffset = motionNodeOffset; exports.motionNodePose = motionNodePose; exports.motionPartsFromVectorEffect = motionPartsFromVectorEffect; exports.motionPath = motionPath; exports.motionPathLength = motionPathLength; exports.motionPathsSvg = motionPathsSvg; exports.motionPropertyValue = motionPropertyValue; exports.motionRestValue = motionRestValue; exports.motionRevealClip = motionRevealClip; exports.motionRevealPadding = motionRevealPadding; exports.motionRulerTicks = motionRulerTicks; exports.motionSceneEffects = motionSceneEffects; exports.motionSceneHasLayers = motionSceneHasLayers; exports.motionSceneShapeProblems = motionSceneShapeProblems; exports.motionSceneToJson = motionSceneToJson; exports.motionShotAt = motionShotAt; exports.motionShotCameraMove = motionShotCameraMove; exports.motionShotCueTime = motionShotCueTime; exports.motionShotLayersInfo = motionShotLayersInfo; exports.motionShotTimeScale = motionShotTimeScale; exports.motionStageSvg = motionStageSvg; exports.motionTakeProblems = motionTakeProblems; exports.motionTakeShot = motionTakeShot; exports.motionTextLayout = motionTextLayout; exports.motionTextUnits = motionTextUnits; exports.motionTrackKey = motionTrackKey; exports.motionTransitionAt = motionTransitionAt; exports.motionTransitionSvg = motionTransitionSvg; exports.namedWithSuggestion = _chunkDGWOGQJ5cjs.namedWithSuggestion; exports.nextMotionEffectInstance = nextMotionEffectInstance; exports.parseMotionDrivers = parseMotionDrivers; exports.parseMotionFieldInput = parseMotionFieldInput; exports.parseMotionNarration = parseMotionNarration; exports.parseMotionScene = parseMotionScene; exports.planAnimationToolPlayback = planAnimationToolPlayback; exports.resolveMotionCamera = resolveMotionCamera; exports.resolveMotionEditRefs = resolveMotionEditRefs; exports.resolveMotionShotLayer = resolveMotionShotLayer; exports.resolveMotionShotTransition = resolveMotionShotTransition; exports.segmentProgress = segmentProgress; exports.segmentValue = segmentValue; exports.shotLayersShapeProblems = shotLayersShapeProblems; exports.shotShapeProblems = shotShapeProblems; exports.simplifyTimedSamples = simplifyTimedSamples; exports.smoothMotionLegs = smoothMotionLegs; exports.smoothMotionTimes = smoothMotionTimes; exports.suggestName = _chunkDGWOGQJ5cjs.suggestName; exports.svgDataUrl = svgDataUrl; exports.toFlairEffectDocument = toFlairEffectDocument; exports.toFlairSvgMotion = toFlairSvgMotion; exports.toFlairVectorEffect = toFlairVectorEffect; exports.toLottie = toLottie; exports.trackValue = trackValue; exports.withAnimationPlayback = withAnimationPlayback; exports.withCameraViewEffects = withCameraViewEffects; exports.withMotionLayers = withMotionLayers; exports.withMotionShotLayers = withMotionShotLayers; exports.withViewBox = withViewBox; exports.withoutMotionEffect = withoutMotionEffect;