@pixerate/editor 0.72.0 → 0.73.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.
@@ -221,7 +221,7 @@ ${problems.map((p) => `- ${p}`).join("\n")}`);
221
221
  this.problems = problems;
222
222
  }
223
223
  };
224
- var SCENE_KEYS = ["version", "name", "description", "artwork", "shots", "clips", "drivers", "loop", "trigger", "fps", "steps", "beat", "narration", "target", "controls", "presets"];
224
+ var SCENE_KEYS = ["version", "name", "description", "artwork", "shots", "clips", "drivers", "motionBlur", "loop", "trigger", "fps", "steps", "beat", "narration", "target", "controls", "presets"];
225
225
  var SHOT_KEYS = ["id", "label", "camera", "at", "run", "hold", "speed"];
226
226
  var CAMERA_KEYS = ["x", "y", "width", "height", "frame", "padding", "ease", "ms", "geometric"];
227
227
  var BASE_KEYS = ["type", "targets", "duration", "delay", "stagger", "with", "ease", "note", "repeat", "repeatMode", "steps"];
@@ -471,6 +471,11 @@ function motionSceneShapeProblems(input) {
471
471
  problems.push('Scene: "artwork" must be a vector document { width, height, nodes }.');
472
472
  if (input.clips !== void 0) problems.push(...clipsShapeProblems(input.clips));
473
473
  if (input.drivers !== void 0) problems.push(...driversShapeProblems(input.drivers));
474
+ if (input.motionBlur !== void 0) {
475
+ const b = input.motionBlur;
476
+ if (!isObject(b) || Object.keys(b).some((k) => k !== "shutter" && k !== "samples") || b.shutter !== void 0 && !(isNum(b.shutter) && b.shutter > 0 && b.shutter <= 720) || b.samples !== void 0 && !(Number.isInteger(b.samples) && b.samples >= 2 && b.samples <= 32))
477
+ problems.push('Scene: "motionBlur" must be { "shutter"?: degrees above 0 up to 720 (default 180), "samples"?: 2 to 32 (default 8) }.');
478
+ }
474
479
  if (input.trigger !== void 0 && !MOTION_TRIGGERS.includes(input.trigger)) problems.push(`Scene: "trigger" must be one of ${MOTION_TRIGGERS.join(", ")}.`);
475
480
  if (input.loop !== void 0 && !["none", "loop", "ping-pong"].includes(input.loop)) problems.push('Scene: "loop" must be "none", "loop" or "ping-pong".');
476
481
  if (input.fps !== void 0 && !(isNum(input.fps) && input.fps > 0 && input.fps <= 120)) problems.push('Scene: "fps" must be a number from 1 to 120.');
@@ -1574,12 +1579,49 @@ function motionPathsSvg(paths, camera, color = "#f43f5e") {
1574
1579
  (p) => `<g data-motion-path="${p.id.replace(/"/g, "&quot;")}" fill="none" stroke="${color}" stroke-width="${f(w)}" stroke-linecap="round" stroke-linejoin="round" opacity="0.8"><polyline points="${p.points.map((q) => `${f(q.x)},${f(q.y)}`).join(" ")}" stroke-dasharray="${f(w * 3)} ${f(w * 2)}"/>${p.keys.map((k) => `<circle cx="${f(k.x)}" cy="${f(k.y)}" r="${f(w * 2.5)}" fill="${color}" stroke="none"/>`).join("")}</g>`
1575
1580
  ).join("");
1576
1581
  }
1582
+ function motionBlurTimes(scene, time, blur = {}) {
1583
+ const shutter = Math.min(720, Math.max(0, _nullishCoalesce(blur.shutter, () => ( 180))));
1584
+ const most = Math.max(1, Math.min(32, Math.floor(_nullishCoalesce(blur.samples, () => ( 8)))));
1585
+ const span = 1e3 / (_nullishCoalesce(scene.fps, () => ( 30))) * shutter / 360;
1586
+ if (span <= 0 || most <= 1) return [time];
1587
+ const ids = motionMovingIds(scene);
1588
+ if (!ids.length) return [time];
1589
+ const a = indexMotionNodes(motionFrameAt(scene, time - span / 2).document.nodes);
1590
+ const b = indexMotionNodes(motionFrameAt(scene, time + span / 2).document.nodes);
1591
+ let travel = 0;
1592
+ for (const id of ids) {
1593
+ const p = _optionalChain([a, 'access', _58 => _58.get, 'call', _59 => _59(id), 'optionalAccess', _60 => _60.bounds]);
1594
+ const q = _optionalChain([b, 'access', _61 => _61.get, 'call', _62 => _62(id), 'optionalAccess', _63 => _63.bounds]);
1595
+ 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));
1596
+ }
1597
+ const n = Math.min(most, Math.ceil(travel / 1.5));
1598
+ if (n <= 1) return [time];
1599
+ return Array.from({ length: n }, (_, k) => time - span / 2 + span * (k + 0.5) / n);
1600
+ }
1601
+ function averagedFrames(scene, times, options, prefix) {
1602
+ const n = times.length;
1603
+ return `<g data-motion-blur="${n}" style="isolation:isolate">${times.map((t, k) => {
1604
+ const own = `${prefix}blur${k}-`;
1605
+ const frame = motionFrameToSvg(scene, t, { ...options, idPrefix: own, background: false });
1606
+ const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _64 => _64.exec, 'call', _65 => _65(frame), 'optionalAccess', _66 => _66[1]]), () => ( ""));
1607
+ const inner = k === n - 1 ? innerSvg(frame) : innerSvg(frame).replace(new RegExp(` id="(?!${own.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
1608
+ return `<svg viewBox="${view}" preserveAspectRatio="none" style="mix-blend-mode:plus-lighter" opacity="${Math.round(1e4 / n) / 1e4}">${inner}</svg>`;
1609
+ }).join("")}</g>`;
1610
+ }
1577
1611
  var innerSvg = (svg) => svg.slice(svg.indexOf(">") + 1, svg.lastIndexOf("</svg>"));
1578
1612
  function motionStageSvg(scene, time, options = {}) {
1579
- const { onionSkin, motionPaths, motionPathColor, ...svgOptions } = options;
1580
- const current = motionFrameToSvg(scene, time, svgOptions);
1581
- if (!onionSkin && !motionPaths) return current;
1613
+ const { onionSkin, motionPaths, motionPathColor, motionBlur, ...svgOptions } = options;
1582
1614
  const prefix = _nullishCoalesce(svgOptions.idPrefix, () => ( "vg-"));
1615
+ let current = motionFrameToSvg(scene, time, svgOptions);
1616
+ const blur = motionBlur === false ? void 0 : _nullishCoalesce(motionBlur, () => ( scene.motionBlur));
1617
+ const blurTimes = blur ? motionBlurTimes(scene, time, blur) : [time];
1618
+ if (blurTimes.length > 1) {
1619
+ const open2 = current.indexOf(">") + 1;
1620
+ const body2 = current.slice(open2, current.lastIndexOf("</svg>"));
1621
+ const bg2 = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _67 => _67.exec, 'call', _68 => _68(body2), 'optionalAccess', _69 => _69[0]]), () => ( "")) : "";
1622
+ current = `${current.slice(0, open2)}${bg2}${averagedFrames(scene, blurTimes, svgOptions, prefix)}</svg>`;
1623
+ }
1624
+ if (!onionSkin && !motionPaths) return current;
1583
1625
  const ghosts = onionSkin ? motionGhosts(scene, time, onionSkin).map((g, i) => {
1584
1626
  const own = `${prefix}ghost${i}-`;
1585
1627
  const inner = innerSvg(motionFrameToSvg(scene, g.time, { ...svgOptions, idPrefix: own, background: false })).replace(new RegExp(` id="(?!${own.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
@@ -1591,7 +1633,7 @@ function motionStageSvg(scene, time, options = {}) {
1591
1633
  const open = current.indexOf(">") + 1;
1592
1634
  const close = current.lastIndexOf("</svg>");
1593
1635
  const body = current.slice(open, close);
1594
- const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _58 => _58.exec, 'call', _59 => _59(body), 'optionalAccess', _60 => _60[0]]), () => ( "")) : "";
1636
+ const bg = svgOptions.background !== false && scene.artwork.background ? _nullishCoalesce(_optionalChain([/^\s*<rect[^>]*\/>/, 'access', _70 => _70.exec, 'call', _71 => _71(body), 'optionalAccess', _72 => _72[0]]), () => ( "")) : "";
1595
1637
  return `${current.slice(0, open)}${bg}${ghosts.join("")}${body.slice(bg.length)}${paths}</svg>`;
1596
1638
  }
1597
1639
  function motionContactSheetSvg(scene, times, options = {}) {
@@ -1610,7 +1652,7 @@ function motionContactSheetSvg(scene, times, options = {}) {
1610
1652
  const x = gap + i % cols * (w + gap);
1611
1653
  const y = gap + Math.floor(i / cols) * (h + label + gap);
1612
1654
  const frame = motionStageSvg(scene, t, { ...frameOptions, idPrefix: `${prefix}f${i}-` });
1613
- const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _61 => _61.exec, 'call', _62 => _62(frame), 'optionalAccess', _63 => _63[1]]), () => ( `0 0 ${w} ${h}`));
1655
+ const view = _nullishCoalesce(_optionalChain([/viewBox="([^"]*)"/, 'access', _73 => _73.exec, 'call', _74 => _74(frame), 'optionalAccess', _75 => _75[1]]), () => ( `0 0 ${w} ${h}`));
1614
1656
  const inner = innerSvg(frame).replace(new RegExp(` id="(?!${`${prefix}f${i}-`.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})[^"]*"`, "g"), "");
1615
1657
  const shot = _nullishCoalesce(timeline.shots.find((s) => t >= s.start && t < s.end), () => ( timeline.shots[timeline.shots.length - 1]));
1616
1658
  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>` : "";
@@ -1622,7 +1664,7 @@ function motionContactSheetSvg(scene, times, options = {}) {
1622
1664
  // src/animation-editor/check.ts
1623
1665
  var MOTION_MIN_CHANGE_MS = 120;
1624
1666
  var NUMERIC_PARAMS = (node, effectId) => {
1625
- const effect = _optionalChain([node, 'access', _64 => _64.effects, 'optionalAccess', _65 => _65.find, 'call', _66 => _66((e) => e.id === effectId)]);
1667
+ const effect = _optionalChain([node, 'access', _76 => _76.effects, 'optionalAccess', _77 => _77.find, 'call', _78 => _78((e) => e.id === effectId)]);
1626
1668
  if (!effect) return null;
1627
1669
  return Object.entries(effect.params).filter(([, v]) => typeof v === "number").map(([k]) => k);
1628
1670
  };
@@ -1703,7 +1745,7 @@ function checkMotionScene(scene) {
1703
1745
  if (command2.type === "follow" && known(command2.path, "unknown-path", "path", where) && nodes.get(command2.path).node.type !== "path")
1704
1746
  issues.push(motionIssue("not-a-path", `"${command2.path}" is a group; follow a single path node.`, { ...where, ids: [command2.path] }));
1705
1747
  if (command2.type === "morph") known(command2.into, "unknown-shape", "shape", where);
1706
- if (command2.type === "play" && !_optionalChain([scene, 'access', _67 => _67.clips, 'optionalAccess', _68 => _68[command2.clip]]))
1748
+ if (command2.type === "play" && !_optionalChain([scene, 'access', _79 => _79.clips, 'optionalAccess', _80 => _80[command2.clip]]))
1707
1749
  issues.push(motionIssue("unknown-clip", `There is no clip ${namedWithSuggestion(command2.clip, Object.keys(_nullishCoalesce(scene.clips, () => ( {}))))}; define it with set_animation_clip.`, where));
1708
1750
  if (command2.type === "match" && known(command2.into, "unknown-shape", "group", where)) {
1709
1751
  const into = nodes.get(command2.into);
@@ -1818,7 +1860,7 @@ function checkMotionScene(scene) {
1818
1860
  if (!shot.at) return;
1819
1861
  const timing = timeline.shots[i];
1820
1862
  if (timing.cue === void 0) {
1821
- 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', _69 => _69.narration, 'optionalAccess', _70 => _70.words, 'access', _71 => _71.length]), () => ( 0))} words (indexes from 0)`;
1863
+ 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', _81 => _81.narration, 'optionalAccess', _82 => _82.words, 'access', _83 => _83.length]), () => ( 0))} words (indexes from 0)`;
1822
1864
  issues.push(motionIssue("unknown-cue", `Shot "${shot.id}" is cued to ${what}.`, { shot: shot.id }));
1823
1865
  } else if (timing.missedBy !== void 0) {
1824
1866
  const late = Math.round(timing.missedBy);
@@ -1851,7 +1893,7 @@ function checkMotionScene(scene) {
1851
1893
  const cam = frame.camera;
1852
1894
  const docIndex = indexMotionNodes(frame.document.nodes);
1853
1895
  for (const id of command2.targets) {
1854
- const b = _optionalChain([docIndex, 'access', _72 => _72.get, 'call', _73 => _73(id), 'optionalAccess', _74 => _74.bounds]);
1896
+ const b = _optionalChain([docIndex, 'access', _84 => _84.get, 'call', _85 => _85(id), 'optionalAccess', _86 => _86.bounds]);
1855
1897
  if (!b || reported.has(id) || !visibleAt(timeline, id, timing.end, nodes.get(id).node)) continue;
1856
1898
  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;
1857
1899
  if (outside) {
@@ -1934,12 +1976,12 @@ function applyOne(scene, edit) {
1934
1976
  if (!(typeof edit.steps === "number" && edit.steps > 0 && edit.steps <= 120)) fail('"steps" must be frames per second from above 0 to 120 (12 is "on twos" at 24 fps), or null for smooth.');
1935
1977
  next.steps = edit.steps;
1936
1978
  }
1937
- for (const key of ["beat", "narration"]) {
1979
+ for (const key of ["beat", "narration", "motionBlur"]) {
1938
1980
  if (edit[key] === null) delete next[key];
1939
1981
  else if (edit[key] !== void 0) next[key] = edit[key];
1940
1982
  }
1941
1983
  {
1942
- const problems = motionSceneShapeProblems(next).filter((p) => /"beat"|"narration"/.test(p));
1984
+ const problems = motionSceneShapeProblems(next).filter((p) => /"beat"|"narration"|"motionBlur"/.test(p));
1943
1985
  if (problems.length) fail(problems.join(" "));
1944
1986
  }
1945
1987
  if (edit.target === null) delete next.target;
@@ -2083,9 +2125,9 @@ function applyMotionEdits(scene, edits) {
2083
2125
  let next = scene;
2084
2126
  for (let i = 0; i < list.length; i++) {
2085
2127
  const edit = list[i];
2086
- const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _75 => _75.op])}): ` : "";
2128
+ const prefix = list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _87 => _87.op])}): ` : "";
2087
2129
  if (!edit || !MOTION_EDIT_OPS.includes(edit.op))
2088
- return { ok: false, error: `${prefix}Unknown op ${namedWithSuggestion(String(_optionalChain([edit, 'optionalAccess', _76 => _76.op])), MOTION_EDIT_OPS)}. Nothing was changed.` };
2130
+ return { ok: false, error: `${prefix}Unknown op ${namedWithSuggestion(String(_optionalChain([edit, 'optionalAccess', _88 => _88.op])), MOTION_EDIT_OPS)}. Nothing was changed.` };
2089
2131
  try {
2090
2132
  next = applyOne(next, edit);
2091
2133
  } catch (error) {
@@ -2107,7 +2149,7 @@ function resolveMotionEditRefs(scene, edits) {
2107
2149
  const resolved = [];
2108
2150
  let next = scene;
2109
2151
  for (let i = 0; i < edits.length; i++) {
2110
- const where = `op ${i + 1} (${_optionalChain([edits, 'access', _77 => _77[i], 'optionalAccess', _78 => _78.op])})`;
2152
+ const where = `op ${i + 1} (${_optionalChain([edits, 'access', _89 => _89[i], 'optionalAccess', _90 => _90.op])})`;
2111
2153
  let problem = null;
2112
2154
  const walk = (v) => {
2113
2155
  if (typeof v === "string") {
@@ -2140,9 +2182,9 @@ function resolveMotionEditRefs(scene, edits) {
2140
2182
  resolved.push(edit);
2141
2183
  const out = { op: edit.op };
2142
2184
  if (edit.op === "insert_shots") out.shots = edit.shots.map((s) => s.id);
2143
- if ("shot" in edit && typeof edit.shot === "string") out.shot = edit.op === "update_shot" && _optionalChain([edit, 'access', _79 => _79.set, 'optionalAccess', _80 => _80.id]) ? edit.set.id : edit.shot;
2185
+ if ("shot" in edit && typeof edit.shot === "string") out.shot = edit.op === "update_shot" && _optionalChain([edit, 'access', _91 => _91.set, 'optionalAccess', _92 => _92.id]) ? edit.set.id : edit.shot;
2144
2186
  if (edit.op === "add_commands") {
2145
- const at = _nullishCoalesce(_nullishCoalesce(edit.at, () => ( _optionalChain([before, 'access', _81 => _81.shots, 'access', _82 => _82.find, 'call', _83 => _83((s) => s.id === edit.shot), 'optionalAccess', _84 => _84.run, 'access', _85 => _85.length]))), () => ( 0));
2187
+ const at = _nullishCoalesce(_nullishCoalesce(edit.at, () => ( _optionalChain([before, 'access', _93 => _93.shots, 'access', _94 => _94.find, 'call', _95 => _95((s) => s.id === edit.shot), 'optionalAccess', _96 => _96.run, 'access', _97 => _97.length]))), () => ( 0));
2146
2188
  out.indexes = edit.commands.map((_, k) => at + k);
2147
2189
  }
2148
2190
  if (edit.op === "move_command") out.indexes = [edit.to];
@@ -2301,7 +2343,7 @@ function toFlairSvgMotion(scene, options = {}) {
2301
2343
  const wrapper = freshId(`${id}--follow`);
2302
2344
  const centre = freshId(`${id}--follow-centre`);
2303
2345
  const c = entry.center;
2304
- const at = _optionalChain([motionNodeOffset, 'call', _86 => _86(entry, timeline, 0, nodes), 'optionalAccess', _87 => _87.follow]);
2346
+ const at = _optionalChain([motionNodeOffset, 'call', _98 => _98(entry, timeline, 0, nodes), 'optionalAccess', _99 => _99.follow]);
2305
2347
  const placed = at ? `translate(${f(at.x)} ${f(at.y)})${at.angle ? ` rotate(${f(at.angle)})` : ""}` : void 0;
2306
2348
  (wrap[id] ??= []).push({ id: wrapper, ...placed ? { transform: placed } : {} }, { id: centre, transform: `translate(${f(-c.x)} ${f(-c.y)})` });
2307
2349
  follows.push({ id, wrapper, routes: ids, segments: followSegments.filter((seg) => seg.path && ids.has(seg.path)) });
@@ -2362,7 +2404,7 @@ function toFlairSvgMotion(scene, options = {}) {
2362
2404
  exact: () => false,
2363
2405
  value: (t) => {
2364
2406
  const frame = motionFrameAt(scene, t, { draw: false, offsets: false });
2365
- const now = _chunkYJLCBA7Vcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _88 => _88(frame.document.nodes), 'access', _89 => _89.get, 'call', _90 => _90(id), 'optionalAccess', _91 => _91.node]));
2407
+ const now = _chunkYJLCBA7Vcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _100 => _100(frame.document.nodes), 'access', _101 => _101.get, 'call', _102 => _102(id), 'optionalAccess', _103 => _103.node]));
2366
2408
  if (!now || now.type !== "path") return "";
2367
2409
  const bezier = _chunkO2UH7LZEcjs.transformBezierPath.call(void 0, _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, now, _chunkYJLCBA7Vcjs.IDENTITY_MATRIX, { effects: false }), inverse);
2368
2410
  return _chunkO2UH7LZEcjs.bezierPathToSvg.call(void 0, bezier, { precision: Math.min(precision, 2) });
@@ -2383,8 +2425,8 @@ function toFlairSvgMotion(scene, options = {}) {
2383
2425
  ...delay > 0 ? { delay: round2(delay) } : {},
2384
2426
  ms: round2(ms),
2385
2427
  ease: written,
2386
- ..._optionalChain([segment, 'optionalAccess', _92 => _92.behavior]) ? { behavior: segment.behavior } : {},
2387
- ..._optionalChain([segment, 'optionalAccess', _93 => _93.steps]) ? { strobe: segment.steps } : {}
2428
+ ..._optionalChain([segment, 'optionalAccess', _104 => _104.behavior]) ? { behavior: segment.behavior } : {},
2429
+ ..._optionalChain([segment, 'optionalAccess', _105 => _105.steps]) ? { strobe: segment.steps } : {}
2388
2430
  };
2389
2431
  if (channel.kind === "view") animate.push({ ...base, view: to });
2390
2432
  else if (channel.kind === "draw") animate.push({ ...base, draw: [from, to] });
@@ -2469,11 +2511,12 @@ function toFlairSvgMotion(scene, options = {}) {
2469
2511
  }
2470
2512
  animate.sort((x, y) => (_nullishCoalesce(x.step, () => ( 0))) - (_nullishCoalesce(y.step, () => ( 0))) || (_nullishCoalesce(x.delay, () => ( 0))) - (_nullishCoalesce(y.delay, () => ( 0))));
2471
2513
  }
2514
+ if (scene.motionBlur) notes.push("Motion blur is drawn on the stage only; Flair plays the scene sharp.");
2472
2515
  if (trigger && (_nullishCoalesce(scene.loop, () => ( "none"))) !== "none") notes.push(`The scene plays on ${trigger}, so its loop does not apply in Flair.`);
2473
2516
  let svg = _chunkYJLCBA7Vcjs.vectorDocumentToSvg.call(void 0, { ...scene.artwork, nodes: _chunkYJLCBA7Vcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) }, { pretty: false, precision, wrap });
2474
2517
  if (routes.length) svg = svg.replace(/^(<svg[^>]*>)/, `$1<defs>${routes.join("")}</defs>`);
2475
2518
  let motion = { svg, animate };
2476
- if (_optionalChain([scene, 'access', _94 => _94.controls, 'optionalAccess', _95 => _95.length])) {
2519
+ if (_optionalChain([scene, 'access', _106 => _106.controls, 'optionalAccess', _107 => _107.length])) {
2477
2520
  const declarations = scene.controls.map((c) => `{{${c.name}: color default=${c.default.toUpperCase()}${c.label ? ` label='${c.label.replace(/'/g, "\u2019")}'` : ""}}}`).join(" ");
2478
2521
  const swap = (text) => scene.controls.reduce((s, c) => s.replace(new RegExp(`${c.default}(?![0-9a-fA-F])`, "gi"), `{{${c.name}}}`), text);
2479
2522
  svg = svg.replace(/^(<svg[^>]*>)/, `$1<!-- ${declarations} -->`);
@@ -2495,7 +2538,7 @@ function toFlairVectorEffect(scene, options = {}) {
2495
2538
  tags: _nullishCoalesce(options.tags, () => ( [])),
2496
2539
  motion: out.motion,
2497
2540
  hold: out.hold,
2498
- ..._optionalChain([scene, 'access', _96 => _96.presets, 'optionalAccess', _97 => _97.length]) ? { presets: scene.presets } : {},
2541
+ ..._optionalChain([scene, 'access', _108 => _108.presets, 'optionalAccess', _109 => _109.length]) ? { presets: scene.presets } : {},
2499
2542
  ...out.trigger ? { trigger: out.trigger } : {}
2500
2543
  };
2501
2544
  }
@@ -2539,7 +2582,7 @@ function resolvePlaceholders(motion, notes) {
2539
2582
  const filled = text.replace(PLACEHOLDER, (_, name) => {
2540
2583
  const d = declared.get(name);
2541
2584
  if (!d) unknown.add(name);
2542
- return (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _98 => _98.value]), () => ( ""))).replace(/"/g, '\\"');
2585
+ return (_nullishCoalesce(_optionalChain([d, 'optionalAccess', _110 => _110.value]), () => ( ""))).replace(/"/g, '\\"');
2543
2586
  });
2544
2587
  for (const name of unknown) notes.push(`Placeholder "{{${name}}}" has no declaration, so it was left empty.`);
2545
2588
  return { motion: JSON.parse(filled), controls };
@@ -2593,7 +2636,7 @@ function commandsFor(anim, nodes, entered, notes) {
2593
2636
  if (anim.along) {
2594
2637
  const path = nodes.get(anim.along);
2595
2638
  if (!path || path.node.type !== "path") notes.push(`${where} follows "${anim.along}", which is not a path in the artwork; the follow was skipped.`);
2596
- else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access', _99 => _99.at, 'optionalAccess', _100 => _100[0]]), () => ( 0)), to: _nullishCoalesce(_optionalChain([anim, 'access', _101 => _101.at, 'optionalAccess', _102 => _102[1]]), () => ( 1)), ...anim.orient ? { rotate: true } : {}, ...timing, ...ease(MOTION_EASE_IN_OUT) });
2639
+ else out.push({ type: "follow", targets, path: anim.along, from: _nullishCoalesce(_optionalChain([anim, 'access', _111 => _111.at, 'optionalAccess', _112 => _112[0]]), () => ( 0)), to: _nullishCoalesce(_optionalChain([anim, 'access', _113 => _113.at, 'optionalAccess', _114 => _114[1]]), () => ( 1)), ...anim.orient ? { rotate: true } : {}, ...timing, ...ease(MOTION_EASE_IN_OUT) });
2597
2640
  }
2598
2641
  for (const [attr, [a, b]] of Object.entries(_nullishCoalesce(anim.attrs, () => ( {})))) {
2599
2642
  if (attr === "opacity") {
@@ -2604,7 +2647,7 @@ function commandsFor(anim, nodes, entered, notes) {
2604
2647
  out.push({ type: "enter", targets, style: "fade", ...timing, ...ease(MOTION_EASE_OUT) });
2605
2648
  } else out.push({ type: "fade", targets, to: Math.min(1, Math.max(0, to)), ...timing, ...ease(MOTION_EASE_IN_OUT) });
2606
2649
  } else if (attr === "fill" || attr === "stroke") {
2607
- if (_optionalChain([behavior, 'optionalAccess', _103 => _103.type]) === "pulse") out.push({ type: "color-pulse", targets, color: String(b), paint: attr, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...ease("linear") });
2650
+ if (_optionalChain([behavior, 'optionalAccess', _115 => _115.type]) === "pulse") out.push({ type: "color-pulse", targets, color: String(b), paint: attr, count: _nullishCoalesce(behavior.count, () => ( 1)), ...timing, ...ease("linear") });
2608
2651
  else {
2609
2652
  if (behavior) notes.push(`${where}: a ${attr} ${behavior.type} became a plain colour change.`);
2610
2653
  out.push({ type: "style", targets, [attr]: String(b), ...timing, ...ease(MOTION_EASE_IN_OUT) });
@@ -2785,6 +2828,7 @@ function toLottie(scene, options = {}) {
2785
2828
  const timeline = compileMotionScene(scene);
2786
2829
  const nodes = indexMotionNodes(scene.artwork.nodes);
2787
2830
  const notes = [];
2831
+ if (scene.motionBlur) notes.push("Motion blur is drawn on the stage only; the Lottie plays sharp.");
2788
2832
  if (scene.trigger) notes.push(`The scene plays on ${scene.trigger}; Lottie has no trigger, so it plays through (markers show where each shot starts, for a player to stop at).`);
2789
2833
  let sampled = 0;
2790
2834
  const track = (id, p) => _nullishCoalesce(timeline.tracks.get(motionTrackKey(id, p)), () => ( []));
@@ -2893,7 +2937,7 @@ function toLottie(scene, options = {}) {
2893
2937
  const shapeSegments = [...timeline.tracks.entries()].filter(([k]) => k.startsWith(`${path.id}\0`) && (k.endsWith("\0shape") || k.includes("\0effect:"))).flatMap(([, v]) => v);
2894
2938
  const geometryAt = (t) => {
2895
2939
  if (!shapeSegments.length) return _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path);
2896
- const now = _optionalChain([indexMotionNodes, 'call', _104 => _104(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes), 'access', _105 => _105.get, 'call', _106 => _106(path.id), 'optionalAccess', _107 => _107.node]);
2940
+ const now = _optionalChain([indexMotionNodes, 'call', _116 => _116(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes), 'access', _117 => _117.get, 'call', _118 => _118(path.id), 'optionalAccess', _119 => _119.node]);
2897
2941
  return now && now.type === "path" ? _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, _chunkYJLCBA7Vcjs.expandVectorAppearances.call(void 0, [now])[0]) : _chunkYJLCBA7Vcjs.vectorPathWorldBezier.call(void 0, path);
2898
2942
  };
2899
2943
  const rest = lottieContours(_chunkO2UH7LZEcjs.toBezierPath.call(void 0, geometryAt(0)), round2);
@@ -2941,7 +2985,7 @@ function toLottie(scene, options = {}) {
2941
2985
  lj: path.strokeLinejoin === "round" ? 2 : path.strokeLinejoin === "bevel" ? 3 : 1,
2942
2986
  ml: 4
2943
2987
  });
2944
- if (_optionalChain([path, 'access', _108 => _108.strokeDasharray, 'optionalAccess', _109 => _109.length])) notes.push(`"${path.id}": its dashes are not exported; the stroke is solid.`);
2988
+ if (_optionalChain([path, 'access', _120 => _120.strokeDasharray, 'optionalAccess', _121 => _121.length])) notes.push(`"${path.id}": its dashes are not exported; the stroke is solid.`);
2945
2989
  if (path.strokeAlign && path.strokeAlign !== "center") notes.push(`"${path.id}": its stroke is drawn centred (Lottie has no inside or outside strokes).`);
2946
2990
  }
2947
2991
  const fill = paintOf(path.fill, "fill", path.id);
@@ -3092,7 +3136,7 @@ var AnimationEditorController = (_class = class {
3092
3136
  time: p.time,
3093
3137
  playing: p.playing,
3094
3138
  loop: p.loop,
3095
- currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _110 => _110(timeline, p.time), 'optionalAccess', _111 => _111.id]), () => ( null)),
3139
+ currentShot: _nullishCoalesce(_optionalChain([motionShotAt, 'call', _122 => _122(timeline, p.time), 'optionalAccess', _123 => _123.id]), () => ( null)),
3096
3140
  selection: this.selection,
3097
3141
  readOnly: this.readOnly,
3098
3142
  error: this.error,
@@ -3131,7 +3175,7 @@ var AnimationEditorController = (_class = class {
3131
3175
  this.applying = false;
3132
3176
  }
3133
3177
  this.notify();
3134
- _optionalChain([this, 'access', _112 => _112.onChange, 'optionalCall', _113 => _113(scene)]);
3178
+ _optionalChain([this, 'access', _124 => _124.onChange, 'optionalCall', _125 => _125(scene)]);
3135
3179
  }
3136
3180
  commit(label, next) {
3137
3181
  const before = this.scene;
@@ -3451,6 +3495,10 @@ var SETTINGS = {
3451
3495
  },
3452
3496
  fps: { type: "number", minimum: 1, maximum: 120, description: "Sampling rate for exports (default 30)." },
3453
3497
  steps: { anyOf: [{ type: "number", exclusiveMinimum: 0, maximum: 120 }, { type: "null" }], description: 'Stepped time for every command without its own: changes hold on a grid of this many frames per second (12 = "on twos" at 24 fps) for a hand-drawn look; null makes it smooth again (default).' },
3498
+ motionBlur: {
3499
+ anyOf: [object({ shutter: { type: "number", exclusiveMinimum: 0, maximum: 720, description: "Shutter angle in degrees of a frame (default 180; 360 is a whole frame, more blur)." }, samples: { type: "integer", minimum: 2, maximum: 32, description: "Most moments a frame averages (default 8)." } }), { type: "null" }],
3500
+ description: "Motion blur on the stage and in rendered frames (not exported to Flair or Lottie); null turns it off."
3501
+ },
3454
3502
  beat: {
3455
3503
  anyOf: [
3456
3504
  object({ bpm: { type: "number", minimum: 1, maximum: 1e3, description: "Beats a minute." }, offset: { type: "number", description: "When beat 0 falls, ms (default 0)." }, meter: { type: "integer", minimum: 1, description: "Beats a bar (default 4)." } }, ["bpm"]),
@@ -3577,6 +3625,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
3577
3625
  shots: describeMotionShots(s),
3578
3626
  ...s.clips ? { clips: Object.fromEntries(Object.entries(s.clips).map(([name, c]) => [name, { ...c.description ? { description: c.description } : {}, commands: c.run.length, length: Math.round(motionClipLayout(s, name, "$").length) }])) } : {},
3579
3627
  ...s.drivers ? { drivers: s.drivers } : {},
3628
+ ...s.motionBlur ? { motionBlur: s.motionBlur } : {},
3580
3629
  ...s.controls ? { controls: s.controls } : {},
3581
3630
  ...s.presets ? { presets: s.presets } : {},
3582
3631
  issues: issueSummary(controller.getState().issues),
@@ -3691,9 +3740,9 @@ function createAnimationEditorToolProvider(controller, options = {}) {
3691
3740
  handler: ({ from = 0, to, limit = 200 }) => {
3692
3741
  const s = scene();
3693
3742
  const timeline = compileMotionScene(s);
3694
- const words = _nullishCoalesce(_optionalChain([s, 'access', _114 => _114.narration, 'optionalAccess', _115 => _115.words]), () => ( []));
3743
+ const words = _nullishCoalesce(_optionalChain([s, 'access', _126 => _126.narration, 'optionalAccess', _127 => _127.words]), () => ( []));
3695
3744
  const end = _nullishCoalesce(to, () => ( Math.max(timeline.duration, words.length ? _nullishCoalesce(words[words.length - 1].end, () => ( words[words.length - 1].start)) : 0)));
3696
- const shotAt = (ms) => _optionalChain([timeline, 'access', _116 => _116.shots, 'access', _117 => _117.find, 'call', _118 => _118((x) => ms >= x.start && ms < x.end), 'optionalAccess', _119 => _119.id]);
3745
+ const shotAt = (ms) => _optionalChain([timeline, 'access', _128 => _128.shots, 'access', _129 => _129.find, 'call', _130 => _130((x) => ms >= x.start && ms < x.end), 'optionalAccess', _131 => _131.id]);
3697
3746
  const beats = [];
3698
3747
  if (s.beat && s.beat.bpm > 0) {
3699
3748
  const first = Math.max(0, Math.ceil(motionBeatAt(s.beat, from).beat - 1e-9));
@@ -3973,7 +4022,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
3973
4022
  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"]),
3974
4023
  annotations: { title: "Add commands to a shot", ...WRITE },
3975
4024
  handler: ({ shot, commands, at }) => {
3976
- const before = _nullishCoalesce(_optionalChain([scene, 'call', _120 => _120(), 'access', _121 => _121.shots, 'access', _122 => _122.find, 'call', _123 => _123((s) => s.id === shot), 'optionalAccess', _124 => _124.run, 'access', _125 => _125.length]), () => ( 0));
4025
+ const before = _nullishCoalesce(_optionalChain([scene, 'call', _132 => _132(), 'access', _133 => _133.shots, 'access', _134 => _134.find, 'call', _135 => _135((s) => s.id === shot), 'optionalAccess', _136 => _136.run, 'access', _137 => _137.length]), () => ( 0));
3977
4026
  const first = _nullishCoalesce(at, () => ( before));
3978
4027
  return apply({ op: "add_commands", shot, commands, at }, "Add commands", (s) => {
3979
4028
  const shots = describeMotionShots(s);
@@ -4138,6 +4187,7 @@ Commands
4138
4187
  - enter also reveals through a clip over the target's box: wipe (behind an edge travelling "direction", default right), iris (from the centre; "shape": circle or box) and split (from a line through the middle; "orientation": horizontal or vertical). exit wipe hides the same way. Reveals need a width and a height: use draw / undraw for a straight line.
4139
4188
  - draw draws strokes on to a fraction; move, rotate, scale and fade change the pose (about the centre); style blends solid colours and stroke width.
4140
4189
  - Drawing a group of strokes: "order": "sequence" draws them one after another (a signature, a diagram), "sortBy": "distance" from "origin" reveals outward, "pace": "speed" keeps one speed; draw with "comet": 0.2 sends a short stroke along a path and off the end.
4190
+ - Fast motion reads better with blur: set_animation_settings { "motionBlur": { "shutter": 180 } } (stage and rendered frames only).
4141
4191
  - Tie properties together with set_animation_drivers: parallax layers, a wheel that turns as it rolls, a shadow that fades as something rises. Drivers add to the target's own commands.
4142
4192
  - Reuse motion: set_animation_clip defines a clip (commands targeting "$"), and { "type": "play", "clip": name, "targets": [...] } plays it on any nodes (with stagger and speed). copy_animation gives other nodes one node's motion.
4143
4193
  - move takes "via" waypoints for a curved, smooth path that passes exactly through each point (steady speed by default); use it instead of chaining moves, which stop at each point.
@@ -4167,7 +4217,7 @@ var TIMING = /* @__PURE__ */ new Set(["duration", "delay", "stagger", "with", "e
4167
4217
  var COLORS = /* @__PURE__ */ new Set(["fill", "stroke", "color"]);
4168
4218
  var NODES = /* @__PURE__ */ new Set(["into", "path"]);
4169
4219
  function branch(type) {
4170
- return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access', _126 => _126.properties, 'optionalAccess', _127 => _127.type, 'optionalAccess', _128 => _128.const]) === type);
4220
+ return (_nullishCoalesce(MOTION_COMMAND_SCHEMA.oneOf, () => ( []))).find((b) => _optionalChain([b, 'access', _138 => _138.properties, 'optionalAccess', _139 => _139.type, 'optionalAccess', _140 => _140.const]) === type);
4171
4221
  }
4172
4222
  function unitOf(type, name) {
4173
4223
  if (name === "duration" || name === "delay" || name === "stagger") return "ms";
@@ -4202,7 +4252,7 @@ function kindOf(name, schema) {
4202
4252
  }
4203
4253
  function motionCommandFields(type) {
4204
4254
  const schema = branch(type);
4205
- if (!_optionalChain([schema, 'optionalAccess', _129 => _129.properties])) return [];
4255
+ if (!_optionalChain([schema, 'optionalAccess', _141 => _141.properties])) return [];
4206
4256
  const required = new Set(_nullishCoalesce(schema.required, () => ( [])));
4207
4257
  const fields = Object.entries(schema.properties).filter(([name]) => name !== "type").map(([name, s]) => {
4208
4258
  const kind = kindOf(name, s);
@@ -4259,7 +4309,7 @@ function defaultMotionCommand(type, targets, scene) {
4259
4309
  if (type === "wait") return { type: "wait", duration: 500 };
4260
4310
  if (!targets.length) return { error: "Pick a target first: click an element on the stage." };
4261
4311
  const nodes = [...indexMotionNodes(scene.artwork.nodes).values()];
4262
- const other = (pred) => _optionalChain([nodes, 'access', _130 => _130.find, 'call', _131 => _131((e) => !targets.includes(e.node.id) && pred(e)), 'optionalAccess', _132 => _132.node, 'access', _133 => _133.id]);
4312
+ const other = (pred) => _optionalChain([nodes, 'access', _142 => _142.find, 'call', _143 => _143((e) => !targets.includes(e.node.id) && pred(e)), 'optionalAccess', _144 => _144.node, 'access', _145 => _145.id]);
4263
4313
  const base = { targets: [...targets] };
4264
4314
  switch (type) {
4265
4315
  case "scale":
@@ -4290,8 +4340,8 @@ function defaultMotionCommand(type, targets, scene) {
4290
4340
  }
4291
4341
  case "effect": {
4292
4342
  for (const id of targets) {
4293
- const node = _optionalChain([nodes, 'access', _134 => _134.find, 'call', _135 => _135((e) => e.node.id === id), 'optionalAccess', _136 => _136.node]);
4294
- for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess', _137 => _137.effects]), () => ( []))) {
4343
+ const node = _optionalChain([nodes, 'access', _146 => _146.find, 'call', _147 => _147((e) => e.node.id === id), 'optionalAccess', _148 => _148.node]);
4344
+ for (const effect of _nullishCoalesce(_optionalChain([node, 'optionalAccess', _149 => _149.effects]), () => ( []))) {
4295
4345
  const param = Object.entries(effect.params).find(([, v]) => typeof v === "number");
4296
4346
  if (param) return { type, ...base, effect: effect.id, param: param[0], to: param[1] + 1 };
4297
4347
  }
@@ -4316,11 +4366,11 @@ function motionCommandRows(scene, shotId) {
4316
4366
  return scene.shots[index].run.map((command2, i) => {
4317
4367
  const t = timeline.commands.find((c) => c.shotIndex === index && c.command === i);
4318
4368
  const primitive = motionFlairPrimitive(command2);
4319
- return { index: i, command: command2, summary: motionCommandSummary(command2), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _138 => _138.start]), () => ( 0)), end: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _139 => _139.end]), () => ( 0)), ...primitive ? { flairPrimitive: primitive } : {} };
4369
+ return { index: i, command: command2, summary: motionCommandSummary(command2), start: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _150 => _150.start]), () => ( 0)), end: _nullishCoalesce(_optionalChain([t, 'optionalAccess', _151 => _151.end]), () => ( 0)), ...primitive ? { flairPrimitive: primitive } : {} };
4320
4370
  });
4321
4371
  }
4322
4372
  function duplicateMotionCommandEdit(scene, shot, command2) {
4323
- const run = _nullishCoalesce(_optionalChain([scene, 'access', _140 => _140.shots, 'access', _141 => _141.find, 'call', _142 => _142((s) => s.id === shot), 'optionalAccess', _143 => _143.run]), () => ( []));
4373
+ const run = _nullishCoalesce(_optionalChain([scene, 'access', _152 => _152.shots, 'access', _153 => _153.find, 'call', _154 => _154((s) => s.id === shot), 'optionalAccess', _155 => _155.run]), () => ( []));
4324
4374
  return { op: "add_commands", shot, commands: run[command2] ? [structuredClone(run[command2])] : [], at: command2 + 1 };
4325
4375
  }
4326
4376
  var MOTION_EASE_CHOICES = [
@@ -4405,7 +4455,7 @@ function motionCueFromKey(key) {
4405
4455
  function motionCueChoices(scene, limit = 256) {
4406
4456
  const choices = [];
4407
4457
  const s = (ms) => `${(ms / 1e3).toFixed(2)} s`;
4408
- const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access', _144 => _144.narration, 'optionalAccess', _145 => _145.words, 'access', _146 => _146.map, 'call', _147 => _147((w) => _nullishCoalesce(w.end, () => ( w.start)))]), () => ( [0]))) + 2e3;
4458
+ const end = Math.max(compileMotionScene(scene).duration, ..._nullishCoalesce(_optionalChain([scene, 'access', _156 => _156.narration, 'optionalAccess', _157 => _157.words, 'access', _158 => _158.map, 'call', _159 => _159((w) => _nullishCoalesce(w.end, () => ( w.start)))]), () => ( [0]))) + 2e3;
4409
4459
  if (scene.beat && scene.beat.bpm > 0) {
4410
4460
  for (let beat = 0; choices.length < limit; beat++) {
4411
4461
  const time = motionBeatTime(scene.beat, beat);
@@ -4415,7 +4465,7 @@ function motionCueChoices(scene, limit = 256) {
4415
4465
  choices.push({ value: `beat:${beat}`, label: `Beat ${beat} (bar ${at.bar}.${at.beatInBar}) \xB7 ${s(time)}`, time });
4416
4466
  }
4417
4467
  }
4418
- _optionalChain([scene, 'access', _148 => _148.narration, 'optionalAccess', _149 => _149.words, 'access', _150 => _150.forEach, 'call', _151 => _151((w, i) => {
4468
+ _optionalChain([scene, 'access', _160 => _160.narration, 'optionalAccess', _161 => _161.words, 'access', _162 => _162.forEach, 'call', _163 => _163((w, i) => {
4419
4469
  if (choices.length < limit * 2) choices.push({ value: `word:${i}`, label: `Word ${i} \u201C${w.text}\u201D \xB7 ${s(w.start)}`, time: w.start });
4420
4470
  })]);
4421
4471
  return choices;
@@ -4424,7 +4474,7 @@ function motionShotCueTime(scene, cue) {
4424
4474
  return cue ? motionCueTime(scene, cue) : void 0;
4425
4475
  }
4426
4476
  function formatMotionNarration(narration) {
4427
- return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess', _152 => _152.words]), () => ( []))).map((w) => `${w.start} ${w.text}`).join("\n");
4477
+ return (_nullishCoalesce(_optionalChain([narration, 'optionalAccess', _164 => _164.words]), () => ( []))).map((w) => `${w.start} ${w.text}`).join("\n");
4428
4478
  }
4429
4479
  function parseMotionNarration(text) {
4430
4480
  const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
@@ -4595,5 +4645,6 @@ function parseMotionDrivers(text) {
4595
4645
 
4596
4646
 
4597
4647
 
4598
- 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_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 = 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.applyMotionDrivers = applyMotionDrivers; 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.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.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.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 = 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.motionPropertyValue = motionPropertyValue; 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.parseMotionDrivers = parseMotionDrivers; 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;
4648
+
4649
+ 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_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 = 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.applyMotionDrivers = applyMotionDrivers; 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.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.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.motionBlurTimes = motionBlurTimes; 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.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 = 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.motionPropertyValue = motionPropertyValue; 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.parseMotionDrivers = parseMotionDrivers; 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;
4599
4650
  //# sourceMappingURL=index.cjs.map