mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-31 19:06:14 +00:00
* perf(studio): coalesce timeline zoom to one write per frame and zoom to a range * perf(studio): coalesce timeline zoom to one write per frame and zoom to a range Every zoom input goes through one owner that writes the store once per frame. zoomTimelineToRange eases to a time range; the zoom buttons use it. * feat(studio): zoom-to-range reports done or cancelled and is not a person zoom * perf(studio): place clips in percent of a one-second layer so zoom restyles nothing A zoom step rewrites one width per row; clip thumbnails start decoding at rest. * perf(studio): a zoom step re-renders only clips whose drawing it changes Clip handlers read the lane callbacks when pressed, so a skipped clip never acts on a stale zoom. * refactor(studio): move at-rest waveform and thumbnail work to its own change * perf(studio): draw waveforms and decode thumbnails when the timeline rests Waveforms draw only the visible span of a clip. A zoom or scroll step stretches what is drawn until the timeline rests. * test(studio): fade handles measure the clip only while they show * test(studio): waveforms draw their visible span and redraw at rest * feat(studio): export currentTimelineRange, the inverse of zoomTimelineToRange * perf(studio): zoom gestures scale the drawn timeline and lay it out once at rest No layout or render per gesture frame; past 0.67x or 2x the preview is laid out and continues. * perf(studio): rebase a zoom preview only past 0.5x or 4x Clips stay mounted half a viewport each side, so a zoom-out preview to 0.5x shows no gaps. * fix(studio): keep the zoom readout, edges and Fit right during a preview The slider and its percent follow a zoom while it is previewed. A zoom in from below Fit no longer jumps when it lands: the preview now uses the width the timeline will really have. A zoom-out lays out as soon as it would show time with no clips mounted, so edges never show empty strips. Fit, a press or a scroll on the timeline end a pending zoom first. An older abort no longer cancels a newer zoom-to-range, and an instant one lands on the next frame instead of inside its caller. Range selection and gap tints scale with the preview. * fix(studio): keep a pending zoom when the timeline re-registers its view The timeline registers its scroll view again whenever clips are added or removed, which used to drop a zoom still waiting to be laid out. Now only a real unmount drops it, and unmounting one timeline leaves a newer timeline's registration alone. * refactor(studio): share the zoom geometry helpers the timeline already has Zoom scales come from getTimelinePixelsPerSecond, the range margins from one helper, and a zoom step starts from the same playhead anchor every zoom uses. The clip minimum width is named and shared with the drag ghost. * fix(studio): keep a zoom preview through scrolls and past the content end A scroll during a zoom preview, including the one a laid-out zoom makes itself, used to lay the next preview out at once, so a pinch or eased zoom fell back to a full layout every frame. A scroll now only rescales what it mounted. Zooming out also lays out before the preview shows past where the ruler and beat lines are drawn, not only past the mounted clips. * fix(studio): keep a moved audio clip's waveform drawn where it shows A waveform now draws the stretch of its clip that the shared strip tracker reports near the screen, the tracker the filmstrip already uses. It follows scrolls, resizes and moves (a drag or ripple that shifts the clip), so a moved clip no longer shows a blank stretch, and a zoom redraws it once. * perf(studio): lay a zoom-out step out once, at its start An eased zoom-out (a Zoom out click, or zooming out to a range) used to lay out partway, once the shrinking preview reached time with nothing mounted, and again at the end. It now lays the target out first and eases by scaling that layout down from the old view, so each click lays out once. Each eased frame also draws its preview in that frame. * fix(studio): keep previewing a zoom at the content's end and over new rows The preview now lays out only once it would show past the window the timeline draws clips, ruler ticks and beat lines in, with two pixels of slack, so rounding at the content's end no longer lays out every frame. Rows mounted into the timeline during a preview are scaled before they paint. * fix(studio): redraw a waveform when its clip's width changes or it moves A waveform drawn whole kept its old bars, stretched, after a zoom changed its clip's width; it now redraws on any resize. A long clip also marks its undrawn ends for the strip tracker, so a move without a scroll (a ripple, a drop, an undo) re-measures it instead of leaving a blank stretch. * perf(studio): skip the layout when a zoom preview ends on its own scale An eased zoom-out lays its target out first, so its preview ends on the scale already laid out. Landing it now only sets the scroll, instead of writing the same zoom again and counting the person's zoom twice. * fix(studio): measure filmstrips and waveforms after a zoom preview ends The strip tracker measured clip boxes while a zoom preview scaled them, so after a Zoom out click a long clip's filmstrip or waveform could stay blank across part of the screen. Reads during a preview now wait, and the tracker re-measures every strip once the preview ends. A zoom-out lays its target out in its first frame rather than inside the caller, and only when the new view holds the old one. * perf(studio): watch a waveform's undrawn ends only when it has some A clip drawn whole has no undrawn ends, so it no longer mounts the two empty markers the strip tracker watches; with hundreds of short audio clips that saved an observer entry each. * fix(studio): measure strips each time a zoom preview is laid out A zoom-out lays its target out partway into the ease, after which the strip tracker kept the stretch it measured before the zoom, so a long filmstrip or waveform went partly blank near the end of the ease. Every layout of a preview now re-measures the strips. * docs(studio): keep the zoom-to-range doc comment within four lines * docs(studio): keep the zoom files' comment share at or under main's The comment ratchet failed on ten files this branch touches. Comments are cut or folded into one line; no code changes.
@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
Related packages
@hyperframes/core— types and parsers used by the editorhyperframes— CLI that serves the studio viahyperframes preview