-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat: Various features + performance bits #1390
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Warning Rate limit exceeded@richiemcilroy has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 7 minutes and 42 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (13)
WalkthroughReplaces inline debounced project saves with an explicit scheduler; inlines and relocates tauri camera/mic input logic (adds retries, removes idle restoration); simplifies camera frame forwarding; migrates waveform canvas to Path2D; consolidates muxing/timestamp handling; adds an initializing recording state and RecordingStatus. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Editor as Editor Context
participant Scheduler as Save Scheduler
participant Commands as commands.setProjectConfig
Editor->>Scheduler: trackStore change -> scheduleProjectConfigSave()
Scheduler->>Scheduler: start/refresh timeout (250ms)
alt no saveInFlight
Scheduler->>Commands: flushProjectConfig()
Commands-->>Scheduler: save result
Scheduler->>Scheduler: saveInFlight = false
else saveInFlight
Scheduler->>Scheduler: shouldResave = true
end
alt shouldResave
Scheduler->>Scheduler: scheduleProjectConfigSave()
end
Note over Editor,Scheduler: onCleanup -> flushProjectConfig()
sequenceDiagram
autonumber
participant App as Tauri App
participant Lib as lib.rs (set_* handlers)
participant Camera as Camera Feed
App->>Lib: start_recording()
Lib->>Camera: set_camera_input (init with up-to-3 retries)
alt camera init succeeds
Camera-->>Lib: ready
Lib->>App: recording started (status: Recording)
else fail
Lib-->>App: error
end
sequenceDiagram
autonumber
participant Pipeline as Output Pipeline
participant Encoder as spawn_video_encoder
participant Muxer as Mp4Muxer
Pipeline->>Encoder: spawn_video_encoder(mut video_rx, stop_token)
loop until stop_token cancelled
Encoder->>Encoder: recv frame from video_rx
alt frame received
Encoder->>Muxer: send_video_frame(frame, timestamp)
end
end
Note over Encoder: cancellation -> clean shutdown (stop muxer)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
apps/desktop/src-tauri/src/recording.rs (3)
34-35: Pruning helper is sound; consider documenting semantics
prune_excluded_windows_without_shareable_contentis implemented safely: usingmem::takeavoids in-place mutation pitfalls, and dropping any IDs that can’t be resolved or don’t appear in the currentShareableContent(with aremovedcounter) is exactly what you want for keeping the exclusion list in sync.As this will be subtle to reason about later (it removes both “never-resolvable” and “no longer present” windows), a short doc comment explaining that behavior would help future maintainers.
Also applies to: 68-69, 191-230
695-715: macOS excluded-window initialization and pruning fit well into the actor setupInitializing
excluded_windowsfrom general settings, resolving them viaresolve_window_ids, and immediately callingprune_excluded_windows_without_shareable_contentagainst the freshly acquiredshareable_contentensures the pipeline starts with a minimal, valid exclusion set. That should reduce the chance of later ScreenCaptureKit filter errors and keep the occlusion behavior aligned with the UI settings.One thing to keep in mind for future performance work: all of this currently runs while the state write lock is held inside the actor-spawn path. If lock contention around recording startup ever shows up in profiling, this block (resolving/pruning exclusions and acquiring shareable content) would be a good candidate to move outside the critical section.
Also applies to: 717-721
840-851: Guard against unbounded retries on shareable-content errorsOn macOS,
is_shareable_content_errortriggers a re-acquire ofshareable_contentand re-pruning ofexcluded_windows, thencontinue-s the loop. IfSourceError::AsContentFilterkeeps occurring even after refresh (for example due to a persistent OS-level issue), this loop can theoretically retry forever.Consider adding a small attempt counter or time-based cutoff for this specific retry path, and after exceeding it, surface a more explicit error to the user instead of looping indefinitely. This keeps the behavior robust even when ScreenCaptureKit misbehaves.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/desktop/src-tauri/src/recording.rs(8 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Format Rust code usingrustfmtand ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.
Files:
apps/desktop/src-tauri/src/recording.rs
🧠 Learnings (1)
📚 Learning: 2025-10-28T08:39:42.230Z
Learnt from: Brendonovich
Repo: CapSoftware/Cap PR: 1305
File: crates/recording/src/output_pipeline/macos.rs:80-90
Timestamp: 2025-10-28T08:39:42.230Z
Learning: In `crates/recording/src/output_pipeline/macos.rs`, the `AVFoundationMp4Muxer` intentionally holds the `Mutex<MP4Encoder>` lock during retry attempts in `send_video_frame()` and `send_audio_frame()`. This blocking behavior is correct because frame processing must happen sequentially to prevent audio and video frames from being interleaved incorrectly in the encoder.
Applied to files:
apps/desktop/src-tauri/src/recording.rs
🧬 Code graph analysis (1)
apps/desktop/src-tauri/src/recording.rs (3)
crates/recording/src/sources/screen_capture/mod.rs (1)
window(74-79)crates/scap-targets/src/platform/macos.rs (1)
from_id(48-51)crates/scap-targets/src/lib.rs (2)
from_id(32-34)from_id(122-124)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Clippy (aarch64-apple-darwin, macos-latest)
- GitHub Check: Analyze (rust)
🔇 Additional comments (1)
apps/desktop/src-tauri/src/recording.rs (1)
111-147: Shareable content acquisition and missing-window handling look correctThe new
acquire_shareable_content_for_targetlogic plusshareable_content_missing_windowsgives a clear, bounded retry: one refresh on stale ScreenCaptureKit content, withDisplayMissingsurfaced cleanly and excluded-window mismatches tolerated after a refresh. The distinction between display-missing and window-missing cases, and only treating known-but-unresolved windows as “missing”, matches the later pruning behavior and should avoid spurious failures.Also applies to: 164-189
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
apps/desktop/src/routes/editor/Timeline/ClipTrack.tsx (1)
46-94: Consider defensive bounds checking and add documentation.The waveform path generation logic is sophisticated but could be clearer:
Line 82:
amplitudeAt(index - 1)on the first iteration may accesswaveform[-1]whensegment.startis 0. While safely handled by theamplitudeAtfunction's type checks, consider clamping:Math.max(index - 1, 0).The bezier curve construction with control points and the coordinate normalization could benefit from inline comments explaining the approach.
Apply this diff to add bounds checking:
const prevY = 1 - amplitudeAt(index - 1); + const prevY = 1 - amplitudeAt(Math.max(index - 1, 0));
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/desktop/src/routes/editor/Timeline/ClipTrack.tsx(4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by runningpnpm format.
Files:
apps/desktop/src/routes/editor/Timeline/ClipTrack.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g.,user-menu.tsx).
Use PascalCase for React/Solid components.
Files:
apps/desktop/src/routes/editor/Timeline/ClipTrack.tsx
🧬 Code graph analysis (1)
apps/desktop/src/routes/editor/Timeline/ClipTrack.tsx (3)
crates/editor/src/audio.rs (1)
gain(63-65)crates/project/src/configuration.rs (2)
duration(526-528)duration(591-593)crates/editor/src/segments.rs (1)
segments(7-36)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Analyze (rust)
🔇 Additional comments (5)
apps/desktop/src/routes/editor/Timeline/ClipTrack.tsx (5)
33-44: LGTM! Good constants and scaling logic.The waveform constants are well-defined and the
gainToScalefunction correctly converts decibel gain to a linear scale factor for visualization purposes.
110-171: Excellent Path2D-based rendering optimization!The refactor to use memoized Path2D objects with transform-based scaling is a solid performance improvement over per-frame waveform calculations. The reactive dependencies are correctly tracked.
Minor note: Line 136's
canvas.width = canvasWidthalready clears the canvas, making line 138'sclearRectredundant (though harmless).
196-205: LGTM! Excellent performance optimization.Precomputing segment offsets eliminates redundant calculations across multiple segments. The cumulative offset logic correctly accounts for timescale and provides O(1) lookups.
233-233: LGTM! Clean integration with segmentOffsets.Using the precomputed offsets is more efficient and the nullish coalescing provides good safety.
489-495: LGTM! Appropriate conditional rendering.Limiting waveform rendering to segments with
timescale === 1is sensible, as time-stretched segments would require different waveform handling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
crates/recording/src/sources/camera.rs (1)
53-66: Consider adding logging when reattachment occurs.The reattachment logic provides good resilience when the camera sender disconnects, but there's no info-level log when reattachment succeeds. This makes it difficult to diagnose intermittent disconnection issues in production.
Consider adding an info log after successful reattachment:
Err(_) => { let (new_tx, new_rx) = flume::bounded(8); if let Err(err) = feed_lock.ask(camera::AddSender(new_tx)).await { warn!( ?err, "Camera sender disconnected and could not be reattached" ); break Err(err); } + info!( + frame_count, + "Camera sender disconnected; successfully reattached" + ); receiver = new_rx; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
.claude/settings.local.json(1 hunks)crates/recording/src/sources/camera.rs(2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Format Rust code usingrustfmtand ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.
Files:
crates/recording/src/sources/camera.rs
crates/*/src/**/*
📄 CodeRabbit inference engine (AGENTS.md)
Rust crates should place tests within the
src/and/or a siblingtests/directory for each crate insidecrates/*.
Files:
crates/recording/src/sources/camera.rs
🧠 Learnings (1)
📚 Learning: 2025-10-28T08:39:42.230Z
Learnt from: Brendonovich
Repo: CapSoftware/Cap PR: 1305
File: crates/recording/src/output_pipeline/macos.rs:80-90
Timestamp: 2025-10-28T08:39:42.230Z
Learning: In `crates/recording/src/output_pipeline/macos.rs`, the `AVFoundationMp4Muxer` intentionally holds the `Mutex<MP4Encoder>` lock during retry attempts in `send_video_frame()` and `send_audio_frame()`. This blocking behavior is correct because frame processing must happen sequentially to prevent audio and video frames from being interleaved incorrectly in the encoder.
Applied to files:
crates/recording/src/sources/camera.rs
🧬 Code graph analysis (1)
crates/recording/src/sources/camera.rs (1)
crates/recording/src/feeds/camera.rs (1)
drop(175-179)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (rust)
🔇 Additional comments (3)
.claude/settings.local.json (1)
7-8: Approve: Configuration update is consistent.The addition of
"Bash(cargo build:*)"follows the established pattern of wildcard permissions for build tool operations and complements the existing"Bash(cargo check:*)"entry.crates/recording/src/sources/camera.rs (2)
37-37: LGTM! Good observability improvement.The frame counting and enhanced error logging provide valuable diagnostics for tracking camera forwarding behavior. Including the frame count in error logs will help identify whether issues occur at startup or during sustained operation.
Also applies to: 42-50
68-76: LGTM! Explicit cleanup with observable logging.The explicit drop statements and final info log provide clear lifecycle boundaries and telemetry. This makes it easier to diagnose camera forwarding issues and ensures resources are cleaned up promptly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/recording/src/output_pipeline/core.rs (1)
429-482:mux-videonow swallows video encoder errors; consider logging or propagating themThe new
mux-videotask:stop_token .run_until_cancelled(async { while let Some(frame) = video_rx.next().await { let timestamp = frame.timestamp(); if let Some(first_tx) = first_tx.take() { let _ = first_tx.send(timestamp); } muxer .lock() .await .send_video_frame(frame, timestamp.duration_since(timestamps)) .map_err(|e| anyhow!("Error queueing video frame: {e}"))?; } Ok::<(), anyhow::Error>(()) }) .await; muxer.lock().await.stop(); Ok(())turns any
send_video_framefailure into anErr, but that error is discarded because therun_until_cancelledresult is ignored and the outer future always returnsOk(()). This means:
- The
TaskPoolwill log “Task finished successfully” even if the video encoder failed mid-stream.finish_buildwill only see muxer-level errors fromfinish(...), not from the streaming side, and you lose visibility into where things went wrong.For parity with the audio path (which logs
Audio encoder: {e}) and better diagnosability, I’d recommend at least logging and breaking on error, e.g.:- setup_ctx.tasks().spawn("mux-video", async move { - use futures::StreamExt; - - let mut first_tx = Some(first_tx); - - stop_token - .run_until_cancelled(async { - while let Some(frame) = video_rx.next().await { - let timestamp = frame.timestamp(); - - if let Some(first_tx) = first_tx.take() { - let _ = first_tx.send(timestamp); - } - - muxer - .lock() - .await - .send_video_frame(frame, timestamp.duration_since(timestamps)) - .map_err(|e| anyhow!("Error queueing video frame: {e}"))?; - } - - Ok::<(), anyhow::Error>(()) - }) - .await; - - muxer.lock().await.stop(); - - Ok(()) - }); + setup_ctx.tasks().spawn("mux-video", async move { + use futures::StreamExt; + + let mut first_tx = Some(first_tx); + + stop_token + .run_until_cancelled(async { + while let Some(frame) = video_rx.next().await { + let timestamp = frame.timestamp(); + + if let Some(first_tx) = first_tx.take() { + let _ = first_tx.send(timestamp); + } + + let duration = timestamp.duration_since(timestamps); + if let Err(e) = muxer.lock().await.send_video_frame(frame, duration) { + error!("Video encoder: {e:#}"); + break; + } + } + }) + .await; + + muxer.lock().await.stop(); + + Ok(()) + });This keeps the cancellation-driven structure while ensuring video encoder failures are visible in logs and will still cause the pipeline to stop cleanly. It also aligns behavior with the audio mux loop, which logs but does not silently ignore errors. Based on learnings.
🧹 Nitpick comments (3)
apps/desktop/src/routes/camera.tsx (1)
25-25: Verify that the new camera mutation-based close behavior matches the intended UXSwitching the “X” controls to
setCamera.mutate(null)decouples them fromgetCurrentWindow().close()and makes them purely “clear the selected camera” actions.Two things to double-check:
Native preview window:
NativeCameraPreviewPageno longer has any logic that closes the Tauri window when the camera is cleared. If the desired behavior is still “close the floating camera window” when the user clicks X, you may want to either:
- explicitly close the window after a successful
mutate(null), or- add a small effect that observes the relevant recording option (e.g. camera ID/label) and closes on
null, similar to the legacy path.Legacy preview window: the X button now relies on
createCameraMutationindirectly drivingrawOptions.cameraLabeltonullforgetCurrentWindow().close()to fire (lines 273–281). Please confirm that the options store wiring actually clearscameraLabelwhencameraIDis set tonull; otherwise the X button will stop closing the legacy window as well.If the new UX is intentionally “keep the window around but just stop using the camera”, consider also updating the legacy window behavior to avoid the asymmetric handling between the two code paths.
Also applies to: 91-91, 104-104, 271-271, 297-297
crates/recording/src/sources/camera.rs (1)
25-35: Break out of the forwarding loop whenvideo_tx.sendfails to avoid silent dropsThe new forwarding task keeps reading from
rxeven ifvideo_tx.send(frame).awaitstarts failing (e.g., muxing side is shut down). Because the send result is ignored, frames are silently dropped and the task stays alive until the flume sender is dropped.Consider treating a send error as an exit condition:
tokio::spawn(async move { - while let Ok(frame) = rx.recv_async().await { - let _ = video_tx.send(frame).await; - } + while let Ok(frame) = rx.recv_async().await { + if video_tx.send(frame).await.is_err() { + // Downstream is gone; stop forwarding. + break; + } + } });This keeps the behavior simple while avoiding unnecessary work and making frame loss semantics more predictable.
apps/desktop/src-tauri/src/lib.rs (1)
2540-2549: Reusingset_mic_input/set_camera_inputfor startup is a good unificationCalling
set_mic_input(app.state(), settings.mic_name)andset_camera_input(app.clone(), app.state(), settings.camera_id)here makes the “start recording from event” path share the exact same logic as the explicit commands, which is nice for consistency.You currently discard the
Resultwithlet _ = …;. If failures here should be visible (e.g., to help debug why a recording started without the expected mic/camera), consider at least logging the error:if let Err(e) = set_mic_input(app.state(), settings.mic_name).await { warn!("Failed to apply mic input before recording: {e}"); }Same idea for
set_camera_input. If best-effort behavior is intentional, the current code is fine.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
apps/desktop/src-tauri/src/deeplink_actions.rs(2 hunks)apps/desktop/src-tauri/src/lib.rs(1 hunks)apps/desktop/src-tauri/src/recording.rs(0 hunks)apps/desktop/src-tauri/src/windows.rs(1 hunks)apps/desktop/src/routes/camera.tsx(5 hunks)crates/recording/examples/camera_stream.rs(0 hunks)crates/recording/src/output_pipeline/core.rs(2 hunks)crates/recording/src/output_pipeline/ffmpeg.rs(2 hunks)crates/recording/src/sources/camera.rs(1 hunks)
💤 Files with no reviewable changes (2)
- apps/desktop/src-tauri/src/recording.rs
- crates/recording/examples/camera_stream.rs
🧰 Additional context used
📓 Path-based instructions (4)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Format Rust code usingrustfmtand ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.
Files:
crates/recording/src/sources/camera.rscrates/recording/src/output_pipeline/ffmpeg.rsapps/desktop/src-tauri/src/windows.rsapps/desktop/src-tauri/src/lib.rscrates/recording/src/output_pipeline/core.rsapps/desktop/src-tauri/src/deeplink_actions.rs
crates/*/src/**/*
📄 CodeRabbit inference engine (AGENTS.md)
Rust crates should place tests within the
src/and/or a siblingtests/directory for each crate insidecrates/*.
Files:
crates/recording/src/sources/camera.rscrates/recording/src/output_pipeline/ffmpeg.rscrates/recording/src/output_pipeline/core.rs
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by runningpnpm format.
Files:
apps/desktop/src/routes/camera.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g.,user-menu.tsx).
Use PascalCase for React/Solid components.
Files:
apps/desktop/src/routes/camera.tsx
🧠 Learnings (3)
📓 Common learnings
Learnt from: Brendonovich
Repo: CapSoftware/Cap PR: 1305
File: crates/recording/src/output_pipeline/macos.rs:80-90
Timestamp: 2025-10-28T08:39:42.230Z
Learning: In `crates/recording/src/output_pipeline/macos.rs`, the `AVFoundationMp4Muxer` intentionally holds the `Mutex<MP4Encoder>` lock during retry attempts in `send_video_frame()` and `send_audio_frame()`. This blocking behavior is correct because frame processing must happen sequentially to prevent audio and video frames from being interleaved incorrectly in the encoder.
📚 Learning: 2025-10-28T08:39:42.230Z
Learnt from: Brendonovich
Repo: CapSoftware/Cap PR: 1305
File: crates/recording/src/output_pipeline/macos.rs:80-90
Timestamp: 2025-10-28T08:39:42.230Z
Learning: In `crates/recording/src/output_pipeline/macos.rs`, the `AVFoundationMp4Muxer` intentionally holds the `Mutex<MP4Encoder>` lock during retry attempts in `send_video_frame()` and `send_audio_frame()`. This blocking behavior is correct because frame processing must happen sequentially to prevent audio and video frames from being interleaved incorrectly in the encoder.
Applied to files:
crates/recording/src/sources/camera.rscrates/recording/src/output_pipeline/ffmpeg.rscrates/recording/src/output_pipeline/core.rsapps/desktop/src-tauri/src/deeplink_actions.rs
📚 Learning: 2025-10-17T05:58:22.586Z
Learnt from: Brendonovich
Repo: CapSoftware/Cap PR: 1219
File: crates/enc-avfoundation/src/mp4.rs:350-373
Timestamp: 2025-10-17T05:58:22.586Z
Learning: In crates/enc-avfoundation/src/mp4.rs, the `finish()` method intentionally skips video extension when `is_paused` is true. This is correct behavior because if recording is paused, the video should not be extended beyond the pause point—the pause is user-initiated, unlike the case where ScreenCaptureKit stops providing frames during static content.
Applied to files:
crates/recording/src/output_pipeline/ffmpeg.rscrates/recording/src/output_pipeline/core.rs
🧬 Code graph analysis (4)
crates/recording/src/output_pipeline/ffmpeg.rs (1)
crates/enc-ffmpeg/src/video/h264.rs (1)
builder(234-236)
crates/recording/src/output_pipeline/core.rs (3)
crates/recording/src/output_pipeline/ffmpeg.rs (1)
timestamp(22-24)crates/recording/src/sources/screen_capture/windows.rs (1)
timestamp(63-65)crates/recording/src/sources/screen_capture/macos.rs (1)
timestamp(61-63)
apps/desktop/src-tauri/src/deeplink_actions.rs (1)
apps/desktop/src-tauri/src/lib.rs (8)
set_camera_input(413-490)app(1522-1523)app(2582-2582)app(2613-2613)app(2674-2674)app(2680-2680)app(2904-2905)set_mic_input(333-401)
apps/desktop/src/routes/camera.tsx (1)
apps/desktop/src/utils/queries.ts (1)
createCameraMutation(195-236)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (rust)
🔇 Additional comments (3)
apps/desktop/src-tauri/src/windows.rs (1)
23-30: Imports look consistent with current usagesThe updated
use crate::{ App, ArcLock, RequestScreenCapturePrewarm, fake_window, …, recording_settings::RecordingTargetMode, … }set matches how these types are used later in the file. Nothing to fix here.apps/desktop/src-tauri/src/deeplink_actions.rs (1)
9-10: Deep link start-recording now correctly reuses the shared input settersUsing
App/ArcLock<App>plus:let state = app.state::<ArcLock<App>>(); crate::set_camera_input(app.clone(), state.clone(), camera).await?; crate::set_mic_input(state.clone(), mic_label).await?;before building
StartRecordingInputscleanly aligns deep-link initiated recordings with the same mic/camera selection logic used elsewhere in the app. This should reduce divergence and subtle bugs between entry points.The error propagation via
?also keeps handling consistent: if either setter fails, the deep link action surfaces aResult::Errback to the caller.Also applies to: 117-145
crates/recording/src/output_pipeline/ffmpeg.rs (1)
49-52: MP4 muxer setup and timestamp handling look consistent with the refactor
The new video encoder initialization:
let video_encoder = video_config .map(|video_config| H264Encoder::builder(video_config).build(&mut output)) .transpose() .context("video encoder")?;correctly mirrors the audio path (
AACEncoder::init) and yields aResult<Option<H264Encoder>>.
send_video_framenow simply forwards theDurationit’s given intoqueue_frame, with timestamp derivation handled by the caller (seespawn_video_encoderincore.rs), which keeps muxer responsibilities narrow and consistent withsend_audio_frame.No changes needed from this diff.
Also applies to: 96-103
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
apps/desktop/src/routes/camera.tsx (2)
25-25: Remove unused camera mutation declaration.The
createCameraMutationimport andsetCameravariable are declared but never used inNativeCameraPreviewPage. This appears to be dead code.Apply this diff to remove the unused code:
-import { createCameraMutation } from "~/utils/queries";- const setCamera = createCameraMutation(); -Also applies to: 91-91
271-271: Remove unused camera mutation declaration.The
setCameravariable is declared but never used inLegacyCameraPreviewPage. This appears to be dead code.Apply this diff to remove the unused code:
- const setCamera = createCameraMutation(); -
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
apps/desktop/src-tauri/src/lib.rs(7 hunks)apps/desktop/src-tauri/src/recording.rs(2 hunks)apps/desktop/src-tauri/src/windows.rs(2 hunks)apps/desktop/src/routes/camera.tsx(5 hunks)apps/desktop/src/routes/in-progress-recording.tsx(9 hunks)apps/desktop/src/utils/tauri.ts(2 hunks)crates/recording/src/feeds/camera.rs(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/desktop/src-tauri/src/windows.rs
- apps/desktop/src-tauri/src/lib.rs
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by runningpnpm format.
Files:
apps/desktop/src/utils/tauri.tsapps/desktop/src/routes/camera.tsxapps/desktop/src/routes/in-progress-recording.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g.,user-menu.tsx).
Use PascalCase for React/Solid components.
Files:
apps/desktop/src/utils/tauri.tsapps/desktop/src/routes/camera.tsxapps/desktop/src/routes/in-progress-recording.tsx
**/tauri.ts
📄 CodeRabbit inference engine (AGENTS.md)
Do not edit auto-generated files named
tauri.ts.
Files:
apps/desktop/src/utils/tauri.ts
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Format Rust code usingrustfmtand ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.
Files:
crates/recording/src/feeds/camera.rsapps/desktop/src-tauri/src/recording.rs
crates/*/src/**/*
📄 CodeRabbit inference engine (AGENTS.md)
Rust crates should place tests within the
src/and/or a siblingtests/directory for each crate insidecrates/*.
Files:
crates/recording/src/feeds/camera.rs
🧠 Learnings (2)
📓 Common learnings
Learnt from: Brendonovich
Repo: CapSoftware/Cap PR: 1305
File: crates/recording/src/output_pipeline/macos.rs:80-90
Timestamp: 2025-10-28T08:39:42.230Z
Learning: In `crates/recording/src/output_pipeline/macos.rs`, the `AVFoundationMp4Muxer` intentionally holds the `Mutex<MP4Encoder>` lock during retry attempts in `send_video_frame()` and `send_audio_frame()`. This blocking behavior is correct because frame processing must happen sequentially to prevent audio and video frames from being interleaved incorrectly in the encoder.
📚 Learning: 2025-10-28T08:39:42.230Z
Learnt from: Brendonovich
Repo: CapSoftware/Cap PR: 1305
File: crates/recording/src/output_pipeline/macos.rs:80-90
Timestamp: 2025-10-28T08:39:42.230Z
Learning: In `crates/recording/src/output_pipeline/macos.rs`, the `AVFoundationMp4Muxer` intentionally holds the `Mutex<MP4Encoder>` lock during retry attempts in `send_video_frame()` and `send_audio_frame()`. This blocking behavior is correct because frame processing must happen sequentially to prevent audio and video frames from being interleaved incorrectly in the encoder.
Applied to files:
apps/desktop/src-tauri/src/recording.rs
🧬 Code graph analysis (3)
apps/desktop/src/routes/camera.tsx (1)
apps/desktop/src/utils/queries.ts (1)
createCameraMutation(195-236)
apps/desktop/src-tauri/src/recording.rs (1)
apps/desktop/src-tauri/src/windows.rs (6)
id(779-805)app(215-215)app(372-372)app(462-462)app(785-785)app(1006-1006)
apps/desktop/src/routes/in-progress-recording.tsx (1)
apps/desktop/src/utils/tauri.ts (3)
stopRecording(20-22)restartRecording(29-31)deleteRecording(32-34)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (rust)
🔇 Additional comments (9)
apps/desktop/src/routes/camera.tsx (1)
104-104: LGTM!Removing the
voidoperator fromgetCurrentWindow().close()is cleaner and doesn't change behavior.Also applies to: 297-297
apps/desktop/src/utils/tauri.ts (1)
386-386: LGTM!The addition of
RecordingStatustype andstatusfield toCurrentRecordingcorrectly extends the type system to support the new pending/recording state tracking.Also applies to: 459-459
crates/recording/src/feeds/camera.rs (2)
729-753: LGTM!The addition of explicit cleanup in
InputConnectFailed(clearingconnectingand drainingon_ready) ensures that listeners are properly notified when a connection attempt fails, making the feed available for new operations.
592-603: This is a correct and intentional semantic change — no issues found.The relaxed pattern matching at lines 592-603 is a legitimate improvement, not a race condition risk. The change allows
ListenForReadyto signal readiness once a connection attempt completes (connecting: None), rather than requiring the feed to be completely idle (connected: NoneANDattached: None).This aligns with the semantics: the only direct caller (
await_camera_preview_ready) is a Tauri UI command waiting for the camera preview to become displayable, not for the feed to be completely disconnected. The function name itself clarifies the intent—"preview ready," not "feed idle."The code correctly fires ready notifications (line 721-722) after connection finalization, and no callers make assumptions about the
attachedstate beingNonewhen the signal fires.apps/desktop/src-tauri/src/recording.rs (2)
550-599: LGTM! Camera initialization logic is more robust.The new staged initialization correctly handles the case where a camera is selected but not yet initialized. The flow is:
- Attempt to lock the camera (optimistic path)
- If it fails with
NoInputand a camera ID is selected, initialize viaSetInputand retry the lock- Handle and log errors appropriately at each stage
The nested matching is complex but correct. The logic properly sets
camera_in_usebased on whether a camera feed lock was obtained.
1074-1082: LGTM! Input state cleanup is correct.Clearing
selected_mic_label,selected_camera_id, andcamera_in_usewhen the main window is absent ensures clean state management. This correctly handles the case where the recording session is ending without the main window being available to retain the input selections.apps/desktop/src/routes/in-progress-recording.tsx (3)
40-40: LGTM! Initializing state improves UX.The addition of an
"initializing"state variant provides better feedback during the transition from countdown to actual recording, improving the user experience.Also applies to: 60-68
481-489: LGTM! Helper functions improve readability.The helper functions (
isInitializing,isCountdown,countdownCurrent) and the updatedadjustedTimelogic correctly handle the new initializing state.Also applies to: 518-523
547-547: LGTM! UI updates properly reflect initialization states.The UI changes correctly:
- Add visual polish with border and shadow styling
- Disable controls appropriately during initializing and countdown states
- Show contextual feedback ("Starting...", countdown progress)
- Improve user experience by preventing premature actions
Also applies to: 552-575, 625-660
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
apps/desktop/src/routes/in-progress-recording.tsx (1)
193-203: Remove the unsafe type cast (duplicate issue).This is the same issue flagged in the previous review. The
as CurrentRecordingcast on line 195 bypasses TypeScript's type safety and could lead to runtime errors if the data structure changes.Apply this diff to use safer optional chaining:
createEffect(() => { if (state().variant === "initializing") { - const recording = currentRecording.data as CurrentRecording; - if (recording?.status === "recording") { + if (currentRecording.data?.status === "recording") { setDisconnectedInputs({ microphone: false, camera: false }); setRecordingFailure(null); setState({ variant: "recording" }); setStart(Date.now()); } } });
🧹 Nitpick comments (1)
apps/desktop/src/routes/in-progress-recording.tsx (1)
552-554: Consider disabling pause, restart, and delete during initializing state.The stop button is disabled during both
initializingandcountdownstates (lines 552-554), but the pause (lines 626-630), restart (line 652), and delete (line 660) buttons only check forcountdown. For consistency and to prevent potential issues during the initialization phase, consider also disabling these buttons whenisInitializing()is true.Apply this diff to add initializing checks:
<ActionButton disabled={ togglePause.isPending || hasDisconnectedInput() || - isCountdown() + isCountdown() || + isInitializing() } onClick={() => togglePause.mutate()}<ActionButton - disabled={restartRecording.isPending || isCountdown()} + disabled={restartRecording.isPending || isCountdown() || isInitializing()} onClick={() => restartRecording.mutate()}<ActionButton - disabled={deleteRecording.isPending || isCountdown()} + disabled={deleteRecording.isPending || isCountdown() || isInitializing()} onClick={() => deleteRecording.mutate()}Also applies to: 626-630, 652-652, 660-660
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/desktop/src/routes/in-progress-recording.tsx(9 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by runningpnpm format.
Files:
apps/desktop/src/routes/in-progress-recording.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g.,user-menu.tsx).
Use PascalCase for React/Solid components.
Files:
apps/desktop/src/routes/in-progress-recording.tsx
🧬 Code graph analysis (1)
apps/desktop/src/routes/in-progress-recording.tsx (1)
apps/desktop/src/utils/tauri.ts (4)
CurrentRecording(386-386)stopRecording(20-22)restartRecording(29-31)deleteRecording(32-34)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Clippy (aarch64-apple-darwin, macos-latest)
- GitHub Check: Analyze (rust)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (7)
apps/desktop/src/routes/(window-chrome)/settings/experimental.tsx (1)
66-75: OS‑gating the native camera preview toggleConditionally hiding the “Native camera preview” toggle when the OS type is
"windows"is a good UX guard for a feature that’s unreliable on that platform. The inlinetype()call is cheap, but if this component re-renders frequently you could cache the OS type at module scope or in a small helper to avoid repeated calls.apps/desktop/src-tauri/src/camera.rs (1)
44-59: State clamping and serialization migration behaviorThe new numeric
CameraPreviewState::sizeplusclamp_sizeusage inget_state,set_state, andCursorConfiguration::defaultgive a clear, bounded size model and ensure uniforms see a normalized[0, 1]value. That part looks solid.One side effect: existing JSON in the
"cameraPreview"store that was serialized with the old enum‑basedsizewill fail to deserialize into the new struct and will fall back toCameraPreviewState::default()viaunwrap_or_default(). That means any previously storedshape/mirroredoverrides will be lost on first read.If preserving previous user‑chosen shape/mirroring is important, consider a small migration path (e.g., read the raw JSON and map legacy string values into the new struct) instead of relying solely on
unwrap_or_default(). Otherwise, the current behavior is acceptable as a reset to sane defaults.Also applies to: 82-92, 96-102, 463-488
apps/desktop/src-tauri/src/camera.wgsl (1)
102-106: Continuous corner-radius interpolation matches normalizedsizebut comment is staleUsing
mix(0.10, 0.14, size)for the square shape andmix(0.08, 0.12, size)for the full shape is a nice continuous generalization of the previous small/large corner radii and lines up with the host side now sending a normalized[0, 1]sizeuniform.The top comment still says
// Size constants: 0 = Sm, 1 = Lg, which no longer reflects the continuous range semantics. Consider updating that comment to describesizeas a normalized 0‑1 value derived from[MIN_CAMERA_SIZE, MAX_CAMERA_SIZE]to avoid confusion for future shader changes.Also applies to: 127-129, 142-144
apps/desktop/src/routes/camera.tsx (4)
121-173: Consider extracting resize logic to reduce duplication.The resize handlers (
handleResizeStart,handleResizeMove,handleResizeEnd) and the effect that manages event listeners are duplicated inLegacyCameraPreviewPage(lines 355-407). This represents significant code duplication that could be extracted into a shared hook or utility function.Consider creating a custom hook like
createResizeHandlerthat returns these handlers and manages the state:function createResizeHandler( state: { size: number }, setState: (key: "size", value: number) => void, minSize: number, maxSize: number ) { const [isResizing, setIsResizing] = createSignal(false); const [resizeStart, setResizeStart] = createSignal({ size: 0, x: 0, y: 0, corner: "", }); // ... rest of the logic return { handleResizeStart, isResizing }; }
260-272: Clarify or remove unusedactiveprop.The
activeprop is defined in the component signature but never used. If it's intended for future use or external access, consider adding a comment. Otherwise, remove it to avoid confusion.
349-353: Extract duplicatescalefunction.This
scalefunction is identical to the one inNativeCameraPreviewPage(lines 115-119). Consider extracting it to a shared location to eliminate duplication.Define it once at the module level:
function calculateScale( size: number, minSize: number = CAMERA_MIN_SIZE, maxSize: number = CAMERA_MAX_SIZE ) { const normalized = (size - minSize) / (maxSize - minSize); return 0.7 + normalized * 0.3; }
306-323: SimplifyimageDataHandlerto reduce side effects.The function has multiple responsibilities: updating frame state, updating dimensions, and rendering to canvas. Consider separating concerns:
- Frame data updates
- Dimension tracking
- Canvas rendering (could be handled by a separate effect)
This would improve testability and make the data flow clearer.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/desktop/src-tauri/src/camera.rs(5 hunks)apps/desktop/src-tauri/src/camera.wgsl(2 hunks)apps/desktop/src/routes/(window-chrome)/settings/experimental.tsx(2 hunks)apps/desktop/src/routes/camera.tsx(1 hunks)apps/desktop/src/utils/tauri.ts(3 hunks)crates/project/src/configuration.rs(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Format Rust code usingrustfmtand ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.
Files:
crates/project/src/configuration.rsapps/desktop/src-tauri/src/camera.rs
crates/*/src/**/*
📄 CodeRabbit inference engine (AGENTS.md)
Rust crates should place tests within the
src/and/or a siblingtests/directory for each crate insidecrates/*.
Files:
crates/project/src/configuration.rs
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by runningpnpm format.
Files:
apps/desktop/src/routes/(window-chrome)/settings/experimental.tsxapps/desktop/src/utils/tauri.tsapps/desktop/src/routes/camera.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g.,user-menu.tsx).
Use PascalCase for React/Solid components.
Files:
apps/desktop/src/routes/(window-chrome)/settings/experimental.tsxapps/desktop/src/utils/tauri.tsapps/desktop/src/routes/camera.tsx
**/tauri.ts
📄 CodeRabbit inference engine (AGENTS.md)
Do not edit auto-generated files named
tauri.ts.
Files:
apps/desktop/src/utils/tauri.ts
🧠 Learnings (1)
📚 Learning: 2025-09-22T14:19:56.010Z
Learnt from: CR
Repo: CapSoftware/Cap PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-22T14:19:56.010Z
Learning: Applies to **/tauri.ts : Do not edit auto-generated files named `tauri.ts`.
Applied to files:
apps/desktop/src/utils/tauri.ts
🧬 Code graph analysis (3)
apps/desktop/src/routes/(window-chrome)/settings/experimental.tsx (1)
apps/desktop/src/routes/(window-chrome)/settings/Setting.tsx (1)
ToggleSettingItem(24-40)
apps/desktop/src/routes/camera.tsx (6)
apps/desktop/src/store.ts (1)
generalSettingsStore(61-62)apps/desktop/src/utils/createEventListener.ts (1)
createTauriEventListener(30-44)apps/desktop/src/utils/tauri.ts (4)
events(297-341)commands(7-292)LogicalSize(425-425)LogicalPosition(424-424)apps/desktop/src/routes/(window-chrome)/OptionsContext.tsx (2)
RecordingOptionsProvider(18-18)useRecordingOptions(9-16)apps/desktop/src/utils/queries.ts (1)
createCameraMutation(195-236)apps/desktop/src/utils/socket.ts (2)
createLazySignal(88-109)createImageDataWS(4-86)
apps/desktop/src-tauri/src/camera.rs (1)
apps/desktop/src/utils/tauri.ts (2)
CameraPreviewState(368-368)CameraPreviewShape(367-367)
🪛 GitHub Actions: CI
apps/desktop/src/routes/(window-chrome)/settings/experimental.tsx
[error] 1-1: Biome: File content differs from formatting output. The imports and exports are not sorted.
apps/desktop/src/routes/camera.tsx
[error] 1-1: Biome: File content differs from formatting output. The imports and exports are not sorted.
apps/desktop/src-tauri/src/camera.rs
[error] 62-62: clamp-like pattern without using clamp function
[note] 62-62: clamp will panic if max < min, min.is_nan(), or max.is_nan()
[note] 62-62: clamp returns NaN if the input is NaN
[help] 62-62: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.91.0/index.html#manual_clamp
[help] 62-62: replace with clamp
🪛 GitHub Check: Clippy (aarch64-apple-darwin, macos-latest)
apps/desktop/src-tauri/src/camera.rs
[failure] 62-62: clamp-like pattern without using clamp function
error: clamp-like pattern without using clamp function
--> apps/desktop/src-tauri/src/camera.rs:62:5
|
62 | size.max(MIN_CAMERA_SIZE).min(MAX_CAMERA_SIZE)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: size.clamp(MIN_CAMERA_SIZE, MAX_CAMERA_SIZE)
|
= note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
= note: clamp returns NaN if the input is NaN
= help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.91.0/index.html#manual_clamp
= note: requested on the command line with -D clippy::manual-clamp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (rust)
🔇 Additional comments (9)
crates/project/src/configuration.rs (1)
401-407: Confirm intentional change of default cursor animation style
CursorAnimationStylenow defaults toMellowinstead ofSlow, and the serde aliases ("regular","quick","rapid","fast") all deserialize toMellow. This also changes the defaultCursorConfigurationsmoothing parameters viapreset(). If this UX change is intentional, the implementation looks fine; just be aware this will subtly alter existing projects’ default cursor feel.apps/desktop/src/utils/tauri.ts (1)
368-369: Generated API surface changes look consistent; ensure callers are updatedThe generated bindings now expose:
CameraPreviewState.sizeas anumberinstead of a"sm" | "lg"-style union.CurrentRecordingwith a newstatus: RecordingStatusfield.- A new
RecordingStatus = "pending" | "recording"type.These changes align with the tauri-side API evolution and look consistent for a tauri-specta output. Just make sure any hand-written TS/JS that references these types (e.g., assumes
sizeis a string enum or that recordings lackstatus) is updated accordingly. Since this file is auto-generated, any further tweaks should be done by changing the Rust definitions and regenerating, not by editing this file directly.Also applies to: 385-386, 458-458
apps/desktop/src-tauri/src/camera.rs (2)
623-637: Normalized size for shader uniforms matches new sizing modelUsing
clamp_sizefollowed by a normalized size(clamped_size - MIN_CAMERA_SIZE) / (MAX_CAMERA_SIZE - MIN_CAMERA_SIZE)is a good fit for driving the shader’ssizeuniform as a 0‑1 interpolation parameter. This keeps GPU behavior stable even if the stored size drifts, and matches the continuous corner-radius interpolation added incamera.wgsl.
676-693: Window resize logic correctly reuses clamped base size
resize_windownow useslet base = clamp_size(state.size);and deriveswindow_width/window_heightfrom it, including the toolbar height. This keeps OS window sizing consistent with the clamped range and the normalized shader size, without introducing any obvious edge cases (aspect ratio handling forFullstill behaves as before).apps/desktop/src/routes/camera.tsx (5)
34-45: LGTM!The type definitions and constants are well-structured and provide clear boundaries for camera window sizing and configuration.
51-53: Verify handling of undefined generalSettings during initial load.The condition accesses
generalSettings.data?.enableNativeCameraPreviewwhich might be undefined during the initial query load. While the optional chaining handles the undefined case, the fallback tofalsemeans native preview will be disabled until settings load.Confirm this behavior is intentional and doesn't cause UI flickering or state issues during the initial render.
452-460: Verify window close behavior on camera removal.When
cameraLabelbecomesnull, the window is immediately closed. Ensure this is the desired behavior and doesn't interrupt user workflows or cause issues when the camera is temporarily unavailable.Consider adding a grace period or user notification before closing:
createEffect( on( () => rawOptions.cameraLabel, (label) => { if (label === null) { // Consider: Show a notification or delay before closing setTimeout(() => getCurrentWindow().close(), 1000); } }, { defer: true } ) );
620-634: LGTM!The helper functions are well-implemented.
cameraBorderRadiusprovides smooth interpolation between min/max sizes for non-round shapes, which will create a nice visual effect.
636-647: LGTM!Clean overlay implementation. Inheriting the parent's border radius ensures visual consistency across different camera shapes.
apps/desktop/src/routes/(window-chrome)/settings/experimental.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/recording/src/sources/camera.rs (1)
37-37: Remove unnecessarymutfrom parameter.The
video_txparameter at line 37 doesn't need to be mutable since it's immediately reassigned when wrapped inLogDropat line 50. Move themutto the reassignment only.Apply this diff:
async fn setup( feed_lock: Self::Config, - mut video_tx: mpsc::Sender<Self::Frame>, + video_tx: mpsc::Sender<Self::Frame>, _: &mut SetupCtx, ) -> anyhow::Result<Self> where Self: Sized, {Also applies to: 50-50
♻️ Duplicate comments (3)
apps/desktop/src/routes/camera.tsx (3)
325-347: Fix WebSocket reconnection logic and unsafe property access.Multiple issues remain in this segment:
- Line 325: Unsafe access to
window.__CAP__with type assertion- Lines 331-342: The reconnection logic attempts to mutate the existing WebSocket via
Object.assign(ws, newWs[0]), which won't properly establish the new connection- The
reconnectIntervalcontinues running even after component unmount if cleanup happens before the next interval tickConsider refactoring to properly manage WebSocket lifecycle:
- const { cameraWsPort } = (window as any).__CAP__; + const { cameraWsPort } = (window as { __CAP__: { cameraWsPort: number } }).__CAP__; - const [ws, isConnected] = createImageDataWS( - `ws://localhost:${cameraWsPort}`, - imageDataHandler, - ); - - const reconnectInterval = setInterval(() => { - if (!isConnected()) { - console.log("Attempting to reconnect..."); - ws.close(); - - const newWs = createImageDataWS( - `ws://localhost:${cameraWsPort}`, - imageDataHandler, - ); - Object.assign(ws, newWs[0]); - } - }, 5000); - - onCleanup(() => { - clearInterval(reconnectInterval); - ws.close(); - }); + const [wsSignal, setWsSignal] = createSignal( + createImageDataWS(`ws://localhost:${cameraWsPort}`, imageDataHandler) + ); + + const ws = () => wsSignal()[0]; + const isConnected = () => wsSignal()[1](); + + const reconnectInterval = setInterval(() => { + if (!isConnected()) { + console.log("Attempting to reconnect..."); + ws().close(); + setWsSignal( + createImageDataWS(`ws://localhost:${cameraWsPort}`, imageDataHandler) + ); + } + }, 5000); + + onCleanup(() => { + clearInterval(reconnectInterval); + ws().close(); + });
113-113: Remove unused variable.The
setCameramutation is created but never used inNativeCameraPreviewPage.Apply this diff:
- const setCamera = createCameraMutation(); -
450-450: Remove unused variable.The
setCameramutation is created but never used inLegacyCameraPreviewPage.Apply this diff:
- const setCamera = createCameraMutation(); -
🧹 Nitpick comments (4)
crates/recording/src/studio_recording.rs (1)
67-67: Minor typo in log message."shuting" should be "shutting".
Apply this diff to fix the typo:
- tracing::info!("pipeline shuting down"); + tracing::info!("pipeline shutting down");crates/recording/src/sources/camera.rs (1)
52-75: Simplified forwarding logic is correct.The removal of retry/reattachment logic aligns with the PR's goal of more explicit control flows. Breaking on pipeline closure (line 65) is the correct behavior—it ensures proper cleanup and disconnection from the camera feed.
Consider removing the uncertain comments.
The comments at lines 61-65 express uncertainty about the control flow decision, but the implementation already makes the correct choice by breaking. Consider removing these comments since the decision is final.
Apply this diff to clean up the comments:
Ok(frame) => { // tracing::trace!("Sending camera frame"); if let Err(e) = video_tx.send(frame).await { tracing::warn!("Failed to send to video pipeline: {e}"); - // If pipeline is closed, we should stop? - // But lets continue to keep rx alive for now to see if it helps, - // or maybe break? - // If we break, we disconnect from CameraFeed. - // If pipeline is closed, we SHOULD disconnect. break; } }apps/desktop/src/routes/camera.tsx (2)
115-173: Consider extracting duplicated resize logic.The resize handlers (
handleResizeStart,handleResizeMove,handleResizeEnd), thescalefunction, and the resize effect are duplicated betweenNativeCameraPreviewPage(lines 115-173) andLegacyCameraPreviewPage(lines 349-407). This duplication makes maintenance harder and increases the risk of inconsistencies.Consider extracting this logic into a shared custom hook or utility function:
function useResizeLogic(state: { size: number }, setState: (key: string, value: any) => void) { const [isResizing, setIsResizing] = createSignal(false); const [resizeStart, setResizeStart] = createSignal({ size: 0, x: 0, y: 0, corner: "", }); const scale = () => { const normalized = (state.size - CAMERA_MIN_SIZE) / (CAMERA_MAX_SIZE - CAMERA_MIN_SIZE); return 0.7 + normalized * 0.3; }; const handleResizeStart = (corner: string) => (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); setIsResizing(true); setResizeStart({ size: state.size, x: e.clientX, y: e.clientY, corner }); }; const handleResizeMove = (e: MouseEvent) => { if (!isResizing()) return; const start = resizeStart(); const deltaX = e.clientX - start.x; const deltaY = e.clientY - start.y; let delta = 0; if (start.corner.includes("e") && start.corner.includes("s")) { delta = Math.max(deltaX, deltaY); } else if (start.corner.includes("e") && start.corner.includes("n")) { delta = Math.max(deltaX, -deltaY); } else if (start.corner.includes("w") && start.corner.includes("s")) { delta = Math.max(-deltaX, deltaY); } else if (start.corner.includes("w") && start.corner.includes("n")) { delta = Math.max(-deltaX, -deltaY); } else if (start.corner.includes("e")) { delta = deltaX; } else if (start.corner.includes("w")) { delta = -deltaX; } else if (start.corner.includes("s")) { delta = deltaY; } else if (start.corner.includes("n")) { delta = -deltaY; } const newSize = Math.max( CAMERA_MIN_SIZE, Math.min(CAMERA_MAX_SIZE, start.size + delta), ); setState("size", newSize); }; const handleResizeEnd = () => { setIsResizing(false); }; createEffect(() => { if (isResizing()) { window.addEventListener("mousemove", handleResizeMove); window.addEventListener("mouseup", handleResizeEnd); onCleanup(() => { window.removeEventListener("mousemove", handleResizeMove); window.removeEventListener("mouseup", handleResizeEnd); }); } }); return { scale, handleResizeStart }; }
229-248: Consider extracting resize handles into a shared component.The four corner resize handles are duplicated between
NativeCameraPreviewPage(lines 229-248) andLegacyCameraPreviewPage(lines 518-537).You could extract them into a reusable component:
function ResizeHandles(props: { onResizeStart: (corner: string) => (e: MouseEvent) => void }) { return ( <> <div class="absolute top-0 left-0 w-4 h-4 cursor-nw-resize opacity-0 group-hover:opacity-100 transition-opacity z-10" style={{ "pointer-events": "auto" }} onMouseDown={props.onResizeStart("nw")} /> <div class="absolute top-0 right-0 w-4 h-4 cursor-ne-resize opacity-0 group-hover:opacity-100 transition-opacity z-10" style={{ "pointer-events": "auto" }} onMouseDown={props.onResizeStart("ne")} /> <div class="absolute bottom-0 left-0 w-4 h-4 cursor-sw-resize opacity-0 group-hover:opacity-100 transition-opacity z-10" style={{ "pointer-events": "auto" }} onMouseDown={props.onResizeStart("sw")} /> <div class="absolute bottom-0 right-0 w-4 h-4 cursor-se-resize opacity-0 group-hover:opacity-100 transition-opacity z-10" style={{ "pointer-events": "auto" }} onMouseDown={props.onResizeStart("se")} /> </> ); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
AGENTS.md(1 hunks)CLAUDE.md(1 hunks)apps/desktop/src/routes/(window-chrome)/settings/experimental.tsx(2 hunks)apps/desktop/src/routes/camera.tsx(13 hunks)crates/recording/src/feeds/camera.rs(4 hunks)crates/recording/src/output_pipeline/core.rs(2 hunks)crates/recording/src/sources/camera.rs(2 hunks)crates/recording/src/studio_recording.rs(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- AGENTS.md
🚧 Files skipped from review as they are similar to previous changes (2)
- crates/recording/src/feeds/camera.rs
- apps/desktop/src/routes/(window-chrome)/settings/experimental.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Format Rust code usingrustfmtand ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.
Files:
crates/recording/src/studio_recording.rscrates/recording/src/sources/camera.rscrates/recording/src/output_pipeline/core.rs
crates/*/src/**/*
📄 CodeRabbit inference engine (AGENTS.md)
Rust crates should place tests within the
src/and/or a siblingtests/directory for each crate insidecrates/*.
Files:
crates/recording/src/studio_recording.rscrates/recording/src/sources/camera.rscrates/recording/src/output_pipeline/core.rs
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by runningpnpm format.
Files:
apps/desktop/src/routes/camera.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g.,user-menu.tsx).
Use PascalCase for React/Solid components.
Files:
apps/desktop/src/routes/camera.tsx
🧠 Learnings (5)
📚 Learning: 2025-09-22T14:19:56.010Z
Learnt from: CR
Repo: CapSoftware/Cap PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-22T14:19:56.010Z
Learning: Applies to **/*.rs : Format Rust code using `rustfmt` and ensure all Rust code passes workspace-level clippy lints.
Applied to files:
CLAUDE.md
📚 Learning: 2025-09-22T14:19:56.010Z
Learnt from: CR
Repo: CapSoftware/Cap PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-22T14:19:56.010Z
Learning: Applies to **/*.{ts,tsx} : Use Biome for formatting and linting TypeScript/JavaScript files by running `pnpm format`.
Applied to files:
CLAUDE.mdapps/desktop/src/routes/camera.tsx
📚 Learning: 2025-09-22T14:19:56.010Z
Learnt from: CR
Repo: CapSoftware/Cap PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-22T14:19:56.010Z
Learning: Applies to **/*.rs : Rust modules should be named with snake_case, and crate directories should be in kebab-case.
Applied to files:
CLAUDE.md
📚 Learning: 2025-10-28T08:39:42.230Z
Learnt from: Brendonovich
Repo: CapSoftware/Cap PR: 1305
File: crates/recording/src/output_pipeline/macos.rs:80-90
Timestamp: 2025-10-28T08:39:42.230Z
Learning: In `crates/recording/src/output_pipeline/macos.rs`, the `AVFoundationMp4Muxer` intentionally holds the `Mutex<MP4Encoder>` lock during retry attempts in `send_video_frame()` and `send_audio_frame()`. This blocking behavior is correct because frame processing must happen sequentially to prevent audio and video frames from being interleaved incorrectly in the encoder.
Applied to files:
crates/recording/src/sources/camera.rscrates/recording/src/output_pipeline/core.rs
📚 Learning: 2025-10-17T05:58:22.586Z
Learnt from: Brendonovich
Repo: CapSoftware/Cap PR: 1219
File: crates/enc-avfoundation/src/mp4.rs:350-373
Timestamp: 2025-10-17T05:58:22.586Z
Learning: In crates/enc-avfoundation/src/mp4.rs, the `finish()` method intentionally skips video extension when `is_paused` is true. This is correct behavior because if recording is paused, the video should not be extended beyond the pause point—the pause is user-initiated, unlike the case where ScreenCaptureKit stops providing frames during static content.
Applied to files:
crates/recording/src/output_pipeline/core.rs
🧬 Code graph analysis (3)
crates/recording/src/sources/camera.rs (1)
crates/recording/src/feeds/camera.rs (2)
drop(175-179)deref(169-171)
apps/desktop/src/routes/camera.tsx (2)
apps/desktop/src/store.ts (1)
generalSettingsStore(61-62)apps/desktop/src/utils/queries.ts (1)
createCameraMutation(195-236)
crates/recording/src/output_pipeline/core.rs (3)
crates/recording/src/output_pipeline/ffmpeg.rs (1)
timestamp(22-24)crates/recording/src/sources/screen_capture/windows.rs (1)
timestamp(63-65)crates/recording/src/sources/screen_capture/macos.rs (1)
timestamp(61-63)
🪛 GitHub Actions: CI
crates/recording/src/sources/camera.rs
[warning] 37-37: variable does not need to be mutable
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (rust)
🔇 Additional comments (6)
CLAUDE.md (1)
417-424: Excellent addition—formatting guidance aligns with repo standards.The new "Code Formatting" section documents the formatting practices correctly and complements the existing development guidelines. The commands match the repository learnings:
pnpm formatfor TypeScript/JavaScript (Biome) andcargo fmtfor Rust (rustfmt). Placing this guidance at the end of the document, after the references section, is logical and serves as a helpful reminder for developers completing their work.crates/recording/src/studio_recording.rs (1)
858-858: LGTM! Error message now matches the pipeline being set up.The error context correctly identifies this as "system audio pipeline setup" instead of the misleading "microphone pipeline setup". This improves debugging accuracy.
crates/recording/src/sources/camera.rs (2)
13-29: LGTM: Clean debugging utility.The
LogDropwrapper provides transparent access while instrumenting drop events, which will help diagnose channel lifetime issues.
43-43: Capacity increase improves buffering.Increasing the frame channel from 8 to 32 provides better buffering against temporary backpressure, reducing the likelihood of dropped frames.
apps/desktop/src/routes/camera.tsx (1)
628-634: LGTM: Dynamic border radius calculation.The updated
cameraBorderRadiusfunction signature and implementation provide a smooth, size-dependent radius calculation. The normalized approach (3rem + up to 1.5rem based on size) is more elegant than fixed mappings.crates/recording/src/output_pipeline/core.rs (1)
432-432: LGTM!The
mutmodifier is necessary becauseStreamExt::next()requires a mutable reference to the receiver.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
apps/desktop/src-tauri/src/camera.rs (2)
31-33: Numeric sizing + clamping pipeline looks good; consider a small robustness guardThe constants +
clamp_size+ normalized size + window base usage form a consistent pipeline, and switching tof32::clampshould keep clippy happy.One edge case to consider is defensive handling of non‑finite or future degenerate values (e.g. if someone later “helpfully” tweaks the constants):
clampwill panic ifMIN_CAMERA_SIZE >= MAX_CAMERA_SIZE.- NaN inputs propagate through to uniforms/window sizing.
If you want extra robustness, you could add a debug assertion and treat non‑finite sizes as the default:
fn clamp_size(size: f32) -> f32 { - size.clamp(MIN_CAMERA_SIZE, MAX_CAMERA_SIZE) + debug_assert!(MIN_CAMERA_SIZE < MAX_CAMERA_SIZE); + if !size.is_finite() { + DEFAULT_CAMERA_SIZE + } else { + size.clamp(MIN_CAMERA_SIZE, MAX_CAMERA_SIZE) + } }Totally optional, but it makes future changes to the constants and any corrupted persisted state safer.
Also applies to: 61-63, 625-628, 635-635, 683-683
44-49: PersistedCameraPreviewStatenow fully resets to defaults on legacy/type‑mismatched dataThe move from an enum‑based
sizetof32plus:.get("state") .and_then(|v| serde_json::from_value::<CameraPreviewState>(v).ok()) .unwrap_or_default();means that any legacy store entry (e.g.
sizepreviously serialized as a string enum) or any type mismatch will cause deserialization to fail and the entire state to fall back toCameraPreviewState::default().Consequence: existing users with stored camera prefs will likely lose not just their old size, but also
shape/mirroredon upgrade, with no logging.If you want smoother migration, consider:
- Trying to deserialize into the new struct first.
- On failure, attempting a lightweight “upgrade” from the raw
serde_json::Value(e.g. salvageshape/mirrored, map legacy size variants to numeric values), and only then falling back toDefault.- Optionally logging when a fallback/upgrade path is taken so unexpected corruption is visible.
This isn’t a correctness bug, but it is a user‑visible behavioral change worth double‑checking is intentional.
Also applies to: 51-59, 83-93, 96-98
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/desktop/src-tauri/src/camera.rs(5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Format Rust code usingrustfmtand ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.
Files:
apps/desktop/src-tauri/src/camera.rs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (1)
apps/desktop/src/routes/in-progress-recording.tsx (1)
36-37: Leverage RecordingStatus without castingcurrentRecording.dataThe new
"initializing"state plusRecordingStatusintegration andadjustedTimeclamping look good and should avoid negative/early time calculations. The remainingcurrentRecording.data as CurrentRecording | undefinedcast in the effect is still unnecessary ifcreateCurrentRecordingQueryis typed to expose.status– you can mirror existing usage likecurrentRecording.data?.modeand write:createEffect(() => { const s = state(); if (s.variant === "initializing" || s.variant === "countdown") { if (currentRecording.data?.status === "recording") { setDisconnectedInputs({ microphone: false, camera: false }); setRecordingFailure(null); setState({ variant: "recording" }); setStart(Date.now()); } } });This avoids the cast altogether and aligns with the prior feedback about removing
as anyin this spot.Also applies to: 43-47, 64-71, 195-206, 485-493, 522-527
🧹 Nitpick comments (2)
apps/desktop/src-tauri/src/target_select_overlay.rs (1)
151-179: Validate and clamp overlay bounds before casting to physical coordinates
update_camera_overlay_boundscurrently castsf64values directly tou32/i32. If the frontend ever passes negative, NaN, or extremely large values, these casts will silently clamp/convert and could result in a 0-sized or off-screen window.Consider validating/clamping
width/height(e.g.,> 0and within a reasonable max) and maybe roundingx/ybefore casting so failures are explicit and easier to diagnose.apps/desktop/src/routes/target-select-overlay.tsx (1)
401-415: Camera overlay sizing/positioning logic is solid; consider minor cleanupsThe camera-window discovery, original-bounds/scale caching, and placement math (using logical coordinates and
scaleFactorto compute physicalx/y/width/height) are coherent and match the new TAURI command surface.revertCameraplusonRecordingStart={() => setOriginalCameraBounds(null)}also correctly distinguish “cancel selection” (revert) from “start recording” (persist new camera position).Two small non-blocking follow-ups you might consider:
- Using the generated
commands.updateCameraOverlayBoundsinstead of rawinvoke("update_camera_overlay_bounds", ...)for consistency with the rest of the TAURI calls in this file.- If you ever add more camera-labelled windows, the
getAll().find((w) => w.label.includes("camera"))fallback could become ambiguous; a stricter label check (e.g. equality or prefix) would avoid accidental matches.Neither affects correctness today, but they would tighten the integration a bit.
Also applies to: 480-561, 563-573, 588-589, 704-710, 715-716
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
apps/desktop/src-tauri/src/lib.rs(8 hunks)apps/desktop/src-tauri/src/target_select_overlay.rs(1 hunks)apps/desktop/src-tauri/src/windows.rs(3 hunks)apps/desktop/src/routes/in-progress-recording.tsx(10 hunks)apps/desktop/src/routes/target-select-overlay.tsx(11 hunks)apps/desktop/src/utils/tauri.ts(4 hunks)packages/ui-solid/src/auto-imports.d.ts(0 hunks)
💤 Files with no reviewable changes (1)
- packages/ui-solid/src/auto-imports.d.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/desktop/src-tauri/src/windows.rs
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by runningpnpm format.
Files:
apps/desktop/src/routes/in-progress-recording.tsxapps/desktop/src/routes/target-select-overlay.tsxapps/desktop/src/utils/tauri.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g.,user-menu.tsx).
Use PascalCase for React/Solid components.
Files:
apps/desktop/src/routes/in-progress-recording.tsxapps/desktop/src/routes/target-select-overlay.tsxapps/desktop/src/utils/tauri.ts
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Format Rust code usingrustfmtand ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.
Files:
apps/desktop/src-tauri/src/target_select_overlay.rsapps/desktop/src-tauri/src/lib.rs
**/tauri.ts
📄 CodeRabbit inference engine (AGENTS.md)
Do not edit auto-generated files named
tauri.ts.
Files:
apps/desktop/src/utils/tauri.ts
🧠 Learnings (1)
📚 Learning: 2025-10-28T08:39:42.230Z
Learnt from: Brendonovich
Repo: CapSoftware/Cap PR: 1305
File: crates/recording/src/output_pipeline/macos.rs:80-90
Timestamp: 2025-10-28T08:39:42.230Z
Learning: In `crates/recording/src/output_pipeline/macos.rs`, the `AVFoundationMp4Muxer` intentionally holds the `Mutex<MP4Encoder>` lock during retry attempts in `send_video_frame()` and `send_audio_frame()`. This blocking behavior is correct because frame processing must happen sequentially to prevent audio and video frames from being interleaved incorrectly in the encoder.
Applied to files:
apps/desktop/src-tauri/src/lib.rs
🧬 Code graph analysis (4)
apps/desktop/src/routes/in-progress-recording.tsx (1)
apps/desktop/src/utils/tauri.ts (4)
CurrentRecording(388-388)stopRecording(20-22)restartRecording(29-31)deleteRecording(32-34)
apps/desktop/src/routes/target-select-overlay.tsx (3)
apps/desktop/src/utils/tauri.ts (1)
PhysicalSize(442-442)crates/scap-targets/src/bounds.rs (2)
size(19-21)size(46-48)crates/scap-targets/src/platform/macos.rs (1)
scale(111-113)
apps/desktop/src-tauri/src/lib.rs (4)
apps/desktop/src/utils/tauri.ts (4)
RecordingStatus(461-461)CurrentRecording(388-388)CurrentRecordingTarget(390-390)RecordingMode(457-457)apps/desktop/src-tauri/src/recording.rs (2)
mode(234-239)capture_target(164-169)apps/desktop/src-tauri/src/target_select_overlay.rs (2)
None(140-140)update_camera_overlay_bounds(154-179)apps/desktop/src-tauri/src/camera.rs (1)
new(72-79)
apps/desktop/src/utils/tauri.ts (3)
crates/camera-windows/src/lib.rs (2)
width(350-352)height(354-356)crates/rendering/src/decoder/mod.rs (2)
width(24-26)height(28-30)crates/camera/src/lib.rs (2)
width(57-59)height(61-63)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Clippy (aarch64-apple-darwin, macos-latest)
- GitHub Check: Analyze (rust)
🔇 Additional comments (5)
apps/desktop/src/utils/tauri.ts (1)
280-282: Generated bindings for camera overlay and recording status look consistentThe new
updateCameraOverlayBoundscommand and theCameraPreviewState,CurrentRecording, andRecordingStatustype changes match the Rust definitions (RecordingStatus::{Pending, Recording}with camelCase serialization and the extrastatusfield onCurrentRecording). Given this file is specta-generated, further changes should go through the Rust side and regeneration rather than manual edits.Also applies to: 371-372, 388-389, 461-462
apps/desktop/src/routes/target-select-overlay.tsx (1)
223-233: Comment-only note about camera revert behaviourThis block only adds commentary explaining why camera revert is handled via
onCleanupinside the Match rather than the Escape handler; no code behaviour change here, so nothing to action.apps/desktop/src-tauri/src/lib.rs (3)
447-479: Camera input retry logic is robust and state-safeThe new
set_camera_inputflow avoids holding theApplock across awaits, cleanly retriesSetInputup to three times with logging, and only re-locks and updatesselected_camera_id/camera_in_useonce the camera is ready. This should make camera startup much less flaky while keeping the recording state andRecordingEvent::InputRestorednotifications consistent.Also applies to: 489-515
756-769:RecordingStatusaddition andget_current_recordingwiring look correctThe new
RecordingStatusenum and correspondingstatusfield onCurrentRecordingcorrectly mirrorRecordingState::{None, Pending, Active}:Pendinguses the stored mode/target, andActivedelegates toinner.mode()andinner.capture_target(). The TS bindings ("pending" | "recording") now have a precise backend source of truth, which matches howin-progress-recording.tsxdrives its"initializing"/"recording"UI.Also applies to: 771-809
2590-2592: Recording start and window-destruction paths now keep mic/camera state coherent
RequestStartRecording::listen_any_spawnnow runsset_mic_inputandset_camera_inputwith the persistedRecordingSettingsStorebefore callingstart_recording, which ensures backend and UI agree on selected devices for hotkey-driven starts.- When the main window is destroyed and no recording is active or pending, you now remove mic/camera inputs and clear
selected_mic_label/selected_camera_id/camera_in_use, avoiding stale “in use” flags.- On camera window close,
camera_preview.on_window_close()is called and the camera input is removed when not recording, resettingcamera_in_useappropriately.- The TAURI command list includes
target_select_overlay::update_camera_overlay_bounds, so the new TS bindings and camera overlay sync can actually invoke it.These changes keep device state, recording state, and window lifecycle much better aligned.
Also applies to: 2660-2671, 2728-2739, 2215-2221, 2295-2298
| disabled={ | ||
| stopRecording.isPending || isInitializing() || isCountdown() | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Disable pause/restart/delete during the new “initializing” phase as well
You correctly disable the Stop button during isInitializing() and isCountdown(), but Pause/Restart/Delete are only gated on isCountdown() (plus other conditions). This means users can trigger pauseRecording/restartRecording/deleteRecording while the overlay is still in the "initializing" state, before the recording is actually active.
To keep the control surface consistent and avoid odd backend calls, consider extending the disabled conditions, e.g.:
- disabled={
- togglePause.isPending ||
- hasDisconnectedInput() ||
- isCountdown()
- }
+ disabled={
+ togglePause.isPending ||
+ hasDisconnectedInput() ||
+ isCountdown() ||
+ isInitializing()
+ }
- disabled={restartRecording.isPending || isCountdown()}
+ disabled={
+ restartRecording.isPending ||
+ isCountdown() ||
+ isInitializing()
+ }
- disabled={deleteRecording.isPending || isCountdown()}
+ disabled={
+ deleteRecording.isPending ||
+ isCountdown() ||
+ isInitializing()
+ }That keeps all destructive actions unavailable until the recording has really started.
Also applies to: 566-623, 674-678, 700-709
🏁 Script executed:
#!/bin/bash
# Extract the relevant code sections from the file
echo "=== Lines 555-557 (Stop button) ==="
sed -n '555,557p' apps/desktop/src/routes/in-progress-recording.tsx
echo ""
echo "=== Lines 566-623 (Pause button area) ==="
sed -n '566,623p' apps/desktop/src/routes/in-progress-recording.tsx
echo ""
echo "=== Lines 674-678 (Restart button area) ==="
sed -n '674,678p' apps/desktop/src/routes/in-progress-recording.tsx
echo ""
echo "=== Lines 700-709 (Delete button area) ==="
sed -n '700,709p' apps/desktop/src/routes/in-progress-recording.tsx
echo ""
echo "=== Search for isInitializing definition and usage ==="
rg -n 'isInitializing' apps/desktop/src/routes/in-progress-recording.tsx -C 2
echo ""
echo "=== Search for isCountdown definition and usage ==="
rg -n 'isCountdown' apps/desktop/src/routes/in-progress-recording.tsx -C 2Length of output: 4843
Add isInitializing() check to Pause, Restart, and Delete button disabled conditions for consistency with Stop button
Verification confirms the review comment is accurate. The Stop button correctly checks isInitializing() (line 556), but the Pause button (line 677), Restart button (line 700), and Delete button (line 708) each check only isCountdown(). This creates an inconsistency in the control surface.
The fix should extend the disabled conditions as suggested:
- Pause button: add
isInitializing()to the condition at line 677 - Restart button: add
isInitializing()to the condition at line 700 - Delete button: add
isInitializing()to the condition at line 708
This ensures all destructive/state-changing actions are consistently unavailable until the recording fully transitions past the initialization phase.
🤖 Prompt for AI Agents
In apps/desktop/src/routes/in-progress-recording.tsx around lines 555–708, the
Pause (line 677), Restart (line 700), and Delete (line 708) buttons only check
isCountdown() when deciding disabled state, creating an inconsistency with the
Stop button which also checks isInitializing(); update each of those three
disabled conditions to include isInitializing() alongside isCountdown() (i.e.,
disable the button when stopRecording.isPending || isInitializing() ||
isCountdown()) so all destructive/state-changing actions remain disabled during
initialization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/recording/src/output_pipeline/core.rs (1)
510-538: Fix formatting in audio timestamp calculation.The audio muxing task uses the same
checked_duration_since(...).unwrap_or(Duration::ZERO)pattern as the video path (line 521). While this ensures consistency between audio and video handling, CI reports formatting differences on lines 518-523.Run
cargo fmtto fix the formatting issue.This calculation shares the same zero-duration fallback behavior as the video path reviewed above. The verification script there will check both occurrences.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
crates/recording/src/output_pipeline/core.rs(3 hunks)crates/timestamp/src/lib.rs(1 hunks)crates/timestamp/src/macos.rs(1 hunks)crates/timestamp/src/win.rs(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Format Rust code usingrustfmtand ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.
Files:
crates/timestamp/src/win.rscrates/timestamp/src/macos.rscrates/timestamp/src/lib.rscrates/recording/src/output_pipeline/core.rs
crates/*/src/**/*
📄 CodeRabbit inference engine (AGENTS.md)
Rust crates should place tests within the
src/and/or a siblingtests/directory for each crate insidecrates/*.
Files:
crates/timestamp/src/win.rscrates/timestamp/src/macos.rscrates/timestamp/src/lib.rscrates/recording/src/output_pipeline/core.rs
🧠 Learnings (2)
📚 Learning: 2025-10-28T08:39:42.230Z
Learnt from: Brendonovich
Repo: CapSoftware/Cap PR: 1305
File: crates/recording/src/output_pipeline/macos.rs:80-90
Timestamp: 2025-10-28T08:39:42.230Z
Learning: In `crates/recording/src/output_pipeline/macos.rs`, the `AVFoundationMp4Muxer` intentionally holds the `Mutex<MP4Encoder>` lock during retry attempts in `send_video_frame()` and `send_audio_frame()`. This blocking behavior is correct because frame processing must happen sequentially to prevent audio and video frames from being interleaved incorrectly in the encoder.
Applied to files:
crates/recording/src/output_pipeline/core.rs
📚 Learning: 2025-10-17T05:58:22.586Z
Learnt from: Brendonovich
Repo: CapSoftware/Cap PR: 1219
File: crates/enc-avfoundation/src/mp4.rs:350-373
Timestamp: 2025-10-17T05:58:22.586Z
Learning: In crates/enc-avfoundation/src/mp4.rs, the `finish()` method intentionally skips video extension when `is_paused` is true. This is correct behavior because if recording is paused, the video should not be extended beyond the pause point—the pause is user-initiated, unlike the case where ScreenCaptureKit stops providing frames during static content.
Applied to files:
crates/recording/src/output_pipeline/core.rs
🧬 Code graph analysis (4)
crates/timestamp/src/win.rs (2)
crates/timestamp/src/lib.rs (1)
checked_duration_since(35-44)crates/timestamp/src/macos.rs (2)
checked_duration_since(33-40)new(14-16)
crates/timestamp/src/macos.rs (2)
crates/timestamp/src/lib.rs (1)
checked_duration_since(35-44)crates/timestamp/src/win.rs (2)
checked_duration_since(49-66)new(26-28)
crates/timestamp/src/lib.rs (2)
crates/timestamp/src/macos.rs (1)
checked_duration_since(33-40)crates/timestamp/src/win.rs (1)
checked_duration_since(49-66)
crates/recording/src/output_pipeline/core.rs (3)
crates/recording/src/output_pipeline/ffmpeg.rs (1)
timestamp(22-24)crates/recording/src/sources/screen_capture/windows.rs (1)
timestamp(63-65)crates/recording/src/sources/screen_capture/macos.rs (1)
timestamp(61-63)
🪛 GitHub Actions: CI
crates/timestamp/src/lib.rs
[error] 37-40: cargo fmt --check failed: formatting differences in PerformanceCounter branch. Run 'cargo fmt' to fix formatting in this region.
crates/recording/src/output_pipeline/core.rs
[error] 464-468: cargo fmt --check failed: formatting differences detected in duration calculation. Run 'cargo fmt' to fix formatting in this region.
[error] 518-523: cargo fmt --check failed: formatting differences detected in timestamp calculation. Run 'cargo fmt' to fix formatting in this region.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (rust)
🔇 Additional comments (4)
crates/timestamp/src/macos.rs (1)
33-40: LGTM!The
checked_duration_sinceimplementation correctly mirrorsduration_sincewhile returningOption<Duration>for safer duration arithmetic. The use ofchecked_subwith the?operator provides clean underflow handling, and the frequency calculation is consistent with the existing method.crates/timestamp/src/win.rs (1)
49-66: LGTM!The Windows implementation of
checked_duration_sincecorrectly handles negative timestamp differences by returningNone, and the duration calculation logic matches the existingduration_sincemethod. The cross-platform API is now consistent between Windows and macOS.crates/recording/src/output_pipeline/core.rs (2)
432-432: LGTM!Adding
muttovideo_rxis necessary for the.next()calls in the refactored mux-video task below.
453-492: Address formatting and document duration fallback intent.Error handling improvement is confirmed good—
send_video_frameerrors are now properly propagated (lines 483-485) via therun_until_cancelledpattern. However, two issues require attention:
Formatting: CI already flagged differences on lines 464-468. Run
cargo fmtlocally to fix before pushing.Duration fallback: Both video (line 467) and audio (line 521) encoders use identical
unwrap_or(Duration::ZERO)fallback whenchecked_duration_sincereturnsNone. This occurs during clock skew or timestamp monotonicity violations. The pattern is consistent across both tasks, suggesting intentional design, but it lacks documentation. Verify this is not masking real timing bugs. If intentional, add a comment explaining the fallback rationale. Otherwise, consider usingsaturating_duration_since(Rust's standard approach for this scenario).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/desktop/src/routes/camera.tsx (1)
80-407: Eliminate code duplication between Native and Legacy components.The resize logic (lines 90-173 vs 288-407), scale function (lines 115-119 vs 349-353), control panel rendering (lines 185-225 vs 473-513), and resize handles (lines 229-248 vs 516-535) are duplicated between
NativeCameraPreviewPageandLegacyCameraPreviewPage.Extract shared logic into reusable hooks and components:
// Extract resize logic function useCameraResize(state: CameraWindowState, setState: SetStoreFunction<CameraWindowState>) { const [isResizing, setIsResizing] = createSignal(false); const [resizeStart, setResizeStart] = createSignal({ size: 0, x: 0, y: 0, corner: "", }); const handleResizeStart = (corner: string) => (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); setIsResizing(true); setResizeStart({ size: state.size, x: e.clientX, y: e.clientY, corner }); }; const handleResizeMove = (e: MouseEvent) => { if (!isResizing()) return; const start = resizeStart(); const deltaX = e.clientX - start.x; const deltaY = e.clientY - start.y; let delta = 0; if (start.corner.includes("e") && start.corner.includes("s")) { delta = Math.max(deltaX, deltaY); } else if (start.corner.includes("e") && start.corner.includes("n")) { delta = Math.max(deltaX, -deltaY); } else if (start.corner.includes("w") && start.corner.includes("s")) { delta = Math.max(-deltaX, deltaY); } else if (start.corner.includes("w") && start.corner.includes("n")) { delta = Math.max(-deltaX, -deltaY); } else if (start.corner.includes("e")) { delta = deltaX; } else if (start.corner.includes("w")) { delta = -deltaX; } else if (start.corner.includes("s")) { delta = deltaY; } else if (start.corner.includes("n")) { delta = -deltaY; } const newSize = Math.max( CAMERA_MIN_SIZE, Math.min(CAMERA_MAX_SIZE, start.size + delta), ); setState("size", newSize); }; const handleResizeEnd = () => { setIsResizing(false); }; createEffect(() => { if (isResizing()) { window.addEventListener("mousemove", handleResizeMove); window.addEventListener("mouseup", handleResizeEnd); onCleanup(() => { window.removeEventListener("mousemove", handleResizeMove); window.removeEventListener("mouseup", handleResizeEnd); }); } }); return { handleResizeStart }; } // Extract scale calculation function useCameraScale(state: CameraWindowState) { return () => { const normalized = (state.size - CAMERA_MIN_SIZE) / (CAMERA_MAX_SIZE - CAMERA_MIN_SIZE); return 0.7 + normalized * 0.3; }; } // Extract control panel and resize handles into components function CameraControls(props: { state: CameraWindowState, setState: SetStoreFunction<CameraWindowState>, scale: Accessor<number> }) { // ... control panel JSX } function ResizeHandles(props: { onResizeStart: (corner: string) => (e: MouseEvent) => void }) { // ... resize handle JSX }Then use these in both Native and Legacy components to eliminate duplication.
♻️ Duplicate comments (1)
apps/desktop/src/routes/camera.tsx (1)
113-113: Remove unused variable.The
setCameramutation is created but never used inNativeCameraPreviewPage.Apply this diff:
- const setCamera = createCameraMutation(); -
🧹 Nitpick comments (3)
crates/recording/src/output_pipeline/core.rs (2)
467-469: Consider logging when timestamp fallback occurs.Using
Duration::ZEROwhenchecked_duration_sincereturnsNonegracefully handles timing anomalies (clock adjustments, time going backwards), but silently applying the fallback could make debugging timing issues difficult. Consider logging a warning when this occurs.Apply this diff to add logging:
- let duration = timestamp - .checked_duration_since(timestamps) - .unwrap_or(Duration::ZERO); + let duration = timestamp + .checked_duration_since(timestamps) + .unwrap_or_else(|| { + warn!("Video frame timestamp before recording start, using ZERO"); + Duration::ZERO + });
523-526: Consider logging when timestamp fallback occurs.Similar to the video path, using
Duration::ZEROwhenchecked_duration_sincereturnsNonegracefully handles timing anomalies but could make debugging difficult. Consider logging a warning when the fallback is applied.Apply this diff to add logging:
- let timestamp = frame - .timestamp - .checked_duration_since(timestamps) - .unwrap_or(Duration::ZERO); + let timestamp = frame + .timestamp + .checked_duration_since(timestamps) + .unwrap_or_else(|| { + warn!("Audio frame timestamp before recording start, using ZERO"); + Duration::ZERO + });apps/desktop/src/routes/camera.tsx (1)
550-565: Consider consolidating window size calculation logic.The canvas sizing logic duplicates the window size calculation from the resource (lines 417-444) for immediate feedback during resize. While this provides better UX, maintaining two copies of the same logic increases the risk of inconsistency.
Consider extracting the calculation into a shared function:
function calculateWindowDimensions( size: number, shape: CameraWindowShape, aspectRatio: number ) { const base = Math.max(CAMERA_MIN_SIZE, Math.min(CAMERA_MAX_SIZE, size)); const windowWidth = shape === "full" ? (aspectRatio >= 1 ? base * aspectRatio : base) : base; const windowHeight = shape === "full" ? (aspectRatio >= 1 ? base : base / aspectRatio) : base; return { base, windowWidth, windowHeight }; }Then use it in both the resource calculation and the style function to ensure consistency.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (4)
apps/desktop/src/routes/camera.tsx(12 hunks)crates/recording/src/output_pipeline/core.rs(3 hunks)crates/timestamp/src/lib.rs(1 hunks)packages/ui-solid/src/auto-imports.d.ts(0 hunks)
💤 Files with no reviewable changes (1)
- packages/ui-solid/src/auto-imports.d.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Format Rust code usingrustfmtand ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.
Files:
crates/timestamp/src/lib.rscrates/recording/src/output_pipeline/core.rs
crates/*/src/**/*
📄 CodeRabbit inference engine (AGENTS.md)
Rust crates should place tests within the
src/and/or a siblingtests/directory for each crate insidecrates/*.
Files:
crates/timestamp/src/lib.rscrates/recording/src/output_pipeline/core.rs
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by runningpnpm format.
Files:
apps/desktop/src/routes/camera.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g.,user-menu.tsx).
Use PascalCase for React/Solid components.
Files:
apps/desktop/src/routes/camera.tsx
🧠 Learnings (3)
📚 Learning: 2025-10-28T08:39:42.230Z
Learnt from: Brendonovich
Repo: CapSoftware/Cap PR: 1305
File: crates/recording/src/output_pipeline/macos.rs:80-90
Timestamp: 2025-10-28T08:39:42.230Z
Learning: In `crates/recording/src/output_pipeline/macos.rs`, the `AVFoundationMp4Muxer` intentionally holds the `Mutex<MP4Encoder>` lock during retry attempts in `send_video_frame()` and `send_audio_frame()`. This blocking behavior is correct because frame processing must happen sequentially to prevent audio and video frames from being interleaved incorrectly in the encoder.
Applied to files:
crates/recording/src/output_pipeline/core.rs
📚 Learning: 2025-10-17T05:58:22.586Z
Learnt from: Brendonovich
Repo: CapSoftware/Cap PR: 1219
File: crates/enc-avfoundation/src/mp4.rs:350-373
Timestamp: 2025-10-17T05:58:22.586Z
Learning: In crates/enc-avfoundation/src/mp4.rs, the `finish()` method intentionally skips video extension when `is_paused` is true. This is correct behavior because if recording is paused, the video should not be extended beyond the pause point—the pause is user-initiated, unlike the case where ScreenCaptureKit stops providing frames during static content.
Applied to files:
crates/recording/src/output_pipeline/core.rs
📚 Learning: 2025-09-22T14:19:56.010Z
Learnt from: CR
Repo: CapSoftware/Cap PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-09-22T14:19:56.010Z
Learning: Applies to **/*.{ts,tsx} : Use Biome for formatting and linting TypeScript/JavaScript files by running `pnpm format`.
Applied to files:
apps/desktop/src/routes/camera.tsx
🧬 Code graph analysis (2)
crates/timestamp/src/lib.rs (2)
crates/timestamp/src/macos.rs (1)
checked_duration_since(33-40)crates/timestamp/src/win.rs (1)
checked_duration_since(49-66)
apps/desktop/src/routes/camera.tsx (3)
apps/desktop/src/store.ts (1)
generalSettingsStore(61-62)apps/desktop/src/utils/tauri.ts (1)
commands(7-295)apps/desktop/src/utils/queries.ts (1)
createCameraMutation(195-236)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (rust)
🔇 Additional comments (3)
crates/timestamp/src/lib.rs (1)
35-46: LGTM! Safe duration calculation implemented correctly.The
checked_duration_sincemethod properly delegates to platform-specific implementations and handles theSystemTimevariant by converting errors toNone. The formatting issue noted in the previous review has been resolved.crates/recording/src/output_pipeline/core.rs (2)
432-432: Mutability correctly added.Declaring
video_rxas mutable is necessary for calling.next().awaitin the loop below.
485-491: Error propagation correctly implemented.The error handling properly propagates errors from
send_video_frameand distinguishes between cancellation and failure cases, addressing the critical issue from the previous review.
commit bdf23c8 Merge: 87ffbc2 d654add Author: Richie McIlroy <[email protected]> Date: Sun Nov 30 21:38:31 2025 +0800 Merge pull request CapSoftware#1417 from CapSoftware/cursor/optimize-editor-performance-for-large-videos-claude-4.5-opus-high-thinking-fb23 Optimize editor performance for large videos commit d654add Author: Richie McIlroy <[email protected]> Date: Sun Nov 30 21:35:01 2025 +0800 fmt commit 3213283 Author: Richie McIlroy <[email protected]> Date: Sun Nov 30 21:27:29 2025 +0800 clippy commit 35f9380 Author: Richie McIlroy <[email protected]> Date: Sun Nov 30 21:13:41 2025 +0800 fmt commit d1e708a Author: Cursor Agent <[email protected]> Date: Sun Nov 30 10:47:39 2025 +0000 Refactor playback prefetching to use tokio mpsc and futures Co-authored-by: richiemcilroy1 <[email protected]> commit 23f8516 Author: Cursor Agent <[email protected]> Date: Sun Nov 30 10:45:29 2025 +0000 Checkpoint before follow-up message Co-authored-by: richiemcilroy1 <[email protected]> commit 9bb14bb Author: Cursor Agent <[email protected]> Date: Sun Nov 30 10:23:27 2025 +0000 Refactor: Improve editor performance and UI responsiveness Co-authored-by: richiemcilroy1 <[email protected]> commit 87ffbc2 Merge: 3cb22ad 0e16f7c Author: Richie McIlroy <[email protected]> Date: Sat Nov 29 16:30:29 2025 +0530 Merge pull request CapSoftware#1410 from p-delorme/fix-camera-init Handles legacy camera size (strings) commit 0e16f7c Merge: 21f197e 3cb22ad Author: Richie McIlroy <[email protected]> Date: Sat Nov 29 18:48:41 2025 +0800 Merge branch 'main' into pr/1410 commit 3cb22ad Merge: 2330c53 48f7aeb Author: Richie McIlroy <[email protected]> Date: Sat Nov 29 10:00:30 2025 +0530 Merge pull request CapSoftware#1395 from CapSoftware/screenshots commit 48f7aeb Author: Richie McIlroy <[email protected]> Date: Sat Nov 29 12:02:29 2025 +0800 fmt commit 126421d Author: Richie McIlroy <[email protected]> Date: Sat Nov 29 11:59:07 2025 +0800 types/clippy bits commit cdc91c0 Author: Richie McIlroy <[email protected]> Date: Sat Nov 29 11:47:16 2025 +0800 Throw SilentError on export cancellation commit 980720e Author: Richie McIlroy <[email protected]> Date: Sat Nov 29 11:47:06 2025 +0800 Add image dimension and data validation to editor commit 860ccd7 Author: Richie McIlroy <[email protected]> Date: Sat Nov 29 11:46:57 2025 +0800 Add camera_cleanup_done flag to prevent redundant cleanup commit 38cc696 Author: Richie McIlroy <[email protected]> Date: Sat Nov 29 00:02:07 2025 +0800 claude settings commit e2545ee Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 23:58:31 2025 +0800 Improve screenshot capture reliability and performance commit ba9ba53 Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 23:58:17 2025 +0800 Add validation for annotation and camera config commit 641a99a Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 23:58:03 2025 +0800 Show toast on screenshot failure in overlay commit ba901dd Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 22:48:55 2025 +0800 Add keyboard shortcuts for export actions in Header commit c73f735 Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 21:47:48 2025 +0800 Add stride check in FFmpeg frame conversion commit 6825eb3 Merge: 4d73f14 2330c53 Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 21:23:41 2025 +0800 Merge branch 'main' into screenshots commit 2330c53 Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 20:22:42 2025 +0800 fix: margin on mobile nav commit 0765fee Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 20:16:06 2025 +0800 feat: Cap Friday commit 4d73f14 Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 04:55:51 2025 +0530 Add fast screenshot capture and shared GPU context commit f8f1d01 Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 04:54:15 2025 +0530 Add toast notifications for screenshot actions commit bb0310d Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 01:16:47 2025 +0530 misc bits commit ed64349 Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 01:16:42 2025 +0530 Refactor camera window cleanup logic commit 2b1eb9e Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 01:16:25 2025 +0530 Handle errors during titlebar initialization commit eafc7d9 Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 01:16:12 2025 +0530 Refactor import and error handling in screenshot_editor.rs commit d776857 Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 01:15:59 2025 +0530 Optimize PNG screenshot encoding settings commit 737b89a Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 01:15:42 2025 +0530 Remove unused mutable binding in camera_legacy.rs commit 37456d3 Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 00:39:08 2025 +0530 Disable cursor capture in screenshot functionality commit 694de05 Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 00:24:07 2025 +0530 Refactor camera preview to use broadcast channel commit 7c68831 Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 00:23:58 2025 +0530 Switch frame channel from flume to tokio broadcast commit 3a39c9c Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 00:23:25 2025 +0530 Refactor WebSocket frame handling to use broadcast channel commit f12c13e Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 00:23:14 2025 +0530 Refactor render shutdown handling in screenshot editor commit 3c4a124 Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 00:23:05 2025 +0530 Remove timestamp from screenshot src commit 032b819 Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 00:21:25 2025 +0530 Handle shutdown in screenshot editor render loop commit 7dd146a Author: Richie McIlroy <[email protected]> Date: Fri Nov 28 00:15:59 2025 +0530 Handle missing ScreenshotEditor instance gracefully commit 21f197e Author: Patrick Delorme <[email protected]> Date: Wed Nov 26 22:18:33 2025 -0500 Handles legacy camera size strings commit 9d22e01 Author: Richie McIlroy <[email protected]> Date: Sat Nov 22 09:24:47 2025 +0000 0.4.0 commit af1cc60 Author: Richie McIlroy <[email protected]> Date: Sat Nov 22 10:45:45 2025 +0530 feat: Screenshots V1 - Enhance screenshot editor with mask tool and export cancel commit 61982eb Author: Richie McIlroy <[email protected]> Date: Thu Nov 20 16:03:26 2025 +0000 Add screenshot export improvements and new icon commit 1996ab6 Author: Richie McIlroy <[email protected]> Date: Thu Nov 20 15:28:16 2025 +0000 Implement Windows screenshot capture support commit 7bd9644 Merge: 6dcb6f2 f272bbf Author: Richie McIlroy <[email protected]> Date: Thu Nov 20 15:22:15 2025 +0000 Merge branch 'main' into screenshots commit a292974 Merge: f272bbf 4a41f99 Author: Richie McIlroy <[email protected]> Date: Thu Nov 20 15:21:54 2025 +0000 Merge pull request CapSoftware#1381 from phuocithcmus/fix/window-memory-leak-processout fix: window memory leak - memory increase while recording commit 6dcb6f2 Author: Richie McIlroy <[email protected]> Date: Thu Nov 20 15:03:01 2025 +0000 Refactor mask blur logic for screenshot editor commit b4c8a17 Author: Richie McIlroy <[email protected]> Date: Thu Nov 20 12:51:21 2025 +0000 feat: Screenshot editor styling/layout + Mask annotation commit f272bbf Merge: 563e238 bab0348 Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 19:01:40 2025 +0000 Merge pull request CapSoftware#1396 from ItsEeleeya/new-theme-previews New theme preview icons commit bab0348 Author: Ilya <[email protected]> Date: Wed Nov 19 21:54:35 2025 +0330 Remove unused commit ef4a54f Author: Ilya <[email protected]> Date: Wed Nov 19 21:45:33 2025 +0330 New theme preview icons commit f92090a Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 17:57:11 2025 +0000 Add screenshot support to target selection UI commit 849b702 Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 17:20:53 2025 +0000 Replace Button with native button in TargetMenuPanel commit f28cffd Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 17:15:52 2025 +0000 Add recordings grid and menu to main window commit 84d1baa Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 16:55:52 2025 +0000 Refactor screenshot saving and editor window management commit f9450c8 Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 16:55:35 2025 +0000 Add screenshot mode and image icon support commit 11007c2 Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 16:55:23 2025 +0000 Refactor screenshot editor and add screenshots tab commit 77cac64 Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 02:45:50 2025 +0000 Integrate live preview for screenshot editor commit c982991 Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 02:45:40 2025 +0000 Add screenshot editor integration and DecodedFrame constructor commit 50ca057 Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 02:45:24 2025 +0000 Add screenshot editor backend implementation commit 4c3b942 Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 02:31:13 2025 +0000 Allow Slider to accept custom history prop commit f7047a0 Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 02:29:18 2025 +0000 feat: v1 of screenshots commit 563e238 Merge: 9edf1ce 2f75043 Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 00:33:18 2025 +0000 Merge pull request CapSoftware#1390 from CapSoftware/editor-perf feat: Various features + performance bits commit 2f75043 Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 00:30:49 2025 +0000 clippy bits commit ef920b2 Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 00:20:04 2025 +0000 gen'd files commit 695bb66 Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 00:19:52 2025 +0000 Fix requestAnimationFrame cleanup in overlay commit 0510be8 Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 00:19:44 2025 +0000 Add __CAP__ property to Window interface commit 756d296 Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 00:19:29 2025 +0000 Improve camera window resizing and positioning logic commit ff78eba Author: Richie McIlroy <[email protected]> Date: Wed Nov 19 00:19:09 2025 +0000 Clarify and emphasize no code comments policy commit 6db1d2d Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 23:41:03 2025 +0000 Remove unused setCamera mutation in camera page commit 51e2f6e Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 23:38:43 2025 +0000 misc packages commit a0aeffa Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 23:38:32 2025 +0000 Refactor duration calculation for readability commit fd30b14 Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 21:52:38 2025 +0000 Add checked_duration_since for timestamp types commit 05f39bb Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 21:46:20 2025 +0000 Improve countdown animation and fix recording logic commit dec0d8e Merge: 0066eb1 5de394f Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 21:44:52 2025 +0000 Merge branch 'editor-perf' of https://github.com/CapSoftware/Cap into editor-perf commit 0066eb1 Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 21:36:48 2025 +0000 Add camera overlay bounds update and revert logic commit 5de394f Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 20:51:15 2025 +0000 Update apps/desktop/src-tauri/src/camera.rs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> commit e6e9320 Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 18:26:21 2025 +0000 Improve logging and error context in recording pipeline commit d24e92c Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 17:10:19 2025 +0000 Add code formatting guidelines to documentation commit 2b504dd Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 16:54:29 2025 +0000 Update camera.tsx commit 891b122 Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 16:54:02 2025 +0000 Improve camera feed sender handling and logging commit 831fb76 Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 16:53:42 2025 +0000 Fix import order in experimental settings route commit dd39425 Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 16:41:22 2025 +0000 Set Mellow as default for CursorAnimationStyle commit ce63e02 Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 16:41:11 2025 +0000 Add resizing to camera window commit 6dd3ff1 Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 16:40:46 2025 +0000 Hide native camera preview toggle on Windows commit 44a5348 Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 15:14:03 2025 +0000 Refactor type casting for currentRecording data commit 41fbc5c Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 14:51:41 2025 +0000 Improve camera initialization and recording state handling commit 6aca28e Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 14:51:33 2025 +0000 Add initializing state to recording flow commit d2b8fd4 Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 14:50:45 2025 +0000 Update close button to use window close action commit fee3391 Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 12:00:28 2025 +0000 Reset camera and mic state on window close and recording end commit 184ad59 Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 10:49:45 2025 +0000 lost a days work (I F'D UP) commit 8b403a8 Author: Richie McIlroy <[email protected]> Date: Tue Nov 18 10:40:07 2025 +0000 Update recording UI container styles commit 37eb7c8 Author: Richie McIlroy <[email protected]> Date: Mon Nov 17 20:52:24 2025 +0000 Update settings.local.json commit cced653 Author: Richie McIlroy <[email protected]> Date: Mon Nov 17 20:52:18 2025 +0000 Improve camera frame forwarding and logging commit dbe8ae1 Author: Richie McIlroy <[email protected]> Date: Mon Nov 17 20:50:12 2025 +0000 Refactor waveform rendering in ClipTrack commit 5d95f24 Author: Richie McIlroy <[email protected]> Date: Mon Nov 17 20:15:25 2025 +0000 Handle excluded windows in macOS screen capture commit b61958c Merge: bd1fc73 9edf1ce Author: Richie McIlroy <[email protected]> Date: Mon Nov 17 17:12:38 2025 +0000 Merge branch 'main' into editor-perf commit bd1fc73 Author: Richie McIlroy <[email protected]> Date: Mon Nov 17 13:07:30 2025 +0000 fmt commit 0a5fea6 Author: Richie McIlroy <[email protected]> Date: Mon Nov 17 13:07:10 2025 +0000 Refactor project config save with custom debounce commit 4a41f99 Author: phuocnd <[email protected]> Date: Wed Nov 12 16:15:42 2025 +0700 fix: window memory leak - memory increase while streaming
Summary by CodeRabbit
Style
New Features
Refactor
Performance
Documentation