Files
Miguel Ángel 707f9a44c3 feat(studio): the first clip on an empty main track starts at 0 (#4099)
* feat(studio): a clip dropped onto an empty main track lands at 0

AD96 PR 1: the existing-clip-drag path (timelineCollision.ts) forces
start=0 for a clip landing on the main track for the first time, since
a track with one clip has no leading gap by convention. The file-drop
path is deferred to a follow-up once #4067 clears its file.

* refactor(studio): share the AD96 main-track drag test setup

Fallow flagged the two new tests as a 22-line clone; factor the shared
DraggedClipState builder into one helper.

* fix(studio): don't retime the rest of a multi-selection on the main-track snap

Adversarial review caught it: forcing the grabbed clip's previewStart to 0
fed straight into resolveMultiSelection's group delta, silently dragging
every other selected clip's time on a vertical-only gesture. Skip the
magnetic snap for a multi-selection drag; generalize
resolveMainTrackDropStart to also serve the upcoming file-drop path
(originTrack/isAudio instead of a full dragged element).

* feat(studio): dropping a new asset or file onto an empty main track lands at 0

AD96 PR 1, file-drop half (unblocked now that #4067 merged): the same
magnetic snap the existing-clip-drag path got now also covers a
catalog-asset drop and an OS file drop, applied once at the single DOM
drop dispatcher (timelineDragDrop.ts) so a multi-file batch still
sequences correctly after the snapped first placement. Composition and
block drops are left untouched — out of scope, per the original scout.

* refactor(studio): share the drag-over-then-drop test act() block

Fallow flagged the repeated act(() => { dragOver; drop }) pattern as
two clone groups; factor it into one dropAt helper.

* fix(studio): a mixed audio+video file drop always snaps, regardless of order

Adversarial review caught it: classifying a multi-file drop by only
the first file's kind meant [song.mp3, clip.mp4] and [clip.mp4,
song.mp3] snapped differently, purely from Finder selection order.
Classify the whole batch as audio only when EVERY file is audio, and
add the missing all-audio and mixed-order test coverage.

* refactor(studio): tighten main-track snap comment and dedupe file-drop array

* fix(studio): snap a top-gutter track-insert that lands on literal track 0

Adversarial review caught it: dragging a clip into the top insert
gutter creates a fractional lane that's never literal track 0 during
preview, but normalizeToZones can renumber it to literal 0 on commit
when the topmost visual lane was non-zero (main track genuinely
empty). That persisted start was never routed through
resolveMainTrackDropStart. Apply the same snap at commit time, using
the real post-normalize track, guarded against multi-selection the
same way the drag-preview path already is.

* fix(studio): compute the track-insert snap once so the z-sync sees it too

The previous fix snapped only the persisted edit after the fact, so the
stacking sync still read the unsnapped start. Resolve the snap inside
buildTrackInsertEdits from the final renumbered track and feed the one
value to both the edit and the z-sync candidate.

* refactor(studio): drop internal ticket codes from main-track snap comments and test names

* refactor(studio): simplify main-track snap

* fix(studio): snap the first main-track clip on its final lane and append new drops

One landing rule now runs in both the drag preview and the commit, after the
top-gutter renumber and the expanded-child-to-host conversion, so the ghost
matches the commit. New file and asset drops onto a filled main track land
after its last clip. The unused excludeKey check is gone.

* refactor(studio): keep media-panel drops out of this change

* refactor(studio): simplify main-track landing

* test(studio): pin the main-track snap on a plain lane change at commit level
2026-09-18 16:07:29 -04:00
..
2026-09-18 11:11:40 -04:00
2026-03-21 22:43:56 -07:00

@hyperframes/studio

Browser-based composition editor UI for Hyperframes. Provides a visual timeline, code editor, and live preview for building video compositions.

Install

npm install @hyperframes/studio

What it does

The studio is a React application with:

  • Visual timeline — drag, resize, and arrange elements on tracks
  • Code editor — edit HTML and GSAP scripts with CodeMirror (syntax highlighting, autocomplete)
  • Live preview — see changes in real time as you edit
  • Composition inspector — view and modify element properties

Development

The studio is embedded in the hyperframes preview command. To develop the studio UI itself:

cd packages/studio
bun run dev        # Start Vite dev server
bun run build      # Build for production
bun run typecheck  # Type-check

Tech stack

  • React 19, Zustand 4/5 (state management; development uses Zustand 5)
  • CodeMirror 6 (editor)
  • Tailwind CSS (styling)
  • Vite (bundler)
  • Phosphor Icons

Documentation

Full documentation: hyperframes.heygen.com/packages/studio