@pixerate/editor 0.143.0 → 0.145.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 (38) hide show
  1. package/dist/agent/index.cjs +6 -4
  2. package/dist/agent/index.js +4 -2
  3. package/dist/animation/index.cjs +2 -2
  4. package/dist/animation/index.js +1 -1
  5. package/dist/animation/player.d.cts +5 -0
  6. package/dist/animation/player.d.ts +5 -0
  7. package/dist/animation-editor/controller.d.cts +101 -1
  8. package/dist/animation-editor/controller.d.ts +101 -1
  9. package/dist/animation-editor/index.cjs +594 -147
  10. package/dist/animation-editor/index.d.cts +1 -0
  11. package/dist/animation-editor/index.d.ts +1 -0
  12. package/dist/animation-editor/index.js +525 -78
  13. package/dist/animation-editor/record.d.cts +114 -0
  14. package/dist/animation-editor/record.d.ts +114 -0
  15. package/dist/animation-editor/tools.d.cts +1 -0
  16. package/dist/animation-editor/tools.d.ts +1 -0
  17. package/dist/animation-editor/types.d.cts +6 -0
  18. package/dist/animation-editor/types.d.ts +6 -0
  19. package/dist/canvas/index.cjs +24 -24
  20. package/dist/canvas/index.js +1 -1
  21. package/dist/{chunk-XH32BATE.cjs → chunk-AG2EXVCR.cjs} +2 -2
  22. package/dist/{chunk-X2HQP7DF.cjs → chunk-D2ENR3LB.cjs} +43 -4
  23. package/dist/{chunk-54GVEVXS.js → chunk-HUHBJWTD.js} +43 -4
  24. package/dist/{chunk-I4T4J27S.js → chunk-QX25HWFL.js} +9 -0
  25. package/dist/{chunk-Y6SGC6KO.js → chunk-SA37RU2O.js} +1 -1
  26. package/dist/{chunk-SVR5FR7B.cjs → chunk-UUTTJK77.cjs} +9 -0
  27. package/dist/commands/index.cjs +2 -2
  28. package/dist/commands/index.js +1 -1
  29. package/dist/tools/index.cjs +5 -3
  30. package/dist/tools/index.js +4 -2
  31. package/dist/tools/playback.d.cts +18 -4
  32. package/dist/tools/playback.d.ts +18 -4
  33. package/dist/video-editor/index.cjs +2 -2
  34. package/dist/video-editor/index.js +1 -1
  35. package/package.json +1 -1
  36. package/skills/pixerate-agent-tools/SKILL.md +7 -1
  37. package/skills/pixerate-animation-editor/SKILL.md +10 -0
  38. package/skills/pixerate-animation-editor/references/commands.md +3 -1
@@ -68,7 +68,7 @@ var _chunkFJ2MSI43cjs = require('../chunk-FJ2MSI43.cjs');
68
68
  var _chunk7B5GTJXBcjs = require('../chunk-7B5GTJXB.cjs');
69
69
 
70
70
 
71
- var _chunkSVR5FR7Bcjs = require('../chunk-SVR5FR7B.cjs');
71
+ var _chunkUUTTJK77cjs = require('../chunk-UUTTJK77.cjs');
72
72
 
73
73
 
74
74
 
@@ -188,7 +188,7 @@ var COMMAND_KEYS = {
188
188
  enter: [...BASE_KEYS, ...DRAW_ORDER_KEYS, "style", "direction", "distance", "scale", "shape", "orientation"],
189
189
  exit: [...BASE_KEYS, ...DRAW_ORDER_KEYS, "style", "direction", "distance"],
190
190
  draw: [...BASE_KEYS, ...DRAW_ORDER_KEYS, "from", "to", "comet"],
191
- move: [...BASE_KEYS, "by", "to", "via", "pacing", "blend"],
191
+ move: [...BASE_KEYS, "by", "to", "via", "pacing", "times", "blend"],
192
192
  rotate: [...BASE_KEYS, "by", "to", "blend"],
193
193
  scale: [...BASE_KEYS, "to", "geometric", "blend"],
194
194
  fade: [...BASE_KEYS, "to"],
@@ -292,6 +292,13 @@ function commandShapeProblems(command3, where) {
292
292
  if (command3.via !== void 0 && !(Array.isArray(command3.via) && command3.via.length > 0 && command3.via.length <= 64 && command3.via.every(isPoint))) p(`"via" must be a list of 1 to 64 points { "x": number, "y": number } (document units).`);
293
293
  if (command3.pacing !== void 0 && command3.pacing !== "even" && command3.pacing !== "equal") p(`"pacing" must be "even" (steady speed) or "equal" (each leg the same time).`);
294
294
  if (command3.pacing !== void 0 && command3.via === void 0) p(`"pacing" goes with "via".`);
295
+ if (command3.times !== void 0) {
296
+ const times = command3.times;
297
+ const via = Array.isArray(command3.via) ? command3.via.length : null;
298
+ if (command3.via === void 0) p(`"times" goes with "via".`);
299
+ else if (!(Array.isArray(times) && times.length === via && times.every((t, i) => isNum(t) && t > 0 && t < 1 && (i === 0 || t > times[i - 1]))))
300
+ p(`"times" must be ${_nullishCoalesce(via, () => ( "one per waypoint"))} rising fractions of the duration between 0 and 1 (one per "via" point).`);
301
+ }
295
302
  break;
296
303
  case "rotate":
297
304
  if (command3.by === void 0 && command3.to === void 0) p(`give "by" or "to" (degrees).`);
@@ -887,8 +894,8 @@ function motionShotTimeScale(shot) {
887
894
  function motionBeatTime(grid, beat) {
888
895
  return (_nullishCoalesce(grid.offset, () => ( 0))) + beat * 6e4 / grid.bpm;
889
896
  }
890
- function motionBeatAt(grid, ms) {
891
- const beat = (ms - (_nullishCoalesce(grid.offset, () => ( 0)))) * grid.bpm / 6e4;
897
+ function motionBeatAt(grid, ms2) {
898
+ const beat = (ms2 - (_nullishCoalesce(grid.offset, () => ( 0)))) * grid.bpm / 6e4;
892
899
  const meter = Math.max(1, Math.round(_nullishCoalesce(grid.meter, () => ( 4))));
893
900
  const whole = Math.floor(beat + 1e-9);
894
901
  return { beat, bar: Math.floor(whole / meter) + 1, beatInBar: (whole % meter + meter) % meter + 1 };
@@ -982,8 +989,8 @@ function motionCycleDuration(command3, scene, target) {
982
989
  if (command3.type === "draw" || command3.type === "enter" && command3.style === "draw" || command3.type === "exit" && command3.style === "undraw") {
983
990
  const entry = target ? indexMotionNodes(scene.artwork.nodes).get(target) : void 0;
984
991
  const length = entry ? motionPathLength(entry.node) : 0;
985
- const ms = length * MOTION_DRAW_MS_PER_UNIT;
986
- return Math.round(Math.min(MOTION_DRAW_RANGE[1], Math.max(MOTION_DRAW_RANGE[0], ms)));
992
+ const ms2 = length * MOTION_DRAW_MS_PER_UNIT;
993
+ return Math.round(Math.min(MOTION_DRAW_RANGE[1], Math.max(MOTION_DRAW_RANGE[0], ms2)));
987
994
  }
988
995
  if (command3.type === "move" && _optionalChain([command3, 'access', _35 => _35.via, 'optionalAccess', _36 => _36.length])) return MOTION_DEFAULT_DURATIONS.move + 400 * command3.via.length;
989
996
  return MOTION_DEFAULT_DURATIONS[command3.type];
@@ -1014,9 +1021,9 @@ function compileMotionScene(scene) {
1014
1021
  if (shot.camera) {
1015
1022
  const rect = resolveMotionCamera(shot.camera, scene);
1016
1023
  if (rect && rect.width > 0 && rect.height > 0) {
1017
- const ms = index === 0 ? 0 : (_nullishCoalesce(shot.camera.ms, () => ( MOTION_CAMERA_MS))) * f;
1018
- cameraMoves.push({ shot: shot.id, start, end: start + ms, to: rect, ease: _nullishCoalesce(shot.camera.ease, () => ( MOTION_EASE_IN_OUT)), geometric: shot.camera.geometric === true });
1019
- cursor = start + ms;
1024
+ const ms2 = index === 0 ? 0 : (_nullishCoalesce(shot.camera.ms, () => ( MOTION_CAMERA_MS))) * f;
1025
+ 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 });
1026
+ cursor = start + ms2;
1020
1027
  }
1021
1028
  }
1022
1029
  const commandsStart = cursor;
@@ -1256,7 +1263,7 @@ function compileMotionScene(scene) {
1256
1263
  const y0 = num2("y");
1257
1264
  const add = command3.blend === "add";
1258
1265
  const offsets = [{ x: 0, y: 0 }, ...command3.via.map((p) => _chunkHRI3ZMDMcjs.applyMatrix.call(void 0, inv, p)).map((p) => ({ x: p.x - entry.center.x - x0, y: p.y - entry.center.y - y0 })), { x: dx, y: dy }];
1259
- const times = smoothMotionTimes(offsets, start, end - start, _nullishCoalesce(command3.pacing, () => ( "even")));
1266
+ const times = _optionalChain([command3, 'access', _50 => _50.times, 'optionalAccess', _51 => _51.length]) === command3.via.length ? [start, ...command3.times.map((f) => start + f * (end - start)), end] : smoothMotionTimes(offsets, start, end - start, _nullishCoalesce(command3.pacing, () => ( "even")));
1260
1267
  for (const axis of ["x", "y"]) {
1261
1268
  const base = add ? 0 : axis === "x" ? x0 : y0;
1262
1269
  for (const leg of smoothMotionLegs(times, offsets.map((o) => o[axis])))
@@ -1348,7 +1355,7 @@ function compileMotionScene(scene) {
1348
1355
  for (const track of tracks.values()) track.sort((a, b) => a.start - b.start);
1349
1356
  segments.sort((a, b) => a.start - b.start);
1350
1357
  const duration = shots.length ? shots[shots.length - 1].end : 0;
1351
- const timeline = { duration, shots, commands, segments, tracks, ..._optionalChain([scene, 'access', _50 => _50.drivers, 'optionalAccess', _51 => _51.length]) ? { drivers: scene.drivers, restOpacity: new Map([...nodes].map(([id, e]) => [id, _nullishCoalesce(e.node.opacity, () => ( 1))])) } : {} };
1358
+ const timeline = { duration, shots, commands, segments, tracks, ..._optionalChain([scene, 'access', _52 => _52.drivers, 'optionalAccess', _53 => _53.length]) ? { drivers: scene.drivers, restOpacity: new Map([...nodes].map(([id, e]) => [id, _nullishCoalesce(e.node.opacity, () => ( 1))])) } : {} };
1352
1359
  timelineCache.set(scene, timeline);
1353
1360
  return timeline;
1354
1361
  }
@@ -1361,7 +1368,7 @@ var MOTION_DRAW_EFFECT_ID = "~draw";
1361
1368
  var TRANSFORM_PROPS = ["x", "y", "rotation", "scaleX", "scaleY", "follow"];
1362
1369
  var tidy = (v) => Math.round(v * 1e6) / 1e6 || 0;
1363
1370
  function applyMotionControlValues(scene, values) {
1364
- if (!values || !_optionalChain([scene, 'access', _52 => _52.controls, 'optionalAccess', _53 => _53.length])) return scene;
1371
+ if (!values || !_optionalChain([scene, 'access', _54 => _54.controls, 'optionalAccess', _55 => _55.length])) return scene;
1365
1372
  const map = /* @__PURE__ */ new Map();
1366
1373
  for (const control of scene.controls) {
1367
1374
  const value = values[control.name];
@@ -1408,7 +1415,7 @@ function shapeIn(entry, shape) {
1408
1415
  }
1409
1416
  function shapeAt(entry, timeline, nodes, time) {
1410
1417
  const track = timeline.tracks.get(motionTrackKey(entry.node.id, "shape"));
1411
- if (!_optionalChain([track, 'optionalAccess', _54 => _54.length])) return null;
1418
+ if (!_optionalChain([track, 'optionalAccess', _56 => _56.length])) return null;
1412
1419
  const s = activeSegment(track, time);
1413
1420
  const fromId = String(s ? s.from : track[0].from);
1414
1421
  if (!s) return fromId === entry.node.id ? null : nodes.get(fromId) ? shapeIn(entry, nodes.get(fromId)) : null;
@@ -1431,8 +1438,8 @@ function motionFollowRoute(entry, path) {
1431
1438
  }
1432
1439
  function followAt(entry, timeline, nodes, time) {
1433
1440
  const track = timeline.tracks.get(motionTrackKey(entry.node.id, "follow"));
1434
- const s = _nullishCoalesce(activeSegment(track, time), () => ( _optionalChain([track, 'optionalAccess', _55 => _55[0]])));
1435
- if (!_optionalChain([s, 'optionalAccess', _56 => _56.path])) return null;
1441
+ const s = _nullishCoalesce(activeSegment(track, time), () => ( _optionalChain([track, 'optionalAccess', _57 => _57[0]])));
1442
+ if (!_optionalChain([s, 'optionalAccess', _58 => _58.path])) return null;
1436
1443
  const route = motionFollowRoute(entry, nodes.get(s.path));
1437
1444
  const progress = Number(trackValue(track, time));
1438
1445
  if (!route || !(progress >= 0)) return null;
@@ -1450,7 +1457,7 @@ function motionNodeOffset(entry, timeline, time, nodes = indexMotionNodes([entry
1450
1457
  const layered = withMotionLayers(timeline, entry.node.id, p, typeof v === "number" ? v : rest, time, raw);
1451
1458
  return p === "follow" ? layered : applyMotionDrivers(timeline, entry.node.id, p, layered, time, raw);
1452
1459
  };
1453
- const driven = _optionalChain([timeline, 'access', _57 => _57.drivers, 'optionalAccess', _58 => _58.some, 'call', _59 => _59((d) => d.target === entry.node.id && d.property !== "opacity")]);
1460
+ const driven = _optionalChain([timeline, 'access', _59 => _59.drivers, 'optionalAccess', _60 => _60.some, 'call', _61 => _61((d) => d.target === entry.node.id && d.property !== "opacity")]);
1454
1461
  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;
1455
1462
  const dx = read("x", 0);
1456
1463
  const dy = read("y", 0);
@@ -1494,7 +1501,7 @@ function motionFrameAt(scene, time, options = {}) {
1494
1501
  if (out.visible === void 0) delete out.visible;
1495
1502
  }
1496
1503
  const effectTracks = [...timeline.tracks.keys()].filter((k) => k.startsWith(`${id}\0effect:`));
1497
- if (effectTracks.length && _optionalChain([out, 'access', _60 => _60.effects, 'optionalAccess', _61 => _61.length])) {
1504
+ if (effectTracks.length && _optionalChain([out, 'access', _62 => _62.effects, 'optionalAccess', _63 => _63.length])) {
1498
1505
  out = {
1499
1506
  ...out,
1500
1507
  effects: out.effects.map((effect) => {
@@ -1534,7 +1541,7 @@ function motionFrameAt(scene, time, options = {}) {
1534
1541
  return p;
1535
1542
  });
1536
1543
  }
1537
- const opacity = _optionalChain([timeline, 'access', _62 => _62.drivers, 'optionalAccess', _63 => _63.some, 'call', _64 => _64((d) => d.target === id && d.property === "opacity")]) ? motionPropertyValue(timeline, id, "opacity", time) : value(id, "opacity");
1544
+ const opacity = _optionalChain([timeline, 'access', _64 => _64.drivers, 'optionalAccess', _65 => _65.some, 'call', _66 => _66((d) => d.target === id && d.property === "opacity")]) ? motionPropertyValue(timeline, id, "opacity", time) : value(id, "opacity");
1538
1545
  if (typeof opacity === "number") {
1539
1546
  const o = Math.min(1, Math.max(0, tidy(opacity)));
1540
1547
  out = { ...out, opacity: o };
@@ -1607,7 +1614,7 @@ function motionPath(scene, id, options = {}) {
1607
1614
  const r = (n) => Math.round(n * 100) / 100;
1608
1615
  const centreAt = (t) => {
1609
1616
  const node = indexMotionNodes(motionFrameAtCache(scene, t)).get(id);
1610
- const b = _optionalChain([node, 'optionalAccess', _65 => _65.bounds]);
1617
+ const b = _optionalChain([node, 'optionalAccess', _67 => _67.bounds]);
1611
1618
  return b ? { t: r(t), x: r(b.x + b.width / 2), y: r(b.y + b.height / 2) } : null;
1612
1619
  };
1613
1620
  const points = [];
@@ -1641,8 +1648,8 @@ function motionBlurTimes(scene, time, blur = {}) {
1641
1648
  const b = indexMotionNodes(motionFrameAt(scene, time + span / 2).document.nodes);
1642
1649
  let travel = 0;
1643
1650
  for (const id of ids) {
1644
- const p = _optionalChain([a, 'access', _66 => _66.get, 'call', _67 => _67(id), 'optionalAccess', _68 => _68.bounds]);
1645
- const q = _optionalChain([b, 'access', _69 => _69.get, 'call', _70 => _70(id), 'optionalAccess', _71 => _71.bounds]);
1651
+ const p = _optionalChain([a, 'access', _68 => _68.get, 'call', _69 => _69(id), 'optionalAccess', _70 => _70.bounds]);
1652
+ const q = _optionalChain([b, 'access', _71 => _71.get, 'call', _72 => _72(id), 'optionalAccess', _73 => _73.bounds]);
1646
1653
  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));
1647
1654
  }
1648
1655
  const n = Math.min(most, Math.ceil(travel / 1.5));
@@ -1654,7 +1661,7 @@ function averagedFrames(scene, times, options, prefix) {
1654
1661
  return `<g data-motion-blur="${n}" style="isolation:isolate">${times.map((t, k) => {
1655
1662
  const own = `${prefix}blur${k}-`;
1656
1663
  const frame = motionFrameToSvg(scene, t, { ...options, idPrefix: own, background: false });
1657
- const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _72 => _72.exec, 'call', _73 => _73(frame), 'optionalAccess', _74 => _74[1]]), () => ( ""));
1664
+ const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _74 => _74.exec, 'call', _75 => _75(frame), 'optionalAccess', _76 => _76[1]]), () => ( ""));
1658
1665
  const inner = k === n - 1 ? innerSvg(frame) : innerSvg(frame).replace(new RegExp(` id="(?!${own.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
1659
1666
  return `<svg viewBox="${view}" preserveAspectRatio="none" style="mix-blend-mode:plus-lighter" opacity="${Math.round(1e4 / n) / 1e4}">${inner}</svg>`;
1660
1667
  }).join("")}</g>`;
@@ -1669,7 +1676,7 @@ function motionStageSvg(scene, time, options = {}) {
1669
1676
  if (blurTimes.length > 1) {
1670
1677
  const open2 = current.indexOf(">") + 1;
1671
1678
  const body2 = current.slice(open2, current.lastIndexOf("</svg>"));
1672
- const bg2 = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _75 => _75.exec, 'call', _76 => _76(body2), 'optionalAccess', _77 => _77[0]]), () => ( "")) : "";
1679
+ const bg2 = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _77 => _77.exec, 'call', _78 => _78(body2), 'optionalAccess', _79 => _79[0]]), () => ( "")) : "";
1673
1680
  current = `${current.slice(0, open2)}${bg2}${averagedFrames(scene, blurTimes, svgOptions, prefix)}</svg>`;
1674
1681
  }
1675
1682
  if (!onionSkin && !motionPaths) return current;
@@ -1684,7 +1691,7 @@ function motionStageSvg(scene, time, options = {}) {
1684
1691
  const open = current.indexOf(">") + 1;
1685
1692
  const close = current.lastIndexOf("</svg>");
1686
1693
  const body = current.slice(open, close);
1687
- const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _78 => _78.exec, 'call', _79 => _79(body), 'optionalAccess', _80 => _80[0]]), () => ( "")) : "";
1694
+ const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _80 => _80.exec, 'call', _81 => _81(body), 'optionalAccess', _82 => _82[0]]), () => ( "")) : "";
1688
1695
  return `${current.slice(0, open)}${bg}${ghosts.join("")}${body.slice(bg.length)}${paths}</svg>`;
1689
1696
  }
1690
1697
  function motionContactSheetSvg(scene, times, options = {}) {
@@ -1703,7 +1710,7 @@ function motionContactSheetSvg(scene, times, options = {}) {
1703
1710
  const x = gap + i % cols * (w + gap);
1704
1711
  const y = gap + Math.floor(i / cols) * (h + label + gap);
1705
1712
  const frame = motionStageSvg(scene, t, { ...frameOptions, idPrefix: `${prefix}f${i}-` });
1706
- const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _81 => _81.exec, 'call', _82 => _82(frame), 'optionalAccess', _83 => _83[1]]), () => ( `0 0 ${w} ${h}`));
1713
+ const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _83 => _83.exec, 'call', _84 => _84(frame), 'optionalAccess', _85 => _85[1]]), () => ( `0 0 ${w} ${h}`));
1707
1714
  const inner = innerSvg(frame).replace(new RegExp(` id="(?!${`${prefix}f${i}-`.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
1708
1715
  const shot = _nullishCoalesce(timeline.shots.find((s) => t >= s.start && t < s.end), () => ( timeline.shots[timeline.shots.length - 1]));
1709
1716
  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>` : "";
@@ -1715,7 +1722,7 @@ function motionContactSheetSvg(scene, times, options = {}) {
1715
1722
  // src/animation-editor/check.ts
1716
1723
  var MOTION_MIN_CHANGE_MS = 120;
1717
1724
  var NUMERIC_PARAMS = (node, effectId) => {
1718
- const effect = _optionalChain([node, 'access', _84 => _84.effects, 'optionalAccess', _85 => _85.find, 'call', _86 => _86((e) => e.id === effectId)]);
1725
+ const effect = _optionalChain([node, 'access', _86 => _86.effects, 'optionalAccess', _87 => _87.find, 'call', _88 => _88((e) => e.id === effectId)]);
1719
1726
  if (!effect) return null;
1720
1727
  return Object.entries(effect.params).filter(([, v]) => typeof v === "number").map(([k]) => k);
1721
1728
  };
@@ -1801,7 +1808,7 @@ function checkMotionScene(scene) {
1801
1808
  if (!motionTextLayout(nodes, id))
1802
1809
  issues.push(_chunkEBDZWIOPcjs.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] }));
1803
1810
  }
1804
- if (command3.type === "play" && !_optionalChain([scene, 'access', _87 => _87.clips, 'optionalAccess', _88 => _88[command3.clip]]))
1811
+ if (command3.type === "play" && !_optionalChain([scene, 'access', _89 => _89.clips, 'optionalAccess', _90 => _90[command3.clip]]))
1805
1812
  issues.push(_chunkEBDZWIOPcjs.motionIssue.call(void 0, "unknown-clip", `There is no clip ${_chunkEBDZWIOPcjs.namedWithSuggestion.call(void 0, command3.clip, Object.keys(_nullishCoalesce(scene.clips, () => ( {}))))}; define it with set_animation_clip.`, where));
1806
1813
  if (command3.type === "match" && known(command3.into, "unknown-shape", "group", where)) {
1807
1814
  const into = nodes.get(command3.into);
@@ -1916,7 +1923,7 @@ function checkMotionScene(scene) {
1916
1923
  if (!shot.at) return;
1917
1924
  const timing = timeline.shots[i];
1918
1925
  if (timing.cue === void 0) {
1919
- 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', _89 => _89.narration, 'optionalAccess', _90 => _90.words, 'access', _91 => _91.length]), () => ( 0))} words (indexes from 0)`;
1926
+ 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', _91 => _91.narration, 'optionalAccess', _92 => _92.words, 'access', _93 => _93.length]), () => ( 0))} words (indexes from 0)`;
1920
1927
  issues.push(_chunkEBDZWIOPcjs.motionIssue.call(void 0, "unknown-cue", `Shot "${shot.id}" is cued to ${what}.`, { shot: shot.id }));
1921
1928
  } else if (timing.missedBy !== void 0) {
1922
1929
  const late = Math.round(timing.missedBy);
@@ -1949,7 +1956,7 @@ function checkMotionScene(scene) {
1949
1956
  const cam = frame.camera;
1950
1957
  const docIndex = indexMotionNodes(frame.document.nodes);
1951
1958
  for (const id of command3.targets) {
1952
- const b = _optionalChain([docIndex, 'access', _92 => _92.get, 'call', _93 => _93(id), 'optionalAccess', _94 => _94.bounds]);
1959
+ const b = _optionalChain([docIndex, 'access', _94 => _94.get, 'call', _95 => _95(id), 'optionalAccess', _96 => _96.bounds]);
1953
1960
  if (!b || reported.has(id) || !visibleAt(timeline, id, timing.end, nodes.get(id).node)) continue;
1954
1961
  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;
1955
1962
  if (outside) {
@@ -2181,9 +2188,9 @@ function applyMotionEdits(scene, edits) {
2181
2188
  let next = scene;
2182
2189
  for (let i = 0; i < list.length; i++) {
2183
2190
  const edit = list[i];
2184
- const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _95 => _95.op])}): ` : "";
2191
+ const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _97 => _97.op])}): ` : "";
2185
2192
  if (!edit || !MOTION_EDIT_OPS.includes(edit.op))
2186
- return { ok: false, error: `${prefix}Unknown op ${_chunkEBDZWIOPcjs.namedWithSuggestion.call(void 0, String(_optionalChain([edit, 'optionalAccess', _96 => _96.op])), MOTION_EDIT_OPS)}. Nothing was changed.` };
2193
+ return { ok: false, error: `${prefix}Unknown op ${_chunkEBDZWIOPcjs.namedWithSuggestion.call(void 0, String(_optionalChain([edit, 'optionalAccess', _98 => _98.op])), MOTION_EDIT_OPS)}. Nothing was changed.` };
2187
2194
  try {
2188
2195
  next = applyOne(next, edit);
2189
2196
  } catch (error) {
@@ -2205,7 +2212,7 @@ function resolveMotionEditRefs(scene, edits) {
2205
2212
  const resolved = [];
2206
2213
  let next = scene;
2207
2214
  for (let i = 0; i < edits.length; i++) {
2208
- const where = `op ${i + 1} (${_optionalChain([edits, 'access', _97 => _97[i], 'optionalAccess', _98 => _98.op])})`;
2215
+ const where = `op ${i + 1} (${_optionalChain([edits, 'access', _99 => _99[i], 'optionalAccess', _100 => _100.op])})`;
2209
2216
  let problem = null;
2210
2217
  const walk = (v) => {
2211
2218
  if (typeof v === "string") {
@@ -2238,9 +2245,9 @@ function resolveMotionEditRefs(scene, edits) {
2238
2245
  resolved.push(edit);
2239
2246
  const out = { op: edit.op };
2240
2247
  if (edit.op === "insert_shots") out.shots = edit.shots.map((s) => s.id);
2241
- if ("shot" in edit && typeof edit.shot === "string") out.shot = edit.op === "update_shot" && _optionalChain([edit, 'access', _99 => _99.set, 'optionalAccess', _100 => _100.id]) ? edit.set.id : edit.shot;
2248
+ if ("shot" in edit && typeof edit.shot === "string") out.shot = edit.op === "update_shot" && _optionalChain([edit, 'access', _101 => _101.set, 'optionalAccess', _102 => _102.id]) ? edit.set.id : edit.shot;
2242
2249
  if (edit.op === "add_commands") {
2243
- const at = _nullishCoalesce(_nullishCoalesce(edit.at, () => ( _optionalChain([before, 'access', _101 => _101.shots, 'access', _102 => _102.find, 'call', _103 => _103((s) => s.id === edit.shot), 'optionalAccess', _104 => _104.run, 'access', _105 => _105.length]))), () => ( 0));
2250
+ const at = _nullishCoalesce(_nullishCoalesce(edit.at, () => ( _optionalChain([before, 'access', _103 => _103.shots, 'access', _104 => _104.find, 'call', _105 => _105((s) => s.id === edit.shot), 'optionalAccess', _106 => _106.run, 'access', _107 => _107.length]))), () => ( 0));
2244
2251
  out.indexes = edit.commands.map((_, k) => at + k);
2245
2252
  }
2246
2253
  if (edit.op === "move_command") out.indexes = [edit.to];
@@ -2255,8 +2262,8 @@ function createMotionScene(artwork, init = {}) {
2255
2262
 
2256
2263
  // src/animation-editor/fit.ts
2257
2264
  var MOTION_SPEED_RANGE = [0.05, 20];
2258
- function fitMotionScene(scene, ms, options = {}) {
2259
- if (!(Number.isFinite(ms) && ms > 0)) throw new Error("Give the length to fit as a number of milliseconds above 0.");
2265
+ function fitMotionScene(scene, ms2, options = {}) {
2266
+ if (!(Number.isFinite(ms2) && ms2 > 0)) throw new Error("Give the length to fit as a number of milliseconds above 0.");
2260
2267
  const ids = scene.shots.map((s) => s.id);
2261
2268
  const keep = new Set(_nullishCoalesce(options.keep, () => ( [])));
2262
2269
  for (const id of keep) if (!ids.includes(id)) throw new Error(`There is no shot ${_chunkEBDZWIOPcjs.namedWithSuggestion.call(void 0, id, ids)} to keep.`);
@@ -2265,14 +2272,14 @@ function fitMotionScene(scene, ms, options = {}) {
2265
2272
  let rest = 0;
2266
2273
  timeline.shots.forEach((t) => keep.has(t.id) ? kept += t.end - t.start : rest += t.end - t.start);
2267
2274
  if (rest <= 0) throw new Error(keep.size ? "Every shot is kept, so nothing can change pace; keep fewer shots." : "The scene has no length to fit; add shots first.");
2268
- if (ms <= kept) throw new Error(`The kept shots alone last ${Math.round(kept)} ms, so the scene cannot fit ${Math.round(ms)} ms; keep fewer shots or give at least ${Math.ceil(kept) + 1} ms.`);
2269
- const k = (ms - kept) / rest;
2275
+ if (ms2 <= kept) throw new Error(`The kept shots alone last ${Math.round(kept)} ms, so the scene cannot fit ${Math.round(ms2)} ms; keep fewer shots or give at least ${Math.ceil(kept) + 1} ms.`);
2276
+ const k = (ms2 - kept) / rest;
2270
2277
  const speeds = {};
2271
2278
  const shots = scene.shots.map((shot) => {
2272
2279
  if (keep.has(shot.id)) return shot;
2273
2280
  const speed = Math.round((_nullishCoalesce(shot.speed, () => ( 1))) / k * 1e4) / 1e4;
2274
2281
  if (speed < MOTION_SPEED_RANGE[0] || speed > MOTION_SPEED_RANGE[1])
2275
- throw new Error(`Fitting ${Math.round(ms)} ms would play shot "${shot.id}" at ${speed}\xD7 (speeds run ${MOTION_SPEED_RANGE[0]}\u2013${MOTION_SPEED_RANGE[1]}); aim nearer the current ${Math.round(timeline.duration)} ms.`);
2282
+ throw new Error(`Fitting ${Math.round(ms2)} ms would play shot "${shot.id}" at ${speed}\xD7 (speeds run ${MOTION_SPEED_RANGE[0]}\u2013${MOTION_SPEED_RANGE[1]}); aim nearer the current ${Math.round(timeline.duration)} ms.`);
2276
2283
  speeds[shot.id] = speed;
2277
2284
  const { speed: _old, ...rest2 } = shot;
2278
2285
  return speed === 1 ? rest2 : { ...rest2, speed };
@@ -2281,6 +2288,214 @@ function fitMotionScene(scene, ms, options = {}) {
2281
2288
  return { scene: fitted, duration: compileMotionScene(fitted).duration, speeds };
2282
2289
  }
2283
2290
 
2291
+ // src/animation-editor/record.ts
2292
+ var TOLERANCE = { move: 1, scale: 0.01, rotation: 0.5, opacity: 0.01, strokeWidth: 0.25 };
2293
+ var COLOR_GAP = 80;
2294
+ var MAX_VIA = 64;
2295
+ function motionNodePose(scene, id, time) {
2296
+ const entry = indexMotionNodes(scene.artwork.nodes).get(id);
2297
+ if (!entry) throw new Error(`There is no element ${_chunkEBDZWIOPcjs.namedWithSuggestion.call(void 0, id, [...indexMotionNodes(scene.artwork.nodes).keys()])} in the artwork.`);
2298
+ const timeline = compileMotionScene(scene);
2299
+ const drawn = indexMotionNodes(motionFrameAt(scene, time).document.nodes).get(id);
2300
+ const centre = _chunkHRI3ZMDMcjs.applyMatrix.call(void 0, entry.parentWorld, { x: entry.center.x + motionPropertyValue(timeline, id, "x", time), y: entry.center.y + motionPropertyValue(timeline, id, "y", time) });
2301
+ const paint = (property) => _nullishCoalesce(trackValue(timeline.tracks.get(motionTrackKey(id, property)), time), () => ( motionRestValue(entry, property, scene.artwork)));
2302
+ const fill = paint("fill");
2303
+ const stroke = paint("stroke");
2304
+ const width = paint("strokeWidth");
2305
+ return {
2306
+ x: centre.x,
2307
+ y: centre.y,
2308
+ scaleX: motionPropertyValue(timeline, id, "scaleX", time),
2309
+ scaleY: motionPropertyValue(timeline, id, "scaleY", time),
2310
+ rotation: motionPropertyValue(timeline, id, "rotation", time),
2311
+ opacity: motionPropertyValue(timeline, id, "opacity", time),
2312
+ ...typeof fill === "string" && fill !== "transparent" ? { fill } : {},
2313
+ ...typeof stroke === "string" && stroke !== "transparent" ? { stroke } : {},
2314
+ ...typeof width === "number" ? { strokeWidth: width } : {},
2315
+ bounds: _nullishCoalesce(_optionalChain([drawn, 'optionalAccess', _108 => _108.bounds]), () => ( null))
2316
+ };
2317
+ }
2318
+ function simplifyTimedSamples(times, values, tol) {
2319
+ const n = times.length;
2320
+ if (n <= 2) return [...Array(n).keys()];
2321
+ const keep = new Uint8Array(n);
2322
+ keep[0] = keep[n - 1] = 1;
2323
+ const stack = [[0, n - 1]];
2324
+ while (stack.length) {
2325
+ const [a, b] = stack.pop();
2326
+ let worst = -1;
2327
+ let at = -1;
2328
+ const span = times[b] - times[a];
2329
+ for (let i = a + 1; i < b; i++) {
2330
+ const u = span > 0 ? (times[i] - times[a]) / span : 0;
2331
+ let e = 0;
2332
+ for (let d = 0; d < values[i].length; d++) {
2333
+ const v = values[a][d] + (values[b][d] - values[a][d]) * u;
2334
+ e += (values[i][d] - v) ** 2;
2335
+ }
2336
+ if (e > worst) {
2337
+ worst = e;
2338
+ at = i;
2339
+ }
2340
+ }
2341
+ if (at >= 0 && Math.sqrt(worst) > tol) {
2342
+ keep[at] = 1;
2343
+ stack.push([a, at], [at, b]);
2344
+ }
2345
+ }
2346
+ const out = [];
2347
+ keep.forEach((k, i) => k && out.push(i));
2348
+ return out;
2349
+ }
2350
+ var round = (n, places = 2) => Math.round(n * 10 ** places) / 10 ** places || 0;
2351
+ var ms = (n) => Math.max(0, Math.round(n));
2352
+ function motionTakeProblems(scene, take) {
2353
+ const problems = [];
2354
+ const nodes = indexMotionNodes(scene.artwork.nodes);
2355
+ if (!Array.isArray(take.samples) || !take.samples.length) return ["Give at least one sample."];
2356
+ const num2 = (v) => typeof v === "number" && Number.isFinite(v);
2357
+ take.samples.forEach((s, i) => {
2358
+ const where = `Sample ${i + 1}`;
2359
+ if (!s || typeof s !== "object") return problems.push(`${where} must be an object.`);
2360
+ if (typeof s.node !== "string" || !nodes.has(s.node)) problems.push(`${where}: the artwork has no element ${_chunkEBDZWIOPcjs.namedWithSuggestion.call(void 0, String(s.node), [...nodes.keys()])}; get_animation lists the element ids.`);
2361
+ if (!num2(s.t) || s.t < 0) problems.push(`${where}: "t" must be ms from the start of the take, \u2265 0.`);
2362
+ if (s.x === void 0 !== (s.y === void 0)) problems.push(`${where}: give "x" and "y" together (the centre, document units).`);
2363
+ 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.`);
2364
+ if (s.opacity !== void 0 && num2(s.opacity) && (s.opacity < 0 || s.opacity > 1)) problems.push(`${where}: "opacity" must be from 0 to 1.`);
2365
+ if (s.strokeWidth !== void 0 && num2(s.strokeWidth) && s.strokeWidth < 0) problems.push(`${where}: "strokeWidth" must be \u2265 0.`);
2366
+ for (const k of ["fill", "stroke"]) if (s[k] !== void 0 && (typeof s[k] !== "string" || _chunkA6QWKBJPcjs.normalizeTokenColor.call(void 0, s[k]) === null)) problems.push(`${where}: "${k}" must be a colour such as "#0ea5e9".`);
2367
+ if (["x", "scaleX", "scaleY", "rotation", "opacity", "fill", "stroke", "strokeWidth"].every((k) => s[k] === void 0)) problems.push(`${where} records nothing; give at least one of x and y, scaleX, scaleY, rotation, opacity, fill, stroke, strokeWidth.`);
2368
+ });
2369
+ if (take.length !== void 0 && !(num2(take.length) && take.length >= 0)) problems.push('"length" must be ms \u2265 0.');
2370
+ return problems;
2371
+ }
2372
+ function channelSamples(samples, pick) {
2373
+ const out = [];
2374
+ for (const s of samples) {
2375
+ const v = pick(s);
2376
+ if (v === void 0) continue;
2377
+ if (out.length && Math.abs(out[out.length - 1].t - s.t) < 0.5) out[out.length - 1] = { t: s.t, v };
2378
+ else out.push({ t: s.t, v });
2379
+ }
2380
+ return out;
2381
+ }
2382
+ function channelCommands(node, channel, pose, samples, tol, kept) {
2383
+ const targets = [node];
2384
+ const out = [];
2385
+ if (channel === "fill" || channel === "stroke") {
2386
+ const list2 = channelSamples(samples, (s) => s[channel] !== void 0 ? _nullishCoalesce(_chunkA6QWKBJPcjs.normalizeTokenColor.call(void 0, s[channel]), () => ( void 0)) : void 0);
2387
+ let last = pose[channel] ? _chunkA6QWKBJPcjs.normalizeTokenColor.call(void 0, pose[channel]) : null;
2388
+ let lastT = -Infinity;
2389
+ list2.forEach((s, i) => {
2390
+ const final = i === list2.length - 1;
2391
+ if (s.v === last || !final && s.t - lastT < COLOR_GAP) return;
2392
+ const duration = lastT === -Infinity ? 0 : s.t - lastT;
2393
+ out.push({ start: s.t - duration, command: { type: "style", targets, [channel]: s.v, duration: ms(duration), ease: "linear" } });
2394
+ kept.n++;
2395
+ last = s.v;
2396
+ lastT = s.t;
2397
+ });
2398
+ return out;
2399
+ }
2400
+ const pick = channel === "move" ? (s) => s.x !== void 0 && s.y !== void 0 ? [s.x, s.y] : void 0 : channel === "scale" ? (s) => s.scaleX !== void 0 || s.scaleY !== void 0 ? [_nullishCoalesce(s.scaleX, () => ( s.scaleY)), _nullishCoalesce(s.scaleY, () => ( s.scaleX))] : void 0 : (s) => s[channel] !== void 0 ? [s[channel]] : void 0;
2401
+ const list = channelSamples(samples, pick);
2402
+ if (!list.length) return out;
2403
+ const rest = channel === "move" ? [pose.x, pose.y] : channel === "scale" ? [pose.scaleX, pose.scaleY] : [_nullishCoalesce(pose[channel], () => ( (channel === "strokeWidth" ? 1 : 0)))];
2404
+ const limit = TOLERANCE[channel] * tol;
2405
+ const differs = (a, b) => Math.hypot(...a.map((v, d) => v - b[d])) > limit;
2406
+ const make = (to, duration, ease, extra = {}) => {
2407
+ const base = { targets, duration: ms(duration), ease, ...extra };
2408
+ switch (channel) {
2409
+ case "move":
2410
+ return { type: "move", ...base, to: { x: round(to[0]), y: round(to[1]) } };
2411
+ case "scale":
2412
+ return { type: "scale", ...base, to: Math.abs(to[0] - to[1]) < 1e-4 ? round(to[0], 4) : { x: round(to[0], 4), y: round(to[1], 4) } };
2413
+ case "rotation":
2414
+ return { type: "rotate", ...base, to: round(to[0]) };
2415
+ case "opacity":
2416
+ return { type: "fade", ...base, to: round(to[0], 4) };
2417
+ default:
2418
+ return { type: "style", ...base, strokeWidth: round(to[0]) };
2419
+ }
2420
+ };
2421
+ if (differs(list[0].v, rest)) {
2422
+ out.push({ start: list[0].t, command: make(list[0].v, 0, "linear") });
2423
+ kept.n++;
2424
+ }
2425
+ if (list.length < 2) return out;
2426
+ const times = list.map((s) => s.t);
2427
+ const values = list.map((s) => s.v);
2428
+ let factor = 1;
2429
+ let keep = simplifyTimedSamples(times, values, limit);
2430
+ if (channel === "move") {
2431
+ while (keep.length - 2 > MAX_VIA) keep = simplifyTimedSamples(times, values, limit * (factor *= 1.5));
2432
+ const first = list[keep[0]];
2433
+ const end = list[keep[keep.length - 1]];
2434
+ if (!keep.some((k) => differs(values[k], first.v))) return out;
2435
+ const span = end.t - first.t;
2436
+ const via = [];
2437
+ for (const k of keep.slice(1, -1)) {
2438
+ const f = round((list[k].t - first.t) / span, 6);
2439
+ if (f > (_nullishCoalesce(_optionalChain([via, 'access', _109 => _109.at, 'call', _110 => _110(-1), 'optionalAccess', _111 => _111.f]), () => ( 0))) && f < 1) via.push({ s: list[k], f });
2440
+ }
2441
+ const extra = via.length ? { via: via.map(({ s }) => ({ x: round(s.v[0]), y: round(s.v[1]) })), times: via.map(({ f }) => f) } : {};
2442
+ out.push({ start: first.t, command: make(end.v, span, "linear", extra) });
2443
+ kept.n += via.length + 2;
2444
+ return out;
2445
+ }
2446
+ const kt = keep.map((k) => times[k]);
2447
+ const legs = values[0].map((_, d) => smoothMotionLegs(kt, keep.map((k) => values[k][d])));
2448
+ for (let i = 0; i + 1 < keep.length; i++) {
2449
+ const a = values[keep[i]];
2450
+ const b = values[keep[i + 1]];
2451
+ if (!differs(a, b)) continue;
2452
+ const lead = a.length > 1 && Math.abs(b[1] - a[1]) > Math.abs(b[0] - a[0]) ? 1 : 0;
2453
+ const leg = legs[lead][i];
2454
+ out.push({ start: leg.start, command: make(b, leg.end - leg.start, leg.ease) });
2455
+ kept.n++;
2456
+ }
2457
+ return out;
2458
+ }
2459
+ function motionTakeShot(scene, take, options = {}) {
2460
+ const problems = motionTakeProblems(scene, take);
2461
+ if (problems.length) throw new Error(problems.join(" "));
2462
+ const tol = options.tolerance !== void 0 && options.tolerance > 0 ? options.tolerance : 1;
2463
+ const at = _nullishCoalesce(options.at, () => ( scene.shots.length));
2464
+ if (!Number.isInteger(at) || at < 0 || at > scene.shots.length) throw new Error(`"at" must be a whole number from 0 to ${scene.shots.length} (got ${at}).`);
2465
+ const timeline = compileMotionScene(scene);
2466
+ const start = at < timeline.shots.length ? timeline.shots[at].start : timeline.duration;
2467
+ const samples = [...take.samples].sort((a, b) => a.t - b.t);
2468
+ const placed = [];
2469
+ const kept = { n: 0 };
2470
+ const byNode = /* @__PURE__ */ new Map();
2471
+ for (const s of samples) byNode.set(s.node, [..._nullishCoalesce(byNode.get(s.node), () => ( [])), s]);
2472
+ for (const [node, list] of byNode) {
2473
+ const pose = motionNodePose(scene, node, start);
2474
+ for (const channel of ["move", "scale", "rotation", "opacity", "strokeWidth", "fill", "stroke"]) placed.push(...channelCommands(node, channel, pose, list, tol, kept));
2475
+ }
2476
+ placed.sort((a, b) => a.start - b.start);
2477
+ const run = [];
2478
+ let prev = 0;
2479
+ let end = 0;
2480
+ placed.forEach((p, i) => {
2481
+ const s = ms(p.start);
2482
+ const delay = i === 0 ? s : s - prev;
2483
+ run.push({ ...p.command, ...i > 0 ? { with: "previous" } : {}, ...delay > 0 ? { delay } : {} });
2484
+ prev = s;
2485
+ end = Math.max(end, s + (_nullishCoalesce(p.command.duration, () => ( 0))));
2486
+ });
2487
+ const length = _nullishCoalesce(take.length, () => ( samples[samples.length - 1].t));
2488
+ const shot = { id: _nullishCoalesce(options.id, () => ( freshShotId(scene, "take"))), label: _nullishCoalesce(options.label, () => ( "Recorded take")), run, hold: ms(Math.max(0, length - end)) };
2489
+ if (!run.length) Object.assign(shot, { run: [{ type: "wait", duration: ms(Math.max(1, length)) }], hold: 0 });
2490
+ return { shot, at, start, samples: { given: take.samples.length, kept: kept.n } };
2491
+ }
2492
+ function motionClientToDocument(camera, box, clientX, clientY) {
2493
+ const k = Math.min(box.width / camera.width, box.height / camera.height) || 1;
2494
+ const ox = box.left + (box.width - camera.width * k) / 2;
2495
+ const oy = box.top + (box.height - camera.height * k) / 2;
2496
+ return { x: camera.x + (clientX - ox) / k, y: camera.y + (clientY - oy) / k };
2497
+ }
2498
+
2284
2499
  // src/animation-editor/flair.ts
2285
2500
  var FLAIR_EASES = {
2286
2501
  linear: (t) => t,
@@ -2327,7 +2542,7 @@ function toFlairSvgMotion(scene, options = {}) {
2327
2542
  const behaviors = _nullishCoalesce(options.behaviors, () => ( flairAtLeast(flair, "0.38")));
2328
2543
  const springs = flairAtLeast(flair, "0.39");
2329
2544
  const strobes = flairAtLeast(flair, "0.39");
2330
- const writeEase = (ease) => typeof ease === "string" || "spring" in ease ? ease : ease.map(round2);
2545
+ const writeEase = (ease) => typeof ease === "string" || "spring" in ease ? ease : ease.map(round3);
2331
2546
  const easeOf2 = (segment) => {
2332
2547
  const resolved = _chunkDWJJI6RTcjs.resolveEasing.call(void 0, segment.ease);
2333
2548
  if (resolved !== "hold" && resolved.type === "spring") {
@@ -2340,8 +2555,8 @@ function toFlairSvgMotion(scene, options = {}) {
2340
2555
  return back && curves ? flairCurveFor(back, curves) : void 0;
2341
2556
  };
2342
2557
  const plays = (s) => (!s.behavior || behaviors) && (!s.steps || strobes) && !s.geometric;
2343
- const round2 = (n) => Math.round(n * 10 ** precision) / 10 ** precision;
2344
- const f = (n) => String(round2(n));
2558
+ const round3 = (n) => Math.round(n * 10 ** precision) / 10 ** precision;
2559
+ const f = (n) => String(round3(n));
2345
2560
  const timeline = compileMotionScene(scene);
2346
2561
  const nodes = indexMotionNodes(scene.artwork.nodes);
2347
2562
  const notes = [];
@@ -2399,7 +2614,7 @@ function toFlairSvgMotion(scene, options = {}) {
2399
2614
  const wrapper = freshId(`${id}--follow`);
2400
2615
  const centre = freshId(`${id}--follow-centre`);
2401
2616
  const c = entry.center;
2402
- const at = _optionalChain([motionNodeOffset, 'call', _106 => _106(entry, timeline, 0, nodes), 'optionalAccess', _107 => _107.follow]);
2617
+ const at = _optionalChain([motionNodeOffset, 'call', _112 => _112(entry, timeline, 0, nodes), 'optionalAccess', _113 => _113.follow]);
2403
2618
  const placed = at ? `translate(${f(at.x)} ${f(at.y)})${at.angle ? ` rotate(${f(at.angle)})` : ""}` : void 0;
2404
2619
  (wrap[id] ??= []).push({ id: wrapper, ...placed ? { transform: placed } : {} }, { id: centre, transform: `translate(${f(-c.x)} ${f(-c.y)})` });
2405
2620
  follows.push({ id, wrapper, routes: ids, segments: followSegments.filter((seg) => seg.path && ids.has(seg.path)) });
@@ -2407,7 +2622,7 @@ function toFlairSvgMotion(scene, options = {}) {
2407
2622
  }
2408
2623
  const opacitySources = sourcesOf(id, ["opacity"]);
2409
2624
  if (timeline.tracks.has(motionTrackKey(id, "opacity")) || opacitySources.length)
2410
- channels.push({ id, kind: "attr", attr: "opacity", segments: [...track(id, "opacity"), ...opacitySources], exact: (s) => plays(s) && !drivenBy.has(s), value: (t) => round2(motionPropertyValue(timeline, id, "opacity", t)) });
2625
+ channels.push({ id, kind: "attr", attr: "opacity", segments: [...track(id, "opacity"), ...opacitySources], exact: (s) => plays(s) && !drivenBy.has(s), value: (t) => round3(motionPropertyValue(timeline, id, "opacity", t)) });
2411
2626
  const followed = follows.some((x) => x.id === id);
2412
2627
  const transformSegments = [...TRANSFORM_PROPS2.flatMap((p) => followed && p === "follow" ? [] : [...track(id, p), ..._nullishCoalesce(timeline.tracks.get(motionAddTrackKey(id, p)), () => ( []))]), ...sourcesOf(id, ["x", "y", "rotation", "scaleX", "scaleY"])];
2413
2628
  if (transformSegments.length) {
@@ -2440,11 +2655,11 @@ function toFlairSvgMotion(scene, options = {}) {
2440
2655
  paint("fill", "fill");
2441
2656
  paint("stroke", "stroke");
2442
2657
  const width = owner(path, "strokeWidth");
2443
- if (width) channels.push({ id: path.id, kind: "attr", attr: "stroke-width", segments: [...track(width, "strokeWidth")], exact: () => true, value: (t) => round2(Number(trackValue(track(width, "strokeWidth"), t))) });
2658
+ if (width) channels.push({ id: path.id, kind: "attr", attr: "stroke-width", segments: [...track(width, "strokeWidth")], exact: () => true, value: (t) => round3(Number(trackValue(track(width, "strokeWidth"), t))) });
2444
2659
  const fade = owner(path, "fillOpacity");
2445
- if (fade) channels.push({ id: path.id, kind: "attr", attr: "fill-opacity", segments: [...track(fade, "fillOpacity")], exact: () => true, value: (t) => round2(Number(trackValue(track(fade, "fillOpacity"), t))) });
2660
+ if (fade) channels.push({ id: path.id, kind: "attr", attr: "fill-opacity", segments: [...track(fade, "fillOpacity")], exact: () => true, value: (t) => round3(Number(trackValue(track(fade, "fillOpacity"), t))) });
2446
2661
  const draw = owner(path, "drawEnd");
2447
- if (draw) channels.push({ id: path.id, kind: "draw", segments: [...track(draw, "drawEnd")], exact: () => true, value: (t) => round2(Math.min(1, Math.max(0, Number(trackValue(track(draw, "drawEnd"), t))))) });
2662
+ if (draw) channels.push({ id: path.id, kind: "draw", segments: [...track(draw, "drawEnd")], exact: () => true, value: (t) => round3(Math.min(1, Math.max(0, Number(trackValue(track(draw, "drawEnd"), t))))) });
2448
2663
  if (owner(path, "drawStart")) notes.push(`"${path.id}": drawing from a start other than 0 is not exported; Flair draws from the start of the path.`);
2449
2664
  }
2450
2665
  const shapeSegments = [...timeline.tracks.entries()].filter(([k]) => k.startsWith(`${id}\0`) && (k.endsWith("\0shape") || k.includes("\0effect:"))).flatMap(([, v]) => v);
@@ -2460,7 +2675,7 @@ function toFlairSvgMotion(scene, options = {}) {
2460
2675
  exact: () => false,
2461
2676
  value: (t) => {
2462
2677
  const frame = motionFrameAt(scene, t, { draw: false, offsets: false });
2463
- const now = _chunkHRI3ZMDMcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _108 => _108(frame.document.nodes), 'access', _109 => _109.get, 'call', _110 => _110(id), 'optionalAccess', _111 => _111.node]));
2678
+ const now = _chunkHRI3ZMDMcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _114 => _114(frame.document.nodes), 'access', _115 => _115.get, 'call', _116 => _116(id), 'optionalAccess', _117 => _117.node]));
2464
2679
  if (!now || now.type !== "path") return "";
2465
2680
  const bezier = _chunkGF2RBSDMcjs.transformBezierPath.call(void 0, _chunkHRI3ZMDMcjs.vectorPathWorldBezier.call(void 0, now, _chunkHRI3ZMDMcjs.IDENTITY_MATRIX, { effects: false }), inverse);
2466
2681
  return _chunkGF2RBSDMcjs.bezierPathToSvg.call(void 0, bezier, { precision: Math.min(precision, 2) });
@@ -2470,19 +2685,19 @@ function toFlairSvgMotion(scene, options = {}) {
2470
2685
  }
2471
2686
  }
2472
2687
  const camera = track("", "camera");
2473
- if (camera.length) channels.push({ id: "", kind: "view", segments: [...camera], exact: () => true, overshoot: true, value: (t) => trackValue(camera, t).map(round2) });
2688
+ if (camera.length) channels.push({ id: "", kind: "view", segments: [...camera], exact: () => true, overshoot: true, value: (t) => trackValue(camera, t).map(round3) });
2474
2689
  const animate = [];
2475
2690
  let sampled = 0;
2476
2691
  const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
2477
- const emit = (channel, delay, ms, from, to, ease, segment) => {
2692
+ const emit = (channel, delay, ms2, from, to, ease, segment) => {
2478
2693
  const written = writeEase(ease);
2479
2694
  const base = {
2480
2695
  ...channel.id ? { id: channel.id } : {},
2481
- ...delay > 0 ? { delay: round2(delay) } : {},
2482
- ms: round2(ms),
2696
+ ...delay > 0 ? { delay: round3(delay) } : {},
2697
+ ms: round3(ms2),
2483
2698
  ease: written,
2484
- ..._optionalChain([segment, 'optionalAccess', _112 => _112.behavior]) ? { behavior: segment.behavior } : {},
2485
- ..._optionalChain([segment, 'optionalAccess', _113 => _113.steps]) ? { strobe: segment.steps } : {}
2699
+ ..._optionalChain([segment, 'optionalAccess', _118 => _118.behavior]) ? { behavior: segment.behavior } : {},
2700
+ ..._optionalChain([segment, 'optionalAccess', _119 => _119.steps]) ? { strobe: segment.steps } : {}
2486
2701
  };
2487
2702
  if (channel.kind === "view") animate.push({ ...base, view: to });
2488
2703
  else if (channel.kind === "draw") animate.push({ ...base, draw: [from, to] });
@@ -2533,11 +2748,11 @@ function toFlairSvgMotion(scene, options = {}) {
2533
2748
  const starts = follow.segments.map((seg) => seg.start);
2534
2749
  for (const seg of follow.segments) {
2535
2750
  const along = { id: follow.wrapper, along: follow.routes.get(seg.path), ...seg.rotate ? { orient: true } : {} };
2536
- const progress = (t) => round2(Math.min(1, Math.max(0, Number(segmentValue(seg, t)))));
2751
+ const progress = (t) => round3(Math.min(1, Math.max(0, Number(segmentValue(seg, t)))));
2537
2752
  const curve = seg.end > seg.start ? plays(seg) ? easeOf2(seg) : void 0 : "linear";
2538
2753
  if (curve && !(overshoots(curve) && seg.rotate)) {
2539
2754
  const ends = seg.reverse ? [Number(seg.to), Number(seg.from)] : [Number(seg.from), Number(seg.to)];
2540
- animate.push({ ...along, ...seg.start > 0 ? { delay: round2(seg.start) } : {}, ms: round2(seg.end - seg.start), ease: writeEase(curve), ...seg.steps ? { strobe: seg.steps } : {}, at: [round2(ends[0]), round2(ends[1])] });
2755
+ animate.push({ ...along, ...seg.start > 0 ? { delay: round3(seg.start) } : {}, ms: round3(seg.end - seg.start), ease: writeEase(curve), ...seg.steps ? { strobe: seg.steps } : {}, at: [round3(ends[0]), round3(ends[1])] });
2541
2756
  continue;
2542
2757
  }
2543
2758
  const until = Math.min(seg.end, ...starts.filter((t) => t > seg.start));
@@ -2545,7 +2760,7 @@ function toFlairSvgMotion(scene, options = {}) {
2545
2760
  for (let k = 1; k <= n; k++) {
2546
2761
  const t0 = seg.start + (until - seg.start) * (k - 1) / n;
2547
2762
  const t1 = seg.start + (until - seg.start) * k / n;
2548
- animate.push({ ...along, ...t0 > 0 ? { delay: round2(t0) } : {}, ms: round2(t1 - t0), ease: "linear", at: [progress(t0), progress(t1)] });
2763
+ animate.push({ ...along, ...t0 > 0 ? { delay: round3(t0) } : {}, ms: round3(t1 - t0), ease: "linear", at: [progress(t0), progress(t1)] });
2549
2764
  sampled++;
2550
2765
  }
2551
2766
  }
@@ -2560,7 +2775,7 @@ function toFlairSvgMotion(scene, options = {}) {
2560
2775
  timeline.shots.forEach((shot, i) => {
2561
2776
  if (shot.start <= at + 1e-6) step = i;
2562
2777
  });
2563
- const delay = round2(at - timeline.shots[step].start);
2778
+ const delay = round3(at - timeline.shots[step].start);
2564
2779
  delete a.delay;
2565
2780
  if (delay > 0) a.delay = delay;
2566
2781
  a.step = step + 1;
@@ -2572,7 +2787,7 @@ function toFlairSvgMotion(scene, options = {}) {
2572
2787
  let svg = _chunkHRI3ZMDMcjs.vectorDocumentToSvg.call(void 0, { ...scene.artwork, nodes: _chunkHRI3ZMDMcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) }, { pretty: false, precision, wrap });
2573
2788
  if (routes.length) svg = svg.replace(/^(<svg[^>]*>)/, `$1<defs>${routes.join("")}</defs>`);
2574
2789
  let motion = { svg, animate };
2575
- if (_optionalChain([scene, 'access', _114 => _114.controls, 'optionalAccess', _115 => _115.length])) {
2790
+ if (_optionalChain([scene, 'access', _120 => _120.controls, 'optionalAccess', _121 => _121.length])) {
2576
2791
  const declarations = scene.controls.map((c) => `{{${c.name}: color default=${c.default.toUpperCase()}${c.label ? ` label='${c.label.replace(/'/g, "\u2019")}'` : ""}}}`).join(" ");
2577
2792
  const swap = (text) => scene.controls.reduce((s, c) => s.replace(new RegExp(`${c.default}(?![0-9a-fA-F])`, "gi"), `{{${c.name}}}`), text);
2578
2793
  svg = svg.replace(/^(<svg[^>]*>)/, `$1<!-- ${declarations} -->`);
@@ -2580,7 +2795,7 @@ function toFlairSvgMotion(scene, options = {}) {
2580
2795
  svg = svg.slice(0, header) + swap(svg.slice(header));
2581
2796
  motion = { svg, animate: animate.map((a) => a.attrs ? { ...a, attrs: Object.fromEntries(Object.entries(a.attrs).map(([k, [x, y]]) => [k, [typeof x === "string" ? swap(x) : x, typeof y === "string" ? swap(y) : y]])) } : a) };
2582
2797
  }
2583
- return { motion, duration: timeline.duration, hold: Math.max(0, round2(timeline.duration - lastChange)), sampled, notes: [...new Set(notes)], ...trigger ? { trigger } : {} };
2798
+ return { motion, duration: timeline.duration, hold: Math.max(0, round3(timeline.duration - lastChange)), sampled, notes: [...new Set(notes)], ...trigger ? { trigger } : {} };
2584
2799
  }
2585
2800
  var kebab = (text) => text.trim().replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "animation";
2586
2801
  function toFlairVectorEffect(scene, options = {}) {
@@ -2594,7 +2809,7 @@ function toFlairVectorEffect(scene, options = {}) {
2594
2809
  tags: _nullishCoalesce(options.tags, () => ( [])),
2595
2810
  motion: out.motion,
2596
2811
  hold: out.hold,
2597
- ..._optionalChain([scene, 'access', _116 => _116.presets, 'optionalAccess', _117 => _117.length]) ? { presets: scene.presets } : {},
2812
+ ..._optionalChain([scene, 'access', _122 => _122.presets, 'optionalAccess', _123 => _123.length]) ? { presets: scene.presets } : {},
2598
2813
  ...out.trigger ? { trigger: out.trigger } : {}
2599
2814
  };
2600
2815
  }
@@ -2638,7 +2853,7 @@ function resolvePlaceholders(motion, notes) {
2638
2853
  const filled = text.replace(PLACEHOLDER, (_, name) => {
2639
2854
  const d = declared.get(name);
2640
2855
  if (!d) unknown.add(name);
2641
- return (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _118 => _118.value]), () => ( ""))).replace(/"/g, '\\"');
2856
+ return (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _124 => _124.value]), () => ( ""))).replace(/"/g, '\\"');
2642
2857
  });
2643
2858
  for (const name of unknown) notes.push(`Placeholder "{{${name}}}" has no declaration, so it was left empty.`);
2644
2859
  return { motion: JSON.parse(filled), controls };
@@ -2692,7 +2907,7 @@ function commandsFor(anim, nodes, entered, notes) {
2692
2907
  if (anim.along) {
2693
2908
  const path = nodes.get(anim.along);
2694
2909
  if (!path || path.node.type !== "path") notes.push(`${where} follows "${anim.along}", which is not a path in the artwork; the follow was skipped.`);
2695
- else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access', _119 => _119.at, 'optionalAccess', _120 => _120[0]]), () => ( 0)), to: _nullishCoalesce(_optionalChain([anim, 'access', _121 => _121.at, 'optionalAccess', _122 => _122[1]]), () => ( 1)), ...anim.orient ? { rotate: true } : {}, ...timing, ...ease(MOTION_EASE_IN_OUT) });
2910
+ else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access', _125 => _125.at, 'optionalAccess', _126 => _126[0]]), () => ( 0)), to: _nullishCoalesce(_optionalChain([anim, 'access', _127 => _127.at, 'optionalAccess', _128 => _128[1]]), () => ( 1)), ...anim.orient ? { rotate: true } : {}, ...timing, ...ease(MOTION_EASE_IN_OUT) });
2696
2911
  }
2697
2912
  for (const [attr, [a, b]] of Object.entries(_nullishCoalesce(anim.attrs, () => ( {})))) {
2698
2913
  if (attr === "opacity") {
@@ -2703,7 +2918,7 @@ function commandsFor(anim, nodes, entered, notes) {
2703
2918
  out.push({ type: "enter", targets, style: "fade", ...timing, ...ease(MOTION_EASE_OUT) });
2704
2919
  } else out.push({ type: "fade", targets, to: Math.min(1, Math.max(0, to)), ...timing, ...ease(MOTION_EASE_IN_OUT) });
2705
2920
  } else if (attr === "fill" || attr === "stroke") {
2706
- if (_optionalChain([behavior, 'optionalAccess', _123 => _123.type]) === "pulse") out.push({ type: "color-pulse", targets, color: String(b), paint: attr, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...ease("linear") });
2921
+ if (_optionalChain([behavior, 'optionalAccess', _129 => _129.type]) === "pulse") out.push({ type: "color-pulse", targets, color: String(b), paint: attr, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...ease("linear") });
2707
2922
  else {
2708
2923
  if (behavior) notes.push(`${where}: a ${attr} ${behavior.type} became a plain colour change.`);
2709
2924
  out.push({ type: "style", targets, [attr]: String(b), ...timing, ...ease(MOTION_EASE_IN_OUT) });
@@ -2769,10 +2984,10 @@ function fromFlairSvgMotion(input, options = {}) {
2769
2984
  notes.push(`Step ${step}: a camera move ${camera ? "after the first" : `${anim.delay} ms in`} was dropped; a shot frames the camera once, at its start.`);
2770
2985
  continue;
2771
2986
  }
2772
- const ms = _nullishCoalesce(anim.ms, () => ( 800));
2987
+ const ms2 = _nullishCoalesce(anim.ms, () => ( 800));
2773
2988
  const ease = easingOf(anim.ease, MOTION_EASE_IN_OUT);
2774
- camera = typeof anim.view === "string" ? { frame: anim.view, padding: 0, ms, ease } : { x: anim.view[0], y: anim.view[1], width: anim.view[2], height: anim.view[3], ms, ease };
2775
- cameraMs = index === 0 ? 0 : ms;
2989
+ camera = typeof anim.view === "string" ? { frame: anim.view, padding: 0, ms: ms2, ease } : { x: anim.view[0], y: anim.view[1], width: anim.view[2], height: anim.view[3], ms: ms2, ease };
2990
+ cameraMs = index === 0 ? 0 : ms2;
2776
2991
  continue;
2777
2992
  }
2778
2993
  if (!anim.id) continue;
@@ -2826,7 +3041,7 @@ function rgba(color) {
2826
3041
  const n = (i) => parseInt(hex.slice(i, i + 2), 16) / 255;
2827
3042
  return [n(1), n(3), n(5), hex.length >= 9 ? parseInt(hex.slice(7, 9), 16) / 255 : 1];
2828
3043
  }
2829
- function lottieContours(path, round2) {
3044
+ function lottieContours(path, round3) {
2830
3045
  const out = [];
2831
3046
  let cur = null;
2832
3047
  let px = 0;
@@ -2867,7 +3082,7 @@ function lottieContours(path, round2) {
2867
3082
  }
2868
3083
  }
2869
3084
  if (cur) out.push(cur);
2870
- return out.map((c) => ({ c: c.c, v: c.v.map((p) => p.map(round2)), i: c.i.map((p) => p.map(round2)), o: c.o.map((p) => p.map(round2)) }));
3085
+ return out.map((c) => ({ c: c.c, v: c.v.map((p) => p.map(round3)), i: c.i.map((p) => p.map(round3)), o: c.o.map((p) => p.map(round3)) }));
2871
3086
  }
2872
3087
  function staticParts(m) {
2873
3088
  const sx = Math.hypot(m[0], m[1]);
@@ -2879,8 +3094,8 @@ function staticParts(m) {
2879
3094
  function toLottie(scene, options = {}) {
2880
3095
  const fps = _nullishCoalesce(_nullishCoalesce(options.fps, () => ( scene.fps)), () => ( 30));
2881
3096
  const precision = _nullishCoalesce(options.precision, () => ( 3));
2882
- const round2 = (n) => Math.round(n * 10 ** precision) / 10 ** precision;
2883
- const frame = (ms) => round2(ms * fps / 1e3);
3097
+ const round3 = (n) => Math.round(n * 10 ** precision) / 10 ** precision;
3098
+ const frame = (ms2) => round3(ms2 * fps / 1e3);
2884
3099
  const timeline = compileMotionScene(scene);
2885
3100
  const nodes = indexMotionNodes(scene.artwork.nodes);
2886
3101
  const notes = [];
@@ -2897,16 +3112,16 @@ function toLottie(scene, options = {}) {
2897
3112
  return [e.x1, e.y1, e.x2, e.y2];
2898
3113
  };
2899
3114
  const animate = (segments, value, exact = true) => {
2900
- const rest = value(0).map(round2);
3115
+ const rest = value(0).map(round3);
2901
3116
  if (!segments.length) return { a: 0, k: rest.length === 1 ? rest[0] : rest };
2902
3117
  const keys = [];
2903
3118
  const key = (t, v, ease) => {
2904
- const k = { t: frame(t), s: v.map(round2) };
3119
+ const k = { t: frame(t), s: v.map(round3) };
2905
3120
  if (ease === "hold") k.h = 1;
2906
3121
  else {
2907
3122
  const [x1, y1, x2, y2] = ease === "linear" || !ease ? [0, 0, 1, 1] : ease;
2908
- k.o = { x: [round2(x1)], y: [round2(y1)] };
2909
- k.i = { x: [round2(x2)], y: [round2(y2)] };
3123
+ k.o = { x: [round3(x1)], y: [round3(y1)] };
3124
+ k.i = { x: [round3(x2)], y: [round3(y2)] };
2910
3125
  }
2911
3126
  if (keys.length && keys[keys.length - 1].t === k.t) keys[keys.length - 1] = k;
2912
3127
  else keys.push(k);
@@ -2954,7 +3169,7 @@ function toLottie(scene, options = {}) {
2954
3169
  const poseTransform = (entry) => {
2955
3170
  const id = entry.node.id;
2956
3171
  const tr = STATIC_TR();
2957
- const c = [entry.center.x, entry.center.y].map(round2);
3172
+ const c = [entry.center.x, entry.center.y].map(round3);
2958
3173
  const own = (p) => track(id, p);
2959
3174
  const sources = motionDriverSources(timeline, id, ["x", "y", "rotation", "scaleX", "scaleY"]);
2960
3175
  const layered = ["x", "y", "rotation", "scaleX", "scaleY"].some((p) => layers(id, p).length) || sources.length > 0;
@@ -2965,7 +3180,7 @@ function toLottie(scene, options = {}) {
2965
3180
  tr.p = animate(mixed ? [...own("x"), ...own("y"), ...own("follow"), ...layers(id, "x"), ...layers(id, "y"), ...sources] : [...own("x"), ...own("y")], (t) => poseAt(entry, t).p, !mixed);
2966
3181
  tr.r = animate(mixed ? [...own("rotation"), ...own("follow"), ...layers(id, "rotation"), ...sources] : own("rotation"), (t) => [poseAt(entry, t).r], !mixed);
2967
3182
  tr.s = animate(mixed ? [...own("scaleX"), ...own("scaleY"), ...layers(id, "scaleX"), ...layers(id, "scaleY"), ...sources] : [...own("scaleX"), ...own("scaleY")], (t) => poseAt(entry, t).s, !mixed);
2968
- if (tr.p.a === 0) tr.p = { a: 0, k: poseAt(entry, 0).p.map(round2) };
3183
+ if (tr.p.a === 0) tr.p = { a: 0, k: poseAt(entry, 0).p.map(round3) };
2969
3184
  }
2970
3185
  const opacitySources = motionDriverSources(timeline, id, ["opacity"]);
2971
3186
  if (opacitySources.length) {
@@ -2974,7 +3189,7 @@ function toLottie(scene, options = {}) {
2974
3189
  }
2975
3190
  const opacity = own("opacity");
2976
3191
  const rest = _nullishCoalesce(entry.node.opacity, () => ( 1));
2977
- tr.o = opacity.length ? animate(opacity, (t) => [Math.min(1, Math.max(0, Number(_nullishCoalesce(trackValue(opacity, t), () => ( rest))))) * 100]) : { a: 0, k: round2(rest * 100) };
3192
+ tr.o = opacity.length ? animate(opacity, (t) => [Math.min(1, Math.max(0, Number(_nullishCoalesce(trackValue(opacity, t), () => ( rest))))) * 100]) : { a: 0, k: round3(rest * 100) };
2978
3193
  return tr;
2979
3194
  };
2980
3195
  const owner = (path, p) => {
@@ -2993,10 +3208,10 @@ function toLottie(scene, options = {}) {
2993
3208
  const shapeSegments = [...timeline.tracks.entries()].filter(([k]) => k.startsWith(`${path.id}\0`) && (k.endsWith("\0shape") || k.includes("\0effect:"))).flatMap(([, v]) => v);
2994
3209
  const geometryAt = (t) => {
2995
3210
  if (!shapeSegments.length) return _chunkHRI3ZMDMcjs.vectorPathWorldBezier.call(void 0, path);
2996
- const now = _optionalChain([indexMotionNodes, 'call', _124 => _124(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes), 'access', _125 => _125.get, 'call', _126 => _126(path.id), 'optionalAccess', _127 => _127.node]);
3211
+ const now = _optionalChain([indexMotionNodes, 'call', _130 => _130(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes), 'access', _131 => _131.get, 'call', _132 => _132(path.id), 'optionalAccess', _133 => _133.node]);
2997
3212
  return now && now.type === "path" ? _chunkHRI3ZMDMcjs.vectorPathWorldBezier.call(void 0, _chunkHRI3ZMDMcjs.expandVectorAppearances.call(void 0, [now])[0]) : _chunkHRI3ZMDMcjs.vectorPathWorldBezier.call(void 0, path);
2998
3213
  };
2999
- const rest = lottieContours(_chunkGF2RBSDMcjs.toBezierPath.call(void 0, geometryAt(0)), round2);
3214
+ const rest = lottieContours(_chunkGF2RBSDMcjs.toBezierPath.call(void 0, geometryAt(0)), round3);
3000
3215
  rest.forEach((contour, n) => {
3001
3216
  let ks = { a: 0, k: contour };
3002
3217
  if (shapeSegments.length) {
@@ -3005,7 +3220,7 @@ function toLottie(scene, options = {}) {
3005
3220
  const count = Math.max(1, Math.ceil(end * fps / 1e3));
3006
3221
  for (let k = 0; k <= count && fits; k++) {
3007
3222
  const t = end * k / count;
3008
- const c = lottieContours(_chunkGF2RBSDMcjs.toBezierPath.call(void 0, geometryAt(t)), round2)[n];
3223
+ const c = lottieContours(_chunkGF2RBSDMcjs.toBezierPath.call(void 0, geometryAt(t)), round3)[n];
3009
3224
  if (!c || c.v.length !== contour.v.length || c.c !== contour.c) fits = false;
3010
3225
  else keys.push({ t: frame(t), s: [c], ...k < count ? { o: { x: [0], y: [0] }, i: { x: [1], y: [1] } } : {} });
3011
3226
  }
@@ -3034,14 +3249,14 @@ function toLottie(scene, options = {}) {
3034
3249
  items.push({
3035
3250
  ty: "st",
3036
3251
  nm: "Stroke",
3037
- c: colour2.length ? animate(colour2, (t) => _nullishCoalesce(_nullishCoalesce(rgba(trackValue(colour2, t)), () => ( stroke)), () => ( [0, 0, 0, 1]))) : { a: 0, k: (_nullishCoalesce(stroke, () => ( [0, 0, 0, 1]))).map(round2) },
3252
+ c: colour2.length ? animate(colour2, (t) => _nullishCoalesce(_nullishCoalesce(rgba(trackValue(colour2, t)), () => ( stroke)), () => ( [0, 0, 0, 1]))) : { a: 0, k: (_nullishCoalesce(stroke, () => ( [0, 0, 0, 1]))).map(round3) },
3038
3253
  o: { a: 0, k: 100 },
3039
- w: widthFrom ? animate(track(widthFrom, "strokeWidth"), (t) => [Math.max(0, Number(_nullishCoalesce(_nullishCoalesce(trackValue(track(widthFrom, "strokeWidth"), t), () => ( path.strokeWidth)), () => ( 1))))]) : { a: 0, k: round2(_nullishCoalesce(path.strokeWidth, () => ( 1))) },
3254
+ w: widthFrom ? animate(track(widthFrom, "strokeWidth"), (t) => [Math.max(0, Number(_nullishCoalesce(_nullishCoalesce(trackValue(track(widthFrom, "strokeWidth"), t), () => ( path.strokeWidth)), () => ( 1))))]) : { a: 0, k: round3(_nullishCoalesce(path.strokeWidth, () => ( 1))) },
3040
3255
  lc: path.strokeLinecap === "round" ? 2 : path.strokeLinecap === "square" ? 3 : 1,
3041
3256
  lj: path.strokeLinejoin === "round" ? 2 : path.strokeLinejoin === "bevel" ? 3 : 1,
3042
3257
  ml: 4
3043
3258
  });
3044
- if (_optionalChain([path, 'access', _128 => _128.strokeDasharray, 'optionalAccess', _129 => _129.length])) notes.push(`"${path.id}": its dashes are not exported; the stroke is solid.`);
3259
+ if (_optionalChain([path, 'access', _134 => _134.strokeDasharray, 'optionalAccess', _135 => _135.length])) notes.push(`"${path.id}": its dashes are not exported; the stroke is solid.`);
3045
3260
  if (path.strokeAlign && path.strokeAlign !== "center") notes.push(`"${path.id}": its stroke is drawn centred (Lottie has no inside or outside strokes).`);
3046
3261
  }
3047
3262
  const fill = paintOf(path.fill, "fill", path.id);
@@ -3051,7 +3266,7 @@ function toLottie(scene, options = {}) {
3051
3266
  items.push({
3052
3267
  ty: "fl",
3053
3268
  nm: "Fill",
3054
- c: colour2.length ? animate(colour2, (t) => _nullishCoalesce(_nullishCoalesce(rgba(trackValue(colour2, t)), () => ( fill)), () => ( [0, 0, 0, 1]))) : { a: 0, k: (_nullishCoalesce(fill, () => ( [0, 0, 0, 1]))).map(round2) },
3269
+ c: colour2.length ? animate(colour2, (t) => _nullishCoalesce(_nullishCoalesce(rgba(trackValue(colour2, t)), () => ( fill)), () => ( [0, 0, 0, 1]))) : { a: 0, k: (_nullishCoalesce(fill, () => ( [0, 0, 0, 1]))).map(round3) },
3055
3270
  o: fade.length ? animate(fade, (t) => [Math.min(1, Math.max(0, Number(_nullishCoalesce(trackValue(fade, t), () => ( 1))))) * 100]) : { a: 0, k: 100 },
3056
3271
  r: path.fillRule === "evenodd" ? 2 : 1
3057
3272
  });
@@ -3077,9 +3292,9 @@ function toLottie(scene, options = {}) {
3077
3292
  const parts = staticParts(m);
3078
3293
  if (!parts) notes.push(`"${node.id}": its transform skews, which is not exported; it is drawn without the skew.`);
3079
3294
  else {
3080
- innerTr.p = { a: 0, k: parts.p.map(round2) };
3081
- innerTr.s = { a: 0, k: parts.s.map(round2) };
3082
- innerTr.r = { a: 0, k: round2(parts.r) };
3295
+ innerTr.p = { a: 0, k: parts.p.map(round3) };
3296
+ innerTr.s = { a: 0, k: parts.s.map(round3) };
3297
+ innerTr.r = { a: 0, k: round3(parts.r) };
3083
3298
  }
3084
3299
  }
3085
3300
  }
@@ -3089,7 +3304,7 @@ function toLottie(scene, options = {}) {
3089
3304
  const artwork = { ...scene.artwork, nodes: _chunkHRI3ZMDMcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) };
3090
3305
  const content = shown(artwork.nodes).reverse().map(nodeGroup);
3091
3306
  if (artwork.background && rgba(artwork.background))
3092
- content.push({ ty: "gr", nm: "Background", it: [{ ty: "rc", nm: "Background", p: { a: 0, k: [artwork.width / 2, artwork.height / 2] }, s: { a: 0, k: [artwork.width, artwork.height] }, r: { a: 0, k: 0 } }, { ty: "fl", c: { a: 0, k: rgba(artwork.background).map(round2) }, o: { a: 0, k: 100 }, r: 1 }, STATIC_TR()] });
3307
+ content.push({ ty: "gr", nm: "Background", it: [{ ty: "rc", nm: "Background", p: { a: 0, k: [artwork.width / 2, artwork.height / 2] }, s: { a: 0, k: [artwork.width, artwork.height] }, r: { a: 0, k: 0 } }, { ty: "fl", c: { a: 0, k: rgba(artwork.background).map(round3) }, o: { a: 0, k: 100 }, r: 1 }, STATIC_TR()] });
3093
3308
  const camera = track("", "camera");
3094
3309
  const root = STATIC_TR();
3095
3310
  if (camera.length) {
@@ -3135,7 +3350,7 @@ var kids = (el, name) => el ? Array.from(el.children).filter((c) => c.localName
3135
3350
  var kid = (el, name) => _nullishCoalesce(kids(el, name)[0], () => ( null));
3136
3351
  var deep = (el, name) => el ? Array.from(el.getElementsByTagNameNS("*", name)) : [];
3137
3352
  var num = (el, attr, fallback = 0) => {
3138
- const v = _optionalChain([el, 'optionalAccess', _130 => _130.getAttribute, 'call', _131 => _131(attr)]);
3353
+ const v = _optionalChain([el, 'optionalAccess', _136 => _136.getAttribute, 'call', _137 => _137(attr)]);
3139
3354
  const n = v === null || v === void 0 ? NaN : Number(v);
3140
3355
  return Number.isFinite(n) ? n : fallback;
3141
3356
  };
@@ -3243,8 +3458,8 @@ function placement(xfrm, map) {
3243
3458
  const ext = kid(xfrm, "ext");
3244
3459
  const [x, y, w, h] = map(num(off, "x"), num(off, "y"), num(ext, "cx"), num(ext, "cy"));
3245
3460
  const rot = num(xfrm, "rot") / 6e4 * (Math.PI / 180);
3246
- const fx = _optionalChain([xfrm, 'optionalAccess', _132 => _132.getAttribute, 'call', _133 => _133("flipH")]) === "1" ? -1 : 1;
3247
- const fy = _optionalChain([xfrm, 'optionalAccess', _134 => _134.getAttribute, 'call', _135 => _135("flipV")]) === "1" ? -1 : 1;
3461
+ const fx = _optionalChain([xfrm, 'optionalAccess', _138 => _138.getAttribute, 'call', _139 => _139("flipH")]) === "1" ? -1 : 1;
3462
+ const fy = _optionalChain([xfrm, 'optionalAccess', _140 => _140.getAttribute, 'call', _141 => _141("flipV")]) === "1" ? -1 : 1;
3248
3463
  const cx = x + w / 2;
3249
3464
  const cy = y + h / 2;
3250
3465
  const cos = Math.cos(rot);
@@ -3271,7 +3486,7 @@ function shapes(ctx, tree, map) {
3271
3486
  const nv = el.firstElementChild;
3272
3487
  const cNvPr = kid(nv, "cNvPr");
3273
3488
  const spid = num(cNvPr, "id", -1);
3274
- const name = _nullishCoalesce(_optionalChain([cNvPr, 'optionalAccess', _136 => _136.getAttribute, 'call', _137 => _137("name")]), () => ( ""));
3489
+ const name = _nullishCoalesce(_optionalChain([cNvPr, 'optionalAccess', _142 => _142.getAttribute, 'call', _143 => _143("name")]), () => ( ""));
3275
3490
  if (el.localName === "grpSp") {
3276
3491
  const xfrm = kid(kid(el, "grpSpPr"), "xfrm");
3277
3492
  const off = kid(xfrm, "off");
@@ -3292,7 +3507,7 @@ function shapes(ctx, tree, map) {
3292
3507
  if (el.localName !== "sp" && el.localName !== "cxnSp") continue;
3293
3508
  const spPr = kid(el, "spPr");
3294
3509
  const { w, h, transform } = placement(kid(spPr, "xfrm"), map);
3295
- const prst = _optionalChain([kid, 'call', _138 => _138(spPr, "prstGeom"), 'optionalAccess', _139 => _139.getAttribute, 'call', _140 => _140("prst")]);
3510
+ const prst = _optionalChain([kid, 'call', _144 => _144(spPr, "prstGeom"), 'optionalAccess', _145 => _145.getAttribute, 'call', _146 => _146("prst")]);
3296
3511
  const cust = kid(spPr, "custGeom");
3297
3512
  let subpaths = cust ? custom(cust, w, h) : prst ? preset(prst, w, h) : null;
3298
3513
  if (!subpaths) {
@@ -3406,13 +3621,13 @@ function command(ctx, ctn, scene, artwork, target, hidden) {
3406
3621
  }
3407
3622
  if (cls === "path") {
3408
3623
  const motion = deep(ctn, "animMotion")[0];
3409
- const d = _optionalChain([motion, 'optionalAccess', _141 => _141.getAttribute, 'call', _142 => _142("path")]);
3624
+ const d = _optionalChain([motion, 'optionalAccess', _147 => _147.getAttribute, 'call', _148 => _148("path")]);
3410
3625
  if (!d) return null;
3411
3626
  const entry = artwork.flatMap(function all(n) {
3412
3627
  return n.type === "group" ? [n, ...n.children.flatMap(all)] : [n];
3413
3628
  }).find((n) => n.id === target);
3414
- const t = _nullishCoalesce(_optionalChain([entry, 'optionalAccess', _143 => _143.transform]), () => ( [1, 0, 0, 1, 0, 0]));
3415
- const p = _optionalChain([entry, 'optionalAccess', _144 => _144.type]) === "path" ? _optionalChain([entry, 'access', _145 => _145.subpaths, 'access', _146 => _146[0], 'optionalAccess', _147 => _147.anchors]) : void 0;
3629
+ const t = _nullishCoalesce(_optionalChain([entry, 'optionalAccess', _149 => _149.transform]), () => ( [1, 0, 0, 1, 0, 0]));
3630
+ const p = _optionalChain([entry, 'optionalAccess', _150 => _150.type]) === "path" ? _optionalChain([entry, 'access', _151 => _151.subpaths, 'access', _152 => _152[0], 'optionalAccess', _153 => _153.anchors]) : void 0;
3416
3631
  const xs = (_nullishCoalesce(p, () => ( []))).map((a) => a.x);
3417
3632
  const ys = (_nullishCoalesce(p, () => ( []))).map((a) => a.y);
3418
3633
  const lx = xs.length ? (Math.min(...xs) + Math.max(...xs)) / 2 : 0;
@@ -3464,9 +3679,9 @@ async function fromPptxAnimation(data, options = {}) {
3464
3679
  if (!path.includes("theme/")) continue;
3465
3680
  const theme = await xml(ctx, path);
3466
3681
  const scheme = deep(theme, "clrScheme")[0];
3467
- for (const c of Array.from(_nullishCoalesce(_optionalChain([scheme, 'optionalAccess', _148 => _148.children]), () => ( [])))) {
3682
+ for (const c of Array.from(_nullishCoalesce(_optionalChain([scheme, 'optionalAccess', _154 => _154.children]), () => ( [])))) {
3468
3683
  const v = c.firstElementChild;
3469
- const hex = _optionalChain([v, 'optionalAccess', _149 => _149.localName]) === "srgbClr" ? v.getAttribute("val") : _optionalChain([v, 'optionalAccess', _150 => _150.getAttribute, 'call', _151 => _151("lastClr")]);
3684
+ const hex = _optionalChain([v, 'optionalAccess', _155 => _155.localName]) === "srgbClr" ? v.getAttribute("val") : _optionalChain([v, 'optionalAccess', _156 => _156.getAttribute, 'call', _157 => _157("lastClr")]);
3470
3685
  if (hex) ctx.theme.set(c.localName, `#${hex.toLowerCase()}`);
3471
3686
  }
3472
3687
  break;
@@ -3584,7 +3799,7 @@ function withoutMotionEffect(scene, instance) {
3584
3799
  const shots = [];
3585
3800
  for (const shot of scene.shots) {
3586
3801
  const run = shot.run.filter((c, i) => {
3587
- const mine = _optionalChain([sourceOf, 'call', _152 => _152(c), 'optionalAccess', _153 => _153.instance]) === instance;
3802
+ const mine = _optionalChain([sourceOf, 'call', _158 => _158(c), 'optionalAccess', _159 => _159.instance]) === instance;
3588
3803
  if (mine && !place) place = { shot: shot.id, index: i };
3589
3804
  return !mine;
3590
3805
  });
@@ -3597,6 +3812,8 @@ function withoutMotionEffect(scene, instance) {
3597
3812
  }
3598
3813
 
3599
3814
  // src/animation-editor/controller.ts
3815
+ var MOTION_RECORD_GESTURE_MODES = ["move", "scale", "rotate"];
3816
+ var MOTION_MAX_TAKE_MS = 10 * 6e4;
3600
3817
  var BLANK = { width: 800, height: 450, background: "#ffffff", nodes: [] };
3601
3818
  var AnimationEditorController = (_class = class {
3602
3819
 
@@ -3614,14 +3831,15 @@ var AnimationEditorController = (_class = class {
3614
3831
  __init6() {this.listeners = /* @__PURE__ */ new Set()}
3615
3832
  __init7() {this.unsubscribers = []}
3616
3833
 
3617
- 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);
3834
+ __init8() {this.recording = null}
3835
+ 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);
3618
3836
  this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkFJ2MSI43cjs.HistoryManager)()));
3619
3837
  this.scene = options.scene ? parseMotionScene(options.scene) : createMotionScene(BLANK);
3620
3838
  this.issues = checkMotionScene(this.scene);
3621
3839
  this.readOnly = _nullishCoalesce(options.readOnly, () => ( false));
3622
3840
  this.onChange = options.onChange;
3623
3841
  if (options.effects) this.effects.set(options.effects);
3624
- this.player = new (0, _chunkSVR5FR7Bcjs.AnimationPlayer)({ clock: options.clock, duration: () => compileMotionScene(this.scene).duration, loop: _nullishCoalesce(this.scene.loop, () => ( "none")) });
3842
+ this.player = new (0, _chunkUUTTJK77cjs.AnimationPlayer)({ clock: options.clock, duration: () => compileMotionScene(this.scene).duration, loop: _nullishCoalesce(this.scene.loop, () => ( "none")) });
3625
3843
  this.state = this.computeState();
3626
3844
  this.connect();
3627
3845
  }
@@ -3640,11 +3858,12 @@ var AnimationEditorController = (_class = class {
3640
3858
  * controller survives React StrictMode's unmount / remount.
3641
3859
  */
3642
3860
  destroy() {
3861
+ this.cancelRecording();
3643
3862
  this.player.pause();
3644
3863
  this.unsubscribers.splice(0).forEach((u) => u());
3645
3864
  }
3646
- __init8() {this.getState = () => this.state}
3647
- __init9() {this.subscribe = (listener) => {
3865
+ __init9() {this.getState = () => this.state}
3866
+ __init10() {this.subscribe = (listener) => {
3648
3867
  this.connect();
3649
3868
  this.listeners.add(listener);
3650
3869
  return () => {
@@ -3667,7 +3886,7 @@ var AnimationEditorController = (_class = class {
3667
3886
  time: p.time,
3668
3887
  playing: p.playing,
3669
3888
  loop: p.loop,
3670
- currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _154 => _154(timeline, p.time), 'optionalAccess', _155 => _155.id]), () => ( null)),
3889
+ currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _160 => _160(timeline, p.time), 'optionalAccess', _161 => _161.id]), () => ( null)),
3671
3890
  selection: this.selection,
3672
3891
  readOnly: this.readOnly,
3673
3892
  error: this.error,
@@ -3676,7 +3895,15 @@ var AnimationEditorController = (_class = class {
3676
3895
  undoLabel: h.undoName,
3677
3896
  redoLabel: h.redoName,
3678
3897
  revision: this.revision,
3679
- effectsProvider: this.effects.provider
3898
+ effectsProvider: this.effects.provider,
3899
+ recording: this.recording ? {
3900
+ shot: this.recording.options.id,
3901
+ at: this.recording.options.at,
3902
+ start: this.recording.start,
3903
+ elapsed: Math.max(0, p.time - this.recording.start),
3904
+ samples: this.recording.samples.length,
3905
+ gesture: this.recording.gesture ? { node: this.recording.gesture.node, mode: this.recording.gesture.mode } : null
3906
+ } : null
3680
3907
  };
3681
3908
  }
3682
3909
  notify() {
@@ -3707,7 +3934,7 @@ var AnimationEditorController = (_class = class {
3707
3934
  this.applying = false;
3708
3935
  }
3709
3936
  this.notify();
3710
- _optionalChain([this, 'access', _156 => _156.onChange, 'optionalCall', _157 => _157(scene)]);
3937
+ _optionalChain([this, 'access', _162 => _162.onChange, 'optionalCall', _163 => _163(scene)]);
3711
3938
  }
3712
3939
  commit(label, next, merge) {
3713
3940
  const before = this.scene;
@@ -3740,7 +3967,7 @@ var AnimationEditorController = (_class = class {
3740
3967
  * `dryRun` it reports what would happen and changes nothing.
3741
3968
  */
3742
3969
  apply(edits, label = "Edit animation", options = {}) {
3743
- const reason = this.readOnlyReason();
3970
+ const reason = _nullishCoalesce(this.readOnlyReason(), () => ( (this.recording ? "A take is being recorded; stop or cancel the recording first." : null)));
3744
3971
  if (reason) return this.refuse(reason);
3745
3972
  const result = applyMotionEdits(this.scene, edits);
3746
3973
  if (!result.ok) return options.dryRun ? result : this.refuse(result.error);
@@ -3869,7 +4096,7 @@ var AnimationEditorController = (_class = class {
3869
4096
  const { group, shots: shots2, notes: notes2 } = motionPartsFromVectorEffect(bare.artwork, motion2, source);
3870
4097
  const index = this.scene.artwork.nodes.findIndex((n) => n.id === instance);
3871
4098
  const nodes = [...bare.artwork.nodes];
3872
- nodes.splice(Math.max(0, index), 0, _optionalChain([old, 'optionalAccess', _158 => _158.transform]) ? { ...group, transform: old.transform } : group);
4099
+ nodes.splice(Math.max(0, index), 0, _optionalChain([old, 'optionalAccess', _164 => _164.transform]) ? { ...group, transform: old.transform } : group);
3873
4100
  const at = this.scene.shots.findIndex((s) => was.shots.includes(s.id));
3874
4101
  const kept = bare.shots;
3875
4102
  const nextShots = [...kept.slice(0, Math.max(0, at)), ...shots2, ...kept.slice(Math.max(0, at))];
@@ -3880,7 +4107,7 @@ var AnimationEditorController = (_class = class {
3880
4107
  let shots = bare.shots;
3881
4108
  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);
3882
4109
  else {
3883
- const at = this.scene.shots.findIndex((s) => s.id === _optionalChain([place, 'optionalAccess', _159 => _159.shot]));
4110
+ const at = this.scene.shots.findIndex((s) => s.id === _optionalChain([place, 'optionalAccess', _165 => _165.shot]));
3884
4111
  shots = [...shots.slice(0, Math.max(0, at)), { id: instance, label: d.name, run: commands }, ...shots.slice(Math.max(0, at))];
3885
4112
  }
3886
4113
  return { scene: { ...bare, shots }, label: `Change ${d.name}`, instance, notes };
@@ -3895,6 +4122,7 @@ var AnimationEditorController = (_class = class {
3895
4122
  }
3896
4123
  /** Opens a scene (JSON or object). `clearHistory` drops the undo stack (opening a file); otherwise it is one undo step. */
3897
4124
  load(scene, options = {}) {
4125
+ if (options.clearHistory) this.cancelRecording();
3898
4126
  let parsed;
3899
4127
  try {
3900
4128
  parsed = parseMotionScene(scene);
@@ -3914,6 +4142,7 @@ var AnimationEditorController = (_class = class {
3914
4142
  setReadOnly(readOnly) {
3915
4143
  if (this.readOnly === readOnly) return;
3916
4144
  this.readOnly = readOnly;
4145
+ if (readOnly) this.cancelRecording();
3917
4146
  this.notify();
3918
4147
  }
3919
4148
  /** Selects a shot (and optionally a command in it); `null` clears. Unknown shots clear. */
@@ -3933,30 +4162,186 @@ var AnimationEditorController = (_class = class {
3933
4162
  toggle() {
3934
4163
  this.player.toggle();
3935
4164
  }
3936
- /** Moves the playhead (ms). */
4165
+ /** Moves the playhead (ms). Ignored while recording: a take runs on from where it started. */
3937
4166
  seek(time) {
4167
+ if (this.recording) return;
3938
4168
  this.player.seek(time);
3939
4169
  }
3940
4170
  /** Moves the playhead to the start or end of a shot. Returns false for an unknown shot. */
3941
4171
  seekShot(id, at = "start") {
3942
4172
  const shot = compileMotionScene(this.scene).shots.find((s) => s.id === id);
3943
- if (!shot) return false;
4173
+ if (!shot || this.recording) return false;
3944
4174
  this.player.seek(at === "start" ? shot.start : shot.end);
3945
4175
  return true;
3946
4176
  }
3947
4177
  undo() {
3948
- return this.history.undo();
4178
+ return this.recording ? false : this.history.undo();
3949
4179
  }
3950
4180
  redo() {
3951
- return this.history.redo();
4181
+ return this.recording ? false : this.history.redo();
4182
+ }
4183
+ /** The scene as shown: while recording, with the take so far as its shot. */
4184
+ viewScene() {
4185
+ const r = this.recording;
4186
+ if (!r) return this.scene;
4187
+ if (_optionalChain([r, 'access', _166 => _166.preview, 'optionalAccess', _167 => _167.count]) === r.samples.length) return r.preview.scene;
4188
+ let scene = this.scene;
4189
+ if (r.samples.length) {
4190
+ try {
4191
+ const { shot, at } = motionTakeShot(this.scene, { samples: r.samples, length: Math.max(0, this.player.currentTime - r.start) }, r.options);
4192
+ scene = { ...this.scene, shots: [...this.scene.shots.slice(0, at), shot, ...this.scene.shots.slice(at)] };
4193
+ } catch (e6) {
4194
+ }
4195
+ }
4196
+ r.preview = { count: r.samples.length, scene };
4197
+ return scene;
3952
4198
  }
3953
4199
  /** The scene at `time` (default: the playhead). */
3954
4200
  frame(time = this.player.currentTime, options = {}) {
3955
- return motionFrameAt(this.scene, time, options);
4201
+ return motionFrameAt(this.viewScene(), time, options);
3956
4202
  }
3957
4203
  /** The scene at `time` (default: the playhead) as SVG. */
3958
4204
  toSvg(time = this.player.currentTime, options = {}) {
3959
- return motionStageSvg(this.scene, time, options);
4205
+ return motionStageSvg(this.viewScene(), time, options);
4206
+ }
4207
+ // ---------------------------------------------------------------------------------------
4208
+ // Recording
4209
+ // ---------------------------------------------------------------------------------------
4210
+ /**
4211
+ * Starts recording a take: the playhead goes to where the take's shot will start (default:
4212
+ * the end of the scene) and plays on, and every `recordSample` (or stage gesture) is a value
4213
+ * at that moment. Frames show the take so far. `stopRecording` turns it into one shot, one
4214
+ * undo step. Edits, seeks and undo wait until the recording stops; `pause` pauses the take.
4215
+ */
4216
+ startRecording(options = {}) {
4217
+ const reason = _nullishCoalesce(this.readOnlyReason(), () => ( (this.recording ? "A take is already being recorded." : null)));
4218
+ if (reason) return this.refuse(reason);
4219
+ const at = _nullishCoalesce(options.at, () => ( this.scene.shots.length));
4220
+ if (!Number.isInteger(at) || at < 0 || at > this.scene.shots.length) return this.refuse(`"at" must be a whole number from 0 to ${this.scene.shots.length} (got ${at}).`);
4221
+ const id = _nullishCoalesce(options.id, () => ( freshShotId(this.scene, "take")));
4222
+ if (this.scene.shots.some((s) => s.id === id)) return this.refuse(`Shot id "${id}" is already used; choose another (e.g. "${freshShotId(this.scene, id)}").`);
4223
+ const timeline = compileMotionScene(this.scene);
4224
+ const start = at < timeline.shots.length ? timeline.shots[at].start : timeline.duration;
4225
+ this.error = null;
4226
+ this.recording = { options: { ...options, id, at }, start, samples: [], gesture: null, preview: null, restore: { loop: this.player.getState().loop } };
4227
+ this.player.pause();
4228
+ this.player.setPlaybackRate(1);
4229
+ this.player.setLoop("none");
4230
+ this.player.setDuration(start + MOTION_MAX_TAKE_MS);
4231
+ this.player.seek(start);
4232
+ this.player.play();
4233
+ return { ok: true, recording: this.state.recording };
4234
+ }
4235
+ /**
4236
+ * Records `values` for `node` at `t` (ms into the take; default now). Returns why a sample was
4237
+ * refused (an unknown node, a value out of range), or null.
4238
+ */
4239
+ recordSample(node, values, t) {
4240
+ const r = this.recording;
4241
+ if (!r) return "Nothing is being recorded; startRecording first.";
4242
+ const sample = { ...values, node, t: Math.max(0, _nullishCoalesce(t, () => ( this.player.liveTime - r.start))) };
4243
+ const problems = motionTakeProblems(this.scene, { samples: [sample] });
4244
+ if (problems.length) return problems.join(" ").replace("Sample 1", "The sample");
4245
+ r.samples.push(sample);
4246
+ this.notify();
4247
+ return null;
4248
+ }
4249
+ /** Where `node` is and how it looks now (while recording: in the take so far), or at `time`. */
4250
+ pose(node, time = this.player.currentTime) {
4251
+ return motionNodePose(this.viewScene(), node, time);
4252
+ }
4253
+ /** A point on a stage element (client pixels, e.g. a pointer event) in document units, through the camera now. */
4254
+ clientToDocument(box, clientX, clientY) {
4255
+ return motionClientToDocument(this.frame().camera, box, clientX, clientY);
4256
+ }
4257
+ /**
4258
+ * Starts a stage drag while recording: from `point` (document units), `move` drags the node,
4259
+ * `scale` scales it about its centre by how much nearer or farther the pointer is, and
4260
+ * `rotate` turns it with the pointer. Returns false when not recording or `node` is unknown.
4261
+ */
4262
+ beginRecordGesture(node, mode, point) {
4263
+ const r = this.recording;
4264
+ if (!r || !indexMotionNodes(this.scene.artwork.nodes).has(node)) return false;
4265
+ const pose = this.pose(node);
4266
+ r.gesture = { node, mode, from: point, pose };
4267
+ this.recordGesture(pose, point);
4268
+ return true;
4269
+ }
4270
+ /** Records the drag's pose with the pointer at `point` (document units). */
4271
+ updateRecordGesture(point) {
4272
+ const g = _optionalChain([this, 'access', _168 => _168.recording, 'optionalAccess', _169 => _169.gesture]);
4273
+ if (g) this.recordGesture(g.pose, point);
4274
+ }
4275
+ /** Ends the stage drag (the take keeps recording). */
4276
+ endRecordGesture() {
4277
+ const r = this.recording;
4278
+ if (!_optionalChain([r, 'optionalAccess', _170 => _170.gesture])) return;
4279
+ r.gesture = null;
4280
+ this.notify();
4281
+ }
4282
+ recordGesture(pose, point) {
4283
+ const g = this.recording.gesture;
4284
+ const c = { x: pose.x, y: pose.y };
4285
+ if (g.mode === "move") {
4286
+ this.recordSample(g.node, { x: pose.x + point.x - g.from.x, y: pose.y + point.y - g.from.y });
4287
+ } else if (g.mode === "scale") {
4288
+ const f = Math.hypot(point.x - c.x, point.y - c.y) / Math.max(1e-6, Math.hypot(g.from.x - c.x, g.from.y - c.y));
4289
+ this.recordSample(g.node, { scaleX: Math.max(0.01, pose.scaleX * f), scaleY: Math.max(0.01, pose.scaleY * f) });
4290
+ } else {
4291
+ 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);
4292
+ const was = _nullishCoalesce(_optionalChain([this, 'access', _171 => _171.recording, 'access', _172 => _172.samples, 'access', _173 => _173.filter, 'call', _174 => _174((s) => s.node === g.node && s.rotation !== void 0), 'access', _175 => _175.at, 'call', _176 => _176(-1), 'optionalAccess', _177 => _177.rotation]), () => ( pose.rotation));
4293
+ let next = pose.rotation + turn;
4294
+ next += 360 * Math.round((was - next) / 360);
4295
+ this.recordSample(g.node, { rotation: next });
4296
+ }
4297
+ }
4298
+ endRecording() {
4299
+ const r = this.recording;
4300
+ if (!r) return null;
4301
+ this.player.pause();
4302
+ this.recording = null;
4303
+ this.player.setLoop(r.restore.loop);
4304
+ this.player.setDuration(() => compileMotionScene(this.scene).duration);
4305
+ return r;
4306
+ }
4307
+ /**
4308
+ * Stops recording and adds the take as a shot (one undo step), selected, with the playhead at
4309
+ * its start. Returns the shot, or why nothing was added (nothing recorded).
4310
+ */
4311
+ stopRecording() {
4312
+ const length = this.recording ? this.player.liveTime - this.recording.start : 0;
4313
+ const r = this.endRecording();
4314
+ if (!r) return this.refuse("Nothing is being recorded.");
4315
+ if (!r.samples.length) {
4316
+ this.player.seek(r.start);
4317
+ return this.refuse("Nothing was recorded, so no shot was added: drag an element on the stage (or change its paint) while recording.");
4318
+ }
4319
+ const result = this.recordTake({ samples: r.samples, length }, r.options);
4320
+ this.player.seek(r.start);
4321
+ return result;
4322
+ }
4323
+ /** Stops recording and throws the take away. */
4324
+ cancelRecording() {
4325
+ const r = this.endRecording();
4326
+ if (!r) return;
4327
+ this.player.seek(r.start);
4328
+ this.notify();
4329
+ }
4330
+ /**
4331
+ * Adds a take given as samples (an agent's performance, or one recorded elsewhere) as a shot,
4332
+ * one undo step, selected. With `dryRun` it returns the shot and changes nothing.
4333
+ */
4334
+ recordTake(take, options = {}) {
4335
+ let made;
4336
+ try {
4337
+ made = motionTakeShot(this.scene, take, options);
4338
+ } catch (error) {
4339
+ return options.dryRun ? { ok: false, error: error.message } : this.refuse(error.message);
4340
+ }
4341
+ const result = this.apply({ op: "insert_shots", shots: [made.shot], at: made.at }, "Record take", { dryRun: options.dryRun });
4342
+ if (!result.ok) return result;
4343
+ if (!options.dryRun) this.select({ shot: made.shot.id });
4344
+ return { ok: true, ...made, issues: result.issues };
3960
4345
  }
3961
4346
  /** The scene as a Flair `SvgMotion`. */
3962
4347
  toFlairMotion(options = {}) {
@@ -3998,7 +4383,7 @@ function elementsOf(args) {
3998
4383
  if (Array.isArray(args.ops)) {
3999
4384
  for (const op of args.ops) {
4000
4385
  fromShot(op);
4001
- if (Array.isArray(_optionalChain([op, 'optionalAccess', _160 => _160.shots]))) op.shots.forEach(fromShot);
4386
+ if (Array.isArray(_optionalChain([op, 'optionalAccess', _178 => _178.shots]))) op.shots.forEach(fromShot);
4002
4387
  }
4003
4388
  }
4004
4389
  return out.slice(0, 4);
@@ -4111,7 +4496,7 @@ var MOTION_COMMAND_TYPE_SCHEMAS = {
4111
4496
  distance: { type: "number", description: "For slide: document units (default 40)." }
4112
4497
  }),
4113
4498
  draw: command2("draw", "Draws strokes on (or off) to a fraction of their length.", { ...DRAW_ORDER, comet: FRACTION("A travelling stroke this fraction of the length long: it runs along the path and off the end (leave out from and to)."), to: FRACTION("Fraction drawn at the end (default 1)."), from: FRACTION("Fraction drawn at the start (default: what is drawn now).") }),
4114
- move: command2("move", "Moves targets by an offset or so their centre lands on a point.", { via: { type: "array", items: POINT("A point the centre passes through."), minItems: 1, maxItems: 64, description: 'Waypoints (document units) the centre passes through on the way to "to" or "by", smoothly (velocity carries through each) and without overshooting any. Default length 700 ms + 400 per waypoint.' }, pacing: { type: "string", enum: ["even", "equal"], description: "With via: even (default) keeps the speed steady along the path; equal gives each stretch the same time." }, blend: BLEND("replace"), by: POINT("Offset in document units."), to: POINT("Point (document coordinates) for the centre.") }),
4499
+ move: command2("move", "Moves targets by an offset or so their centre lands on a point.", { via: { type: "array", items: POINT("A point the centre passes through."), minItems: 1, maxItems: 64, description: 'Waypoints (document units) the centre passes through on the way to "to" or "by", smoothly (velocity carries through each) and without overshooting any. Default length 700 ms + 400 per waypoint.' }, pacing: { type: "string", enum: ["even", "equal"], description: "With via: even (default) keeps the speed steady along the path; equal gives each stretch the same time." }, times: { type: "array", items: { type: "number", exclusiveMinimum: 0, exclusiveMaximum: 1 }, minItems: 1, maxItems: 64, description: "With via: when the centre passes each waypoint, as rising fractions of the duration (0\u20131), one per waypoint; overrides pacing. Recorded takes set it." }, blend: BLEND("replace"), by: POINT("Offset in document units."), to: POINT("Point (document coordinates) for the centre.") }),
4115
4500
  rotate: command2("rotate", "Turns targets about their centre (degrees, clockwise).", { blend: BLEND("replace"), by: { type: "number", description: "Degrees to turn by." }, to: { type: "number", description: "Angle to turn to, in degrees from rest." } }),
4116
4501
  scale: command2("scale", "Scales targets about their centre (1 = rest size).", { blend: BLEND("replace"), to: { description: "A factor, or { x, y } factors.", anyOf: [{ type: "number" }, POINT("Factors per axis.")] }, geometric: { type: "boolean", description: "Grow by a steady factor, which reads as an even zoom (default false)." } }, ["to"]),
4117
4502
  fade: command2("fade", "Changes opacity.", { to: FRACTION("Opacity at the end (0\u20131).") }, ["to"]),
@@ -4180,7 +4565,7 @@ var MOTION_COMMAND_SCHEMA = {
4180
4565
  oneOf: MOTION_COMMAND_TYPES.map((type) => MOTION_COMMAND_TYPE_SCHEMAS[type])
4181
4566
  };
4182
4567
  var REF2 = (name, description) => ({ $ref: `#/$defs/${name}`, ...description ? { description } : {} });
4183
- var firstSentence = (text) => _nullishCoalesce(_optionalChain([/^.*?[.;](?=\s|$)/, 'access', _161 => _161.exec, 'call', _162 => _162(text), 'optionalAccess', _163 => _163[0]]), () => ( text));
4568
+ var firstSentence = (text) => _nullishCoalesce(_optionalChain([/^.*?[.;](?=\s|$)/, 'access', _179 => _179.exec, 'call', _180 => _180(text), 'optionalAccess', _181 => _181[0]]), () => ( text));
4184
4569
  var COMMAND_TYPE_LINES = MOTION_COMMAND_TYPES.map((type) => `${type}: ${firstSentence(_nullishCoalesce(MOTION_COMMAND_TYPE_SCHEMAS[type].description, () => ( "")))}`);
4185
4570
  var EASE_COMPACT = {
4186
4571
  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.`,
@@ -4242,6 +4627,7 @@ var COMMAND = object(
4242
4627
  comet: FRACTION("draw: a travelling stroke this fraction of the length long (leave out from and to)."),
4243
4628
  via: { type: "array", items: REF2("point"), minItems: 1, maxItems: 64, description: 'move: waypoints the centre passes through smoothly on the way to "to" or "by".' },
4244
4629
  pacing: { type: "string", enum: ["even", "equal"], description: "move with via: steady speed (even, default) or the same time per stretch (equal)." },
4630
+ times: { type: "array", items: { type: "number", exclusiveMinimum: 0, exclusiveMaximum: 1 }, description: "move with via: rising fractions of the duration (0\u20131) at which each waypoint is passed; overrides pacing." },
4245
4631
  geometric: { type: "boolean", description: "scale: grow by a steady factor, an even zoom (default false)." },
4246
4632
  fill: { type: "string", description: "style: fill colour, CSS (hex, rgb(), rgba())." },
4247
4633
  stroke: { type: "string", description: "style: stroke colour, CSS." },
@@ -4381,11 +4767,11 @@ function describeElement(node, ancestors) {
4381
4767
  ...effects.length ? { effects } : {}
4382
4768
  };
4383
4769
  }
4384
- var round = (n) => Math.round(n * 10) / 10;
4770
+ var round2 = (n) => Math.round(n * 10) / 10;
4385
4771
  function describeMotionElements(scene) {
4386
4772
  return [...indexMotionNodes(scene.artwork.nodes).values()].map((e) => ({
4387
4773
  ...describeElement(e.node, e.ancestors),
4388
- ...e.bounds ? { bounds: { x: round(e.bounds.x), y: round(e.bounds.y), width: round(e.bounds.width), height: round(e.bounds.height) } } : {}
4774
+ ...e.bounds ? { bounds: { x: round2(e.bounds.x), y: round2(e.bounds.y), width: round2(e.bounds.width), height: round2(e.bounds.height) } } : {}
4389
4775
  }));
4390
4776
  }
4391
4777
  function describeMotionShots(scene) {
@@ -4588,18 +4974,18 @@ function createAnimationEditorToolProvider(controller, options = {}) {
4588
4974
  handler: ({ from = 0, to, limit = 200 }) => {
4589
4975
  const s = scene();
4590
4976
  const timeline = compileMotionScene(s);
4591
- const words = _nullishCoalesce(_optionalChain([s, 'access', _164 => _164.narration, 'optionalAccess', _165 => _165.words]), () => ( []));
4977
+ const words = _nullishCoalesce(_optionalChain([s, 'access', _182 => _182.narration, 'optionalAccess', _183 => _183.words]), () => ( []));
4592
4978
  const end = _nullishCoalesce(to, () => ( Math.max(timeline.duration, words.length ? _nullishCoalesce(words[words.length - 1].end, () => ( words[words.length - 1].start)) : 0)));
4593
- const shotAt = (ms) => _optionalChain([timeline, 'access', _166 => _166.shots, 'access', _167 => _167.find, 'call', _168 => _168((x) => ms >= x.start && ms < x.end), 'optionalAccess', _169 => _169.id]);
4979
+ const shotAt = (ms2) => _optionalChain([timeline, 'access', _184 => _184.shots, 'access', _185 => _185.find, 'call', _186 => _186((x) => ms2 >= x.start && ms2 < x.end), 'optionalAccess', _187 => _187.id]);
4594
4980
  const beats = [];
4595
4981
  if (s.beat && s.beat.bpm > 0) {
4596
4982
  const first = Math.max(0, Math.ceil(motionBeatAt(s.beat, from).beat - 1e-9));
4597
4983
  for (let b = first; beats.length < limit; b++) {
4598
- const ms = motionBeatTime(s.beat, b);
4599
- if (ms > end) break;
4600
- const at = motionBeatAt(s.beat, ms);
4601
- const shot = shotAt(ms);
4602
- beats.push({ beat: b, ms: Math.round(ms), bar: at.bar, beatInBar: at.beatInBar, ...shot ? { shot } : {} });
4984
+ const ms2 = motionBeatTime(s.beat, b);
4985
+ if (ms2 > end) break;
4986
+ const at = motionBeatAt(s.beat, ms2);
4987
+ const shot = shotAt(ms2);
4988
+ beats.push({ beat: b, ms: Math.round(ms2), bar: at.bar, beatInBar: at.beatInBar, ...shot ? { shot } : {} });
4603
4989
  }
4604
4990
  }
4605
4991
  const listed = words.map((w, index) => ({ index, text: w.text, start: w.start, ...w.end !== void 0 ? { end: w.end } : {}, ...shotAt(w.start) ? { shot: shotAt(w.start) } : {} })).filter((w) => w.start >= from && w.start <= end).slice(0, limit);
@@ -4774,6 +5160,60 @@ function createAnimationEditorToolProvider(controller, options = {}) {
4774
5160
  return apply(edits, "Fit to length", () => report);
4775
5161
  }
4776
5162
  };
5163
+ const recordTake = {
5164
+ name: "record_animation_take",
5165
+ title: "Record a performed take",
5166
+ description: "Performs changes live and keeps them as a new shot (one undo step), the way a person records by dragging, resizing, turning and recolouring on the stage: give timed samples of where elements are and how they look, and they become smooth commands that play the performance back (a path is one move through waypoints at the recorded times; scales, turns, fades and stroke widths a few eased commands; paints change as picked). Returns the shot id, where it starts, its commands with their start and end times, and how many samples were kept. With dry_run it returns the shot without adding it.",
5167
+ inputSchema: object(
5168
+ {
5169
+ samples: {
5170
+ type: "array",
5171
+ minItems: 1,
5172
+ maxItems: 5e3,
5173
+ description: "Values at moments of the take, in any order. Before an element's first sample a property keeps its pose and the change into the first sample is instant, so start a movement with a sample at the element's current pose (get_motion_path's last point) to have it travel from there.",
5174
+ items: object(
5175
+ {
5176
+ node: { type: "string", minLength: 1, description: "Element id from get_animation." },
5177
+ t: MS("Time from the start of the take"),
5178
+ x: { type: "number", description: "Centre x, document units (with y)." },
5179
+ y: { type: "number", description: "Centre y, document units (with x)." },
5180
+ scaleX: { type: "number", exclusiveMinimum: 0, description: "Horizontal scale from rest (1 = rest size; give scaleX alone for a uniform scale)." },
5181
+ scaleY: { type: "number", exclusiveMinimum: 0, description: "Vertical scale from rest (1 = rest size)." },
5182
+ rotation: { type: "number", description: "Turn from rest, degrees clockwise (keep counting past 360 for more turns)." },
5183
+ opacity: FRACTION("Opacity, 0\u20131."),
5184
+ fill: { type: "string", description: 'Fill colour, e.g. "#0ea5e9".' },
5185
+ stroke: { type: "string", description: 'Stroke colour, e.g. "#0f172a".' },
5186
+ strokeWidth: { type: "number", minimum: 0, description: "Stroke width, document units." }
5187
+ },
5188
+ ["node", "t"]
5189
+ )
5190
+ },
5191
+ length: MS("How long the take ran (default: the last sample); the shot holds after its last change until then"),
5192
+ id: { ...SHOT_ID, description: 'Id for the new shot (default "take", "take-2"\u2026).' },
5193
+ label: { type: "string", description: 'Label for the new shot (default "Recorded take").' },
5194
+ at: { type: "integer", minimum: 0, description: "Position among the shots, 0 = first (default: the end); the take starts where that shot starts." },
5195
+ tolerance: { type: "number", exclusiveMinimum: 0, description: "How closely commands follow the samples, as a multiple of 1 document unit, 0.01 of scale or opacity, 0.5\xB0 and 0.25 of stroke width (default 1; lower keeps more points)." },
5196
+ dry_run: { type: "boolean", description: "Return the shot without adding it (default false)." }
5197
+ },
5198
+ ["samples"]
5199
+ ),
5200
+ annotations: { title: "Record a performed take", ...WRITE },
5201
+ handler: ({ samples, length, id, label, at, tolerance, dry_run = false }) => {
5202
+ const result = controller.recordTake({ samples, length }, { id, label, at, tolerance, dryRun: dry_run });
5203
+ if (!result.ok) throw new Error(result.error);
5204
+ const s = dry_run ? scene() : controller.getScene();
5205
+ const report = { shot: result.shot.id, at: result.at, start: Math.round(result.start), samples: result.samples };
5206
+ 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." };
5207
+ const timeline = compileMotionScene(s);
5208
+ const described = describeMotionShots(s).find((x) => x.id === result.shot.id);
5209
+ return {
5210
+ ...report,
5211
+ commands: described.commands.map((c) => ({ index: c.index, type: c.type, targets: "targets" in c ? c.targets : [], start: c.start, end: c.end })),
5212
+ duration: timeline.duration,
5213
+ issues: issueSummary(result.issues)
5214
+ };
5215
+ }
5216
+ };
4777
5217
  const PROP = { type: "string", enum: ["x", "y", "rotation", "scaleX", "scaleY", "opacity"], description: "x / y (offset, document units), rotation (degrees), scaleX / scaleY or opacity (1 = rest)." };
4778
5218
  const setDrivers = {
4779
5219
  name: "set_animation_drivers",
@@ -4912,7 +5352,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
4912
5352
  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"]),
4913
5353
  annotations: { title: "Add commands to a shot", ...WRITE },
4914
5354
  handler: ({ shot, commands, at }) => {
4915
- const before = _nullishCoalesce(_optionalChain([scene, 'call', _170 => _170(), 'access', _171 => _171.shots, 'access', _172 => _172.find, 'call', _173 => _173((s) => s.id === shot), 'optionalAccess', _174 => _174.run, 'access', _175 => _175.length]), () => ( 0));
5355
+ const before = _nullishCoalesce(_optionalChain([scene, 'call', _188 => _188(), 'access', _189 => _189.shots, 'access', _190 => _190.find, 'call', _191 => _191((s) => s.id === shot), 'optionalAccess', _192 => _192.run, 'access', _193 => _193.length]), () => ( 0));
4916
5356
  const first = _nullishCoalesce(at, () => ( before));
4917
5357
  return apply({ op: "add_commands", shot, commands, at }, "Add commands", (s) => {
4918
5358
  const shots = describeMotionShots(s);
@@ -5045,7 +5485,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
5045
5485
  };
5046
5486
  const finish = (tools) => tools.map((tool) => ({ ...tool, inputSchema: withDefs(tool.inputSchema) }));
5047
5487
  const reads = finish([getAnimation, getShot, describeCommand, findElements, getTextLayout, getMotionPath, getTimingGrid, checkAnimation, renderFrame, exportFlairMotion, exportFlairEffect, exportLottie, seekAnimation]);
5048
- const writes = finish([setArtworkSvg, importPptx, setDrivers, setClip, copyAnimation, fitAnimation, importFlairEffect, insertShots, updateShot, deleteShots, moveShot, addCommands, updateCommand, deleteCommands, moveCommand, setSettings, setControls, editAnimation, writeAnimation, undo, redo]);
5488
+ const writes = finish([setArtworkSvg, importPptx, setDrivers, setClip, copyAnimation, fitAnimation, recordTake, importFlairEffect, insertShots, updateShot, deleteShots, moveShot, addCommands, updateCommand, deleteCommands, moveCommand, setSettings, setControls, editAnimation, writeAnimation, undo, redo]);
5049
5489
  return withAnimationPlayback({
5050
5490
  id: _nullishCoalesce(options.id, () => ( "animation")),
5051
5491
  namespace: options.namespace,
@@ -5113,7 +5553,7 @@ var TIMING = /* @__PURE__ */ new Set(["duration", "delay", "stagger", "with", "e
5113
5553
  var COLORS = /* @__PURE__ */ new Set(["fill", "stroke", "color"]);
5114
5554
  var NODES = /* @__PURE__ */ new Set(["into", "path"]);
5115
5555
  function branch(type) {
5116
- return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access', _176 => _176.properties, 'optionalAccess', _177 => _177.type, 'optionalAccess', _178 => _178.const]) === type);
5556
+ return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access', _194 => _194.properties, 'optionalAccess', _195 => _195.type, 'optionalAccess', _196 => _196.const]) === type);
5117
5557
  }
5118
5558
  function unitOf(type, name) {
5119
5559
  if (name === "duration" || name === "delay" || name === "stagger") return "ms";
@@ -5148,7 +5588,7 @@ function kindOf(name, schema) {
5148
5588
  }
5149
5589
  function motionCommandFields(type) {
5150
5590
  const schema = branch(type);
5151
- if (!_optionalChain([schema, 'optionalAccess', _179 => _179.properties])) return [];
5591
+ if (!_optionalChain([schema, 'optionalAccess', _197 => _197.properties])) return [];
5152
5592
  const required = new Set(_nullishCoalesce(schema.required, () => ( [])));
5153
5593
  const fields = Object.entries(schema.properties).filter(([name]) => name !== "type").map(([name, s]) => {
5154
5594
  const kind = kindOf(name, s);
@@ -5205,7 +5645,7 @@ function defaultMotionCommand(type, targets, scene) {
5205
5645
  if (type === "wait") return { type: "wait", duration: 500 };
5206
5646
  if (!targets.length) return { error: "Pick a target first: click an element on the stage." };
5207
5647
  const nodes = [...indexMotionNodes(scene.artwork.nodes).values()];
5208
- const other = (pred) => _optionalChain([nodes, 'access', _180 => _180.find, 'call', _181 => _181((e) => !targets.includes(e.node.id) && pred(e)), 'optionalAccess', _182 => _182.node, 'access', _183 => _183.id]);
5648
+ const other = (pred) => _optionalChain([nodes, 'access', _198 => _198.find, 'call', _199 => _199((e) => !targets.includes(e.node.id) && pred(e)), 'optionalAccess', _200 => _200.node, 'access', _201 => _201.id]);
5209
5649
  const base = { targets: [...targets] };
5210
5650
  switch (type) {
5211
5651
  case "scale":
@@ -5236,8 +5676,8 @@ function defaultMotionCommand(type, targets, scene) {
5236
5676
  }
5237
5677
  case "effect": {
5238
5678
  for (const id of targets) {
5239
- const node = _optionalChain([nodes, 'access', _184 => _184.find, 'call', _185 => _185((e) => e.node.id === id), 'optionalAccess', _186 => _186.node]);
5240
- for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess', _187 => _187.effects]), () => ( []))) {
5679
+ const node = _optionalChain([nodes, 'access', _202 => _202.find, 'call', _203 => _203((e) => e.node.id === id), 'optionalAccess', _204 => _204.node]);
5680
+ for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess', _205 => _205.effects]), () => ( []))) {
5241
5681
  const param = Object.entries(effect.params).find(([, v]) => typeof v === "number");
5242
5682
  if (param) return { type, ...base, effect: effect.id, param: param[0], to: param[1] + 1 };
5243
5683
  }
@@ -5262,11 +5702,11 @@ function motionCommandRows(scene, shotId) {
5262
5702
  return scene.shots[index].run.map((command3, i) => {
5263
5703
  const t = timeline.commands.find((c) => c.shotIndex === index && c.command === i);
5264
5704
  const primitive = motionFlairPrimitive(command3);
5265
- return { index: i, command: command3, summary: motionCommandSummary(command3), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _188 => _188.start]), () => ( 0)), end: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _189 => _189.end]), () => ( 0)), ...primitive ? { flairPrimitive: primitive } : {} };
5705
+ return { index: i, command: command3, summary: motionCommandSummary(command3), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _206 => _206.start]), () => ( 0)), end: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _207 => _207.end]), () => ( 0)), ...primitive ? { flairPrimitive: primitive } : {} };
5266
5706
  });
5267
5707
  }
5268
5708
  function duplicateMotionCommandEdit(scene, shot, command3) {
5269
- const run = _nullishCoalesce(_optionalChain([scene, 'access', _190 => _190.shots, 'access', _191 => _191.find, 'call', _192 => _192((s) => s.id === shot), 'optionalAccess', _193 => _193.run]), () => ( []));
5709
+ const run = _nullishCoalesce(_optionalChain([scene, 'access', _208 => _208.shots, 'access', _209 => _209.find, 'call', _210 => _210((s) => s.id === shot), 'optionalAccess', _211 => _211.run]), () => ( []));
5270
5710
  return { op: "add_commands", shot, commands: run[command3] ? [structuredClone(run[command3])] : [], at: command3 + 1 };
5271
5711
  }
5272
5712
  var MOTION_EASE_CHOICES = [
@@ -5279,15 +5719,15 @@ function motionEaseKey(ease) {
5279
5719
  }
5280
5720
  function motionEasingCurve(ease, samples = 48, durationMs = 1e3) {
5281
5721
  const resolved = _chunkDWJJI6RTcjs.resolveEasing.call(void 0, ease);
5282
- const ms = resolved !== "hold" && resolved.type === "spring" ? _chunkDWJJI6RTcjs.springSettleMs.call(void 0, resolved) : durationMs;
5722
+ const ms2 = resolved !== "hold" && resolved.type === "spring" ? _chunkDWJJI6RTcjs.springSettleMs.call(void 0, resolved) : durationMs;
5283
5723
  const n = Math.max(2, Math.floor(samples));
5284
5724
  const points = [];
5285
5725
  for (let i = 0; i <= n; i++) {
5286
5726
  const u = i / n;
5287
- points.push({ u, v: resolved === "hold" ? i === n ? 1 : 0 : _chunkDWJJI6RTcjs.evaluateEasing.call(void 0, ease, u, ms) });
5727
+ points.push({ u, v: resolved === "hold" ? i === n ? 1 : 0 : _chunkDWJJI6RTcjs.evaluateEasing.call(void 0, ease, u, ms2) });
5288
5728
  }
5289
5729
  const values = points.map((p) => p.v);
5290
- return { points, min: Math.min(0, ...values), max: Math.max(1, ...values), durationMs: ms };
5730
+ return { points, min: Math.min(0, ...values), max: Math.max(1, ...values), durationMs: ms2 };
5291
5731
  }
5292
5732
  function motionEasingPath(curve, width, height, pad = 4) {
5293
5733
  const span = curve.max - curve.min || 1;
@@ -5328,8 +5768,8 @@ function motionRulerTicks(duration, width, minGap = 56) {
5328
5768
  }
5329
5769
  return ticks;
5330
5770
  }
5331
- function fitMotionSceneEdits(scene, ms, options = {}) {
5332
- const fit = fitMotionScene(scene, ms, options);
5771
+ function fitMotionSceneEdits(scene, ms2, options = {}) {
5772
+ const fit = fitMotionScene(scene, ms2, options);
5333
5773
  return fit.scene.shots.flatMap((shot, i) => {
5334
5774
  const before = scene.shots[i].speed;
5335
5775
  if (before === shot.speed) return [];
@@ -5350,8 +5790,8 @@ function motionCueFromKey(key) {
5350
5790
  }
5351
5791
  function motionCueChoices(scene, limit = 256) {
5352
5792
  const choices = [];
5353
- const s = (ms) => `${(ms / 1e3).toFixed(2)} s`;
5354
- const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access', _194 => _194.narration, 'optionalAccess', _195 => _195.words, 'access', _196 => _196.map, 'call', _197 => _197((w) => _nullishCoalesce(w.end, () => ( w.start)))]), () => ( [0]))) + 2e3;
5793
+ const s = (ms2) => `${(ms2 / 1e3).toFixed(2)} s`;
5794
+ const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access', _212 => _212.narration, 'optionalAccess', _213 => _213.words, 'access', _214 => _214.map, 'call', _215 => _215((w) => _nullishCoalesce(w.end, () => ( w.start)))]), () => ( [0]))) + 2e3;
5355
5795
  if (scene.beat && scene.beat.bpm > 0) {
5356
5796
  for (let beat = 0; choices.length < limit; beat++) {
5357
5797
  const time = motionBeatTime(scene.beat, beat);
@@ -5361,7 +5801,7 @@ function motionCueChoices(scene, limit = 256) {
5361
5801
  choices.push({ value: `beat:${beat}`, label: `Beat ${beat} (bar ${at.bar}.${at.beatInBar}) \xB7 ${s(time)}`, time });
5362
5802
  }
5363
5803
  }
5364
- _optionalChain([scene, 'access', _198 => _198.narration, 'optionalAccess', _199 => _199.words, 'access', _200 => _200.forEach, 'call', _201 => _201((w, i) => {
5804
+ _optionalChain([scene, 'access', _216 => _216.narration, 'optionalAccess', _217 => _217.words, 'access', _218 => _218.forEach, 'call', _219 => _219((w, i) => {
5365
5805
  if (choices.length < limit * 2) choices.push({ value: `word:${i}`, label: `Word ${i} \u201C${w.text}\u201D \xB7 ${s(w.start)}`, time: w.start });
5366
5806
  })]);
5367
5807
  return choices;
@@ -5370,7 +5810,7 @@ function motionShotCueTime(scene, cue) {
5370
5810
  return cue ? motionCueTime(scene, cue) : void 0;
5371
5811
  }
5372
5812
  function formatMotionNarration(narration) {
5373
- return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess', _202 => _202.words]), () => ( []))).map((w) => `${w.start} ${w.text}`).join("\n");
5813
+ return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess', _220 => _220.words]), () => ( []))).map((w) => `${w.start} ${w.text}`).join("\n");
5374
5814
  }
5375
5815
  function parseMotionNarration(text) {
5376
5816
  const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
@@ -5643,4 +6083,11 @@ function createAnimationEffectsToolProvider(controller, options = {}) {
5643
6083
 
5644
6084
 
5645
6085
 
5646
- 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 = _chunkEBDZWIOPcjs.MOTION_ISSUE_CODES; exports.MOTION_MIN_CHANGE_MS = MOTION_MIN_CHANGE_MS; 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.applyMotionControlValues = applyMotionControlValues; exports.applyMotionDrivers = applyMotionDrivers; exports.applyMotionEdits = applyMotionEdits; exports.checkMotionScene = checkMotionScene; exports.clipsShapeProblems = clipsShapeProblems; exports.commandShapeProblems = commandShapeProblems; exports.compileMotionScene = compileMotionScene; 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.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.motionCascade = motionCascade; 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 = _chunkEBDZWIOPcjs.motionIssue; exports.motionIssueKey = _chunkEBDZWIOPcjs.motionIssueKey; exports.motionIssueLine = _chunkEBDZWIOPcjs.motionIssueLine; exports.motionMovingIds = motionMovingIds; exports.motionNodeAtElement = motionNodeAtElement; exports.motionNodeClips = motionNodeClips; exports.motionNodeOffset = motionNodeOffset; 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.motionShotCueTime = motionShotCueTime; exports.motionShotTimeScale = motionShotTimeScale; exports.motionStageSvg = motionStageSvg; exports.motionTextLayout = motionTextLayout; exports.motionTextUnits = motionTextUnits; exports.motionTrackKey = motionTrackKey; exports.namedWithSuggestion = _chunkEBDZWIOPcjs.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.smoothMotionLegs = smoothMotionLegs; exports.smoothMotionTimes = smoothMotionTimes; exports.suggestName = _chunkEBDZWIOPcjs.suggestName; exports.toFlairEffectDocument = toFlairEffectDocument; exports.toFlairSvgMotion = toFlairSvgMotion; exports.toFlairVectorEffect = toFlairVectorEffect; exports.toLottie = toLottie; exports.trackValue = trackValue; exports.withAnimationPlayback = withAnimationPlayback; exports.withMotionLayers = withMotionLayers; exports.withViewBox = withViewBox; exports.withoutMotionEffect = withoutMotionEffect;
6086
+
6087
+
6088
+
6089
+
6090
+
6091
+
6092
+
6093
+ 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 = _chunkEBDZWIOPcjs.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.applyMotionControlValues = applyMotionControlValues; exports.applyMotionDrivers = applyMotionDrivers; exports.applyMotionEdits = applyMotionEdits; exports.checkMotionScene = checkMotionScene; exports.clipsShapeProblems = clipsShapeProblems; exports.commandShapeProblems = commandShapeProblems; exports.compileMotionScene = compileMotionScene; 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.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.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 = _chunkEBDZWIOPcjs.motionIssue; exports.motionIssueKey = _chunkEBDZWIOPcjs.motionIssueKey; exports.motionIssueLine = _chunkEBDZWIOPcjs.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.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 = _chunkEBDZWIOPcjs.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 = _chunkEBDZWIOPcjs.suggestName; exports.toFlairEffectDocument = toFlairEffectDocument; exports.toFlairSvgMotion = toFlairSvgMotion; exports.toFlairVectorEffect = toFlairVectorEffect; exports.toLottie = toLottie; exports.trackValue = trackValue; exports.withAnimationPlayback = withAnimationPlayback; exports.withMotionLayers = withMotionLayers; exports.withViewBox = withViewBox; exports.withoutMotionEffect = withoutMotionEffect;