Addresses review feedback on the source in-point split.
`buildTimelineItems` still derived `sourceSpan.end` from the timeline
`startMs`, so a split clip's source-audio waveform rendered the wrong range
while `sourceSpan.start` was already correct.
`clipsToTrims` walked clips in timeline order while treating the source
in-point as monotonic. That held before, when the two were the same field,
but a move now keeps its footage, so the cursor could run backwards: source
spans [20,30] then [0,10] emitted overlapping gaps that trimmed away a
range a clip was still using. It now walks the claimed source spans in
source order and merges overlaps.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ZyJLpqdwGjGvR4yy44xWU
Splitting a sped-up clip and deleting the middle leaves the deleted footage
in the export and drops a matching stretch from the end of the recording.
`ClipRegion.startMs` is a source position while the split position is a
timeline position, and the two only coincide at 1x. `handleClipSplit`
assigned the timeline position as the right half's source start, so at 2x
the right half re-read footage the left half already covered. Cutting the
10s-20s playback window out of a 2x clip removed source [100s,120s] instead
of [20s,40s]: nothing in the middle was cut and the last 20s of the
recording silently disappeared.
Anchoring the right half at the source time the split maps to fixes the
export, but it would also drag the clip across the timeline, since the clip
row draws items at `startMs`. So separate the two meanings: `sourceStartMs`
says where a clip reads from, `startMs` stays where it sits. It falls back
to `startMs` when absent, so stored projects behave exactly as before and
need no migration.
Left-edge drags in `handleClipSpanChange` were wrong in the same way and now
advance `sourceStartMs` by the source the trimmed edge covered; moves carry
their footage along unchanged.
The split itself moves into a pure `planClipSplit`, matching how
`planClipSpeedChange` is already factored, so it can be covered by tests.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ZyJLpqdwGjGvR4yy44xWU
getCompanionAudioFallbackInfo returned only the video path when a macOS
system sidecar existed, assuming the inline mp4 track was a complete mix.
The capture helper writes system audio alone to that track, so the
microphone was dropped from preview and export whenever both sources were
enabled — the recorded narration was silent even though it had been
captured correctly to recording-<ts>.mic.m4a.
Return both macOS sidecars instead, so the renderer routes them as
separate system and mic tracks and mutes the silent embedded track.
Existing recordings recover their audio on reopen.
Fixeswebadderallorg/Recordly#912