Skip to content

fix(cli): timeline split keeps a sub-composition's place in both halves - #5410

Merged
miguel-heygen merged 1 commit into
mainfrom
fix/timeline-split-media-start
Oct 10, 2026
Merged

miguel-heygen merged 1 commit into
mainfrom
fix/timeline-split-media-start

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator

What changes for a user

Splitting a sub-composition clip from the command line (hyperframes timeline split <clip> <time>) now keeps the second half's place in the scene. Before, only Studio's razor marked where each half starts inside the sub-composition (data-playback-start), so after a CLI split the second half restarted the scene from the beginning.

hyperframes timeline split '#host' 3
before:  <div id="host" ...>          <div id="host-2" ...>            (no in-point: host-2 replays the scene from 0 s)
after:   <div id="host" data-playback-start="0" ...>   <div id="host-2" data-playback-start="2" ...>

Why

Every split goes through splitElementInHtml, but whether the element is a sub-composition was decided by each caller and passed in as a flag. Only Studio's razor passed it; the single-split route and the CLI did not. The split function now reads it off the element itself (data-composition-src or data-composition-id, what the runtime treats as a composition), and the flag is gone, so every caller gets the same result. Studio's razor behaves as before. Freeze frame is unaffected: it only accepts a <video> and splits it with its linked media (usually audio), never a sub-composition.

Checked

  • New split-function tests: a host with data-composition-src or data-composition-id, split with no flag, gets data-playback-start 0 and 2 on its halves. The existing composition-offset test now uses a real host instead of the flag.
  • New CLI test: timeline split on a sub-composition host writes both in-points.
  • All of them fail on main. On Linux, three runs in a row: studio-server split, files and freeze-frame tests (182 pass), CLI timeline tests (104 pass), Studio razor tests (20 pass). Typecheck passes for studio-server, cli and studio.

No visible change

The only Studio edit drops the isComposition field from the razor's split request (razorSplitTransaction.ts, 2 lines), since the server now reads it off the element. The razor's result in the timeline is the same as before; the Studio razor tests pass unchanged.

@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 2061 (base branch 2061), smooth 1484 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (0)

@jrusso1020 jrusso1020 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.

Approve at 35a73998. Whether to stamp an in-point is now decided from the element itself, inside the one function every split goes through. The CLI, the single-split route and Studio's razor therefore get the same result.

Checked

  • Second half's offset. For a host with no in-point, data-playback-start is now chosen, the first half gets 0, and the clone gets currentTrim + firstDuration * rate. That comes from readMediaOffsetSeconds and readPlaybackRate, unchanged.
    • An authored data-playback-start or legacy data-media-start still wins, as before.
    • Splitting a second half again starts from its own stamped in-point.
    • A non-default data-playback-rate on the host is read from the attribute, so the CLI (which passes no rate) still advances correctly.
  • Parity with the razor. Studio set isComposition from kind === "composition", meaning a non-root element with data-composition-id (timelineDOM.ts, and core/runtime/timeline.ts). In the source file a host may carry only data-composition-src; the runtime adds the id when it loads it. The new selector [data-composition-src], [data-composition-id] covers both, so the razor's result is unchanged. The decision now lives only in splitElementInHtml, not in each caller.
  • Every caller.
    • The CLI's splitMutation (a2Shared.ts), the single-split route and the cut batch (files.ts) all reach the same branch.
    • freezeFrame.ts splits only a <video> and its linked media, so it never reaches the new branch.
    • No isComposition or stampPlaybackStart is left anywhere.
  • Nested sub-compositions. The function is file-agnostic, so a host inside a sub-composition's own file is split the same way. The runtime already honours data-playback-start on a nested host, as the razor relied on.
  • Undo. Nothing in the undo paths changes. timeline undo restores the file from its receipt's backupPath and version (a2Commands.ts:284-327), so the new attribute goes away with the rest of the split. The razor's request loses only the isComposition field.
  • The tests catch it. Swapping the new selector for one that never matches fails both new split tests, the existing composition-offset test, and the new CLI test (expected … to contain 'data-playback-start="0"').

Nit

  • The selector also matches the root composition (data-composition-id on the root). Splitting the root isn't a real operation, and a stamp there would be harmless, so this is only worth a comment if you want the selector to say "host".

Tests: studio-server split, files and freeze-frame pass 169/169, three runs in a row. CLI src/timeline passes 104/104 and Studio razorSplitTransaction 8/8.

No HeyGen calls and no real backend. I didn't run a real render of a split host.

— Rames

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 10, 2026
Merged via the queue into main with commit 165be85 Oct 10, 2026
159 of 160 checks passed
@miguel-heygen
miguel-heygen deleted the fix/timeline-split-media-start branch October 10, 2026 08:22
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.

2 participants