diff --git a/packages/core/src/runtime/init.test.ts b/packages/core/src/runtime/init.test.ts
index ab054e557..03e7a1ad1 100644
--- a/packages/core/src/runtime/init.test.ts
+++ b/packages/core/src/runtime/init.test.ts
@@ -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 = '
HOLD ME
';
+ root.appendChild(child);
+
+ (window as Window & { __timelines?: Record }).__timelines = {
+ main: createMockTimeline(3),
+ sub: createMockTimeline(1),
+ };
+
+ initSandboxRuntimeModular();
+ await new Promise((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");
diff --git a/packages/core/src/runtime/init.ts b/packages/core/src/runtime/init.ts
index 68d100329..5c677959e 100644
--- a/packages/core/src/runtime/init.ts
+++ b/packages/core/src/runtime/init.ts
@@ -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.
diff --git a/packages/producer/src/services/htmlCompiler.ts b/packages/producer/src/services/htmlCompiler.ts
index ca5dec6a3..1eba3fc30 100644
--- a/packages/producer/src/services/htmlCompiler.ts
+++ b/packages/producer/src/services/htmlCompiler.ts
@@ -689,6 +689,7 @@ function inlineSubCompositions(
host.innerHTML = contentDoc.toString();
}
+ host.setAttribute("data-composition-file", srcPath);
host.removeAttribute("data-composition-src");
// Set explicit pixel dimensions on the host element so children using