From e2da4ea865353efb4afeca4269fb5ba33fe61299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Sat, 12 Sep 2026 08:58:40 +0000 Subject: [PATCH] fix(runtime): publish progress from parent ticks --- packages/core/src/runtime/init.test.ts | 26 ++++++++++++++++++++++++++ packages/core/src/runtime/init.ts | 3 +++ 2 files changed, 29 insertions(+) diff --git a/packages/core/src/runtime/init.test.ts b/packages/core/src/runtime/init.test.ts index 2bb0a83fdc..2a0fedc9fb 100644 --- a/packages/core/src/runtime/init.test.ts +++ b/packages/core/src/runtime/init.test.ts @@ -124,6 +124,32 @@ describe("initSandboxRuntimeModular", () => { window.cancelAnimationFrame = (() => {}) as typeof window.cancelAnimationFrame; }); + it("publishes progress from parent ticks while the iframe animation frame is suspended", () => { + document.body.innerHTML = '
'; + window.__timelines = { main: createMockTimeline(10) }; + initSandboxRuntimeModular(); + const post = vi.spyOn(window.parent, "postMessage").mockImplementation(() => {}); + let now = performance.now(); + vi.spyOn(performance, "now").mockImplementation(() => now); + window.__player?.play(); + post.mockClear(); + now += 500; + window.dispatchEvent( + new MessageEvent("message", { + source: window, + data: { source: "hf-parent", type: "control", action: "tick" }, + }), + ); + expect(post).toHaveBeenCalledWith( + expect.objectContaining({ + source: "hf-preview", + type: "state", + isPlaying: true, + frame: 15, + }), + "*", + ); + }); it.each([ ["2x", 5], ["0x2", 10], diff --git a/packages/core/src/runtime/init.ts b/packages/core/src/runtime/init.ts index fec231c865..5198ca243b 100644 --- a/packages/core/src/runtime/init.ts +++ b/packages/core/src/runtime/init.ts @@ -3988,7 +3988,10 @@ export function initSandboxRuntimeModular(): void { runAdapters("pause"); syncMediaForCurrentState(); postState(true); + return; } + // When iframe rAF is throttled, parent ticks also own position reporting. + postState(false); }, onEnablePickMode: () => picker.enablePickMode(), onDisablePickMode: () => picker.disablePickMode(),