fix(studio): patch runtime clock when document duration exceeds player duration

When the runtime player's clock duration is smaller than the effective
timeline (computed from data-start + data-hf-authored-duration on
sub-composition elements), the seek is clamped too early and sub-compositions
beyond the clock duration are invisible.

Detect this mismatch in getAdapter() and pad the root GSAP timeline to the
document duration, then force a timeline rebind so the clock updates.
This commit is contained in:
Miguel Ángel
2026-05-18 18:10:43 -04:00
parent 1a4e534d0f
commit ca6069763d
2 changed files with 26 additions and 0 deletions
@@ -43,6 +43,27 @@ import {
shouldMutePreviewAudio,
} from "../lib/timelineIframeHelpers";
function patchRuntimeClockDuration(win: IframeWindow, targetDuration: number): void {
try {
const rootEl = win.document?.querySelector("[data-composition-id]");
const rootId = rootEl?.getAttribute("data-composition-id");
const tl = rootId && win.__timelines?.[rootId];
if (tl && typeof tl.duration === "function" && tl.duration() < targetDuration) {
const tlWithTo = tl as typeof tl & {
to?: (t: object, v: { duration: number }, p: number) => void;
};
if (typeof tlWithTo.to === "function") {
tlWithTo.to({}, { duration: 0 }, targetDuration);
}
}
if (typeof win.__hfForceTimelineRebind === "function") {
win.__hfForceTimelineRebind();
}
} catch {
// cross-origin or missing runtime — non-fatal
}
}
// ---------------------------------------------------------------------------
// Hook
// ---------------------------------------------------------------------------
@@ -119,6 +140,10 @@ export function useTimelinePlayer() {
const playerAdapter =
win.__player && typeof win.__player.play === "function" ? win.__player : null;
if (getAdapterDuration(playerAdapter) > 0) {
const docDur = readTimelineDurationFromDocument(iframe?.contentDocument);
if (docDur > 0 && docDur > playerAdapter!.getDuration()) {
patchRuntimeClockDuration(win, docDur);
}
return playerAdapter;
}
@@ -57,4 +57,5 @@ export type IframeWindow = Window & {
__timeline?: TimelineLike;
__timelines?: Record<string, TimelineLike>;
__clipManifest?: ClipManifest;
__hfForceTimelineRebind?: () => void;
};