Skip to content

fix(studio): activate a composition at any path, not just compositions/ - #3547

Merged
miguel-heygen merged 1 commit into
heygen-com:mainfrom
rajanpanth:fix/studio-activate-comp-any-path
Aug 29, 2026
Merged

fix(studio): activate a composition at any path, not just compositions/#3547
miguel-heygen merged 1 commit into
heygen-com:mainfrom
rajanpanth:fix/studio-activate-comp-any-path

Conversation

@rajanpanth

Copy link
Copy Markdown
Contributor

What

Fixes #3543. Selecting a composition in the Comps panel now actually switches the canvas and timeline to it, for any path, not only files under compositions/.

Why

useCompositionStack's activation effect ran an if/else-if chain:

if (activeCompositionPath === "index.html") { /* master */ }
else if (activeCompositionPath && activeCompositionPath.startsWith("compositions/")) { /* push level */ }
else if (!activeCompositionPath) { /* master */ }

A path like parts/part-1.html is not index.html, does not start with compositions/, and is not falsy, so it matched no branch at all and the effect did nothing. The Comps panel still highlighted the row and the URL hash still updated, but the stack kept the master mounted: the canvas and timeline stayed on index.html, and, as the issue notes, any edit made there landed in the root file instead of the part.

The reporter's setup is the case that exposes it, a generated multi-part build with index.html plus parts/part-1.html through parts/part-4.html. Nothing requires a project's compositions to live under compositions/; the server already serves any of them (GET /api/projects/<id>/preview/comp/parts/part-1.html returns the right page, per the issue).

How

Replaced the prefix test with a plain truthiness check, so the root composition stays on the master level and every other path pushes its own level. The final else now covers null/empty.

Label derivation is unchanged (replace(/^compositions\//, "").replace(/\.html$/, "")), which is the same expression CompositionsTab uses for the panel's own card labels, so a compositions/-relative project's labels are byte-identical to before and parts/part-1.html reads as parts/part-1.

Test plan

  • Unit tests added/updated
  • Manual testing performed (see note)
  • Documentation updated (if applicable)

Extended useCompositionStack.test.tsx with 6 cases: a parameterised set asserting a level is pushed with the right id and previewUrl for compositions/scene-a.html (the previously-working case, as a regression guard), parts/part-1.html (the issue's case), a bare chapter-2.html, and a deep a/b/c/deep.html; plus a label assertion and a guard that index.html still leaves the master alone.

Mutation-tested: reverting only useCompositionStack.ts and keeping the tests fails the three non-compositions/ cases (stack length 1 instead of 2), while the compositions/ and index.html cases still pass, which is exactly the reported split. 8/8 pass with the fix.

I did not reproduce this in a running Studio: it needs a multi-part project and a live preview server, and the mechanism is fully determined by this effect's branch conditions, which the tests cover directly. The issue author already confirmed the runtime symptom headlessly with puppeteer.

lefthook run pre-commit all green (lint, format, fallow, typecheck, filesize, largefiles, tracked-artifacts, commitlint).

The Comps panel sets activeCompositionPath to the selected file, but
useCompositionStack's effect only pushed a stack level when that path
started with compositions/. A project laying its comps out anywhere
else, for example a generated multi-part build with parts/part-1.html
next to the root index.html, matched no branch at all: the row
highlighted and the URL hash updated while the stack silently kept the
master mounted, so the canvas and timeline stayed on index.html and any
edit landed in the root file instead of the part.

Replaced the prefix test with a plain truthiness check, so the root
stays on the master level and every other path pushes its own level.
Label derivation is unchanged, matching CompositionsTab's own
convention.

@miguel-heygen miguel-heygen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed end to end at adf9b0cceeb561fd5424e8cad73b781f5383a9ef.

Strengths

  • packages/studio/src/components/nle/useCompositionStack.ts:108 preserves the explicit root-composition path while :111-124 correctly routes every other truthy project-relative composition path through the existing encoded preview contract.
  • packages/studio/src/components/nle/useCompositionStack.test.tsx:68-109 covers the prior compositions/ behavior, the reported parts/ case, bare/deep paths, label preservation, and the root guard.

Verification

  • Focused Vitest: 8/8 passed.
  • Studio TypeScript: tsc --noEmit passed.
  • Synthetic local PR fixture, desktop + mobile: selecting parts/part-1.html updated the URL hash, preview iframe (PART ONE CANVAS), composition breadcrumb, and timeline (Part Title) together.
  • Browser console: no errors. The optional local FFmpeg/thumbnail probes were unavailable/timed out in the synthetic environment; the target composition preview endpoint loaded and rendered correctly.
  • Required GitHub CI: 8/8 passed; no review comments or unresolved threads.

Verdict: APPROVE
Reasoning: The fix addresses the no-branch path bug without changing root behavior, is directly regression-tested, passes the relevant local and required CI gates, and the exact user-facing selection flow works at the reviewed SHA.

— Magi

@miguel-heygen
miguel-heygen merged commit 7bd0782 into heygen-com:main Aug 29, 2026
45 checks passed
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.

Studio: selecting a non-root composition file in the Comps panel keeps showing the master composition

2 participants