@hyperframes/parsers 0.7.18 → 0.7.19

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.
@@ -25,6 +25,15 @@ declare function addAnimationWithKeyframesToScript(script: string, targetSelecto
25
25
  id: string;
26
26
  };
27
27
  declare function removeAnimationFromScript(script: string, animationId: string): string;
28
+ /**
29
+ * Recast twin of {@link dedupePositionWritesInScript} (acorn). Enforce "exactly
30
+ * one position write per element": keep `keepId` (or the LAST position write in
31
+ * source order if stale), remove every OTHER pure-position write
32
+ * (`propertyGroup === "position"` — tl.to/from/fromTo flat-or-keyframed, tl.set,
33
+ * standalone gsap.set, incl. degenerate duration:0 tweens). Non-position writes
34
+ * for the selector are left untouched.
35
+ */
36
+ declare function dedupePositionWritesInScript(script: string, selector: string, keepId?: string): string;
28
37
  /** True for a `tl.set(...)` this module emitted to hold a keyframe before its tween.
29
38
  * The Studio filters these out so they never appear as user keyframes/diamonds. */
30
39
  declare function isStudioHoldSet(anim: GsapAnimation): boolean;
@@ -65,6 +74,29 @@ declare function addKeyframeToScript(script: string, animationId: string, percen
65
74
  * remaining keyframe's properties.
66
75
  */
67
76
  declare function removeKeyframeFromScript(script: string, animationId: string, percentage: number): string;
77
+ /**
78
+ * Retime a keyframe: move the keyframe at `fromPercentage` to `toPercentage`,
79
+ * PRESERVING its properties and per-keyframe ease (the Studio "Move to Playhead"
80
+ * gesture). Re-sorts keyframes by percentage. If a keyframe already exists at
81
+ * `toPercentage`, it is overwritten by the moved one (no duplicate). No-op when
82
+ * the animation/keyframe isn't found, the tween has no object-form keyframes, or
83
+ * the move resolves onto the same keyframe. Acorn twin: moveKeyframeInScript.
84
+ */
85
+ declare function moveKeyframeInScript(script: string, animationId: string, fromPercentage: number, toPercentage: number): string;
86
+ /**
87
+ * Resize a keyframed tween's window (boundary drag-to-retime): set the tween's
88
+ * `position` + `duration` and RE-KEY each existing keyframe to its new percentage
89
+ * via `pctRemap` (each `{ from, to }` matches an existing keyframe by its current
90
+ * tween-% and re-keys it to `to`).
91
+ *
92
+ * Re-keys the percentage KEY in place, leaving every value node (so `_auto` +
93
+ * per-keyframe `ease` survive), the keyframes-object `easeEach`, and the OUTER
94
+ * tween `ease` untouched. Acorn twin: resizeKeyframedTweenInScript.
95
+ */
96
+ declare function resizeKeyframedTweenInScript(script: string, animationId: string, newPosition: number, newDuration: number, pctRemap: ReadonlyArray<{
97
+ from: number;
98
+ to: number;
99
+ }>): string;
68
100
  /**
69
101
  * Replace the properties (and optionally ease) at an existing keyframe percentage.
70
102
  */
@@ -155,4 +187,4 @@ declare function unrollDynamicAnimations(script: string, animationId: string, el
155
187
  easeEach?: string;
156
188
  }>): string;
157
189
 
158
- export { ArcPathConfig, ArcPathSegment, GsapAnimation, ParsedGsap, type SplitAnimationsOptions, type SplitAnimationsResult, addAnimationToScript, addAnimationWithKeyframesToScript, addKeyframeToScript, addMotionPathPointInScript, addMotionPathToScript, convertToKeyframesInScript, isStudioHoldSet, materializeKeyframesInScript, parseGsapScript, removeAllKeyframesFromScript, removeAnimationFromScript, removeArcPathFromScript, removeKeyframeFromScript, removeMotionPathPointInScript, scalePositionsInScript, setArcPathInScript, shiftPositionsInScript, splitAnimationsInScript, splitIntoPropertyGroups, syncPositionHoldsBeforeKeyframes, unrollDynamicAnimations, updateAnimationInScript, updateArcSegmentInScript, updateKeyframeInScript, updateMotionPathPointInScript };
190
+ export { ArcPathConfig, ArcPathSegment, GsapAnimation, ParsedGsap, type SplitAnimationsOptions, type SplitAnimationsResult, addAnimationToScript, addAnimationWithKeyframesToScript, addKeyframeToScript, addMotionPathPointInScript, addMotionPathToScript, convertToKeyframesInScript, dedupePositionWritesInScript, isStudioHoldSet, materializeKeyframesInScript, moveKeyframeInScript, parseGsapScript, removeAllKeyframesFromScript, removeAnimationFromScript, removeArcPathFromScript, removeKeyframeFromScript, removeMotionPathPointInScript, resizeKeyframedTweenInScript, scalePositionsInScript, setArcPathInScript, shiftPositionsInScript, splitAnimationsInScript, splitIntoPropertyGroups, syncPositionHoldsBeforeKeyframes, unrollDynamicAnimations, updateAnimationInScript, updateArcSegmentInScript, updateKeyframeInScript, updateMotionPathPointInScript };
@@ -1470,9 +1470,13 @@ function removeAnimationFromScript(script, animationId) {
1470
1470
  target = parsed.located.find((l) => l.id === convertedId);
1471
1471
  }
1472
1472
  if (!target) return script;
1473
- const node = target.call.node;
1474
- const stmtPath = findStatementPath(target.call.path);
1475
- if (!stmtPath) return script;
1473
+ removeCallFromAst(target.call);
1474
+ return recast.print(parsed.ast).code;
1475
+ }
1476
+ function removeCallFromAst(call) {
1477
+ const node = call.node;
1478
+ const stmtPath = findStatementPath(call.path);
1479
+ if (!stmtPath) return;
1476
1480
  const parentCall = findChainParentCall(stmtPath.node, node);
1477
1481
  if (parentCall) {
1478
1482
  parentCall.callee.object = node.callee.object;
@@ -1481,6 +1485,23 @@ function removeAnimationFromScript(script, animationId) {
1481
1485
  } else {
1482
1486
  stmtPath.prune();
1483
1487
  }
1488
+ }
1489
+ function dedupePositionWritesInScript(script, selector, keepId) {
1490
+ let parsed;
1491
+ try {
1492
+ parsed = parseGsapAst(script);
1493
+ } catch {
1494
+ return script;
1495
+ }
1496
+ const posWrites = parsed.located.filter(
1497
+ (l) => l.animation.targetSelector === selector && l.animation.propertyGroup === "position"
1498
+ );
1499
+ if (posWrites.length <= 1) return script;
1500
+ const keeper = posWrites.find((l) => l.id === keepId) ?? posWrites[posWrites.length - 1];
1501
+ for (const l of posWrites) {
1502
+ if (l === keeper) continue;
1503
+ removeCallFromAst(l.call);
1504
+ }
1484
1505
  return recast.print(parsed.ast).code;
1485
1506
  }
1486
1507
  function insertInheritedStateSet(script, selector, position, properties) {
@@ -1667,6 +1688,7 @@ function percentageFromKey(key) {
1667
1688
  return m ? Number.parseFloat(m[1]) : Number.NaN;
1668
1689
  }
1669
1690
  var PCT_TOLERANCE = 2;
1691
+ var MOVE_NOOP_EPSILON_PCT = 0.05;
1670
1692
  function findKeyframePropByPct(kfNode, percentage) {
1671
1693
  const props = kfNode.properties;
1672
1694
  for (let i = 0; i < props.length; i++) {
@@ -1897,6 +1919,49 @@ function removeKeyframeFromScript(script, animationId, percentage) {
1897
1919
  }
1898
1920
  return recast.print(loc.parsed.ast).code;
1899
1921
  }
1922
+ function moveKeyframeInScript(script, animationId, fromPercentage, toPercentage) {
1923
+ const loc = locateAnimationWithFallback(script, animationId);
1924
+ if (!loc) return script;
1925
+ const kfNode = findKeyframesObjectNode(loc.target.call.varsArg);
1926
+ if (!kfNode) return script;
1927
+ const match = findKeyframePropByPct(kfNode, fromPercentage);
1928
+ if (!match) return script;
1929
+ if (Math.abs(fromPercentage - toPercentage) < MOVE_NOOP_EPSILON_PCT) return script;
1930
+ const dest = findKeyframePropByPct(kfNode, toPercentage);
1931
+ const collision = dest && dest.prop !== match.prop ? dest : null;
1932
+ const movedValue = match.prop.value;
1933
+ const entries = [];
1934
+ for (const prop of filterPercentageProps(kfNode)) {
1935
+ if (prop === match.prop) continue;
1936
+ if (collision && prop === collision.prop) continue;
1937
+ const pct = percentageFromKey(propKeyName(prop) ?? "");
1938
+ if (Number.isNaN(pct)) continue;
1939
+ entries.push({ pct, value: prop.value });
1940
+ }
1941
+ entries.push({ pct: toPercentage, value: movedValue });
1942
+ entries.sort((a, b) => a.pct - b.pct);
1943
+ kfNode.properties = entries.map((e) => {
1944
+ const p = parseExpr(`{ ${JSON.stringify(`${e.pct}%`)}: {} }`).properties[0];
1945
+ p.value = e.value;
1946
+ return p;
1947
+ });
1948
+ return recast.print(loc.parsed.ast).code;
1949
+ }
1950
+ function resizeKeyframedTweenInScript(script, animationId, newPosition, newDuration, pctRemap) {
1951
+ const loc = locateAnimationWithFallback(script, animationId);
1952
+ if (!loc) return script;
1953
+ const kfNode = findKeyframesObjectNode(loc.target.call.varsArg);
1954
+ if (!kfNode) return script;
1955
+ const seen = /* @__PURE__ */ new Set();
1956
+ for (const { from, to } of pctRemap) {
1957
+ const match = findKeyframePropByPct(kfNode, from);
1958
+ if (!match || seen.has(match.prop)) continue;
1959
+ seen.add(match.prop);
1960
+ match.prop.key = parseExpr(`{ ${JSON.stringify(`${to}%`)}: 0 }`).properties[0].key;
1961
+ }
1962
+ applyUpdatesToCall(loc.target.call, { position: newPosition, duration: newDuration });
1963
+ return recast.print(loc.parsed.ast).code;
1964
+ }
1900
1965
  function updateKeyframeInScript(script, animationId, percentage, properties, ease) {
1901
1966
  const arrLoc = locateAnimationWithFallback(script, animationId);
1902
1967
  const arrVal = arrLoc && findPropertyNode(arrLoc.target.call.varsArg, "keyframes");
@@ -2019,6 +2084,17 @@ function removeAllKeyframesFromScript(script, animationId) {
2019
2084
  const collapseEntry = method === "from" ? kfEntries[0] : kfEntries[kfEntries.length - 1];
2020
2085
  const record = objectExpressionToRecord(collapseEntry.prop.value, loc.parsed.scope);
2021
2086
  collapseKeyframesToFlat(loc.target.call.varsArg, record);
2087
+ removeVarsKey(loc.target.call.varsArg, "ease");
2088
+ setVarsKey(loc.target.call.varsArg, "duration", 0);
2089
+ setVarsKey(loc.target.call.varsArg, "immediateRender", true);
2090
+ if (loc.target.animation.propertyGroup === "position") {
2091
+ for (const l of loc.parsed.located) {
2092
+ if (l === loc.target) continue;
2093
+ if (l.animation.targetSelector === loc.target.animation.targetSelector && l.animation.propertyGroup === "position") {
2094
+ removeCallFromAst(l.call);
2095
+ }
2096
+ }
2097
+ }
2022
2098
  return recast.print(loc.parsed.ast).code;
2023
2099
  }
2024
2100
  function materializeKeyframesInScript(script, animationId, keyframes, easeEach, resolvedSelector) {
@@ -2453,18 +2529,21 @@ export {
2453
2529
  classifyPropertyGroup,
2454
2530
  classifyTweenPropertyGroup,
2455
2531
  convertToKeyframesInScript,
2532
+ dedupePositionWritesInScript,
2456
2533
  generateSpringEaseData,
2457
2534
  getAnimationsForElementId,
2458
2535
  gsapAnimationsToKeyframes,
2459
2536
  isStudioHoldSet,
2460
2537
  keyframesToGsapAnimations,
2461
2538
  materializeKeyframesInScript,
2539
+ moveKeyframeInScript,
2462
2540
  parseGsapScript,
2463
2541
  removeAllKeyframesFromScript,
2464
2542
  removeAnimationFromScript,
2465
2543
  removeArcPathFromScript,
2466
2544
  removeKeyframeFromScript,
2467
2545
  removeMotionPathPointInScript,
2546
+ resizeKeyframedTweenInScript,
2468
2547
  scalePositionsInScript,
2469
2548
  serializeGsapAnimations,
2470
2549
  setArcPathInScript,