feat(studio): add ripple, roll, slip and slide trim tools - #3341
Open
miguel-heygen wants to merge 3 commits into
Open
feat(studio): add ripple, roll, slip and slide trim tools#3341miguel-heygen wants to merge 3 commits into
miguel-heygen wants to merge 3 commits into
Conversation
This was referenced Aug 19, 2026
The timeline could only trim one clip's own in/out, so every re-edit of a multi-clip track left a gap or an overlap to close by hand. Four tools now own the standard NLE edit operations, each committing as one atomic, single-undo batch through the existing group-resize persist. - Ripple (T): trim an edge, every later clip on the track follows. - Roll (Shift+T): move the cut between two clips; nothing downstream moves. - Slip (Y): change which part of the source plays; nothing moves. - Slide (Shift+Y): move a clip; its neighbours absorb the move. All four are lane-scoped, clamped by available source media and the minimum clip duration, snap the edge that actually moves, and refuse up front when the tool cannot act on the clip (a roll with no clip across the cut, a slip on generated pixels) with a message that says which. Two adjacent cleanups came with it: the blocked-edit toast now reads the intent it was already being handed instead of ignoring it, and the duplicated remove-element POST behind the two delete paths moved into one helper.
miguel-heygen
force-pushed
the
feat/studio-trim-tools
branch
from
August 20, 2026 17:32
16d23c6 to
26962f8
Compare
House style is a comma, colon, or full stop. It usually shortens the sentence, which is the real win in a tooltip read at a glance mid-gesture. Slip's hint gets rewritten rather than repunctuated. "Change which part of the source plays" describes the mechanism and left readers guessing what moves; "slide the media behind it" names the thing being dragged and says outright that the clip stays put. The two thumbnail tooltips and the razor tooltip predate this branch but are the same copy on the same toolbar, so they change with it.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
…Studio A new page per operation, each with a recording of the gesture and the rule it follows, plus the question that picks between them: what has to stay fixed. Slip leads with the analogy rather than the mechanism, because "changes which part of the source plays" is the sentence that left readers guessing what moves. It is sliding a photo behind a picture frame. The timeline page now hands off where trimming is introduced, and the four keys join the editing table on the shortcuts page. Studio itself gets the link. It sits at the top of the shortcuts panel, which is the editor's only help surface and is already where someone goes when they cannot remember what a tool does. Top, not bottom: the panel scrolls, and a link below the fold is a link nobody finds. It points at the Studio tab index rather than a deep link, because pages move and a dead link inside the editor is worse than a general one. Media lives in docs/images/studio (gitignored, served from the CDN) and needs scripts/upload-docs-images.sh before the page renders.
miguel-heygen
force-pushed
the
feat/studio-trim-tools
branch
from
August 20, 2026 21:02
07e0d78 to
5e86ec3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Four dedicated trim tools for the Studio timeline, alongside Select and Razor:
T⇧TY⇧YCloses #3285 (except the optional FCP-style precision view — see "Not covered").
Why
Timeline trim only ever changed the grabbed clip's own in/out. On a track with
more than one clip that means every trim opens a gap or an overlap that has to
be closed by hand, and there is no single action for "trim this and shift the
rest to match". Roll, slip and slide had no equivalent at all.
Demo
Recorded against a real Studio on a track of
A 0-8, B 8-12, C 12-16(plus asub-composition on a second lane). Every clip below is the actual pointer
gesture, not a mock-up.
Ripple — drag A's out point right; B and C follow, and the second lane is
untouched.
Roll — drag the A|B cut; A grows by exactly what B gives up and C never
moves.
Slide — drag B along the track; A stretches and C shortens to absorb it, so
the track keeps its total length.
Slip has no motion to record — that is the whole point of it. On the
sub-composition clip it moved the in point
3s → 4.5swhilestartanddurationstayed at4 + 6. On a clip with no source at all it refuses upfront and says why, instead of starting a gesture that does nothing:
How
timelineTrimOps.tsis pure math with no store or DOM in it:resolveTrimPlandecides which clips an operation touches (and returns nullwhen the gesture is impossible),
resolveTrimDeltaBoundssays how far it maytravel,
applyTrimDeltaproduces the per-clip patches. Semantics follow FCP /Premiere: a head ripple keeps the clip's start and pulls the lane in
behind it, which is what makes it a ripple rather than a plain trim.
timelineTrimSession.tsis the gesture layer. A trim session is aTimelineGroupResizeSession, so the whole existing pipeline downstream —projection rendering, escape-cancel, and the atomic single-undo commit through
commitTimelineGroupResize— is reused unchanged. Only the preview mathdiffers. No new persistence plumbing.
available source media and the minimum clip duration, and snap the edge that
actually moves, with every clip that rides along excluded from the snap grid.
reusing the existing blocked-edit report — which was already being handed an
intent it threw away.
the keyboard handler and the shortcuts panel.
Two adjacent cleanups came along because the repo gates flagged them once these
files were in scope: the duplicated
remove-elementPOST behind the two deletepaths moved into one helper, and the timeline shell's inline style moved into
the theme module (both files were at the 600-line cap).
Test plan
Unit tests added/updated — 43 new tests: the trim math (bounds, clamps,
playback-rate scaling, adjacency invariants for every op), the session
layer (lane scoping, capability refusal, snapping), and six end-to-end
gesture tests through the drag hook asserting exactly what gets persisted.
Manual testing performed — drove a real Chrome against Studio and read
back the project file after each gesture rather than trusting the UI.
Each gesture dragged +2s:
A 0+10 B 10+4 C 14+4— second lane untouchedA 0+8 B 8+2 C 10+4— B's start pinned, lane closed behindA 0+10 B 10+2 C 12+4— C never movedA 0+10 B 10+4 C 14+2— track length unchanged4+6unchanged, in point3 → 4.5One
⌘Zafter a ripple restored the whole lane, confirming the batch is asingle undo step. All four shortcuts (
T,⇧T,Y,⇧Y),Vback toSelect, and the toolbar buttons were driven through the real UI.
Full
packages/studiosuite green (4308 tests), typecheck, oxlint, oxfmt.Not covered
is not in this PR; it is a separate component and follows on its own branch.
clip's own lane, matching the existing "close gap" tooling.
data-playback-startonto clips that have no source at all (adivgetsin=0on an out-trim,in=2on a head-trim). The trim tools here do not dothat — they only write an in point on a clip that has one — but fixing the
shared plain-resize path changes what every head-trim writes and wants its own
change with its own regression tests. The root is that the runtime clip
manifest reports
playbackStart: 0for every element, so nothing downstreamcan tell "no in point" from "in point at zero". Happy to pick it up next.
Size
~1.6k changed lines against the 1k guideline, about 900 of which are the new
pure-math and gesture modules and ~450 are tests. The four operations share one
plan/bounds/apply module and one session type, so splitting them into two PRs
would cut that module in half and leave the first half untestable on its own.
Flagging rather than hiding it.