fix(runtime): hold external sub-compositions in render mode

PR #917 fixed visibility clamping for external sub-compositions in
preview mode by checking data-composition-src. However, the producer's
htmlCompiler strips that attribute during inlining without setting the
data-composition-file marker that the core bundler sets. This caused
the runtime to still clamp duration to Math.min(authored, live) in
rendered output.

Two fixes:
- Runtime: also check data-composition-file (set by the core bundler
  after inlining)
- Producer: set data-composition-file before removing
  data-composition-src, matching the core bundler's behavior

Closes #911

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Miguel Ángel
2026-05-17 15:30:23 +00:00
co-authored by Claude Sonnet 4.6
parent 3f976d454c
commit 2b46565c65
3 changed files with 41 additions and 1 deletions
+37
View File
@@ -211,6 +211,43 @@ describe("initSandboxRuntimeModular", () => {
expect(child.style.visibility).toBe("visible");
});
it("keeps compiled 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-file", "compositions/sub.html");
child.setAttribute("data-start", "0");
child.setAttribute("data-duration", "3");
child.innerHTML = '<div id="hold-marker">HOLD ME</div>';
root.appendChild(child);
(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();
player?.renderSeek(2);
expect(child.style.visibility).toBe("visible");
});
it("pads the root timeline to the authored composition schedule before seeking visibility", () => {
const root = document.createElement("div");
root.setAttribute("data-composition-id", "main");
+3 -1
View File
@@ -1335,7 +1335,9 @@ export function initSandboxRuntimeModular(): void {
}
}
const usesExternalCompositionSlot = rawNode.hasAttribute("data-composition-src");
const usesExternalCompositionSlot =
rawNode.hasAttribute("data-composition-src") ||
rawNode.hasAttribute("data-composition-file");
// Generic child compositions retain legacy behavior and respect both
// the authored parent clip window and the live child timeline duration.