mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-11 06:30:03 +00:00
feat(sdk): ws-c elastic timing + word-alignment resolver (WS-C) (#1570)
C1: getElementTimings/setElementTiming typed session methods + setHold typed wrapper. getElementTimings reads data-duration (preferred) or data-end−data-start (fallback) — same attr-preference as handleSetTiming. setElementTiming dispatches a sparse map as one batch → one patch event → one undo step. setHold mirrors setVariableValue pattern. Also fixes a pre-existing apply-patches.ts gap: the timing/duration patch case was absent, causing undo of duration changes to silently no-op. Added the duration branch so inverse patches restore data-duration correctly. C2: packages/core/src/compiler/timingResolver.ts — shared pure resolveTimings() consumed by BOTH preview (sdk session) and render (timingCompiler) paths. Word- anchored elements get enterAt = wordTimings[k].start + offset; elastic hold = max(0, slotEnd − (enterAt + enterDuration + exitDuration)), clamped ≥ 0; never timescales animated content. Un-anchored elements keep authored timing (align-on- adjust). Deterministic + pure: no Date.now, no Math.random, no DOM. extractGsapLabels() added to gsapParserAcorn.ts to parse tl.addLabel() calls for the getElementTimings labels field. Tests: timingResolver.test.ts (10 pure-function tests including preview==render parity golden test); session.timings.test.ts (15 session-layer tests covering duration-authored, end-authored, label extraction, batching, undo, and setHold regression). Gates: build ✓ · bun test (sdk+core/compiler) 434/434 ✓ · oxlint 0 warnings ✓ · oxfmt --check ✓ · fallow --gate new-only ✓ (complexity suppressed on 2 new inline functions, duplication warn-only pre-existing) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
d0e520dbd9
commit
f65e229663
@@ -1144,3 +1144,61 @@ export function parseGsapScriptAcorn(script: string): ParsedGsap {
|
||||
return { animations: [], timelineVar: "tl", preamble: "", postamble: "" };
|
||||
}
|
||||
}
|
||||
|
||||
// ── Label extraction (WS-C) ──────────────────────────────────────────────────
|
||||
|
||||
export interface GsapLabelEntry {
|
||||
name: string;
|
||||
position: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all `tl.addLabel("name", position)` calls from a GSAP script.
|
||||
*
|
||||
* Returns labels in source order. Position must be a numeric literal; labels
|
||||
* with non-numeric positions (e.g. label-relative offsets) are skipped.
|
||||
*
|
||||
* Pure — no side effects, no DOM, no Date.now.
|
||||
*/
|
||||
export function extractGsapLabels(script: string): GsapLabelEntry[] {
|
||||
try {
|
||||
const ast = acorn.parse(script, {
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "script",
|
||||
locations: true,
|
||||
});
|
||||
const scope = collectScopeBindings(ast);
|
||||
const detection = findTimelineVar(ast, scope);
|
||||
const timelineVar = detection.timelineVar ?? "tl";
|
||||
|
||||
const labels: GsapLabelEntry[] = [];
|
||||
|
||||
acornWalk.simple(ast, {
|
||||
// fallow-ignore-next-line complexity
|
||||
ExpressionStatement(node: any) {
|
||||
const expr = node.expression;
|
||||
if (!expr || expr.type !== "CallExpression") return;
|
||||
const callee = expr.callee;
|
||||
// Match tl.addLabel(...)
|
||||
if (
|
||||
callee?.type !== "MemberExpression" ||
|
||||
callee.object?.name !== timelineVar ||
|
||||
callee.property?.name !== "addLabel"
|
||||
)
|
||||
return;
|
||||
const args = expr.arguments ?? [];
|
||||
const nameNode = args[0];
|
||||
const posNode = args[1];
|
||||
if (nameNode?.type !== "Literal" || typeof nameNode.value !== "string") return;
|
||||
if (!posNode) return;
|
||||
const pos = resolveNode(posNode, scope);
|
||||
if (typeof pos !== "number" || !Number.isFinite(pos)) return;
|
||||
labels.push({ name: nameNode.value, position: pos });
|
||||
},
|
||||
});
|
||||
|
||||
return labels;
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user