MCPcopy Create free account
hub / github.com/heygen-com/hyperframes / moveKeyframeInScript

Function moveKeyframeInScript

packages/parsers/src/gsapParser.ts:2333–2381  ·  view source on GitHub ↗
(
  script: string,
  animationId: string,
  fromPercentage: number,
  toPercentage: number,
)

Source from the content-addressed store, hash-verified

2331 * the move resolves onto the same keyframe. Acorn twin: moveKeyframeInScript.
2332 */
2333export function moveKeyframeInScript(
2334 script: string,
2335 animationId: string,
2336 fromPercentage: number,
2337 toPercentage: number,
2338): string {
2339 const loc = locateAnimationWithFallback(script, animationId);
2340 if (!loc) return script;
2341 // Array-form keyframes can't host an arbitrary destination percentage —
2342 // normalize to object form in place first (mirrors addKeyframeToScript).
2343 const kfNode =
2344 findKeyframesObjectNode(loc.target.call.varsArg) ??
2345 convertArrayKeyframesToObjectNode(loc.target.call.varsArg);
2346 if (!kfNode) return script;
2347
2348 const match = findKeyframePropByPct(kfNode, fromPercentage);
2349 if (!match) return script;
2350 // No-op ONLY for a negligible move (matches the drag's NOOP_EPSILON). The old
2351 // `collision.prop === match.prop` guard dropped EVERY sub-PCT_TOLERANCE (2%)
2352 // retime, because findKeyframePropByPct resolves the destination back onto the
2353 // from-keyframe — so a deliberate 1% drag committed nothing. Acorn twin too.
2354 if (Math.abs(fromPercentage - toPercentage) < MOVE_NOOP_EPSILON_PCT) return script;
2355 // A destination keyframe is only a real collision (overwrite) when it's a
2356 // DIFFERENT keyframe; resolving back onto the from-keyframe is not.
2357 const dest = findKeyframePropByPct(kfNode, toPercentage);
2358 const collision = dest && dest.prop !== match.prop ? dest : null;
2359
2360 // Reuse each keyframe's value node verbatim (preserves properties +
2361 // per-keyframe ease + _auto). Drop the moved keyframe (and any destination
2362 // keyframe it overwrites), re-key the moved value to toPercentage, then re-sort.
2363 const movedValue = match.prop.value;
2364 const entries: Array<{ pct: number; value: AstNode }> = [];
2365 for (const prop of filterPercentageProps(kfNode)) {
2366 if (prop === match.prop) continue;
2367 if (collision && prop === collision.prop) continue;
2368 const pct = percentageFromKey(propKeyName(prop) ?? "");
2369 if (Number.isNaN(pct)) continue;
2370 entries.push({ pct, value: prop.value });
2371 }
2372 entries.push({ pct: toPercentage, value: movedValue });
2373 entries.sort((a, b) => a.pct - b.pct);
2374
2375 kfNode.properties = entries.map((e) => {
2376 const p = parseExpr(`{ ${JSON.stringify(`${e.pct}%`)}: {} }`).properties[0];
2377 p.value = e.value;
2378 return p;
2379 });
2380 return recast.print(loc.parsed.ast).code;
2381}
2382
2383/**
2384 * Resize a keyframed tween's window (boundary drag-to-retime): set the tween's

Callers 2

executeGsapMutationAcornFunction · 0.50

Calls 9

findKeyframesObjectNodeFunction · 0.85
findKeyframePropByPctFunction · 0.85
filterPercentagePropsFunction · 0.85
parseExprFunction · 0.85
absMethod · 0.80
percentageFromKeyFunction · 0.70
propKeyNameFunction · 0.70

Tested by

no test coverage detected