fix(runtime): resume readiness after deferred GSAP batching (#2491)

This commit is contained in:
Miguel Ángel
2026-07-15 15:50:48 -04:00
committed by GitHub
parent 968c90397b
commit b874c4440f
2 changed files with 53 additions and 9 deletions
+30
View File
@@ -1415,6 +1415,36 @@ describe("initSandboxRuntimeModular", () => {
expect(window.__player?.getDuration()).toBe(10);
});
it("resumes readiness when GSAP batching starts after runtime initialization", async () => {
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);
let timelineDuration = 0;
const timeline = createMockTimeline(0);
timeline.duration = () => timelineDuration;
window.__timelines = { main: timeline };
window.__hfTimelinesBuilding = false;
initSandboxRuntimeModular();
expect(window.__renderReady).toBe(true);
window.__hfTimelinesBuilding = true;
await new Promise((resolve) => setTimeout(resolve, 0));
expect(window.__renderReady).toBe(false);
timelineDuration = 10;
window.__hfTimelinesBuilding = false;
window.dispatchEvent(new CustomEvent("hf-timelines-built"));
expect(window.__renderReady).toBe(true);
expect(window.__player?.getDuration()).toBe(10);
});
it("waits for THREE.DefaultLoadingManager to drain before publishing render readiness", async () => {
const root = document.createElement("div");
root.setAttribute("data-composition-id", "main");
+23 -9
View File
@@ -2476,11 +2476,33 @@ export function initSandboxRuntimeModular(): void {
postState(true);
};
let timelinesBuiltListener: (() => void) | null = null;
const waitForTimelinesBuilt = () => {
if (timelinesBuiltListener) return;
const onTimelinesBuilt = () => {
window.removeEventListener("hf-timelines-built", onTimelinesBuilt);
timelinesBuiltListener = null;
maybePublishRenderReady();
};
timelinesBuiltListener = onTimelinesBuilt;
window.addEventListener("hf-timelines-built", onTimelinesBuilt);
};
registerRuntimeCleanup(() => {
if (!timelinesBuiltListener) return;
window.removeEventListener("hf-timelines-built", timelinesBuiltListener);
timelinesBuiltListener = null;
});
maybePublishRenderReady = () => {
if (!externalCompositionsReady || window.__hfTimelinesBuilding) {
if (!externalCompositionsReady) {
window.__renderReady = false;
return;
}
if (window.__hfTimelinesBuilding) {
window.__renderReady = false;
waitForTimelinesBuilt();
return;
}
// Re-run discover so adapters can refresh their state from the current
// DOM — e.g. the Three.js adapter only hooks `DefaultLoadingManager` once
// it sees `window.THREE`, which may have loaded AFTER the initial
@@ -2499,14 +2521,6 @@ export function initSandboxRuntimeModular(): void {
// synchronously. Wait for the "hf-timelines-built" event before the first
// binding attempt so the transport clock receives the finished timeline
// duration instead of permanently publishing duration=0.
if (window.__hfTimelinesBuilding) {
window.__renderReady = false;
const onTimelinesBuilt = () => {
window.removeEventListener("hf-timelines-built", onTimelinesBuilt);
maybePublishRenderReady();
};
window.addEventListener("hf-timelines-built", onTimelinesBuilt);
}
maybePublishRenderReady();
// When the bundler inlines compositions, data-composition-src is removed so