@pixerate/editor 0.70.0 → 0.71.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.
@@ -1537,11 +1537,35 @@ function motionStageSvg(scene, time, options = {}) {
1537
1537
  const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _44 => _44.exec, 'call', _45 => _45(body), 'optionalAccess', _46 => _46[0]]), () => ( "")) : "";
1538
1538
  return `${current.slice(0, open)}${bg}${ghosts.join("")}${body.slice(bg.length)}${paths}</svg>`;
1539
1539
  }
1540
+ function motionContactSheetSvg(scene, times, options = {}) {
1541
+ const { columns, labels = true, ...frameOptions } = options;
1542
+ const n = Math.max(1, times.length);
1543
+ const cols = Math.max(1, Math.min(n, Math.floor(_nullishCoalesce(columns, () => ( Math.ceil(Math.sqrt(n)))))));
1544
+ const rows = Math.ceil(n / cols);
1545
+ const { width: w, height: h } = scene.artwork;
1546
+ const gap = Math.round(Math.max(w, h) * 0.04);
1547
+ const label = labels ? Math.round(Math.max(12, h * 0.08)) : 0;
1548
+ const timeline = compileMotionScene(scene);
1549
+ const W = cols * w + (cols + 1) * gap;
1550
+ const H = rows * (h + label) + (rows + 1) * gap;
1551
+ const prefix = _nullishCoalesce(frameOptions.idPrefix, () => ( "vg-"));
1552
+ const cells = times.map((t, i) => {
1553
+ const x = gap + i % cols * (w + gap);
1554
+ const y = gap + Math.floor(i / cols) * (h + label + gap);
1555
+ const frame = motionStageSvg(scene, t, { ...frameOptions, idPrefix: `${prefix}f${i}-` });
1556
+ const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _47 => _47.exec, 'call', _48 => _48(frame), 'optionalAccess', _49 => _49[1]]), () => ( `0 0 ${w} ${h}`));
1557
+ const inner = innerSvg(frame).replace(new RegExp(` id="(?!${`${prefix}f${i}-`.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
1558
+ const shot = _nullishCoalesce(timeline.shots.find((s) => t >= s.start && t < s.end), () => ( timeline.shots[timeline.shots.length - 1]));
1559
+ 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>` : "";
1560
+ return `<svg x="${x}" y="${y}" width="${w}" height="${h}" viewBox="${view}" overflow="hidden">${inner}</svg><rect x="${x}" y="${y}" width="${w}" height="${h}" fill="none" stroke="#cbd5e1"/>${caption}`;
1561
+ });
1562
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}"><rect width="${W}" height="${H}" fill="#f8fafc"/>${cells.join("")}</svg>`;
1563
+ }
1540
1564
 
1541
1565
  // src/animation-editor/check.ts
1542
1566
  var MOTION_MIN_CHANGE_MS = 120;
1543
1567
  var NUMERIC_PARAMS = (node, effectId) => {
1544
- const effect = _optionalChain([node, 'access', _47 => _47.effects, 'optionalAccess', _48 => _48.find, 'call', _49 => _49((e) => e.id === effectId)]);
1568
+ const effect = _optionalChain([node, 'access', _50 => _50.effects, 'optionalAccess', _51 => _51.find, 'call', _52 => _52((e) => e.id === effectId)]);
1545
1569
  if (!effect) return null;
1546
1570
  return Object.entries(effect.params).filter(([, v]) => typeof v === "number").map(([k]) => k);
1547
1571
  };
@@ -1622,7 +1646,7 @@ function checkMotionScene(scene) {
1622
1646
  if (command2.type === "follow" && known(command2.path, "unknown-path", "path", where) && nodes.get(command2.path).node.type !== "path")
1623
1647
  issues.push(motionIssue("not-a-path", `"${command2.path}" is a group; follow a single path node.`, { ...where, ids: [command2.path] }));
1624
1648
  if (command2.type === "morph") known(command2.into, "unknown-shape", "shape", where);
1625
- if (command2.type === "play" && !_optionalChain([scene, 'access', _50 => _50.clips, 'optionalAccess', _51 => _51[command2.clip]]))
1649
+ if (command2.type === "play" && !_optionalChain([scene, 'access', _53 => _53.clips, 'optionalAccess', _54 => _54[command2.clip]]))
1626
1650
  issues.push(motionIssue("unknown-clip", `There is no clip ${namedWithSuggestion(command2.clip, Object.keys(_nullishCoalesce(scene.clips, () => ( {}))))}; define it with set_animation_clip.`, where));
1627
1651
  if (command2.type === "match" && known(command2.into, "unknown-shape", "group", where)) {
1628
1652
  const into = nodes.get(command2.into);
@@ -1715,7 +1739,7 @@ function checkMotionScene(scene) {
1715
1739
  if (!shot.at) return;
1716
1740
  const timing = timeline.shots[i];
1717
1741
  if (timing.cue === void 0) {
1718
- 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', _52 => _52.narration, 'optionalAccess', _53 => _53.words, 'access', _54 => _54.length]), () => ( 0))} words (indexes from 0)`;
1742
+ 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', _55 => _55.narration, 'optionalAccess', _56 => _56.words, 'access', _57 => _57.length]), () => ( 0))} words (indexes from 0)`;
1719
1743
  issues.push(motionIssue("unknown-cue", `Shot "${shot.id}" is cued to ${what}.`, { shot: shot.id }));
1720
1744
  } else if (timing.missedBy !== void 0) {
1721
1745
  const late = Math.round(timing.missedBy);
@@ -1748,7 +1772,7 @@ function checkMotionScene(scene) {
1748
1772
  const cam = frame.camera;
1749
1773
  const docIndex = indexMotionNodes(frame.document.nodes);
1750
1774
  for (const id of command2.targets) {
1751
- const b = _optionalChain([docIndex, 'access', _55 => _55.get, 'call', _56 => _56(id), 'optionalAccess', _57 => _57.bounds]);
1775
+ const b = _optionalChain([docIndex, 'access', _58 => _58.get, 'call', _59 => _59(id), 'optionalAccess', _60 => _60.bounds]);
1752
1776
  if (!b || reported.has(id) || !visibleAt(timeline, id, timing.end, nodes.get(id).node)) continue;
1753
1777
  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;
1754
1778
  if (outside) {
@@ -1972,9 +1996,9 @@ function applyMotionEdits(scene, edits) {
1972
1996
  let next = scene;
1973
1997
  for (let i = 0; i < list.length; i++) {
1974
1998
  const edit = list[i];
1975
- const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _58 => _58.op])}): ` : "";
1999
+ const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _61 => _61.op])}): ` : "";
1976
2000
  if (!edit || !MOTION_EDIT_OPS.includes(edit.op))
1977
- return { ok: false, error: `${prefix}Unknown op ${namedWithSuggestion(String(_optionalChain([edit, 'optionalAccess', _59 => _59.op])), MOTION_EDIT_OPS)}. Nothing was changed.` };
2001
+ return { ok: false, error: `${prefix}Unknown op ${namedWithSuggestion(String(_optionalChain([edit, 'optionalAccess', _62 => _62.op])), MOTION_EDIT_OPS)}. Nothing was changed.` };
1978
2002
  try {
1979
2003
  next = applyOne(next, edit);
1980
2004
  } catch (error) {
@@ -1990,6 +2014,56 @@ function applyMotionEdits(scene, edits) {
1990
2014
  if (added.length) return { ok: false, error: `Nothing was changed: ${added.map(motionIssueLine).join(" ")}` };
1991
2015
  return { ok: true, scene: next, issues, hints: editHints(before, issues) };
1992
2016
  }
2017
+ var REF = /^\$(\d+|last)((?:\.[A-Za-z_]\w*|\[\d+\])+)$/;
2018
+ function resolveMotionEditRefs(scene, edits) {
2019
+ const outcomes = [];
2020
+ const resolved = [];
2021
+ let next = scene;
2022
+ for (let i = 0; i < edits.length; i++) {
2023
+ const where = `op ${i + 1} (${_optionalChain([edits, 'access', _63 => _63[i], 'optionalAccess', _64 => _64.op])})`;
2024
+ let problem = null;
2025
+ const walk = (v) => {
2026
+ if (typeof v === "string") {
2027
+ if (v.startsWith("$$")) return v.slice(1);
2028
+ const m = REF.exec(v);
2029
+ if (!m) return v;
2030
+ const n = m[1] === "last" ? i - 1 : Number(m[1]);
2031
+ if (!(n >= 0 && n < i)) {
2032
+ problem = `${where}: "${v}" refers to op ${n + 1}, but only ops before this one (1 to ${i}) can be referred to.`;
2033
+ return v;
2034
+ }
2035
+ let value = outcomes[n];
2036
+ for (const part of _nullishCoalesce(m[2].match(/\.[A-Za-z_]\w*|\[\d+\]/g), () => ( []))) value = value == null ? void 0 : value[part.startsWith(".") ? part.slice(1) : part.slice(1, -1)];
2037
+ if (value === void 0) problem = `${where}: "${v}" has no value; op ${n + 1} made ${JSON.stringify(outcomes[n])}.`;
2038
+ return value;
2039
+ }
2040
+ if (Array.isArray(v)) return v.map(walk);
2041
+ if (v && typeof v === "object") return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, walk(x)]));
2042
+ return v;
2043
+ };
2044
+ const edit = walk(edits[i]);
2045
+ if (problem) return { ok: false, error: `${problem} Nothing was changed.` };
2046
+ const before = next;
2047
+ try {
2048
+ next = applyOne(next, edit);
2049
+ } catch (error) {
2050
+ if (error instanceof EditError) return { ok: false, error: `${where}: ${error.message} Nothing was changed.` };
2051
+ throw error;
2052
+ }
2053
+ resolved.push(edit);
2054
+ const out = { op: edit.op };
2055
+ if (edit.op === "insert_shots") out.shots = edit.shots.map((s) => s.id);
2056
+ if ("shot" in edit && typeof edit.shot === "string") out.shot = edit.op === "update_shot" && _optionalChain([edit, 'access', _65 => _65.set, 'optionalAccess', _66 => _66.id]) ? edit.set.id : edit.shot;
2057
+ if (edit.op === "add_commands") {
2058
+ const at = _nullishCoalesce(_nullishCoalesce(edit.at, () => ( _optionalChain([before, 'access', _67 => _67.shots, 'access', _68 => _68.find, 'call', _69 => _69((s) => s.id === edit.shot), 'optionalAccess', _70 => _70.run, 'access', _71 => _71.length]))), () => ( 0));
2059
+ out.indexes = edit.commands.map((_, k) => at + k);
2060
+ }
2061
+ if (edit.op === "move_command") out.indexes = [edit.to];
2062
+ if (edit.op === "set_clip") out.clip = edit.name;
2063
+ outcomes.push(out);
2064
+ }
2065
+ return { ok: true, edits: resolved, outcomes };
2066
+ }
1993
2067
  function createMotionScene(artwork, init = {}) {
1994
2068
  return { version: 1, name: _nullishCoalesce(init.name, () => ( "animation")), ...init, artwork, shots: _nullishCoalesce(init.shots, () => ( [])) };
1995
2069
  }
@@ -2134,7 +2208,7 @@ function toFlairSvgMotion(scene, options = {}) {
2134
2208
  const wrapper = freshId(`${id}--follow`);
2135
2209
  const centre = freshId(`${id}--follow-centre`);
2136
2210
  const c = entry.center;
2137
- const at = _optionalChain([motionNodeOffset, 'call', _60 => _60(entry, timeline, 0, nodes), 'optionalAccess', _61 => _61.follow]);
2211
+ const at = _optionalChain([motionNodeOffset, 'call', _72 => _72(entry, timeline, 0, nodes), 'optionalAccess', _73 => _73.follow]);
2138
2212
  const placed = at ? `translate(${f(at.x)} ${f(at.y)})${at.angle ? ` rotate(${f(at.angle)})` : ""}` : void 0;
2139
2213
  (wrap[id] ??= []).push({ id: wrapper, ...placed ? { transform: placed } : {} }, { id: centre, transform: `translate(${f(-c.x)} ${f(-c.y)})` });
2140
2214
  follows.push({ id, wrapper, routes: ids, segments: followSegments.filter((seg) => seg.path && ids.has(seg.path)) });
@@ -2194,7 +2268,7 @@ function toFlairSvgMotion(scene, options = {}) {
2194
2268
  exact: () => false,
2195
2269
  value: (t) => {
2196
2270
  const frame = motionFrameAt(scene, t, { draw: false, offsets: false });
2197
- const now = _chunkYJLCBA7Vcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _62 => _62(frame.document.nodes), 'access', _63 => _63.get, 'call', _64 => _64(id), 'optionalAccess', _65 => _65.node]));
2271
+ const now = _chunkYJLCBA7Vcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _74 => _74(frame.document.nodes), 'access', _75 => _75.get, 'call', _76 => _76(id), 'optionalAccess', _77 => _77.node]));
2198
2272
  if (!now || now.type !== "path") return "";
2199
2273
  const bezier = _chunkO2UH7LZEcjs.transformBezierPath.call(void 0, _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, now, _chunkYJLCBA7Vcjs.IDENTITY_MATRIX, { effects: false }), inverse);
2200
2274
  return _chunkO2UH7LZEcjs.bezierPathToSvg.call(void 0, bezier, { precision: Math.min(precision, 2) });
@@ -2215,8 +2289,8 @@ function toFlairSvgMotion(scene, options = {}) {
2215
2289
  ...delay > 0 ? { delay: round2(delay) } : {},
2216
2290
  ms: round2(ms),
2217
2291
  ease: written,
2218
- ..._optionalChain([segment, 'optionalAccess', _66 => _66.behavior]) ? { behavior: segment.behavior } : {},
2219
- ..._optionalChain([segment, 'optionalAccess', _67 => _67.steps]) ? { strobe: segment.steps } : {}
2292
+ ..._optionalChain([segment, 'optionalAccess', _78 => _78.behavior]) ? { behavior: segment.behavior } : {},
2293
+ ..._optionalChain([segment, 'optionalAccess', _79 => _79.steps]) ? { strobe: segment.steps } : {}
2220
2294
  };
2221
2295
  if (channel.kind === "view") animate.push({ ...base, view: to });
2222
2296
  else if (channel.kind === "draw") animate.push({ ...base, draw: [from, to] });
@@ -2305,7 +2379,7 @@ function toFlairSvgMotion(scene, options = {}) {
2305
2379
  let svg = _chunkYJLCBA7Vcjs.vectorDocumentToSvg.call(void 0, { ...scene.artwork, nodes: _chunkYJLCBA7Vcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) }, { pretty: false, precision, wrap });
2306
2380
  if (routes.length) svg = svg.replace(/^(<svg[^>]*>)/, `$1<defs>${routes.join("")}</defs>`);
2307
2381
  let motion = { svg, animate };
2308
- if (_optionalChain([scene, 'access', _68 => _68.controls, 'optionalAccess', _69 => _69.length])) {
2382
+ if (_optionalChain([scene, 'access', _80 => _80.controls, 'optionalAccess', _81 => _81.length])) {
2309
2383
  const declarations = scene.controls.map((c) => `{{${c.name}: color default=${c.default.toUpperCase()}${c.label ? ` label='${c.label.replace(/'/g, "\u2019")}'` : ""}}}`).join(" ");
2310
2384
  const swap = (text) => scene.controls.reduce((s, c) => s.replace(new RegExp(`${c.default}(?![0-9a-fA-F])`, "gi"), `{{${c.name}}}`), text);
2311
2385
  svg = svg.replace(/^(<svg[^>]*>)/, `$1<!-- ${declarations} -->`);
@@ -2327,7 +2401,7 @@ function toFlairVectorEffect(scene, options = {}) {
2327
2401
  tags: _nullishCoalesce(options.tags, () => ( [])),
2328
2402
  motion: out.motion,
2329
2403
  hold: out.hold,
2330
- ..._optionalChain([scene, 'access', _70 => _70.presets, 'optionalAccess', _71 => _71.length]) ? { presets: scene.presets } : {},
2404
+ ..._optionalChain([scene, 'access', _82 => _82.presets, 'optionalAccess', _83 => _83.length]) ? { presets: scene.presets } : {},
2331
2405
  ...out.trigger ? { trigger: out.trigger } : {}
2332
2406
  };
2333
2407
  }
@@ -2371,7 +2445,7 @@ function resolvePlaceholders(motion, notes) {
2371
2445
  const filled = text.replace(PLACEHOLDER, (_, name) => {
2372
2446
  const d = declared.get(name);
2373
2447
  if (!d) unknown.add(name);
2374
- return (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _72 => _72.value]), () => ( ""))).replace(/"/g, '\\"');
2448
+ return (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _84 => _84.value]), () => ( ""))).replace(/"/g, '\\"');
2375
2449
  });
2376
2450
  for (const name of unknown) notes.push(`Placeholder "{{${name}}}" has no declaration, so it was left empty.`);
2377
2451
  return { motion: JSON.parse(filled), controls };
@@ -2425,7 +2499,7 @@ function commandsFor(anim, nodes, entered, notes) {
2425
2499
  if (anim.along) {
2426
2500
  const path = nodes.get(anim.along);
2427
2501
  if (!path || path.node.type !== "path") notes.push(`${where} follows "${anim.along}", which is not a path in the artwork; the follow was skipped.`);
2428
- else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access', _73 => _73.at, 'optionalAccess', _74 => _74[0]]), () => ( 0)), to: _nullishCoalesce(_optionalChain([anim, 'access', _75 => _75.at, 'optionalAccess', _76 => _76[1]]), () => ( 1)), ...anim.orient ? { rotate: true } : {}, ...timing, ...ease(MOTION_EASE_IN_OUT) });
2502
+ else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access', _85 => _85.at, 'optionalAccess', _86 => _86[0]]), () => ( 0)), to: _nullishCoalesce(_optionalChain([anim, 'access', _87 => _87.at, 'optionalAccess', _88 => _88[1]]), () => ( 1)), ...anim.orient ? { rotate: true } : {}, ...timing, ...ease(MOTION_EASE_IN_OUT) });
2429
2503
  }
2430
2504
  for (const [attr, [a, b]] of Object.entries(_nullishCoalesce(anim.attrs, () => ( {})))) {
2431
2505
  if (attr === "opacity") {
@@ -2436,7 +2510,7 @@ function commandsFor(anim, nodes, entered, notes) {
2436
2510
  out.push({ type: "enter", targets, style: "fade", ...timing, ...ease(MOTION_EASE_OUT) });
2437
2511
  } else out.push({ type: "fade", targets, to: Math.min(1, Math.max(0, to)), ...timing, ...ease(MOTION_EASE_IN_OUT) });
2438
2512
  } else if (attr === "fill" || attr === "stroke") {
2439
- if (_optionalChain([behavior, 'optionalAccess', _77 => _77.type]) === "pulse") out.push({ type: "color-pulse", targets, color: String(b), paint: attr, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...ease("linear") });
2513
+ if (_optionalChain([behavior, 'optionalAccess', _89 => _89.type]) === "pulse") out.push({ type: "color-pulse", targets, color: String(b), paint: attr, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...ease("linear") });
2440
2514
  else {
2441
2515
  if (behavior) notes.push(`${where}: a ${attr} ${behavior.type} became a plain colour change.`);
2442
2516
  out.push({ type: "style", targets, [attr]: String(b), ...timing, ...ease(MOTION_EASE_IN_OUT) });
@@ -2719,7 +2793,7 @@ function toLottie(scene, options = {}) {
2719
2793
  const shapeSegments = [...timeline.tracks.entries()].filter(([k]) => k.startsWith(`${path.id}\0`) && (k.endsWith("\0shape") || k.includes("\0effect:"))).flatMap(([, v]) => v);
2720
2794
  const geometryAt = (t) => {
2721
2795
  if (!shapeSegments.length) return _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path);
2722
- const now = _optionalChain([indexMotionNodes, 'call', _78 => _78(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes), 'access', _79 => _79.get, 'call', _80 => _80(path.id), 'optionalAccess', _81 => _81.node]);
2796
+ const now = _optionalChain([indexMotionNodes, 'call', _90 => _90(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes), 'access', _91 => _91.get, 'call', _92 => _92(path.id), 'optionalAccess', _93 => _93.node]);
2723
2797
  return now && now.type === "path" ? _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, _chunkYJLCBA7Vcjs.expandVectorAppearances.call(void 0, [now])[0]) : _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path);
2724
2798
  };
2725
2799
  const rest = lottieContours(_chunkO2UH7LZEcjs.toBezierPath.call(void 0, geometryAt(0)), round2);
@@ -2767,7 +2841,7 @@ function toLottie(scene, options = {}) {
2767
2841
  lj: path.strokeLinejoin === "round" ? 2 : path.strokeLinejoin === "bevel" ? 3 : 1,
2768
2842
  ml: 4
2769
2843
  });
2770
- if (_optionalChain([path, 'access', _82 => _82.strokeDasharray, 'optionalAccess', _83 => _83.length])) notes.push(`"${path.id}": its dashes are not exported; the stroke is solid.`);
2844
+ if (_optionalChain([path, 'access', _94 => _94.strokeDasharray, 'optionalAccess', _95 => _95.length])) notes.push(`"${path.id}": its dashes are not exported; the stroke is solid.`);
2771
2845
  if (path.strokeAlign && path.strokeAlign !== "center") notes.push(`"${path.id}": its stroke is drawn centred (Lottie has no inside or outside strokes).`);
2772
2846
  }
2773
2847
  const fill = paintOf(path.fill, "fill", path.id);
@@ -2918,7 +2992,7 @@ var AnimationEditorController = (_class = class {
2918
2992
  time: p.time,
2919
2993
  playing: p.playing,
2920
2994
  loop: p.loop,
2921
- currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _84 => _84(timeline, p.time), 'optionalAccess', _85 => _85.id]), () => ( null)),
2995
+ currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _96 => _96(timeline, p.time), 'optionalAccess', _97 => _97.id]), () => ( null)),
2922
2996
  selection: this.selection,
2923
2997
  readOnly: this.readOnly,
2924
2998
  error: this.error,
@@ -2957,7 +3031,7 @@ var AnimationEditorController = (_class = class {
2957
3031
  this.applying = false;
2958
3032
  }
2959
3033
  this.notify();
2960
- _optionalChain([this, 'access', _86 => _86.onChange, 'optionalCall', _87 => _87(scene)]);
3034
+ _optionalChain([this, 'access', _98 => _98.onChange, 'optionalCall', _99 => _99(scene)]);
2961
3035
  }
2962
3036
  commit(label, next) {
2963
3037
  const before = this.scene;
@@ -3302,9 +3376,9 @@ var EDIT_OP = {
3302
3376
  object({ op: { const: "delete_shots", description: "Like delete_shots." }, shots: { type: "array", items: SHOT_ID, minItems: 1, description: "Shot ids." } }, ["op", "shots"]),
3303
3377
  object({ op: { const: "move_shot", description: "Like move_shot." }, shot: SHOT_ID, to: { type: "integer", minimum: 0, description: "New position (0 = first)." } }, ["op", "shot", "to"]),
3304
3378
  object({ op: { const: "add_commands", description: "Like add_commands." }, shot: SHOT_ID, commands: { type: "array", items: MOTION_COMMAND_SCHEMA, minItems: 1, description: "Commands to add." }, at: { type: "integer", minimum: 0, description: "Index in the shot's run (default: the end)." } }, ["op", "shot", "commands"]),
3305
- object({ op: { const: "update_command", description: "Like update_command." }, shot: SHOT_ID, command: { type: "integer", minimum: 0, description: "Index in the shot's run." }, set: { type: "object", description: "Command fields to replace." }, remove: { type: "array", items: { type: "string" }, description: "Command fields to clear." } }, ["op", "shot", "command"]),
3306
- object({ op: { const: "move_command", description: "Like move_command." }, shot: SHOT_ID, command: { type: "integer", minimum: 0, description: "Index in the shot's run." }, to: { type: "integer", minimum: 0, description: "New index in the run (0 = first)." } }, ["op", "shot", "command", "to"]),
3307
- object({ op: { const: "delete_commands", description: "Like delete_commands." }, shot: SHOT_ID, commands: { type: "array", items: { type: "integer", minimum: 0 }, minItems: 1, description: "Indexes in the shot's run." } }, ["op", "shot", "commands"]),
3379
+ object({ op: { const: "update_command", description: "Like update_command." }, shot: SHOT_ID, command: { anyOf: [{ type: "integer", minimum: 0 }, { type: "string", pattern: "^\\$(\\d+|last)[.\\[]" }], description: `Index in the shot's run, or a reference to an earlier op's outcome such as "$0.indexes[0]".` }, set: { type: "object", description: "Command fields to replace." }, remove: { type: "array", items: { type: "string" }, description: "Command fields to clear." } }, ["op", "shot", "command"]),
3380
+ object({ op: { const: "move_command", description: "Like move_command." }, shot: SHOT_ID, command: { anyOf: [{ type: "integer", minimum: 0 }, { type: "string", pattern: "^\\$(\\d+|last)[.\\[]" }], description: `Index in the shot's run, or a reference to an earlier op's outcome such as "$0.indexes[0]".` }, to: { type: "integer", minimum: 0, description: "New index in the run (0 = first)." } }, ["op", "shot", "command", "to"]),
3381
+ object({ op: { const: "delete_commands", description: "Like delete_commands." }, shot: SHOT_ID, commands: { type: "array", items: { anyOf: [{ type: "integer", minimum: 0 }, { type: "string", pattern: "^\\$(\\d+|last)[.\\[]" }], description: `An index, or a reference to an earlier op's outcome such as "$0.indexes[0]".` }, minItems: 1, description: "Indexes in the shot's run." } }, ["op", "shot", "commands"]),
3308
3382
  object({ op: { const: "set_controls", description: "Like set_controls." }, controls: { type: "array", items: CONTROL, description: "Controls (empty clears)." }, presets: { type: "array", items: PRESET, description: "Presets (empty clears)." } }, ["op", "controls"])
3309
3383
  ]
3310
3384
  };
@@ -3448,7 +3522,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
3448
3522
  const renderFrame = {
3449
3523
  name: "render_animation_frame",
3450
3524
  title: "Render a frame",
3451
- description: 'Draws the animation at one moment without changing it, to check it: standalone SVG by default, or a PNG image (format "png") when the host can rasterize. Give a time in ms, or a shot and where in it.',
3525
+ description: 'Draws the animation at one moment (or, with times, several moments as one labelled contact sheet) without changing it, to check it: standalone SVG by default, or a PNG image (format "png") when the host can rasterize. Give a time in ms, or a shot and where in it.',
3452
3526
  inputSchema: object({
3453
3527
  time: MS("Time from the start"),
3454
3528
  shot: { ...SHOT_ID, description: 'Render a moment of this shot instead of "time".' },
@@ -3463,10 +3537,12 @@ function createAnimationEditorToolProvider(controller, options = {}) {
3463
3537
  [],
3464
3538
  "Draw faded frames either side of the moment, to see motion in one picture."
3465
3539
  ),
3540
+ times: { type: "array", items: { type: "number", minimum: 0 }, minItems: 2, maxItems: 16, description: "Several moments (ms) to draw as one contact sheet, each labelled with its time and shot, instead of one frame. Overrides time and shot." },
3541
+ columns: { type: "integer", minimum: 1, maximum: 8, description: "With times: frames a row (default about square)." },
3466
3542
  motion_paths: { anyOf: [{ type: "boolean" }, { type: "array", items: { type: "string", minLength: 1 } }], description: "Draw where nodes travel (dashed, with a dot where each change starts or ends): true for every node whose place changes, or a list of ids." }
3467
3543
  }),
3468
3544
  annotations: { title: "Render a frame", ...READ },
3469
- handler: async ({ time, shot, at = "end", format = "svg", onion_skin, motion_paths }) => {
3545
+ handler: async ({ time, shot, at = "end", format = "svg", onion_skin, motion_paths, times, columns }) => {
3470
3546
  const s = scene();
3471
3547
  let t = _nullishCoalesce(time, () => ( controller.getState().time));
3472
3548
  if (shot !== void 0) {
@@ -3478,7 +3554,18 @@ function createAnimationEditorToolProvider(controller, options = {}) {
3478
3554
  const known = new Set(describeMotionElements(s).map((e) => e.id));
3479
3555
  for (const id of motion_paths) if (!known.has(id)) throw new Error(`There is no element ${namedWithSuggestion(id, known)} to draw a motion path for.`);
3480
3556
  }
3481
- const svg = controller.toSvg(t, { pretty: false, ...onion_skin ? { onionSkin: onion_skin } : {}, ...motion_paths ? { motionPaths: motion_paths } : {} });
3557
+ const overlays = { ...onion_skin ? { onionSkin: onion_skin } : {}, ...motion_paths ? { motionPaths: motion_paths } : {} };
3558
+ if (times) {
3559
+ const sheet = motionContactSheetSvg(s, times, { pretty: false, ...overlays, ...columns ? { columns } : {} });
3560
+ if (format === "png") {
3561
+ if (!options.rasterize) throw new Error('This host cannot make PNG images; call render_animation_frame with format "svg".');
3562
+ const size = /width="(\d+)" height="(\d+)"/.exec(sheet);
3563
+ const image = await options.rasterize(sheet, { width: Number(size[1]), height: Number(size[2]) });
3564
+ return { content: [{ type: "image", data: image.data, mimeType: image.mimeType }] };
3565
+ }
3566
+ return { times, svg: sheet };
3567
+ }
3568
+ const svg = controller.toSvg(t, { pretty: false, ...overlays });
3482
3569
  if (format === "png") {
3483
3570
  if (!options.rasterize) throw new Error('This host cannot make PNG images; call render_animation_frame with format "svg".');
3484
3571
  const image = await options.rasterize(svg, { width: s.artwork.width, height: s.artwork.height });
@@ -3503,9 +3590,9 @@ function createAnimationEditorToolProvider(controller, options = {}) {
3503
3590
  handler: ({ from = 0, to, limit = 200 }) => {
3504
3591
  const s = scene();
3505
3592
  const timeline = compileMotionScene(s);
3506
- const words = _nullishCoalesce(_optionalChain([s, 'access', _88 => _88.narration, 'optionalAccess', _89 => _89.words]), () => ( []));
3593
+ const words = _nullishCoalesce(_optionalChain([s, 'access', _100 => _100.narration, 'optionalAccess', _101 => _101.words]), () => ( []));
3507
3594
  const end = _nullishCoalesce(to, () => ( Math.max(timeline.duration, words.length ? _nullishCoalesce(words[words.length - 1].end, () => ( words[words.length - 1].start)) : 0)));
3508
- const shotAt = (ms) => _optionalChain([timeline, 'access', _90 => _90.shots, 'access', _91 => _91.find, 'call', _92 => _92((x) => ms >= x.start && ms < x.end), 'optionalAccess', _93 => _93.id]);
3595
+ const shotAt = (ms) => _optionalChain([timeline, 'access', _102 => _102.shots, 'access', _103 => _103.find, 'call', _104 => _104((x) => ms >= x.start && ms < x.end), 'optionalAccess', _105 => _105.id]);
3509
3596
  const beats = [];
3510
3597
  if (s.beat && s.beat.bpm > 0) {
3511
3598
  const first = Math.max(0, Math.ceil(motionBeatAt(s.beat, from).beat - 1e-9));
@@ -3755,7 +3842,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
3755
3842
  inputSchema: object({ shot: SHOT_ID, commands: { type: "array", items: MOTION_COMMAND_SCHEMA, minItems: 1, description: "Commands to add." }, at: { type: "integer", minimum: 0, description: "Index in the run (default: the end)." } }, ["shot", "commands"]),
3756
3843
  annotations: { title: "Add commands to a shot", ...WRITE },
3757
3844
  handler: ({ shot, commands, at }) => {
3758
- const before = _nullishCoalesce(_optionalChain([scene, 'call', _94 => _94(), 'access', _95 => _95.shots, 'access', _96 => _96.find, 'call', _97 => _97((s) => s.id === shot), 'optionalAccess', _98 => _98.run, 'access', _99 => _99.length]), () => ( 0));
3845
+ const before = _nullishCoalesce(_optionalChain([scene, 'call', _106 => _106(), 'access', _107 => _107.shots, 'access', _108 => _108.find, 'call', _109 => _109((s) => s.id === shot), 'optionalAccess', _110 => _110.run, 'access', _111 => _111.length]), () => ( 0));
3759
3846
  const first = _nullishCoalesce(at, () => ( before));
3760
3847
  return apply({ op: "add_commands", shot, commands, at }, "Add commands", (s) => {
3761
3848
  const shots = describeMotionShots(s);
@@ -3822,12 +3909,14 @@ function createAnimationEditorToolProvider(controller, options = {}) {
3822
3909
  const editAnimation = {
3823
3910
  name: "edit_animation",
3824
3911
  title: "Make several edits at once",
3825
- description: 'Applies several edits in order as one undo step, all or nothing: if one fails, nothing changes and the error names the op ("op 2 (add_commands): \u2026"). With dry_run it only reports the timing and issues the edits would give.',
3912
+ description: `Applies several edits in order as one undo step, all or nothing: if one fails, nothing changes and the error names the op ("op 2 (add_commands): \u2026"). A later op can use what an earlier one made: "$0.indexes[0]" is the index of the first command op 1 added, "$last.shot" the shot the op before changed. Returns each op's outcome (shot, shots, indexes, clip), the timing and issues. With dry_run it only reports them.`,
3826
3913
  inputSchema: object({ ops: { type: "array", items: EDIT_OP, minItems: 1, description: "Edits, applied in order." }, dry_run: { type: "boolean", description: "Check without applying (default false)." } }, ["ops"]),
3827
3914
  annotations: { title: "Make several edits at once", ...WRITE },
3828
3915
  handler: ({ ops, dry_run = false }) => {
3829
- const result = controller.apply(ops, "Edit animation", { dryRun: dry_run });
3830
- return outcome(result, dry_run ? { dry_run: true, hint: "Nothing was applied; call again without dry_run to apply." } : {});
3916
+ const refs = resolveMotionEditRefs(controller.getScene(), ops);
3917
+ if (!refs.ok) throw new Error(refs.error);
3918
+ const result = controller.apply(refs.edits, "Edit animation", { dryRun: dry_run });
3919
+ return outcome(result, { outcomes: refs.outcomes, ...dry_run ? { dry_run: true, hint: "Nothing was applied; call again without dry_run to apply." } : {} });
3831
3920
  }
3832
3921
  };
3833
3922
  const writeAnimation = {
@@ -3934,7 +4023,8 @@ Commands
3934
4023
  Working method
3935
4024
  1. get_animation to learn the element ids (set_animation_artwork_svg first if there is no artwork: give every element you will animate an id; or import_flair_effect to start from a Flair effect and edit it).
3936
4025
  2. Plan a few shots, each one idea. Add them with insert_shots (add_commands, update_command, move_command and delete_commands refine a shot), or edit_animation to make several edits as one step (dry_run first when unsure).
3937
- 3. To hit a length, fit_animation (keep the shots whose pace matters). check_animation and fix every error and warning; render_animation_frame (shot + "end") to look at the result; add onion_skin or motion_paths to see the motion in one picture, or get_motion_path for the numbers.
4026
+ - In edit_animation, a later op can use what an earlier one made: "$0.indexes[0]" (the first command op 1 added), "$last.shot".
4027
+ 3. To hit a length, fit_animation (keep the shots whose pace matters). check_animation and fix every error and warning; render_animation_frame (shot + "end") to look at the result, or with "times" for a contact sheet of several moments; add onion_skin or motion_paths to see the motion in one picture, or get_motion_path for the numbers.
3938
4028
  - For something the viewer plays (a button, a toggle, a step-by-step explainer), set "trigger" with set_animation_settings: click plays the next shot on each click and then starts over (make the last shot put things back for a toggle), hover or press plays and rewinds, scroll scrubs.
3939
4029
  4. To make a reusable Flair effect, set_animation_controls (colour controls stand for every use of their default colour) and export_flair_effect.
3940
4030
 
@@ -3945,7 +4035,7 @@ var TIMING = /* @__PURE__ */ new Set(["duration", "delay", "stagger", "with", "e
3945
4035
  var COLORS = /* @__PURE__ */ new Set(["fill", "stroke", "color"]);
3946
4036
  var NODES = /* @__PURE__ */ new Set(["into", "path"]);
3947
4037
  function branch(type) {
3948
- return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access', _100 => _100.properties, 'optionalAccess', _101 => _101.type, 'optionalAccess', _102 => _102.const]) === type);
4038
+ return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access', _112 => _112.properties, 'optionalAccess', _113 => _113.type, 'optionalAccess', _114 => _114.const]) === type);
3949
4039
  }
3950
4040
  function unitOf(type, name) {
3951
4041
  if (name === "duration" || name === "delay" || name === "stagger") return "ms";
@@ -3980,7 +4070,7 @@ function kindOf(name, schema) {
3980
4070
  }
3981
4071
  function motionCommandFields(type) {
3982
4072
  const schema = branch(type);
3983
- if (!_optionalChain([schema, 'optionalAccess', _103 => _103.properties])) return [];
4073
+ if (!_optionalChain([schema, 'optionalAccess', _115 => _115.properties])) return [];
3984
4074
  const required = new Set(_nullishCoalesce(schema.required, () => ( [])));
3985
4075
  const fields = Object.entries(schema.properties).filter(([name]) => name !== "type").map(([name, s]) => {
3986
4076
  const kind = kindOf(name, s);
@@ -4037,7 +4127,7 @@ function defaultMotionCommand(type, targets, scene) {
4037
4127
  if (type === "wait") return { type: "wait", duration: 500 };
4038
4128
  if (!targets.length) return { error: "Pick a target first: click an element on the stage." };
4039
4129
  const nodes = [...indexMotionNodes(scene.artwork.nodes).values()];
4040
- const other = (pred) => _optionalChain([nodes, 'access', _104 => _104.find, 'call', _105 => _105((e) => !targets.includes(e.node.id) && pred(e)), 'optionalAccess', _106 => _106.node, 'access', _107 => _107.id]);
4130
+ const other = (pred) => _optionalChain([nodes, 'access', _116 => _116.find, 'call', _117 => _117((e) => !targets.includes(e.node.id) && pred(e)), 'optionalAccess', _118 => _118.node, 'access', _119 => _119.id]);
4041
4131
  const base = { targets: [...targets] };
4042
4132
  switch (type) {
4043
4133
  case "scale":
@@ -4068,8 +4158,8 @@ function defaultMotionCommand(type, targets, scene) {
4068
4158
  }
4069
4159
  case "effect": {
4070
4160
  for (const id of targets) {
4071
- const node = _optionalChain([nodes, 'access', _108 => _108.find, 'call', _109 => _109((e) => e.node.id === id), 'optionalAccess', _110 => _110.node]);
4072
- for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess', _111 => _111.effects]), () => ( []))) {
4161
+ const node = _optionalChain([nodes, 'access', _120 => _120.find, 'call', _121 => _121((e) => e.node.id === id), 'optionalAccess', _122 => _122.node]);
4162
+ for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess', _123 => _123.effects]), () => ( []))) {
4073
4163
  const param = Object.entries(effect.params).find(([, v]) => typeof v === "number");
4074
4164
  if (param) return { type, ...base, effect: effect.id, param: param[0], to: param[1] + 1 };
4075
4165
  }
@@ -4094,11 +4184,11 @@ function motionCommandRows(scene, shotId) {
4094
4184
  return scene.shots[index].run.map((command2, i) => {
4095
4185
  const t = timeline.commands.find((c) => c.shotIndex === index && c.command === i);
4096
4186
  const primitive = motionFlairPrimitive(command2);
4097
- return { index: i, command: command2, summary: motionCommandSummary(command2), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _112 => _112.start]), () => ( 0)), end: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _113 => _113.end]), () => ( 0)), ...primitive ? { flairPrimitive: primitive } : {} };
4187
+ return { index: i, command: command2, summary: motionCommandSummary(command2), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _124 => _124.start]), () => ( 0)), end: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _125 => _125.end]), () => ( 0)), ...primitive ? { flairPrimitive: primitive } : {} };
4098
4188
  });
4099
4189
  }
4100
4190
  function duplicateMotionCommandEdit(scene, shot, command2) {
4101
- const run = _nullishCoalesce(_optionalChain([scene, 'access', _114 => _114.shots, 'access', _115 => _115.find, 'call', _116 => _116((s) => s.id === shot), 'optionalAccess', _117 => _117.run]), () => ( []));
4191
+ const run = _nullishCoalesce(_optionalChain([scene, 'access', _126 => _126.shots, 'access', _127 => _127.find, 'call', _128 => _128((s) => s.id === shot), 'optionalAccess', _129 => _129.run]), () => ( []));
4102
4192
  return { op: "add_commands", shot, commands: run[command2] ? [structuredClone(run[command2])] : [], at: command2 + 1 };
4103
4193
  }
4104
4194
  var MOTION_EASE_CHOICES = [
@@ -4183,7 +4273,7 @@ function motionCueFromKey(key) {
4183
4273
  function motionCueChoices(scene, limit = 256) {
4184
4274
  const choices = [];
4185
4275
  const s = (ms) => `${(ms / 1e3).toFixed(2)} s`;
4186
- const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access', _118 => _118.narration, 'optionalAccess', _119 => _119.words, 'access', _120 => _120.map, 'call', _121 => _121((w) => _nullishCoalesce(w.end, () => ( w.start)))]), () => ( [0]))) + 2e3;
4276
+ const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access', _130 => _130.narration, 'optionalAccess', _131 => _131.words, 'access', _132 => _132.map, 'call', _133 => _133((w) => _nullishCoalesce(w.end, () => ( w.start)))]), () => ( [0]))) + 2e3;
4187
4277
  if (scene.beat && scene.beat.bpm > 0) {
4188
4278
  for (let beat = 0; choices.length < limit; beat++) {
4189
4279
  const time = motionBeatTime(scene.beat, beat);
@@ -4193,7 +4283,7 @@ function motionCueChoices(scene, limit = 256) {
4193
4283
  choices.push({ value: `beat:${beat}`, label: `Beat ${beat} (bar ${at.bar}.${at.beatInBar}) \xB7 ${s(time)}`, time });
4194
4284
  }
4195
4285
  }
4196
- _optionalChain([scene, 'access', _122 => _122.narration, 'optionalAccess', _123 => _123.words, 'access', _124 => _124.forEach, 'call', _125 => _125((w, i) => {
4286
+ _optionalChain([scene, 'access', _134 => _134.narration, 'optionalAccess', _135 => _135.words, 'access', _136 => _136.forEach, 'call', _137 => _137((w, i) => {
4197
4287
  if (choices.length < limit * 2) choices.push({ value: `word:${i}`, label: `Word ${i} \u201C${w.text}\u201D \xB7 ${s(w.start)}`, time: w.start });
4198
4288
  })]);
4199
4289
  return choices;
@@ -4202,7 +4292,7 @@ function motionShotCueTime(scene, cue) {
4202
4292
  return cue ? motionCueTime(scene, cue) : void 0;
4203
4293
  }
4204
4294
  function formatMotionNarration(narration) {
4205
- return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess', _126 => _126.words]), () => ( []))).map((w) => `${w.start} ${w.text}`).join("\n");
4295
+ return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess', _138 => _138.words]), () => ( []))).map((w) => `${w.start} ${w.text}`).join("\n");
4206
4296
  }
4207
4297
  function parseMotionNarration(text) {
4208
4298
  const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
@@ -4342,5 +4432,7 @@ function parseMotionNarration(text) {
4342
4432
 
4343
4433
 
4344
4434
 
4345
- 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_CLIP_TARGET = MOTION_CLIP_TARGET; exports.MOTION_COMMAND_SCHEMA = MOTION_COMMAND_SCHEMA; exports.MOTION_COMMAND_TYPES = MOTION_COMMAND_TYPES; 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_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 = 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_TRIGGERS = MOTION_TRIGGERS; exports.MotionSceneFormatError = MotionSceneFormatError; exports.activeSegment = activeSegment; exports.applyMotionControlValues = applyMotionControlValues; exports.applyMotionEdits = applyMotionEdits; exports.checkMotionScene = checkMotionScene; exports.clipsShapeProblems = clipsShapeProblems; exports.commandShapeProblems = commandShapeProblems; exports.compileMotionScene = compileMotionScene; exports.createAnimationEditorToolProvider = createAnimationEditorToolProvider; exports.createMotionScene = createMotionScene; exports.defaultMotionCommand = defaultMotionCommand; exports.describeMotionElements = describeMotionElements; exports.describeMotionShots = describeMotionShots; exports.duplicateMotionCommandEdit = duplicateMotionCommandEdit; exports.fitMotionRect = fitMotionRect; exports.fitMotionScene = fitMotionScene; exports.fitMotionSceneEdits = fitMotionSceneEdits; exports.flairAtLeast = flairAtLeast; exports.flairCurveFor = flairCurveFor; exports.flairEaseFor = flairEaseFor; exports.formatMotionNarration = formatMotionNarration; exports.freshShotId = freshShotId; exports.fromFlairSvgMotion = fromFlairSvgMotion; exports.fromFlairVectorEffect = fromFlairVectorEffect; exports.highlightMotionSvg = highlightMotionSvg; exports.indexMotionNodes = indexMotionNodes; exports.interpolateMotionValue = interpolateMotionValue; exports.isMotionCommand = isMotionCommand; exports.isMotionShot = isMotionShot; exports.matchMotionNodes = matchMotionNodes; exports.motionAddTrackKey = motionAddTrackKey; exports.motionBeatAt = motionBeatAt; exports.motionBeatTime = motionBeatTime; exports.motionClipAttrs = motionClipAttrs; exports.motionClipLayout = motionClipLayout; exports.motionCommandDuration = motionCommandDuration; exports.motionCommandFields = motionCommandFields; exports.motionCommandRepeat = motionCommandRepeat; exports.motionCommandRows = motionCommandRows; exports.motionCommandSummary = motionCommandSummary; exports.motionCueChoices = motionCueChoices; exports.motionCueFromKey = motionCueFromKey; exports.motionCueKey = motionCueKey; exports.motionCueTime = motionCueTime; exports.motionCycleDuration = motionCycleDuration; 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 = motionIssue; exports.motionIssueKey = motionIssueKey; exports.motionIssueLine = motionIssueLine; exports.motionMovingIds = motionMovingIds; exports.motionNodeAtElement = motionNodeAtElement; exports.motionNodeClips = motionNodeClips; exports.motionNodeOffset = motionNodeOffset; exports.motionPath = motionPath; exports.motionPathLength = motionPathLength; exports.motionPathsSvg = motionPathsSvg; exports.motionRestValue = motionRestValue; exports.motionRevealClip = motionRevealClip; exports.motionRevealPadding = motionRevealPadding; exports.motionRulerTicks = motionRulerTicks; exports.motionSceneShapeProblems = motionSceneShapeProblems; exports.motionSceneToJson = motionSceneToJson; exports.motionShotAt = motionShotAt; exports.motionShotCueTime = motionShotCueTime; exports.motionShotTimeScale = motionShotTimeScale; exports.motionStageSvg = motionStageSvg; exports.motionTrackKey = motionTrackKey; exports.namedWithSuggestion = namedWithSuggestion; exports.parseMotionFieldInput = parseMotionFieldInput; exports.parseMotionNarration = parseMotionNarration; exports.parseMotionScene = parseMotionScene; exports.resolveMotionCamera = resolveMotionCamera; exports.segmentProgress = segmentProgress; exports.segmentValue = segmentValue; exports.shotShapeProblems = shotShapeProblems; exports.smoothMotionLegs = smoothMotionLegs; exports.smoothMotionTimes = smoothMotionTimes; exports.suggestName = suggestName; exports.toFlairEffectDocument = toFlairEffectDocument; exports.toFlairSvgMotion = toFlairSvgMotion; exports.toFlairVectorEffect = toFlairVectorEffect; exports.toLottie = toLottie; exports.trackValue = trackValue; exports.withMotionLayers = withMotionLayers; exports.withViewBox = withViewBox;
4435
+
4436
+
4437
+ 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_CLIP_TARGET = MOTION_CLIP_TARGET; exports.MOTION_COMMAND_SCHEMA = MOTION_COMMAND_SCHEMA; exports.MOTION_COMMAND_TYPES = MOTION_COMMAND_TYPES; 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_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 = 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_TRIGGERS = MOTION_TRIGGERS; exports.MotionSceneFormatError = MotionSceneFormatError; exports.activeSegment = activeSegment; exports.applyMotionControlValues = applyMotionControlValues; exports.applyMotionEdits = applyMotionEdits; exports.checkMotionScene = checkMotionScene; exports.clipsShapeProblems = clipsShapeProblems; exports.commandShapeProblems = commandShapeProblems; exports.compileMotionScene = compileMotionScene; exports.createAnimationEditorToolProvider = createAnimationEditorToolProvider; exports.createMotionScene = createMotionScene; exports.defaultMotionCommand = defaultMotionCommand; exports.describeMotionElements = describeMotionElements; exports.describeMotionShots = describeMotionShots; exports.duplicateMotionCommandEdit = duplicateMotionCommandEdit; exports.fitMotionRect = fitMotionRect; exports.fitMotionScene = fitMotionScene; exports.fitMotionSceneEdits = fitMotionSceneEdits; exports.flairAtLeast = flairAtLeast; exports.flairCurveFor = flairCurveFor; exports.flairEaseFor = flairEaseFor; exports.formatMotionNarration = formatMotionNarration; exports.freshShotId = freshShotId; exports.fromFlairSvgMotion = fromFlairSvgMotion; exports.fromFlairVectorEffect = fromFlairVectorEffect; exports.highlightMotionSvg = highlightMotionSvg; exports.indexMotionNodes = indexMotionNodes; exports.interpolateMotionValue = interpolateMotionValue; exports.isMotionCommand = isMotionCommand; exports.isMotionShot = isMotionShot; exports.matchMotionNodes = matchMotionNodes; exports.motionAddTrackKey = motionAddTrackKey; exports.motionBeatAt = motionBeatAt; exports.motionBeatTime = motionBeatTime; exports.motionClipAttrs = motionClipAttrs; exports.motionClipLayout = motionClipLayout; exports.motionCommandDuration = motionCommandDuration; exports.motionCommandFields = motionCommandFields; exports.motionCommandRepeat = motionCommandRepeat; exports.motionCommandRows = motionCommandRows; exports.motionCommandSummary = motionCommandSummary; exports.motionContactSheetSvg = motionContactSheetSvg; exports.motionCueChoices = motionCueChoices; exports.motionCueFromKey = motionCueFromKey; exports.motionCueKey = motionCueKey; exports.motionCueTime = motionCueTime; exports.motionCycleDuration = motionCycleDuration; 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 = motionIssue; exports.motionIssueKey = motionIssueKey; exports.motionIssueLine = motionIssueLine; exports.motionMovingIds = motionMovingIds; exports.motionNodeAtElement = motionNodeAtElement; exports.motionNodeClips = motionNodeClips; exports.motionNodeOffset = motionNodeOffset; exports.motionPath = motionPath; exports.motionPathLength = motionPathLength; exports.motionPathsSvg = motionPathsSvg; exports.motionRestValue = motionRestValue; exports.motionRevealClip = motionRevealClip; exports.motionRevealPadding = motionRevealPadding; exports.motionRulerTicks = motionRulerTicks; exports.motionSceneShapeProblems = motionSceneShapeProblems; exports.motionSceneToJson = motionSceneToJson; exports.motionShotAt = motionShotAt; exports.motionShotCueTime = motionShotCueTime; exports.motionShotTimeScale = motionShotTimeScale; exports.motionStageSvg = motionStageSvg; exports.motionTrackKey = motionTrackKey; exports.namedWithSuggestion = namedWithSuggestion; exports.parseMotionFieldInput = parseMotionFieldInput; exports.parseMotionNarration = parseMotionNarration; exports.parseMotionScene = parseMotionScene; exports.resolveMotionCamera = resolveMotionCamera; exports.resolveMotionEditRefs = resolveMotionEditRefs; exports.segmentProgress = segmentProgress; exports.segmentValue = segmentValue; exports.shotShapeProblems = shotShapeProblems; exports.smoothMotionLegs = smoothMotionLegs; exports.smoothMotionTimes = smoothMotionTimes; exports.suggestName = suggestName; exports.toFlairEffectDocument = toFlairEffectDocument; exports.toFlairSvgMotion = toFlairSvgMotion; exports.toFlairVectorEffect = toFlairVectorEffect; exports.toLottie = toLottie; exports.trackValue = trackValue; exports.withMotionLayers = withMotionLayers; exports.withViewBox = withViewBox;
4346
4438
  //# sourceMappingURL=index.cjs.map