Version 3.7.0

In-Place Take Cleanup, Streamlined Recorder UI

Recorder review adds in-place take cleanup, denoise, silence trim, a saved-take player, and a cleaner recording surface.

Major recorder release: takes can now be denoised and silence-trimmed in-place during review (before committing), the review surface reuses the actual silence-remover components instead of a parallel implementation, and the recorder UI is simplified by removing the top navbar in non-idle states.

Features

  • In-place take cleanup — during review, toggle Background Noise Removal and/or Silence Trim in the new TakeCleanupPanel (right sidebar). When you click Keep, the Rust apply_take_cleanup pipeline runs plan → denoise → cut → validate → commit, replacing the take’s source files on disk with cleaned versions. Validate-then-replace safety: originals are never deleted until all 7 checks pass on the new files. Orphan .cleaning.tmp files are swept on the next project load.
  • Review surface reuses silence-remover components — the review state now mounts the real SilencePlayerSlot, SilenceBottomPanel, WaveformTrack, SilenceOverlay, and DenoiseProgressDialog from the silence-remover feature. No parallel implementation — the take is projected onto a synthesized single-clip project via useTakeAsSilenceProject so the components find the data shape they expect.
  • Separate player surface for saved takes — clicking a committed take in the filmstrip now opens TakePlayerSurface, a lightweight native <video> + Web Audio player with the custom PlaybackControls (waveform seek bar, reset, mute). No Remotion Player, no silence-remover stores, no DenoiseProgressDialog leaking into the saved-take view.
  • Streamlined recorder UI — the top navbar (RecorderTopBar) now renders only in the idle slot. In recording, review, replay, and sequence states the bar is removed, reclaiming 56 px of vertical space for the canvas. The Help button and recording controls (Pause / Stop / Resume / Cancel) are relocated to the CanvasIdentityStrip. The DeviceStrip no longer appears during recording, preventing mid-take device changes (a known bug source).
  • Caption max width control — new per-phrase and project-level Max Width % slider (30–100%) caps how far captions span horizontally before wrapping. Available in the Phrase Inspector, Project Defaults Inspector, and per-overlay Style Controls. The renderer’s useTextLayout honours the value via style.maxWidth.
  • “Focus” text style preset — new built-in preset: Inter 900, 42 px, 70% max width, black rectangle background, cumulative highlight mode.

Bug Fixes

  • End-of-playback desync — Remotion’s moveToBeginningWhenEnded defaults to true, causing an auto-rewind to frame 0 before firing ended. The MediaPool saw frame 0 + stale PLAYING state → played all media for a full extra loop. Fixed with one prop: moveToBeginningWhenEnded={false} on SilencePlayer’s <Player>. Applies to both the silence-remover route and the recorder review surface.
  • Cleanup commits to the wrong file when denoise is on — the orchestrator mutated plan.mic.source to the denoised WAV path for the cut step. commit_plan then used that mutated path as the rename destination → cleaned m4a landed at .cleaning.tmp.denoised.wav → orphan sweep deleted it → original mic.m4a untouched. Fixed by using a sibling mic_cut_input variable without mutating the plan.
  • FFmpeg cut filter failures — rewrote cut_file from aselect/select + between() predicates to atrim/trim + concat, which is robust to the edge cases that caused the old filter to occasionally pass through the full input.
  • Saved-take duration stale after cleanup — gatedKeep now patches pendingTake.duration_secs with the cleaned duration from apply_take_cleanup before calling addTake, preventing frozen-frame tails when the cleaned file is shorter than the original.
  • Autosave toast during review — the synthesized in-memory project for the review bridge triggered the persistence layer’s autosave, which failed (no on-disk folder) and surfaced the “Your changes are still in memory” toast. Fixed by adding inMemoryOnly: true to the project metadata and early- returning in saveProjectData.
  • Spacebar only paused preview, not playhead — two shortcut handlers (RECORDER_SHORTCUTS + SILENCE_SHORTCUTS) both bound Space, racing each other. Fixed by dropping RECORDER_SHORTCUTS play/seek wiring entirely in the review surface — the silence-remover’s handler is the canonical one.
  • Denoise never completes — denoise.ts passed clip.file_path (screen video, no audio track) instead of audioSourceFor(clip) (mic.m4a) to denoise_for_analysis. FFmpeg hung on -vn of a video-only file. Fixed by centralizing audio/video source resolution into audioSourceFor() / videoSourceFor() in src/lib/clip-source-resolver.ts.
  • Waveform all-red when silence toggle OFF — setting clipSegments[clipId] to [] meant “no keep segments = everything is silence.” Fixed by setting a full-span keep segment [{start:0, end:duration}] when silence is disabled.

Refactoring

  • Silence-remover PiP composition and renderers extracted into shared src/lib/composition/ — screen/webcam toggle removed, CanvasComposer shared between silence-remover and recorder.
  • DenoiseModePicker and SilenceControls adapter extracted for reuse by the review cleanup panel.
  • Dead Position struct removed from src-tauri/src/assets/types.rs.
  • Centralized clip source resolution (audioSourceFor / videoSourceFor) in src/lib/clip-source-resolver.ts with 6 tests.

Tests

  • 4 new tests for cut_file trim+concat filter shapes (single/multi × audio/video).
  • 6 tests for audioSourceFor / videoSourceFor fallback chains.
  • Existing plan_tests, validate_tests, commit_tests, orphan_sweep_tests cover the full cleanup pipeline.
Local Mac workflow

Keep the edit on your Mac.

Choose a license to record, clean, caption, and export without moving private footage into a browser editor.