fix(studio): a snap the saved clip would miss no longer moves it - #5006
Conversation
Edit accuracy: accurate 1556 (base branch 1556), smooth 1458 of thoseThe gate passes. Quarantined, measured but not gated (0) Unstable (1)
|
jrusso1020
left a comment
There was a problem hiding this comment.
Approving. A snap is now applied only when the clip's edge will save onto the target, so what the drag shows is where the clip saves.
Move
- The fallback is safe:
nextMove.startalready comes out ofresolveTimelineMoverounded to the centisecond. So dropping a missed snap leaves a start that saves exactly where it is drawn. guideIfSavedchecks both saved edges against the target. A snap that put the clip's end on a target is judged by the end, as it should be.- The guide is now just whatever snap survived, and only when
placement.start === snap.start. That matches the oldtargetgate, and the check runs once.
Trim
- Both edges build
onTargetand only take it whenguideIfSavedkeeps the guide. An edge already on the target still keeps its guide, as before. - Dropping the
trimTargets.length > 0 || gridStep > 0guard changes nothing. With no targets the loop never runs, andnearestGridLinereturnsnullwhengridStepis not above 0, sosnapTimelineTimereturns{ time, target: null }.
Walk: in this head's Studio: timeline viewport gate job, timeline-snap-release.mjs reports savedStart: 0.32 and jumpPx: -0.015 with no failures. timeline-trim-snap.mjs also passes on the larger fixture.
Tests: the six touched or neighbouring files pass (204 tests). Of 6 mutations, 5 were caught:
- Applying a missed move snap: 2 failures.
- Keeping the guide on a missed move: 3.
- Applying a missed tail snap: 1.
- Drawing the guide whatever the placement: 1.
- Widening
guideIfSavedto 5 px: 5.
The survivor is applying a missed start-trim snap. For a top-level clip it changes nothing, because applyClipStartTrimDelta already rounds the start with roundTimelineTime. That is what the body says ("only changes the guide").
Non-blocking
- On a nested clip whose host starts off the centisecond grid (the
parentCompositionStart: 1/30case),savedClipEdgesroundsstart - offset, notstart. So the pointer's own start can still save a few px from where it is drawn at deep zoom, with or without a snap. That predates this PR, and the same goes for a start trim on such a clip. It might be worth a follow-up so the fallback rounds in the clip's own time. - When the nearest target misses, the move does not try the next target. That seems right to me, since the next target is a different snap than the one the guide would have shown.
— Rames
What
Two review follow-ups from #4986:
Why
#4986 dropped the guide for such snaps but still applied them, so the clip sat flush during the drag and jumped on release (reported in both reviews of #4986). Without the snap it previews and saves at the same place.
Related work
Refs #4986.
How
computeDragPreviewchecks the move's snap againstguideIfSavedright aftersnapMoveToTargets; a missed target falls back to the pointer's start. The guide is then whatever snap survived, so the check runs once.computeResizePreviewbuilds the snapped trim (onTarget) and applies it only whenguideIfSavedkeeps its guide, for both edges.trimTargets.length > 0 || gridStep > 0guard is gone: with the magnet on the playhead is always a target, and with it offsnapTimelineTimereturns no target.Timelineover its own player (notuseTimelinePlayer) gets no trim edge-frame preview;previewFrameStoreis not exported. In-repo hosts all useuseTimelinePlayer.tests/e2e/timeline-snap-release.mjs(new clips card-c and card-d in the shared fixture) zooms to the deepest level, moves a 31/30 s clip until its end is 4.8 px past card-d's start, and compares where the drag shows it landing with where it saved, both relative to card-d. The landing slot getsdata-testid="timeline-drag-landing", like its sibling drop preview. CI runs it after the trim-snap walk on a fresh copy of the fixture.Before
Deepest zoom, close-up at card-d's start (4x). While dragging, the landing slot ends flush with card-d; after release the saved clip ends past it: a 4.3 px jump.
After
The landing slot already shows where the clip will save, and release does not move it.
What I measured
timeline-snap-release.mjs, headless Chrome on Linux, 1440x900, repo fixture: on main the clip jumps 4.3 px on release (exit 1); with this branch 3 of 3 runs show 0.0 px (saved start 0.32 s either way). With saves held back 2 s (request interception), main still fails and this branch passes, so the walk reads the saved start, not the drag's own.timeline-trim-snap.mjsstill passes 3 of 3 with the larger fixture.timelineClipDragPreview.test.ts50,useTimelineClipDrag.resize.test.tsx27,timelineSnapping.test.ts20,useTimelinePlayer.seek.test.ts20,Timeline.test.ts83. Fullpackages/studiosuite: 664 files, 7325 tests passed, exit 0.tsc --noEmit, oxlint, oxfmt, comment ratchet clean.What I did NOT exercise
Test plan