From 69d05c5cccb98c5831dc2157d478fbd369341495 Mon Sep 17 00:00:00 2001 From: Miguel Angel Simon Sierra Date: Thu, 29 Oct 2026 22:56:22 -0400 Subject: [PATCH] perf(core): a video edit with no animation timeline stops rewriting every clip each frame --- packages/core/src/runtime/init.test.ts | 19 +++++++++++++++++++ packages/core/src/runtime/init.ts | 22 ++++++++++++++-------- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/packages/core/src/runtime/init.test.ts b/packages/core/src/runtime/init.test.ts index 1644832ede..3e5b6c44f0 100644 --- a/packages/core/src/runtime/init.test.ts +++ b/packages/core/src/runtime/init.test.ts @@ -612,6 +612,25 @@ describe("initSandboxRuntimeModular", () => { } }); + it("leaves hidden clips alone on every jump when the page has no timeline", () => { + document.body.innerHTML = + `
` + + `` + + `` + + `
`; + window.__timelines = {}; + initSandboxRuntimeModular(); + const query = vi.spyOn(document, "querySelectorAll"); + const looked = () => + query.mock.calls.filter(([selector]) => selector === "[data-hf-clip-hidden]").length; + try { + for (const t of [0.5, 1, 1.5, 3, 6]) window.__player?.seek(t); + expect(looked()).toBe(0); + } finally { + query.mockRestore(); + } + }); + it("styles a hidden clip visible once while playing toward its start, far scene included", () => { document.body.innerHTML = `
` + diff --git a/packages/core/src/runtime/init.ts b/packages/core/src/runtime/init.ts index e2d997848b..036e980784 100644 --- a/packages/core/src/runtime/init.ts +++ b/packages/core/src/runtime/init.ts @@ -5163,6 +5163,16 @@ export function initSandboxRuntimeModular(): void { return false; } }; + // Registered timelines other than the root composition's: each scene's own clock. + const sceneTimelines = () => { + const timelines = (window.__timelines ?? {}) as Record; + const rootCompositionId = + resolveRootCompositionElement()?.getAttribute("data-composition-id") ?? null; + return Object.entries(timelines).filter( + (entry): entry is [string, RuntimeTimelineLike] => + !!entry[1] && typeof entry[1] === "object" && entry[0] !== rootCompositionId, + ); + }; const seekStandaloneRegisteredTimelines = ( timeSeconds: number, revision: number, @@ -5172,12 +5182,7 @@ export function initSandboxRuntimeModular(): void { sceneClocks = new WeakMap(); sceneClocksRevision = revision; } - const timelines = (window.__timelines ?? {}) as Record; - const rootCompositionId = - resolveRootCompositionElement()?.getAttribute("data-composition-id") ?? null; - for (const [compositionId, timeline] of Object.entries(timelines)) { - if (!timeline || typeof timeline !== "object" || compositionId === rootCompositionId) - continue; + for (const [compositionId, timeline] of sceneTimelines()) { const scene = sceneClockOf(compositionId, timeline); if (!scene) continue; const sourceTime = @@ -5291,9 +5296,10 @@ export function initSandboxRuntimeModular(): void { // Only clips holding a tween that renders for the first time on this seek need their authored style read. const firstRendered = firstRenders && tl ? clipsFirstRenderedIn(tl, tweensRenderedThrough.time, t) : null; - // An empty set means no tween draws for the first time: nothing to show, so no page-wide query. + // An empty set means no tween draws for the first time, and a page with no timeline has no tween: + // nothing to show, so no page-wide query. const hideClipsAgain = - firstRenders && firstRendered?.size !== 0 + firstRenders && firstRendered?.size !== 0 && (tl || sceneTimelines().length > 0) ? showHiddenClips(firstRendered ? (el) => firstRendered.has(el) : undefined) : null; // Scenes first, each paused on its own clock, so the root's pass skips them and its later