Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(player,studio): resolve root timeline from DOM instead of last key - #247

Merged
miguel-heygen merged 1 commit into
mainfrom
fix/root-timeline-detection
Apr 13, 2026
Merged

miguel-heygen merged 1 commit into
mainfrom
fix/root-timeline-detection

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

Problem

Bundled previews register the master composition alongside its sub-compositions in window.__timelines:

{
  main: GSAPTimeline(14s),
  intro: GSAPTimeline(1.5s),
  'scene2-4-canvas': GSAPTimeline(12.6s),
  'scene5-logo-outro': GSAPTimeline(3.2s),
}

Both @hyperframes/player's probe and @hyperframes/studio's useTimelinePlayer.getAdapter() selected the adapter with keys[keys.length - 1]. Object key ordering meant the last-registered sub-composition would win — so the player reported 3.2s as the video duration (from scene5-logo-outro) instead of the master's 14s, and play/pause/seek operated on that sub-composition instead of the full composition.

Fix

Resolve the root composition id from the DOM — the outermost [data-composition-id] element is the master:

const rootId = iframe?.contentDocument
  ?.querySelector("[data-composition-id]")
  ?.getAttribute("data-composition-id");
const key = rootId && rootId in win.__timelines ? rootId : keys[keys.length - 1];

This is authoritative — it uses the actual DOM structure rather than guessing by naming convention or registration order. Falls back to the last key when no [data-composition-id] element exists (standalone sub-composition previews drilled-into via the studio) so that flow keeps working.

Bonus

Also restores main/import entry points on @hyperframes/player's package.json to point at the compiled dist/ output. The src/ paths introduced in #238 broke workspace consumers that only receive the published tarball (the tarball excludes src/ via "files": ["dist"]), producing Module not found: Can't resolve '@hyperframes/player' on build.

Test plan

  • Verified player.duration and PlayerControls time display now report the master duration (14s) on a multi-composition bundled preview where the old code reported 3.2s
  • Confirmed play/pause/seek drive the master timeline rather than a sub-composition
  • Drill-into-composition view (single composition, no master) still detects duration via the last-key fallback

Bundled previews register a master composition alongside its sub-compositions
in `window.__timelines`, e.g. { main, intro, scene2, scene5 }. Both the
player's probe and studio's getAdapter() were using `keys[keys.length - 1]`
to pick the adapter, which returned whichever timeline was registered last.

That made the player report the final sub-composition's duration as the
video length (e.g. 3.2s instead of the master's 14s) and play/pause/seek
targeted that sub-composition instead of the full composition.

Look up the outermost `[data-composition-id]` element in the iframe DOM
and use its id to select the right timeline. Falls back to last-key when
no element is present (standalone sub-composition previews) so drill-down
views keep working.

Also restores `main`/`import` entry points on @hyperframes/player to
point at compiled dist output (the src/ paths broke workspace consumers
that only receive the published tarball).
@miguel-heygen
miguel-heygen merged commit f40447f into main Apr 13, 2026
13 checks passed
@miguel-heygen
miguel-heygen deleted the fix/root-timeline-detection branch April 13, 2026 21:15
miguel-heygen added a commit that referenced this pull request Apr 13, 2026
Ships the root-timeline resolution fix (#247), the SSR-safe player load,
and the captions import cleanup.
miguel-heygen added a commit that referenced this pull request Apr 13, 2026
* fix(studio): load @hyperframes/player lazily to support SSR

Player.tsx had a bare `import "@hyperframes/player"` at module scope. The
player package registers a class that extends HTMLElement as a side effect,
and HTMLElement doesn't exist in a Node server runtime. Any consumer that
imported from @hyperframes/studio during server-side rendering (e.g. the
Next.js App Router evaluating a client component for SSR) threw
`HTMLElement is not defined`.

Move the import inside the mount effect via dynamic `import(...)` so it
only runs in the browser, and wire up a cancellation flag and deferred
cleanup so a fast unmount doesn't leak listeners or DOM nodes.

* fix(studio): remove .js extensions from captions-internal imports

The captions module imported sibling files as `./types.js` and
`./parser.js`. That's legal ESM TypeScript, but Turbopack (and other
bundlers) refuse to resolve those specifiers against .ts files when the
package is consumed from node_modules — the rest of @hyperframes/studio
uses extensionless imports for that reason.

Align captions with the rest of the codebase so the package builds
without bundler-specific configuration in consumers.

* chore: release @hyperframes/[email protected] and @hyperframes/[email protected]

Ships the root-timeline resolution fix (#247), the SSR-safe player load,
and the captions import cleanup.
miguel-heygen added a commit that referenced this pull request Apr 13, 2026
Coordinated minor bump across all published packages:

- @hyperframes/cli        0.2.5 → 0.3.0
- @hyperframes/core       0.2.5 → 0.3.0
- @hyperframes/engine     0.2.5 → 0.3.0
- @hyperframes/player     0.2.7 → 0.3.0
- @hyperframes/producer   0.2.5 → 0.3.0
- @hyperframes/studio     0.2.9 → 0.3.0

Carries the two studio fixes in this PR (SSR-safe player load,
captions-internal imports) plus the DOM-based root timeline resolution
from #247.
miguel-heygen added a commit that referenced this pull request Apr 13, 2026
Coordinated minor bump across all published packages.

- @hyperframes/cli        0.2.5 → 0.3.0
- @hyperframes/core       0.2.5 → 0.3.0
- @hyperframes/engine     0.2.5 → 0.3.0
- @hyperframes/player     0.2.7 → 0.3.0
- @hyperframes/producer   0.2.5 → 0.3.0
- @hyperframes/studio     0.2.9 → 0.3.0

Cuts a single versioned line under studio + player fixes that landed in
#247 and #248 (root timeline DOM resolution, SSR-safe player load, and
captions-internal import cleanup).
dahans-msft2 pushed a commit to dahans-msft2/hyperframes that referenced this pull request Aug 6, 2026
heygen-com#247)

Bundled previews register a master composition alongside its sub-compositions
in `window.__timelines`, e.g. { main, intro, scene2, scene5 }. Both the
player's probe and studio's getAdapter() were using `keys[keys.length - 1]`
to pick the adapter, which returned whichever timeline was registered last.

That made the player report the final sub-composition's duration as the
video length (e.g. 3.2s instead of the master's 14s) and play/pause/seek
targeted that sub-composition instead of the full composition.

Look up the outermost `[data-composition-id]` element in the iframe DOM
and use its id to select the right timeline. Falls back to last-key when
no element is present (standalone sub-composition previews) so drill-down
views keep working.

Also restores `main`/`import` entry points on @hyperframes/player to
point at compiled dist output (the src/ paths broke workspace consumers
that only receive the published tarball).
dahans-msft2 pushed a commit to dahans-msft2/hyperframes that referenced this pull request Aug 6, 2026
…m#248)

* fix(studio): load @hyperframes/player lazily to support SSR

Player.tsx had a bare `import "@hyperframes/player"` at module scope. The
player package registers a class that extends HTMLElement as a side effect,
and HTMLElement doesn't exist in a Node server runtime. Any consumer that
imported from @hyperframes/studio during server-side rendering (e.g. the
Next.js App Router evaluating a client component for SSR) threw
`HTMLElement is not defined`.

Move the import inside the mount effect via dynamic `import(...)` so it
only runs in the browser, and wire up a cancellation flag and deferred
cleanup so a fast unmount doesn't leak listeners or DOM nodes.

* fix(studio): remove .js extensions from captions-internal imports

The captions module imported sibling files as `./types.js` and
`./parser.js`. That's legal ESM TypeScript, but Turbopack (and other
bundlers) refuse to resolve those specifiers against .ts files when the
package is consumed from node_modules — the rest of @hyperframes/studio
uses extensionless imports for that reason.

Align captions with the rest of the codebase so the package builds
without bundler-specific configuration in consumers.

* chore: release @hyperframes/[email protected] and @hyperframes/[email protected]

Ships the root-timeline resolution fix (heygen-com#247), the SSR-safe player load,
and the captions import cleanup.
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.

1 participant