@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.
@@ -24,9 +24,49 @@ declare function addAnimationToScript(script: string, animation: Omit<GsapAnimat
24
24
  id: string;
25
25
  };
26
26
  declare function removeAnimationFromScript(script: string, animationId: string): string;
27
+ /**
28
+ * Enforce "exactly one position write per element". Position commits (drag, add
29
+ * keyframe, static hold) must never leave the same selector with two conflicting
30
+ * position representations (e.g. a degenerate `tl.to("#box",{duration:0,x,y})`
31
+ * AND a `gsap.set("#box",{x,y})`) — the later one silently overrides the earlier,
32
+ * so the element "can't move" / snaps / leaves residue on delete.
33
+ *
34
+ * Keeps `keepId` (the write the commit just edited); falls back to the LAST
35
+ * position write in source order (the runtime-effective one) if `keepId` is stale.
36
+ * Removes every OTHER pure-position write (`propertyGroup === "position"`, which
37
+ * covers tl.to/from/fromTo flat-or-keyframed, tl.set, and standalone gsap.set,
38
+ * including degenerate duration:0 tweens). Non-position writes for the same
39
+ * selector (rotation / opacity / size / mixed) are left untouched.
40
+ */
41
+ declare function dedupePositionWritesInScript(script: string, selector: string, keepId?: string): string;
27
42
  declare function updateKeyframeInScript(script: string, animationId: string, percentage: number, properties: Record<string, number | string>, ease?: string): string;
28
43
  declare function addKeyframeToScript(script: string, animationId: string, percentage: number, properties: Record<string, number | string>, ease?: string, backfillDefaults?: Record<string, number | string>): string;
29
44
  declare function removeKeyframeFromScript(script: string, animationId: string, percentage: number): string;
45
+ /**
46
+ * Retime a keyframe: move the keyframe at `fromPercentage` to `toPercentage`,
47
+ * PRESERVING its properties and per-keyframe ease (the Studio "Move to Playhead"
48
+ * gesture). Re-sorts keyframes by percentage. If a keyframe already exists at
49
+ * `toPercentage`, it is overwritten by the moved one (no duplicate). No-op when
50
+ * the animation/keyframe isn't found, the tween has no object-form keyframes, or
51
+ * the move resolves onto the same keyframe.
52
+ */
53
+ declare function moveKeyframeInScript(script: string, animationId: string, fromPercentage: number, toPercentage: number): string;
54
+ /**
55
+ * Resize a keyframed tween's window (boundary drag-to-retime): set the tween's
56
+ * `position` (trailing position arg) + `duration`, and RE-KEY each existing
57
+ * keyframe to its new percentage via `pctRemap` (each `{ from, to }` matches an
58
+ * existing keyframe by its current tween-% and re-keys it to `to`).
59
+ *
60
+ * Unlike replace-with-keyframes (which rebuilds the keyframes object from a plain
61
+ * array and loses author intent), this re-keys the percentage KEY in place and
62
+ * leaves every value node, each keyframe's `_auto` + per-keyframe `ease`, the
63
+ * keyframes-object `easeEach`, and the OUTER tween `ease` byte-for-byte verbatim.
64
+ * No-op when the animation/keyframes can't be located.
65
+ */
66
+ declare function resizeKeyframedTweenInScript(script: string, animationId: string, newPosition: number, newDuration: number, pctRemap: ReadonlyArray<{
67
+ from: number;
68
+ to: number;
69
+ }>): string;
30
70
  declare function removePropertyFromAnimation(script: string, animationId: string, property: string, from?: boolean): string;
31
71
  /**
32
72
  * Remove all keyframes from a tween, collapsing to a flat tween with one
@@ -91,4 +131,4 @@ type UnrollElement = {
91
131
  */
92
132
  declare function unrollDynamicAnimations(script: string, animationId: string, elements: UnrollElement[]): string;
93
133
 
94
- export { type UnrollElement, addAnimationToScript, addAnimationWithKeyframesToScript, addKeyframeToScript, addLabelToScript, convertToKeyframesFromScript, materializeKeyframesFromScript, removeAllKeyframesFromScript, removeAnimationFromScript, removeArcPathFromScript, removeKeyframeFromScript, removeLabelFromScript, removePropertyFromAnimation, scalePositionsInScript, setArcPathInScript, shiftPositionsInScript, splitAnimationsInScript, splitIntoPropertyGroupsFromScript, unrollDynamicAnimations, updateAnimationInScript, updateArcSegmentInScript, updateKeyframeInScript };
134
+ export { type UnrollElement, addAnimationToScript, addAnimationWithKeyframesToScript, addKeyframeToScript, addLabelToScript, convertToKeyframesFromScript, dedupePositionWritesInScript, materializeKeyframesFromScript, moveKeyframeInScript, removeAllKeyframesFromScript, removeAnimationFromScript, removeArcPathFromScript, removeKeyframeFromScript, removeLabelFromScript, removePropertyFromAnimation, resizeKeyframedTweenInScript, scalePositionsInScript, setArcPathInScript, shiftPositionsInScript, splitAnimationsInScript, splitIntoPropertyGroupsFromScript, unrollDynamicAnimations, updateAnimationInScript, updateArcSegmentInScript, updateKeyframeInScript };
@@ -1223,19 +1223,37 @@ function addAnimationToScript(script, animation) {
1223
1223
  const newId = reParsed?.located[reParsed.located.length - 1]?.id ?? "";
1224
1224
  return { script: result, id: newId };
1225
1225
  }
1226
+ function removeCallFromMagicString(ms, call, script) {
1227
+ const N = call.node;
1228
+ const exprStmt = findEnclosingExpressionStatement(call.ancestors);
1229
+ if (N.callee?.object?.type !== "CallExpression" && exprStmt?.expression === N) {
1230
+ const end = exprStmt.end < script.length && script[exprStmt.end] === "\n" ? exprStmt.end + 1 : exprStmt.end;
1231
+ ms.remove(exprStmt.start, end);
1232
+ } else {
1233
+ ms.remove(N.callee.object.end, N.end);
1234
+ }
1235
+ }
1226
1236
  function removeAnimationFromScript(script, animationId) {
1227
1237
  const parsed = parseGsapScriptAcornForWrite(script);
1228
1238
  if (!parsed) return script;
1229
1239
  const target = parsed.located.find((l) => l.id === animationId);
1230
1240
  if (!target) return script;
1231
1241
  const ms = new MagicString(script);
1232
- const N = target.call.node;
1233
- const exprStmt = findEnclosingExpressionStatement(target.call.ancestors);
1234
- if (N.callee?.object?.type !== "CallExpression" && exprStmt?.expression === N) {
1235
- const end = exprStmt.end < script.length && script[exprStmt.end] === "\n" ? exprStmt.end + 1 : exprStmt.end;
1236
- ms.remove(exprStmt.start, end);
1237
- } else {
1238
- ms.remove(N.callee.object.end, N.end);
1242
+ removeCallFromMagicString(ms, target.call, script);
1243
+ return ms.toString();
1244
+ }
1245
+ function dedupePositionWritesInScript(script, selector, keepId) {
1246
+ const parsed = parseGsapScriptAcornForWrite(script);
1247
+ if (!parsed) return script;
1248
+ const posWrites = parsed.located.filter(
1249
+ (l) => l.animation.targetSelector === selector && l.animation.propertyGroup === "position"
1250
+ );
1251
+ if (posWrites.length <= 1) return script;
1252
+ const keeper = posWrites.find((l) => l.id === keepId) ?? posWrites[posWrites.length - 1];
1253
+ const ms = new MagicString(script);
1254
+ for (const l of posWrites) {
1255
+ if (l === keeper) continue;
1256
+ removeCallFromMagicString(ms, l.call, script);
1239
1257
  }
1240
1258
  return ms.toString();
1241
1259
  }
@@ -1322,6 +1340,7 @@ function convertFlatTweenToKeyframes(script, target) {
1322
1340
  }
1323
1341
  var PERCENTAGE_KEY_RE2 = /^(\d+(?:\.\d+)?)%$/;
1324
1342
  var PCT_TOLERANCE = 2;
1343
+ var MOVE_NOOP_EPSILON_PCT = 0.05;
1325
1344
  function percentageFromKey(key) {
1326
1345
  const m = PERCENTAGE_KEY_RE2.exec(key);
1327
1346
  return m ? Number.parseFloat(m[1] ?? "0") : Number.NaN;
@@ -1631,6 +1650,50 @@ function removeKeyframeFromScript(script, animationId, percentage) {
1631
1650
  removeProp(ms, match.prop, allProps);
1632
1651
  return ms.toString();
1633
1652
  }
1653
+ function moveKeyframeInScript(script, animationId, fromPercentage, toPercentage) {
1654
+ const located = locateWithKeyframes(script, animationId);
1655
+ if (!located) return script;
1656
+ const { kfNode } = located;
1657
+ const match = findKfPropByPct(kfNode, fromPercentage);
1658
+ if (!match) return script;
1659
+ if (Math.abs(fromPercentage - toPercentage) < MOVE_NOOP_EPSILON_PCT) return script;
1660
+ const dest = findKfPropByPct(kfNode, toPercentage);
1661
+ const collision = dest && dest.prop !== match.prop ? dest : null;
1662
+ const entries = [];
1663
+ for (const prop of percentagePropsOf(kfNode)) {
1664
+ if (prop === match.prop) continue;
1665
+ if (collision && prop === collision.prop) continue;
1666
+ const pct = percentageFromKey(propKeyName2(prop) ?? "");
1667
+ if (Number.isNaN(pct)) continue;
1668
+ entries.push({ pct, record: valueNodeToRecord(prop.value, script) });
1669
+ }
1670
+ entries.push({ pct: toPercentage, record: valueNodeToRecord(match.prop.value, script) });
1671
+ entries.sort((a, b) => a.pct - b.pct);
1672
+ const body = entries.map((e) => `${JSON.stringify(`${e.pct}%`)}: ${recordToCode(e.record)}`).join(", ");
1673
+ const ms = new MagicString(script);
1674
+ ms.overwrite(kfNode.start, kfNode.end, `{ ${body} }`);
1675
+ return ms.toString();
1676
+ }
1677
+ function resizeKeyframedTweenInScript(script, animationId, newPosition, newDuration, pctRemap) {
1678
+ const located = locateWithKeyframes(script, animationId);
1679
+ if (!located) return script;
1680
+ const { target, kfNode } = located;
1681
+ const edits = [];
1682
+ const seen = /* @__PURE__ */ new Set();
1683
+ for (const { from, to } of pctRemap) {
1684
+ const match = findKfPropByPct(kfNode, from);
1685
+ if (!match || seen.has(match.prop.key)) continue;
1686
+ seen.add(match.prop.key);
1687
+ edits.push({ keyNode: match.prop.key, to });
1688
+ }
1689
+ const ms = new MagicString(script);
1690
+ for (const { keyNode, to } of edits) {
1691
+ ms.overwrite(keyNode.start, keyNode.end, JSON.stringify(`${to}%`));
1692
+ }
1693
+ overwritePosition(ms, target.call, newPosition);
1694
+ upsertProp(ms, target.call.varsArg, "duration", newDuration);
1695
+ return ms.toString();
1696
+ }
1634
1697
  function removePropertyFromAnimation(script, animationId, property, from = false) {
1635
1698
  const parsed = parseGsapScriptAcornForWrite(script);
1636
1699
  if (!parsed) return script;
@@ -1662,6 +1725,14 @@ function removeAllKeyframesFromScript(script, animationId) {
1662
1725
  target.call,
1663
1726
  buildVarsObjectCode(buildCollapsedFlatVars(target.animation, collapse))
1664
1727
  );
1728
+ if (target.animation.propertyGroup === "position") {
1729
+ for (const l of parsed.located) {
1730
+ if (l === target) continue;
1731
+ if (l.animation.targetSelector === target.animation.targetSelector && l.animation.propertyGroup === "position") {
1732
+ removeCallFromMagicString(ms, l.call, script);
1733
+ }
1734
+ }
1735
+ }
1665
1736
  return ms.toString();
1666
1737
  }
1667
1738
  function buildCollapsedFlatVars(animation, collapse) {
@@ -1669,11 +1740,11 @@ function buildCollapsedFlatVars(animation, collapse) {
1669
1740
  for (const [k, v] of Object.entries(collapse.properties)) {
1670
1741
  if (k !== "ease") flat[k] = v;
1671
1742
  }
1672
- if (animation.duration !== void 0) flat.duration = animation.duration;
1673
- if (animation.ease) flat.ease = animation.ease;
1674
1743
  for (const [k, v] of Object.entries(animation.extras ?? {})) {
1675
1744
  if (typeof v === "number" || typeof v === "string") flat[k] = v;
1676
1745
  }
1746
+ flat.duration = 0;
1747
+ flat.immediateRender = true;
1677
1748
  return flat;
1678
1749
  }
1679
1750
  function buildKeyframesVarsCode(animation, fromProps, toProps, varsNode, source, setDuration) {
@@ -2393,13 +2464,16 @@ export {
2393
2464
  addKeyframeToScript,
2394
2465
  addLabelToScript,
2395
2466
  convertToKeyframesFromScript,
2467
+ dedupePositionWritesInScript,
2396
2468
  materializeKeyframesFromScript,
2469
+ moveKeyframeInScript,
2397
2470
  removeAllKeyframesFromScript,
2398
2471
  removeAnimationFromScript,
2399
2472
  removeArcPathFromScript,
2400
2473
  removeKeyframeFromScript,
2401
2474
  removeLabelFromScript,
2402
2475
  removePropertyFromAnimation,
2476
+ resizeKeyframedTweenInScript,
2403
2477
  scalePositionsInScript,
2404
2478
  setArcPathInScript,
2405
2479
  shiftPositionsInScript,