fix(core): update nested timed element visibility on seek (#823)

This commit is contained in:
Phuong Le
2026-05-14 06:23:53 +02:00
committed by GitHub
parent 0df6985d34
commit 3aa5cf3ab3
2 changed files with 52 additions and 15 deletions
+52
View File
@@ -263,6 +263,58 @@ describe("initSandboxRuntimeModular", () => {
expect(video.currentTime).toBe(9);
});
it("updates visibility for timed elements inside nested compositions", () => {
const root = document.createElement("div");
root.setAttribute("data-composition-id", "main");
root.setAttribute("data-root", "true");
root.setAttribute("data-start", "0");
root.setAttribute("data-width", "1920");
root.setAttribute("data-height", "1080");
document.body.appendChild(root);
const child = document.createElement("div");
child.setAttribute("data-composition-id", "nested");
child.setAttribute("data-start", "10");
child.setAttribute("data-duration", "10");
root.appendChild(child);
const sceneA = document.createElement("section");
sceneA.id = "scene-a";
sceneA.setAttribute("data-start", "0");
sceneA.setAttribute("data-duration", "4");
child.appendChild(sceneA);
const sceneB = document.createElement("section");
sceneB.id = "scene-b";
sceneB.setAttribute("data-start", "4");
sceneB.setAttribute("data-duration", "4");
child.appendChild(sceneB);
(window as Window & { __timelines?: Record<string, RuntimeTimelineLike> }).__timelines = {
main: createMockTimeline(20),
nested: createMockTimeline(8),
};
initSandboxRuntimeModular();
const player = (
window as Window & {
__player?: { seek: (timeSeconds: number) => void };
}
).__player;
expect(player).toBeDefined();
player?.seek(11);
expect(sceneA.style.visibility).toBe("visible");
expect(sceneB.style.visibility).toBe("hidden");
player?.seek(15);
expect(sceneA.style.visibility).toBe("hidden");
expect(sceneB.style.visibility).toBe("visible");
});
it("clamps nested media to the authored host window on seek", () => {
const root = document.createElement("div");
root.setAttribute("data-composition-id", "main");
-15
View File
@@ -1316,27 +1316,12 @@ export function initSandboxRuntimeModular(): void {
postRuntimeMessage({ source: "hf-preview", type: "media-autoplay-blocked" });
},
});
const rootCompId =
document.querySelector("[data-composition-id]")?.getAttribute("data-composition-id") ?? null;
const visibilityNodes = Array.from(document.querySelectorAll("[data-start]"));
for (const rawNode of visibilityNodes) {
if (!(rawNode instanceof HTMLElement)) continue;
const tag = rawNode.tagName.toLowerCase();
if (tag === "script" || tag === "style" || tag === "link" || tag === "meta") continue;
// Skip elements INSIDE sub-compositions — their visibility is managed by GSAP,
// not the global time-based adapter. Only manage visibility for:
// 1. Composition host elements (have data-composition-id themselves)
// 2. Direct children of root composition (audio, etc.)
// Skip: elements whose nearest composition ancestor is NOT the root
const ownCompId = rawNode.getAttribute("data-composition-id");
if (!ownCompId) {
// Not a composition host — check if it's inside a sub-composition
const parentComp = rawNode.closest("[data-composition-id]");
const parentCompId = parentComp?.getAttribute("data-composition-id") ?? null;
if (parentCompId && parentCompId !== rootCompId) continue;
}
const start = resolveStartForElement(rawNode, 0);
let duration = resolveDurationForElement(rawNode);
const compId = rawNode.getAttribute("data-composition-id");