fix: hold external sub-compositions through host duration#917
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Updates runtime visibility duration clamping so external composition hosts remain visible for their authored slot duration even when their child timeline is shorter, and adds a regression test for that behavior.
Changes:
- Adjust visibility window calculation to skip
Math.min(authoredDuration, liveTimelineDuration)fordata-composition-srchosts. - Refresh related inline comments around stripping timing attrs.
- Add a unit test covering external composition host visibility through authored duration.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/core/src/runtime/init.ts | Updates visibility-duration reconciliation for external composition hosts. |
| packages/core/src/runtime/init.test.ts | Adds test ensuring external composition hosts stay visible through authored duration. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const usesExternalCompositionSlot = rawNode.hasAttribute("data-composition-src"); | ||
|
|
||
| // 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 | ||
| ) { |
| initSandboxRuntimeModular(); | ||
| await new Promise<void>((resolve) => window.setTimeout(resolve, 0)); | ||
|
|
||
| const player = ( | ||
| window as Window & { | ||
| __player?: { renderSeek: (timeSeconds: number) => void }; | ||
| } | ||
| ).__player; |
miguel-heygen
left a comment
There was a problem hiding this comment.
Real bug, clean fix. APPROVE.
The problem: When a sub-composition is loaded via data-composition-src with a host data-duration="3" but its child GSAP timeline is only 1s long, the visibility system clamps to Math.min(3, 1) = 1 — the sub-composition vanishes 2s early. The parent authored a 3s window; the child's final frame should hold through it.
Why the fix is correct: The duration derivation path is:
- Stripping phase preserves
data-durationas privatedata-hf-authored-durationbefore removing it resolveDurationForElement()reads the private attr (defaults toincludeAuthoredTimingAttrs: true) → returns the authored 3s- The new
!usesExternalCompositionSlotguard skips theMath.min(duration, liveDuration)clamp for external hosts data-composition-srcis never stripped, sorawNode.hasAttribute("data-composition-src")is reliable at this point in the lifecycle- GSAP
{ paused: true }timelines hold their final state when seeked past their end — so the visual hold is correct
Legacy behavior preserved: Inline child compositions (without data-composition-src) still clamp via Math.min — the !usesExternalCompositionSlot condition is false for them.
Test coverage: The regression test sets up the exact scenario (3s host, 1s child, seek to t=2) and asserts visibility. Good.
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>
Summary
data-composition-srchosts visible for their authoreddata-durationeven when the child GSAP timeline isshorter.
Verification
bun run --filter @hyperframes/core test:hyperframe-runtime-cibun run --filter @hyperframes/core testbun run --filter @hyperframes/core typecheckbunx oxfmt --check packages/core/src/runtime/init.ts packages/core/src/runtime/init.test.ts