feat: lock timeline tracks so clip edits don't move them - #2378
YoavLevavi wants to merge 2 commits into
Conversation
A lock toggle on each track header keeps that track's segments where they are when clips are deleted, trimmed, retimed, given transitions, inserted, or cut from the transcript. Locks live in the project, survive preset application, and aren't stored in presets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| overlayShift, | ||
| ); | ||
| if (timeline.textSegments) | ||
| if (timeline.textSegments && ripples("text")) |
There was a problem hiding this comment.
Locked holds misalign overlays When a locked text track has a fullscreen hold inside a deleted clip, this guard leaves the hold in place. But the shift applied to unlocked overlays still counts that hold as removed, moving overlays after the cut too far and misaligning them with the remaining timeline.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/routes/editor/timeline-utils.ts
Line: 484
Comment:
**Locked holds misalign overlays** When a locked text track has a fullscreen hold inside a deleted clip, this guard leaves the hold in place. But the shift applied to unlocked overlays still counts that hold as removed, moving overlays after the cut too far and misaligning them with the remaining timeline.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Fixed in 36c0ba2. The hold time inside the cut is now added to the overlay shift only when the text track ripples. With text locked, the hold stays, so unlocked overlays after the cut shift by the removed clip time alone. Added keeps a locked hold's time when cutting the clip around it, which fails without the fix.
| if (!kind) return; | ||
| const current = project.lockedTracks ?? []; | ||
| setProject( | ||
| "lockedTracks", | ||
| current.includes(kind) | ||
| ? current.filter((track) => track !== kind) | ||
| : [...current, kind], | ||
| ); | ||
| }; |
There was a problem hiding this comment.
One lock affects every lane The button appears on each lane, but it stores only the track kind. When a project has multiple audio or overlay lanes of the same kind, locking one also locks the others, so clip edits stop rippling lanes the user did not lock.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/routes/editor/Timeline/index.tsx
Line: 1830-1838
Comment:
**One lock affects every lane** The button appears on each lane, but it stores only the track kind. When a project has multiple audio or overlay lanes of the same kind, locking one also locks the others, so clip edits stop rippling lanes the user did not lock.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
The lock is intentionally per track kind: lockedTracks is a list of kinds, and ripple logic runs per kind. Every lane of that kind shows the same locked state, so it's visible rather than silent. Per-lane locks would need stable lane identity, which audio and overlay lanes don't have today; lanes are positional and renumber on reorder or delete. In 36c0ba2 the tooltip and aria-label now say the lock applies to every lane of that kind, e.g. "Lock audio segments on every audio lane…". Happy to move to per-lane locks if you'd like lanes to get stable IDs.
There was a problem hiding this comment.
That makes sense. Given that locks are intentionally scoped to LockableTimelineTrack kinds and the UI now explicitly communicates that locking applies to every lane of that kind, the shared behavior is visible and deliberate rather than an unintended cross-lane side effect. Stable per-lane identity would be a separate design change, so I’m withdrawing this finding.
| timeline: project.timeline ?? null, | ||
| overlayOrder: project.overlayOrder ?? [], | ||
| clips: project.clips, | ||
| lockedTracks: project.lockedTracks ?? [], |
There was a problem hiding this comment.
Default preset clears locks This path preserves the project's locks when applying a saved preset, but applying Default uses the stock configuration's empty lock list instead. If tracks are locked, applying Default silently unlocks them.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/routes/editor/PresetsDropdown.tsx
Line: 108
Comment:
**Default preset clears locks** This path preserves the project's locks when applying a saved preset, but applying Default uses the stock configuration's empty lock list instead. If tracks are locked, applying Default silently unlocks them.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Fixed in 36c0ba2. applyDefaultPreset now carries project.lockedTracks through, the same as saved presets.
- keep a locked fullscreen hold's time out of the overlay ripple shift - keep track locks when applying the Default preset - say in the lock tooltip that it applies to every lane of that kind Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What
Adds a lock toggle to each timeline track header. A locked track keeps its segments where they are when clips are edited, instead of rippling with them.
Clip edits that normally shift the other tracks leave locked tracks alone:
This is useful when, for example, keyboard or text overlays are already placed against the final timing and you keep tightening the clip underneath. With nothing locked, behavior is unchanged.
Implementation
ProjectConfiguration.locked_tracks: Vec<LockableTimelineTrack>, with#[serde(default, skip_serializing_if = "Vec::is_empty")]. Existing projects are unaffected.timeline-utils.ts:unlockedRippleTracksfilters locked tracks out of the boundary-ripple set, and the delete ripple skips them.context.ts,ClipsSidebarandTranscriptPageroute their clip edits through these helpers.Testing
timeline-utilstests cover locked tracks staying in place on clip delete, and being filtered out of boundary ripples.tsc --noEmitare clean;cargo clippy -p cap-project -D warningsis clean andcap-projecttests pass.🤖 Generated with Claude Code
The PR is not ready to merge because clip deletion can misalign overlays, lane locks affect unintended lanes, and applying Default clears locks.
Findings
Fix with agent prompt
Summary
Adds persisted, per-kind timeline locks and skips locked tracks during clip-edit ripples and retiming.
Reviews (1) · Last reviewed commit: "feat: lock timeline tracks so clip edits..."