Repository navigation
@remotion/studio: Fix source-only edits on clips trimmed by parents - #12288
Conversation
Show available source footage while a source-only gesture is held, including for nested clips hidden at the playhead. Render the outline outside the scene mask while keeping clip content and pointer hit areas clipped to the scene.
Let source-only edits reveal footage hidden by an ancestor after the child's own trim reaches zero. Adjust the child start and duration as needed without moving its visible timeline bounds or parent content. Keep live overrides and saved edits consistent, preserve source and keyframe limits, and show source-boundary feedback with deltas measured in composition frames across nested playback rates.
@remotion/studio: Show source-only trim outlines@remotion/studio: Fix source-only edits on clips trimmed by parents
There was a problem hiding this comment.
ℹ️ No critical issues — minor suggestions inline.
Reviewed changes
- Source-only
fromshifting —TimelineSequenceRightEdgeDragHandle.tsxgains a newtrimBeforeOnly && target.sourceOnlyLimitsbranch ingetTimelineSequenceLeftEdgeDragValuesForTargetplus thesourceOnlyLimitsconstruction (hidden-prefix, source-end, keyframe,adjustDuration/requiresExplicitDurationbounds) and now previews/savesfromanddurationInFramesfor source-only drags. - Unclipped outline — new
TimelineTrimOutline.tsxsplits the row wrapper into an outerpointerEvents: nonediv and a clipped inner div, and portals the source-only outline into the outer div viaTimelineUnclippedTrimOutline;TimelineTracks.tsxuses the new container. - Outline visibility —
TimelineSequence.tsxdrops the twoactiveEdgeHighlight === 'source-only'early-returns, computesstart/endfrom the full source range for source-only, and enablesnaturalSequenceDurationfor all edge drags. - Tooltip —
TimelineTrimTooltip.tsxgainsshowSourceLimits, which keepsSource start/Source endvisible for source-only drags; the test file is updated for the new prop.
I traced the new math through getTimelineSequenceTimingLimits, the save path (getTimelineSequenceLeftEdgeDragChanges → saveSequenceProps) and the preview queueStaticDragOverrideIfChanged calls. I could not construct a case where the new branch yields a negative duration, a trimBefore outside the source range, a from past the hidden prefix, or a save/preview mismatch: fromShift ≤ maximumFromShift ≤ hiddenPrefix, and the adjustDuration cap keeps durationInFrames ≥ keyframes.minimumDuration. Existing timeline tests (keyframe-trim-constraints, timeline-selection, timeline-trim-tooltip) pass locally.
ℹ️ minimumTrimBefore is recomputed instead of derived from the timing limits
The new block derives its own keyframe floor (ownTrimBefore + (keyframes.minimumDuration - initialDuration) * sequencePlaybackRate) rather than reusing getTimelineSequenceTimingLimits(...).minimumTrimBefore, which computes the same quantity from a different initialDuration (visibleEnd vs that module's auto-duration-aware value). They agree today modulo the Math.ceil in keyframes.minimumDuration, but the two paths can drift as either side changes.
Technical details
# Duplicated `minimumTrimBefore` derivation
## Affected sites
- `packages/studio/src/components/Timeline/TimelineSequenceRightEdgeDragHandle.tsx:1447-1455` — new inline floor from `ownTrimBefore` + `visibleEnd`.
- `packages/studio/src/components/Timeline/get-timeline-sequence-timing-limits.ts:283-294` — canonical floor from `trimBefore` + its own `initialDuration`, already returned in the `timingLimits` this same target stores.
## Required outcome
- The source-only clamp and the reason strings stay consistent with the shared timing-limit floor, without two independent formulas that can diverge.
## Open questions for the human
- Is the duplicated formula intentional (e.g. because `timingLimits.minimumTrimBefore` is computed against an `initialDuration` that differs from the drag-visible one), or can it be replaced by `timingLimits.minimumTrimBefore` with a comment explaining any required adjustment?deepseek-v4.1-flash | 𝕏
| if (trimBeforeOnly && target.sourceOnlyLimits) { | ||
| const limits = target.sourceOnlyLimits; | ||
| const sourceOffset = Math.max( | ||
| limits.minimumSourceOffset, | ||
| Math.min( | ||
| limits.maximumSourceOffset, | ||
| target.initialTrimBefore - | ||
| deltaFrames * target.parentPlaybackRate * target.playbackRate, | ||
| ), | ||
| ); | ||
| const fromShift = Math.max(0, -sourceOffset / target.playbackRate); | ||
| return { | ||
| from: target.initialFrom + fromShift, | ||
| durationInFrames: | ||
| target.initialDuration - (limits.adjustDuration ? fromShift : 0), | ||
| trimBefore: Math.max(0, sourceOffset), | ||
| }; | ||
| } |
There was a problem hiding this comment.
This branch is the only place the new source-only math is exercised, and it has no automated coverage — the only test change in this PR is timeline-trim-tooltip.test.tsx, and existing left-edge tests all pass trimBeforeOnly: false.
Technical details
# Untested source-only trim path
## Affected sites
- `packages/studio/src/components/Timeline/TimelineSequenceRightEdgeDragHandle.tsx:583-600` — source-only clamp/shift logic.
- `packages/studio/src/components/Timeline/TimelineSequenceRightEdgeDragHandle.tsx:1427-1520` — `sourceOnlyLimits` construction it depends on.
- `packages/studio/src/test/keyframe-trim-constraints.test.ts:321-338` — the closest existing harness (`getTimelineSequenceLeftEdgeDragTargets` + `getTimelineSequenceLeftEdgeDragChanges`), currently only used with `trimBeforeOnly: false`.
## Required outcome
- A focused test that builds a media target with a non-zero `trimBefore` / ancestor-hidden prefix and asserts, for `trimBeforeOnly: true`, the returned `from` / `trimBefore` / `durationInFrames` at: the source-start limit (trim reduced to 0), a mid-range drag, the source-end limit, and a keyframe-locked `trimBefore`.
## Suggested approach (optional)
- Reuse the fixture in `keyframe-trim-constraints.test.ts` (it already builds tracks + prop statuses); add cases mirroring the manual matrix in the PR body (zero/non-zero child trim, explicit/inferred duration, nested playback rate).Exercise source-only target construction and saved changes across source bounds, own and inherited trim, explicit and inferred durations, nested playback rates, and keyframe restrictions. Verify visible clip bounds stay fixed and edits only affect the selected media. Correct source-expression drag tests to derive pixels per frame from the full clip width instead of the distance between inset trim handles.

Source-only timeline edits now slide footage within fixed visible clip bounds, including footage hidden by an ancestor's trim. Dragging right first reduces the child's own
trimBefore, then shifts itsfromto reveal earlier source frames. Explicit durations are adjusted when needed to keep the visible end fixed, while parent scenes and captions retain their timing.Show the available-source outline from pointer-down through dragging, outside parent clipping masks, including for clips hidden at the playhead. Display Source start / Source end when those limits are reached, preserve keyframe and computed-value restrictions, and calculate the delta in composition frames across playback rates.
Validation
bun run buildpassed (81 tasks).bun run stylecheckpassed (266 tasks and skill-link checks).