Skip to content

fix: hold external sub-compositions through host duration#917

Merged
miguel-heygen merged 1 commit into
heygen-com:mainfrom
puneetdixit200:fix/subcomposition-duration-hold
May 17, 2026
Merged

fix: hold external sub-compositions through host duration#917
miguel-heygen merged 1 commit into
heygen-com:mainfrom
puneetdixit200:fix/subcomposition-duration-hold

Conversation

@puneetdixit200
Copy link
Copy Markdown
Contributor

Summary

  • Keep data-composition-src hosts visible for their authored data-duration even when the child GSAP timeline is
    shorter.
  • Preserve legacy live-duration clamping for non-external child compositions.
  • Add a regression test covering a 3s external host slot with a 1s child timeline.

Verification

  • bun run --filter @hyperframes/core test:hyperframe-runtime-ci
  • bun run --filter @hyperframes/core test
  • bun run --filter @hyperframes/core typecheck
  • bunx oxfmt --check packages/core/src/runtime/init.ts packages/core/src/runtime/init.test.ts
  • `bunx oxlint packages/core/src/runtime/init.ts packages/core/src/runtime

Copilot AI review requested due to automatic review settings May 17, 2026 09:45
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) for data-composition-src hosts.
  • 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.

Comment on lines +1338 to +1349
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
) {
Comment on lines +198 to +205
initSandboxRuntimeModular();
await new Promise<void>((resolve) => window.setTimeout(resolve, 0));

const player = (
window as Window & {
__player?: { renderSeek: (timeSeconds: number) => void };
}
).__player;
Copy link
Copy Markdown
Collaborator

@miguel-heygen miguel-heygen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. Stripping phase preserves data-duration as private data-hf-authored-duration before removing it
  2. resolveDurationForElement() reads the private attr (defaults to includeAuthoredTimingAttrs: true) → returns the authored 3s
  3. The new !usesExternalCompositionSlot guard skips the Math.min(duration, liveDuration) clamp for external hosts
  4. data-composition-src is never stripped, so rawNode.hasAttribute("data-composition-src") is reliable at this point in the lifecycle
  5. 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.

@miguel-heygen miguel-heygen merged commit 551607e into heygen-com:main May 17, 2026
43 checks passed
miguel-heygen added a commit that referenced this pull request May 17, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants