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

Function updateKeyframeInScript

packages/parsers/src/gsapParser.ts:2426–2510  ·  view source on GitHub ↗
(
  script: string,
  animationId: string,
  percentage: number,
  properties: Record<string, number | string>,
  ease?: string,
)

Source from the content-addressed store, hash-verified

2424 * Replace the properties (and optionally ease) at an existing keyframe percentage.
2425 */
2426export function updateKeyframeInScript(
2427 script: string,
2428 animationId: string,
2429 percentage: number,
2430 properties: Record<string, number | string>,
2431 ease?: string,
2432): string {
2433 // Array-form keyframes (`keyframes: [{x,y}, …]`) have no explicit percentages —
2434 // GSAP distributes them evenly. The percentage-keyed object path below can't
2435 // match them (findKeyframesObjectNode only matches ObjectExpression), so dragging
2436 // a motion-path node on an array-authored tween silently no-op'd. Resolve the
2437 // element by its implicit percentage and replace it in place. Mirrors the array
2438 // branch in removeKeyframeFromScript.
2439 const arrLoc = locateAnimationWithFallback(script, animationId);
2440 const arrVal = arrLoc && findPropertyNode(arrLoc.target.call.varsArg, "keyframes");
2441 if (arrLoc && arrVal?.type === "ArrayExpression") {
2442 const elements: AstNode[] = (arrVal.elements ?? []).filter(
2443 (e: AstNode | null): e is AstNode => !!e && e.type === "ObjectExpression",
2444 );
2445 const n = elements.length;
2446 if (n === 0) return script;
2447 let matchIdx = -1;
2448 let bestDist = Number.POSITIVE_INFINITY;
2449 for (let i = 0; i < n; i++) {
2450 const pct = n > 1 ? (i / (n - 1)) * 100 : 0;
2451 const dist = Math.abs(pct - percentage);
2452 if (dist <= PCT_TOLERANCE && dist < bestDist) {
2453 matchIdx = i;
2454 bestDist = dist;
2455 }
2456 }
2457 if (matchIdx === -1) return script;
2458 const realIdx = arrVal.elements.indexOf(elements[matchIdx]);
2459 arrVal.elements[realIdx] = buildKeyframeValueNode(properties, ease);
2460 return recast.print(arrLoc.parsed.ast).code;
2461 }
2462
2463 const ctx = locateKeyframeCtx(script, animationId, percentage);
2464 if (!ctx) return script;
2465 const { loc, kfNode } = ctx;
2466
2467 const match = findKeyframePropByPct(kfNode, percentage);
2468 if (!match) return script;
2469
2470 if (Object.keys(properties).length === 0 && ease) {
2471 // Ease-only update: preserve existing properties, just add/replace ease
2472 const existing = match.prop.value;
2473 if (existing?.type === "ObjectExpression") {
2474 const props = (existing.properties ?? []) as AstNode[];
2475 const easeIdx = props.findIndex(
2476 (p: AstNode) => isObjectProperty(p) && propKeyName(p) === "ease",
2477 );
2478 const easeNode = parseExpr(`({ ease: ${JSON.stringify(ease)} })`).properties[0];
2479 if (easeIdx >= 0) {
2480 props[easeIdx] = easeNode;
2481 } else {
2482 props.push(easeNode);
2483 }

Calls 12

buildKeyframeValueNodeFunction · 0.85
locateKeyframeCtxFunction · 0.85
findKeyframePropByPctFunction · 0.85
parseExprFunction · 0.85
valueToCodeFunction · 0.85
absMethod · 0.80
entriesMethod · 0.80
findPropertyNodeFunction · 0.70
isObjectPropertyFunction · 0.70
propKeyNameFunction · 0.70
upsertFunction · 0.70

Tested by

no test coverage detected