mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
fix: hold external sub-compositions through host duration (#917)
This commit is contained in:
@@ -165,6 +165,52 @@ describe("initSandboxRuntimeModular", () => {
|
|||||||
expect(child.style.visibility).toBe("hidden");
|
expect(child.style.visibility).toBe("hidden");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps external composition hosts visible through their authored duration", 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);
|
||||||
|
|
||||||
|
const child = document.createElement("div");
|
||||||
|
child.setAttribute("data-composition-id", "sub");
|
||||||
|
child.setAttribute("data-composition-src", "compositions/sub.html");
|
||||||
|
child.setAttribute("data-start", "0");
|
||||||
|
child.setAttribute("data-duration", "3");
|
||||||
|
root.appendChild(child);
|
||||||
|
|
||||||
|
const template = document.createElement("template");
|
||||||
|
template.id = "sub-template";
|
||||||
|
template.innerHTML = `
|
||||||
|
<div data-composition-id="sub" data-width="1920" data-height="1080">
|
||||||
|
<div id="hold-marker">HOLD ME</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(template);
|
||||||
|
|
||||||
|
(window as Window & { __timelines?: Record<string, RuntimeTimelineLike> }).__timelines = {
|
||||||
|
main: createMockTimeline(3),
|
||||||
|
sub: createMockTimeline(1),
|
||||||
|
};
|
||||||
|
|
||||||
|
initSandboxRuntimeModular();
|
||||||
|
await new Promise<void>((resolve) => window.setTimeout(resolve, 0));
|
||||||
|
|
||||||
|
const player = (
|
||||||
|
window as Window & {
|
||||||
|
__player?: { renderSeek: (timeSeconds: number) => void };
|
||||||
|
}
|
||||||
|
).__player;
|
||||||
|
expect(player).toBeDefined();
|
||||||
|
expect(child.querySelector("#hold-marker")?.textContent).toBe("HOLD ME");
|
||||||
|
|
||||||
|
player?.renderSeek(2);
|
||||||
|
|
||||||
|
expect(child.style.visibility).toBe("visible");
|
||||||
|
});
|
||||||
|
|
||||||
it("pads the root timeline to the authored composition schedule before seeking visibility", () => {
|
it("pads the root timeline to the authored composition schedule before seeking visibility", () => {
|
||||||
const root = document.createElement("div");
|
const root = document.createElement("div");
|
||||||
root.setAttribute("data-composition-id", "main");
|
root.setAttribute("data-composition-id", "main");
|
||||||
|
|||||||
@@ -255,9 +255,9 @@ export function initSandboxRuntimeModular(): void {
|
|||||||
if (authoredEnd != null && !node.hasAttribute(AUTHORED_END_ATTR)) {
|
if (authoredEnd != null && !node.hasAttribute(AUTHORED_END_ATTR)) {
|
||||||
node.setAttribute(AUTHORED_END_ATTR, authoredEnd);
|
node.setAttribute(AUTHORED_END_ATTR, authoredEnd);
|
||||||
}
|
}
|
||||||
// Non-root compositions derive visible duration from timeline.
|
// Strip public timing attrs on non-root compositions after preserving
|
||||||
// Strip both data-duration AND data-end so the visibility system
|
// authored values privately. Runtime timing can still distinguish
|
||||||
// falls back to the GSAP timeline duration (parity with preview).
|
// authored host windows from live child timeline durations.
|
||||||
node.removeAttribute("data-duration");
|
node.removeAttribute("data-duration");
|
||||||
node.removeAttribute("data-end");
|
node.removeAttribute("data-end");
|
||||||
}
|
}
|
||||||
@@ -1335,9 +1335,18 @@ export function initSandboxRuntimeModular(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Composition hosts must respect both the authored parent clip window
|
const usesExternalCompositionSlot = rawNode.hasAttribute("data-composition-src");
|
||||||
// and the child composition's own live timeline duration.
|
|
||||||
if (duration != null && duration > 0 && liveDuration != null) {
|
// Generic child compositions retain legacy behavior and respect both
|
||||||
|
// the authored parent clip window and the live child timeline duration.
|
||||||
|
// External composition hosts render into an authored slot, so a shorter
|
||||||
|
// child timeline should hold its final state through that slot.
|
||||||
|
if (
|
||||||
|
duration != null &&
|
||||||
|
duration > 0 &&
|
||||||
|
liveDuration != null &&
|
||||||
|
!usesExternalCompositionSlot
|
||||||
|
) {
|
||||||
duration = Math.min(duration, liveDuration);
|
duration = Math.min(duration, liveDuration);
|
||||||
} else if ((duration == null || duration <= 0) && liveDuration != null) {
|
} else if ((duration == null || duration <= 0) && liveDuration != null) {
|
||||||
duration = liveDuration;
|
duration = liveDuration;
|
||||||
|
|||||||
Reference in New Issue
Block a user