mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
fix(studio): cover GSAP editor target-resolution limitations (#1116)
Follow-up to #1115. Makes the Design-panel editor recognise every target shape real compositions use. The panel stays behind STUDIO_GSAP_PANEL_ENABLED (default off) — no flag change here. - Array targets: tl.to([a, b], {...}) resolves to a CSS group selector (".a, .b"). The source array is never rewritten — the joined string is for display/matching only; edits still touch just the vars object. - Chained calls: tl.to(a, ...).to(b, ...) — the matcher now walks the member chain to its timeline root, so every link is captured (previously only the first). Deletion is chain-aware: it splices out the single targeted link and re-points the chain instead of dropping the whole statement. - gsap.utils.toArray("sel") resolves like querySelectorAll, inline or via a variable binding. - Lexical scoping: element-variable resolution is now per-scope (walks the enclosing function/program chain) instead of a flat map. Fixes silent wrong-resolution when two IIFEs reuse a variable name, and unlocks multi-scene files. (Addresses review: flat-binding-scope.) - forEach/map callback params (items.forEach(el => tl.to(el, …))) and items[i] indexing resolve to the collection's selector, so loop-generated tweens are editable. - Panel matching: an element matches a tween when its id/selector is any member of a comma-group target, so either element of an array/toArray tween surfaces the shared animation. - Review items: mutation parse failures now console.warn instead of swallowing silently; buildTweenStatementCode no longer emits duration on `set`; the id-only serialize-side filter is renamed getAnimationsForElementId to disambiguate from the panel's id-or-selector matcher; added fromTo round-trip and variable-target overlap-lint tests. Genuinely runtime-only targets (template-literal selectors, unbounded loops) still skip gracefully — they can't be resolved or matched statically.
This commit is contained in:
@@ -629,6 +629,30 @@ describe("GSAP rules", () => {
|
||||
expect(finding).toBeUndefined();
|
||||
});
|
||||
|
||||
it("detects overlapping_gsap_tweens between variable-target tweens", async () => {
|
||||
// Both tweens target the same element via a querySelector variable and their
|
||||
// windows overlap on `opacity`. The structure-driven window builder must see
|
||||
// through the variable target to flag the conflict.
|
||||
const html = `
|
||||
<html><body>
|
||||
<div data-composition-id="c1" data-width="1920" data-height="1080">
|
||||
<div id="hero" class="hero" data-start="0" data-duration="5" data-track-index="0"></div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
window.__timelines = window.__timelines || {};
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
const hero = document.querySelector("#hero");
|
||||
tl.to(hero, { opacity: 1, duration: 1 }, 0);
|
||||
tl.to(hero, { opacity: 0.5, duration: 1 }, 0.5);
|
||||
window.__timelines["c1"] = tl;
|
||||
</script>
|
||||
</body></html>`;
|
||||
const result = await lintHyperframeHtml(html);
|
||||
const finding = result.findings.find((f) => f.code === "overlapping_gsap_tweens");
|
||||
expect(finding).toBeDefined();
|
||||
});
|
||||
|
||||
it("warns when an opacity exit ends at a clip start boundary without a hard kill", async () => {
|
||||
const html = `
|
||||
<html><body>
|
||||
|
||||
Reference in New Issue
Block a user