Skip to content

Conversation

richiemcilroy
Copy link
Member

@richiemcilroy richiemcilroy commented Oct 13, 2025

This pull request introduces a new feature that allows users to configure which application windows should be excluded from screen recordings, by default and via user settings. It also ensures that certain Cap windows (like settings or overlays) are protected from being captured, based on these exclusions.

On macOS, users can exclude any window. However, on Windows, users can only exclude Windows owned by the app due to API limitations.

Demo:
https://internal.cap.so/s/17wy532211mjptb (the native screen recording freezes once I start the Cap recording - but GitHub was successfully excluded from capture)

Summary by CodeRabbit

  • New Features

    • macOS: Exclude specific apps/windows from screen capture (defaults provided) and save custom exclusions.
    • Dynamically refresh window content protection across the app.
  • UI

    • Added Excluded Windows card with menu selection, quick add/remove, reset, and loading skeletons.
    • Window list now shows app identifiers for clearer selection.
  • Terminology

    • Renamed “In Progress Recording” to “Recording Controls.”

Copy link
Contributor

coderabbitai bot commented Oct 13, 2025

Walkthrough

Adds a WindowExclusion model and resolver, surfaces bundle identifiers in window listings, adds UI and storage for excluded windows, runtime commands to refresh content-protection, propagates macOS recording-time exclusions into the capture pipeline, and renames a CapWindowId variant to RecordingControls.

Changes

Cohort / File(s) Change Summary
Local AI settings
\.claude/settings.local.json
Added allow-list entry for Bash(cargo check:*) (adjusted indentation/spacing).
Window exclusion model & resolver
apps/desktop/src-tauri/src/window_exclusion.rs
New public WindowExclusion type with matches and resolve_window_ids to enumerate matching WindowIds (macOS bundle support gated).
General settings store & defaults
apps/desktop/src-tauri/src/general_settings.rs
Added DEFAULT_EXCLUDED_WINDOW_TITLES, default_excluded_windows(), new excluded_windows field on GeneralSettingsStore, default init, and get_default_excluded_windows() Tauri command.
Tauri wiring & public types
apps/desktop/src-tauri/src/lib.rs
Declared mod window_exclusion; exposed WindowExclusion type to specta; added commands windows::refresh_window_content_protection and general_settings::get_default_excluded_windows; renamed CapWindowId::InProgressRecordingRecordingControls.
Window content-protection & refresh command
apps/desktop/src-tauri/src/windows.rs
Added should_protect_window(...) using store/defaults; applied to multiple windows; added pub fn refresh_window_content_protection(app: AppHandle<Wry>) -> Result<(), String>.
Recording orchestration (desktop tauri)
apps/desktop/src-tauri/src/recording.rs
Resolve exclusions (macOS) via window_exclusion::resolve_window_ids, pass resolved WindowIds into Studio/Instant builders with with_excluded_windows (macOS), and replace InProgressRecording references with RecordingControls.
Thumbnails metadata
apps/desktop/src-tauri/src/thumbnails/mod.rs
CaptureWindowWithThumbnail now includes pub bundle_identifier: Option<String> and propagates it when collecting windows.
Frontend settings UI (Excluded Windows)
apps/desktop/src/routes/(window-chrome)/settings/general.tsx
Added excluded-windows UI/components, data flow (derive defaults, add/remove/reset exclusions), persistence, calls to refresh content protection and macOS prewarm; added helper types and components (ExcludedWindowsCard, skeleton).
Frontend Tauri bindings & types
apps/desktop/src/utils/tauri.ts
Added refreshWindowContentProtection() and getDefaultExcludedWindows() commands; CaptureWindow/CaptureWindowWithThumbnail include bundle_identifier; added WindowExclusion type; GeneralSettingsStore gains excludedWindows?.
Recording pipeline entrypoint
crates/recording/src/capture_pipeline.rs
create_screen_capture signature now accepts excluded_windows (macOS) and passes it to ScreenCaptureConfig::init.
Instant recording pipeline
crates/recording/src/instant_recording.rs
ActorBuilder and RecordingBaseInputs gain macOS-only excluded_windows: Vec<WindowId> and with_excluded_windows(...); propagated to screen capture creation.
Studio recording pipeline
crates/recording/src/studio_recording.rs
ActorBuilder gains macOS-only excluded_windows, with_excluded_windows(...), and passes exclusions into segment pipeline.
Recording crate types
crates/recording/src/lib.rs
Imported WindowId and added pub excluded_windows: Vec<WindowId> to RecordingBaseInputs (macOS gated).
Screen capture core (shared, macOS)
crates/recording/src/sources/screen_capture/mod.rs
CaptureWindow gets bundle_identifier: Option<String>; ScreenCaptureConfig gains excluded_windows (macOS); init/clone/list_windows updated to carry bundle id and exclusions.
Screen capture macOS backend
crates/recording/src/sources/screen_capture/macos.rs
Build excluded_sc_windows from WindowId handles and use as_content_filter_excluding_windows(...) when constructing the content filter.
scap-targets macOS platform
crates/scap-targets/src/platform/macos.rs
Added pub fn bundle_identifier(&self) -> Option<String> to WindowImpl.
Debug UI label
apps/desktop/src/routes/debug.tsx
Updated button label to “Show Recording Controls Window”.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as Settings UI
  participant TauriCmd as Tauri Commands
  participant Store as GeneralSettingsStore
  participant WinMgr as Window Manager
  participant Resolver as WindowExclusion.resolve_window_ids
  participant Recorder as Recording Pipeline

  User->>UI: Open Settings / choose exclusions
  UI->>TauriCmd: getDefaultExcludedWindows()
  TauriCmd-->>UI: default exclusions
  UI->>TauriCmd: save excludedWindows
  TauriCmd->>Store: persist excluded_windows
  TauriCmd->>WinMgr: refresh_window_content_protection()
  WinMgr->>Store: read excluded_windows or defaults
  WinMgr->>WinMgr: should_protect_window(title)
  WinMgr-->>UI: update content_protected states

  Note over UI,Recorder: On recording start (macOS)
  UI->>TauriCmd: start recording
  TauriCmd->>Store: read excluded_windows
  TauriCmd->>Resolver: resolve_window_ids(excluded_windows)
  Resolver-->>TauriCmd: WindowId[]
  TauriCmd->>Recorder: start with excluded_windows
  Recorder->>Recorder: ScreenCaptureConfig.init(..., excluded_windows)
  Recorder-->>TauriCmd: recording started
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

I’m a rabbit who tidies the panes,
Hopping through titles and bundle names.
Exclude a window, hide its light—
Recordings stay cozy through the night.
Tiny paws, big features — hop delight! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “feat: Implement new Window excluder system” succinctly identifies the main addition introduced by this pull request, namely the system for excluding windows from screen capture. It uses a conventional prefix and clearly conveys the feature’s purpose. It avoids unnecessary detail and is easily understood when scanning PR history.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch window-excluder

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

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/studio_recording.rs (1)

693-707: Avoid unwrap on async screen capture init

create_screen_capture().await.unwrap() can panic and crash recordings. Propagate an error instead.

-    let screen_config = create_screen_capture(
+    let screen_config = create_screen_capture(
         &base_inputs.capture_target,
         !custom_cursor_capture,
         120,
         start_time.system_time(),
         base_inputs.capture_system_audio,
         #[cfg(windows)]
         d3d_device,
         #[cfg(target_os = "macos")]
         base_inputs.shareable_content,
-        &base_inputs.excluded_windows,
-    )
-    .await
-    .unwrap();
+        &base_inputs.excluded_windows,
+    )
+    .await
+    .context("failed to initialize screen capture")?;

If you prefer a typed error, map it into CreateSegmentPipelineError.

🧹 Nitpick comments (4)
apps/desktop/src-tauri/src/windows.rs (1)

874-879: Confirm fallback semantics for defaults

With an existing store deserialized to an empty excluded_windows, defaults aren’t applied. If you want defaults unless the user explicitly overrides, consider:

-    GeneralSettingsStore::get(app)
-        .ok()
-        .flatten()
-        .map(|settings| matches(&settings.excluded_windows))
-        .unwrap_or_else(|| matches(&general_settings::default_excluded_windows()))
+    match GeneralSettingsStore::get(app).ok().flatten() {
+        Some(settings) if !settings.excluded_windows.is_empty() => {
+            matches(&settings.excluded_windows)
+        }
+        _ => matches(&general_settings::default_excluded_windows()),
+    }

Otherwise, defaults will only apply when no store exists at all.

apps/desktop/src/routes/(window-chrome)/settings/general.tsx (2)

248-256: Inline comments violate repo guidelines

This file contains inline comments; guidelines state no inline/block/doc comments in TS/TSX. Please remove comments throughout the file and rely on self-explanatory code. Run pnpm format (Biome) after changes.


275-301: Keep match semantics aligned with Rust WindowExclusion::matches

The local matchesExclusion mirrors Rust logic. To avoid drift, consider centralizing this rule (e.g., expose prefiltered window options from Tauri) or add tests to assert parity for edge cases (ownerName+windowTitle, bundleIdentifier only).

crates/recording/src/sources/screen_capture/mod.rs (1)

62-109: WindowExclusion::matches: add tests to lock semantics

Logic matches the UI’s matcher. Please add unit tests for:

  • bundle_identifier match
  • owner_name only
  • owner_name + window_title (requires both)
  • window_title only
  • all-none returns false

I can draft these tests in this crate’s tests module if helpful.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7369e1f and 8d4239f.

📒 Files selected for processing (16)
  • .claude/settings.local.json (1 hunks)
  • apps/desktop/src-tauri/src/general_settings.rs (6 hunks)
  • apps/desktop/src-tauri/src/lib.rs (2 hunks)
  • apps/desktop/src-tauri/src/recording.rs (4 hunks)
  • apps/desktop/src-tauri/src/thumbnails/mod.rs (2 hunks)
  • apps/desktop/src-tauri/src/windows.rs (13 hunks)
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx (5 hunks)
  • apps/desktop/src/utils/tauri.ts (4 hunks)
  • crates/recording/src/capture_pipeline.rs (3 hunks)
  • crates/recording/src/instant_recording.rs (6 hunks)
  • crates/recording/src/lib.rs (2 hunks)
  • crates/recording/src/sources/screen_capture/macos.rs (2 hunks)
  • crates/recording/src/sources/screen_capture/mod.rs (7 hunks)
  • crates/recording/src/studio_recording.rs (6 hunks)
  • crates/scap-targets/src/lib.rs (1 hunks)
  • crates/scap-targets/src/platform/macos.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
crates/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

For desktop IPC, use tauri_specta derive/macros on Rust types/events; do not hand-roll bindings

Files:

  • crates/scap-targets/src/lib.rs
  • crates/recording/src/sources/screen_capture/macos.rs
  • crates/recording/src/capture_pipeline.rs
  • crates/recording/src/sources/screen_capture/mod.rs
  • crates/recording/src/studio_recording.rs
  • crates/scap-targets/src/platform/macos.rs
  • crates/recording/src/lib.rs
  • crates/recording/src/instant_recording.rs
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • crates/scap-targets/src/lib.rs
  • apps/desktop/src-tauri/src/thumbnails/mod.rs
  • crates/recording/src/sources/screen_capture/macos.rs
  • apps/desktop/src-tauri/src/general_settings.rs
  • crates/recording/src/capture_pipeline.rs
  • apps/desktop/src-tauri/src/lib.rs
  • apps/desktop/src/utils/tauri.ts
  • crates/recording/src/sources/screen_capture/mod.rs
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx
  • crates/recording/src/studio_recording.rs
  • apps/desktop/src-tauri/src/recording.rs
  • crates/scap-targets/src/platform/macos.rs
  • apps/desktop/src-tauri/src/windows.rs
  • crates/recording/src/lib.rs
  • crates/recording/src/instant_recording.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and 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/scap-targets/src/lib.rs
  • apps/desktop/src-tauri/src/thumbnails/mod.rs
  • crates/recording/src/sources/screen_capture/macos.rs
  • apps/desktop/src-tauri/src/general_settings.rs
  • crates/recording/src/capture_pipeline.rs
  • apps/desktop/src-tauri/src/lib.rs
  • crates/recording/src/sources/screen_capture/mod.rs
  • crates/recording/src/studio_recording.rs
  • apps/desktop/src-tauri/src/recording.rs
  • crates/scap-targets/src/platform/macos.rs
  • apps/desktop/src-tauri/src/windows.rs
  • crates/recording/src/lib.rs
  • crates/recording/src/instant_recording.rs
crates/*/src/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Rust crates should place tests within the src/ and/or a sibling tests/ directory for each crate inside crates/*.

Files:

  • crates/scap-targets/src/lib.rs
  • crates/recording/src/sources/screen_capture/macos.rs
  • crates/recording/src/capture_pipeline.rs
  • crates/recording/src/sources/screen_capture/mod.rs
  • crates/recording/src/studio_recording.rs
  • crates/scap-targets/src/platform/macos.rs
  • crates/recording/src/lib.rs
  • crates/recording/src/instant_recording.rs
**/tauri.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Never edit auto-generated IPC bindings file tauri.ts

Do not edit auto-generated files named tauri.ts.

Files:

  • apps/desktop/src/utils/tauri.ts
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/desktop/**/*.{ts,tsx}: Do not manually import icons in the desktop app; rely on auto-imported icons
In the desktop app, use @tanstack/solid-query for server state management

Files:

  • apps/desktop/src/utils/tauri.ts
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; leverage shared types from packages

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Files:

  • apps/desktop/src/utils/tauri.ts
  • apps/desktop/src/routes/(window-chrome)/settings/general.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.ts
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx
🧬 Code graph analysis (13)
crates/scap-targets/src/lib.rs (1)
crates/scap-targets/src/platform/macos.rs (1)
  • bundle_identifier (350-383)
crates/recording/src/sources/screen_capture/macos.rs (3)
apps/desktop/src-tauri/src/platform/macos/sc_shareable_content.rs (3)
  • new (140-158)
  • window (164-166)
  • display (160-162)
crates/scap-targets/src/lib.rs (5)
  • list (16-18)
  • list (103-105)
  • owner_name (134-136)
  • bundle_identifier (142-144)
  • display (150-152)
crates/scap-targets/src/platform/macos.rs (5)
  • list (32-38)
  • list (237-269)
  • owner_name (320-332)
  • bundle_identifier (350-383)
  • display (524-544)
apps/desktop/src-tauri/src/general_settings.rs (6)
apps/desktop/src/utils/tauri.ts (1)
  • WindowExclusion (488-488)
apps/desktop/src-tauri/src/windows.rs (1)
  • title (120-133)
crates/recording/src/sources/screen_capture/mod.rs (1)
  • title (243-249)
crates/scap-targets/src/lib.rs (2)
  • bundle_identifier (142-144)
  • owner_name (134-136)
crates/scap-targets/src/platform/macos.rs (2)
  • bundle_identifier (350-383)
  • owner_name (320-332)
crates/scap-targets/src/platform/win.rs (1)
  • owner_name (443-483)
crates/recording/src/capture_pipeline.rs (1)
apps/desktop/src/utils/tauri.ts (2)
  • ScreenCaptureTarget (461-461)
  • WindowExclusion (488-488)
apps/desktop/src-tauri/src/lib.rs (3)
apps/desktop/src-tauri/src/windows.rs (1)
  • refresh_window_content_protection (883-894)
apps/desktop/src-tauri/src/general_settings.rs (1)
  • get_default_excluded_windows (270-272)
apps/desktop/src/utils/tauri.ts (1)
  • WindowExclusion (488-488)
crates/recording/src/sources/screen_capture/mod.rs (3)
apps/desktop/src/utils/tauri.ts (2)
  • WindowExclusion (488-488)
  • CaptureWindow (374-374)
crates/scap-targets/src/lib.rs (4)
  • bundle_identifier (142-144)
  • owner_name (134-136)
  • name (40-42)
  • name (154-156)
crates/scap-targets/src/platform/macos.rs (4)
  • bundle_identifier (350-383)
  • owner_name (320-332)
  • name (128-147)
  • name (385-397)
apps/desktop/src/routes/(window-chrome)/settings/general.tsx (2)
apps/desktop/src/utils/tauri.ts (5)
  • WindowExclusion (488-488)
  • CaptureWindow (374-374)
  • GeneralSettingsStore (399-399)
  • commands (7-284)
  • events (289-335)
apps/desktop/src/store.ts (1)
  • generalSettingsStore (61-62)
crates/recording/src/studio_recording.rs (3)
crates/recording/src/capture_pipeline.rs (4)
  • OutputPipeline (40-43)
  • OutputPipeline (53-54)
  • OutputPipeline (81-84)
  • OutputPipeline (100-101)
apps/desktop/src/utils/tauri.ts (1)
  • WindowExclusion (488-488)
crates/recording/src/instant_recording.rs (2)
  • new (227-235)
  • with_excluded_windows (247-250)
apps/desktop/src-tauri/src/recording.rs (2)
apps/desktop/src/utils/tauri.ts (3)
  • GeneralSettingsStore (399-399)
  • PostDeletionBehaviour (436-436)
  • PostStudioRecordingBehaviour (437-437)
apps/desktop/src-tauri/src/general_settings.rs (1)
  • default_excluded_windows (50-59)
crates/scap-targets/src/platform/macos.rs (1)
crates/scap-targets/src/lib.rs (3)
  • bundle_identifier (142-144)
  • id (28-30)
  • id (118-120)
apps/desktop/src-tauri/src/windows.rs (2)
crates/recording/src/sources/screen_capture/mod.rs (2)
  • title (243-249)
  • matches (63-108)
apps/desktop/src-tauri/src/general_settings.rs (2)
  • get (205-216)
  • default_excluded_windows (50-59)
crates/recording/src/lib.rs (1)
apps/desktop/src/utils/tauri.ts (2)
  • ScreenCaptureTarget (461-461)
  • WindowExclusion (488-488)
crates/recording/src/instant_recording.rs (2)
crates/recording/src/studio_recording.rs (3)
  • new (351-361)
  • new (589-606)
  • with_excluded_windows (383-389)
apps/desktop/src/utils/tauri.ts (2)
  • ScreenCaptureTarget (461-461)
  • WindowExclusion (488-488)
🪛 GitHub Check: Clippy
crates/recording/src/sources/screen_capture/macos.rs

[warning] 13-13: unused import: collections::HashSet
warning: unused import: collections::HashSet
--> crates/recording/src/sources/screen_capture/macos.rs:13:5
|
13 | collections::HashSet,
| ^^^^^^^^^^^^^^^^^^^^
|
= note: #[warn(unused_imports)] on by default

⏰ 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). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (23)
.claude/settings.local.json (1)

3-8: Allowlist update aligns with new workflow

Adding Bash(cargo check:*) keeps the local automation in sync with the cargo-based tooling introduced in this PR. Looks good.

crates/scap-targets/src/lib.rs (1)

142-144: New Window::bundle_identifier API looks good; ensure cross‑platform parity

Confirm platform::WindowImpl::bundle_identifier() exists on all targets (or gate this method with cfg) to avoid non‑macOS build breaks. Consider adding a matching Display::bundle_identifier() wrapper if you intend to expose display bundle IDs too.

apps/desktop/src/utils/tauri.ts (4)

47-52: New commands surfaced

refreshWindowContentProtection and getDefaultExcludedWindows look correct in shape. Verify the backend commands exist and are exported, and that calling them is safe across platforms.


374-376: Types extended with bundle_identifier

Adding bundle_identifier: string | null to window types aligns with the Rust changes. LGTM.


399-399: GeneralSettingsStore: excludedWindows

Type looks right. Ensure Rust GeneralSettingsStore maps this field consistently (Option vs Vec with default), and consumers fall back to default exclusions when unset.


488-488: New WindowExclusion type

Shape is clear and minimal for matching. LGTM.

apps/desktop/src-tauri/src/thumbnails/mod.rs (2)

36-37: Propagate bundle_identifier in model

Extending CaptureWindowWithThumbnail is consistent with the rest of the PR. LGTM.


144-145: Plumb through bundle_identifier

Forwarding from capture_window is correct. LGTM.

apps/desktop/src-tauri/src/recording.rs (3)

47-49: Imports updated for settings/exclusions

Imports look correct and scoped. LGTM.


478-480: Studio builder: exclusions wired

Chaining .with_excluded_windows(excluded_windows.clone()) is correct. LGTM.


519-521: Instant builder: exclusions wired

Passing excluded_windows here too keeps behavior consistent across modes. LGTM.

crates/recording/src/studio_recording.rs (2)

347-360: Builder wiring for excluded_windows looks good

Field default and initialization are correct; no concerns.


382-389: with_excluded_windows builder API is appropriate

Simple and clear; threads through as expected.

crates/recording/src/instant_recording.rs (3)

218-234: ActorBuilder excludes wiring

New field and default initialization are correct.


247-251: with_excluded_windows API

Looks good and consistent with studio flow.


291-302: create_screen_capture propagation

Passing excluded_windows by slice and propagating shareable_content is correct.

Consider adding a small integration test ensuring an exclusion actually removes a known window on macOS, to guard regressions.

apps/desktop/src/routes/(window-chrome)/settings/general.tsx (2)

336-348: Apply + refresh flow is solid

Persisting store, refreshing window content protection, and prewarming on macOS is correct. Nice.


37-39: Do not manually import icons in desktop app; rely on auto-import

Per guidelines, remove explicit icon imports. Also ensure IconCapChevronDown is available via auto-import; otherwise this will fail at runtime/build.

-import IconLucidePlus from "~icons/lucide/plus";
-import IconLucideX from "~icons/lucide/x";

Replace usages with the auto-imported components as configured.

crates/recording/src/sources/screen_capture/mod.rs (4)

28-36: CaptureWindow gains bundle_identifier

Good addition; aligns with TS type (nullable). No issues.


51-60: WindowExclusion public type is well-shaped

Serde camelCase + specta Type are correct for IPC.


331-343: ScreenCaptureConfig::init signature extension

Adding excluded_windows parameter is fine; keep threading consistent across platforms.

Ensure downstream callers updated (capture_pipeline, instant/studio recording).


532-548: list_windows: owner_name and bundle_identifier capture

This matches platform capabilities; good macOS gating and Windows fallback to None.

apps/desktop/src-tauri/src/windows.rs (1)

868-895: Content protection ignores bundleIdentifier/ownerName rules; only windowTitle is checked

User exclusions set by bundleIdentifier or ownerName won’t protect Cap windows, since should_protect_window passes None for those. On macOS, UI often stores bundleIdentifier; content_protected will remain off unexpectedly.

Pass app owner/bundle to WindowExclusion::matches. Example:

-fn should_protect_window(app: &AppHandle<Wry>, window_title: &str) -> bool {
-    let matches = |list: &[WindowExclusion]| {
-        list.iter()
-            .any(|entry| entry.matches(None, None, Some(window_title)))
-    };
+fn should_protect_window(app: &AppHandle<Wry>, window_title: &str) -> bool {
+    // Derive identifiers for this app/process
+    // Prefer bundle identifier when available; fall back to product/app name.
+    #[cfg(target_os = "macos")]
+    let bundle_identifier: Option<String> = app
+        .config()
+        .tauri
+        .bundle
+        .identifier
+        .clone();
+    #[cfg(not(target_os = "macos"))]
+    let bundle_identifier: Option<String> = None;
+
+    let owner_name: Option<String> = Some(app.package_info().name.clone());
+
+    let matches = |list: &[WindowExclusion]| {
+        list.iter().any(|entry| {
+            entry.matches(
+                bundle_identifier.as_deref(),
+                owner_name.as_deref(),
+                Some(window_title),
+            )
+        })
+    };

Adjust the retrieval if your Tauri version exposes identifiers differently. This ensures bundle/owner-based rules affect Cap windows’ content protection.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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)
crates/scap-targets/src/platform/win.rs (1)

485-523: DRY via get_executable_path(), normalize to lowercase, and remove inline comments

  • Reuse existing get_executable_path() to avoid code duplication and keep behavior consistent.
  • Lowercase the stem for stable, case-insensitive matching (aligns with IGNORED_EXES and cross‑platform expectations).
  • Remove inline comments to comply with the no‑comments guideline.
-    pub fn bundle_identifier(&self) -> Option<String> {
-        // On Windows, use the executable name as the bundle identifier
-        // This is similar to the macOS bundle identifier concept
-        unsafe {
-            let mut process_id = 0u32;
-            GetWindowThreadProcessId(self.0, Some(&mut process_id));
-
-            if process_id == 0 {
-                return None;
-            }
-
-            let process_handle =
-                OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, process_id).ok()?;
-
-            let mut buffer = [0u16; 1024];
-            let mut buffer_size = buffer.len() as u32;
-
-            let result = QueryFullProcessImageNameW(
-                process_handle,
-                PROCESS_NAME_FORMAT::default(),
-                PWSTR(buffer.as_mut_ptr()),
-                &mut buffer_size,
-            );
-
-            let _ = CloseHandle(process_handle);
-
-            if result.is_ok() && buffer_size > 0 {
-                let path_str = String::from_utf16_lossy(&buffer[..buffer_size as usize]);
-
-                // Return the executable name (without extension) as the bundle identifier
-                std::path::Path::new(&path_str)
-                    .file_stem()
-                    .map(|stem| stem.to_string_lossy().into_owned())
-            } else {
-                None
-            }
-        }
-    }
+    pub fn bundle_identifier(&self) -> Option<String> {
+        let path = self.get_executable_path()?;
+        std::path::Path::new(&path)
+            .file_stem()
+            .map(|stem| stem.to_string_lossy().into_owned().to_lowercase())
+    }

Please confirm that downstream exclusion matching expects a case-insensitive identifier; if so, this normalization avoids mismatches. As per coding guidelines.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d4239f and 8cb13b6.

📒 Files selected for processing (1)
  • crates/scap-targets/src/platform/win.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
crates/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

For desktop IPC, use tauri_specta derive/macros on Rust types/events; do not hand-roll bindings

Files:

  • crates/scap-targets/src/platform/win.rs
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • crates/scap-targets/src/platform/win.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and 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/scap-targets/src/platform/win.rs
crates/*/src/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Rust crates should place tests within the src/ and/or a sibling tests/ directory for each crate inside crates/*.

Files:

  • crates/scap-targets/src/platform/win.rs
🧬 Code graph analysis (1)
crates/scap-targets/src/platform/win.rs (2)
crates/scap-targets/src/lib.rs (1)
  • bundle_identifier (142-144)
crates/scap-targets/src/platform/macos.rs (1)
  • bundle_identifier (350-383)
⏰ 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). (3)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Analyze (rust)

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8cb13b6 and cc9aa2e.

📒 Files selected for processing (13)
  • .claude/settings.local.json (1 hunks)
  • apps/desktop/src-tauri/src/general_settings.rs (6 hunks)
  • apps/desktop/src-tauri/src/lib.rs (3 hunks)
  • apps/desktop/src-tauri/src/recording.rs (5 hunks)
  • apps/desktop/src-tauri/src/window_exclusion.rs (1 hunks)
  • apps/desktop/src-tauri/src/windows.rs (13 hunks)
  • crates/recording/src/capture_pipeline.rs (3 hunks)
  • crates/recording/src/instant_recording.rs (6 hunks)
  • crates/recording/src/lib.rs (2 hunks)
  • crates/recording/src/sources/screen_capture/macos.rs (2 hunks)
  • crates/recording/src/sources/screen_capture/mod.rs (6 hunks)
  • crates/recording/src/studio_recording.rs (6 hunks)
  • crates/scap-targets/src/platform/macos.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • crates/recording/src/capture_pipeline.rs
  • crates/scap-targets/src/platform/macos.rs
  • crates/recording/src/sources/screen_capture/mod.rs
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • apps/desktop/src-tauri/src/general_settings.rs
  • apps/desktop/src-tauri/src/windows.rs
  • apps/desktop/src-tauri/src/recording.rs
  • apps/desktop/src-tauri/src/lib.rs
  • crates/recording/src/studio_recording.rs
  • apps/desktop/src-tauri/src/window_exclusion.rs
  • crates/recording/src/instant_recording.rs
  • crates/recording/src/sources/screen_capture/macos.rs
  • crates/recording/src/lib.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and 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/general_settings.rs
  • apps/desktop/src-tauri/src/windows.rs
  • apps/desktop/src-tauri/src/recording.rs
  • apps/desktop/src-tauri/src/lib.rs
  • crates/recording/src/studio_recording.rs
  • apps/desktop/src-tauri/src/window_exclusion.rs
  • crates/recording/src/instant_recording.rs
  • crates/recording/src/sources/screen_capture/macos.rs
  • crates/recording/src/lib.rs
crates/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

For desktop IPC, use tauri_specta derive/macros on Rust types/events; do not hand-roll bindings

Files:

  • crates/recording/src/studio_recording.rs
  • crates/recording/src/instant_recording.rs
  • crates/recording/src/sources/screen_capture/macos.rs
  • crates/recording/src/lib.rs
crates/*/src/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Rust crates should place tests within the src/ and/or a sibling tests/ directory for each crate inside crates/*.

Files:

  • crates/recording/src/studio_recording.rs
  • crates/recording/src/instant_recording.rs
  • crates/recording/src/sources/screen_capture/macos.rs
  • crates/recording/src/lib.rs
🧬 Code graph analysis (9)
apps/desktop/src-tauri/src/general_settings.rs (5)
apps/desktop/src/utils/tauri.ts (1)
  • WindowExclusion (488-488)
apps/desktop/src-tauri/src/windows.rs (1)
  • title (120-133)
crates/recording/src/sources/screen_capture/mod.rs (1)
  • title (183-189)
crates/scap-targets/src/platform/macos.rs (2)
  • bundle_identifier (350-378)
  • owner_name (320-332)
crates/scap-targets/src/platform/win.rs (2)
  • bundle_identifier (485-522)
  • owner_name (443-483)
apps/desktop/src-tauri/src/windows.rs (2)
apps/desktop/src-tauri/src/window_exclusion.rs (1)
  • matches (18-63)
apps/desktop/src-tauri/src/general_settings.rs (2)
  • get (205-216)
  • default_excluded_windows (50-59)
apps/desktop/src-tauri/src/recording.rs (3)
apps/desktop/src/utils/tauri.ts (3)
  • GeneralSettingsStore (399-399)
  • PostDeletionBehaviour (436-436)
  • PostStudioRecordingBehaviour (437-437)
apps/desktop/src-tauri/src/general_settings.rs (1)
  • default_excluded_windows (50-59)
apps/desktop/src-tauri/src/window_exclusion.rs (1)
  • resolve_window_ids (66-95)
apps/desktop/src-tauri/src/lib.rs (3)
apps/desktop/src-tauri/src/windows.rs (1)
  • refresh_window_content_protection (883-894)
apps/desktop/src-tauri/src/general_settings.rs (1)
  • get_default_excluded_windows (270-272)
apps/desktop/src/utils/tauri.ts (1)
  • WindowExclusion (488-488)
crates/recording/src/studio_recording.rs (2)
apps/desktop/src/utils/tauri.ts (1)
  • WindowId (489-489)
crates/recording/src/instant_recording.rs (2)
  • new (228-236)
  • with_excluded_windows (248-251)
apps/desktop/src-tauri/src/window_exclusion.rs (2)
apps/desktop/src/utils/tauri.ts (2)
  • WindowId (489-489)
  • WindowExclusion (488-488)
crates/scap-targets/src/platform/macos.rs (4)
  • bundle_identifier (350-378)
  • owner_name (320-332)
  • list (32-38)
  • list (237-269)
crates/recording/src/instant_recording.rs (2)
crates/recording/src/studio_recording.rs (3)
  • new (352-362)
  • new (587-604)
  • with_excluded_windows (384-387)
apps/desktop/src/utils/tauri.ts (2)
  • ScreenCaptureTarget (461-461)
  • WindowId (489-489)
crates/recording/src/sources/screen_capture/macos.rs (2)
apps/desktop/src-tauri/src/platform/macos/sc_shareable_content.rs (3)
  • new (140-158)
  • window (164-166)
  • display (160-162)
crates/scap-targets/src/lib.rs (3)
  • from_id (32-34)
  • from_id (122-124)
  • display (150-152)
crates/recording/src/lib.rs (1)
apps/desktop/src/utils/tauri.ts (2)
  • WindowId (489-489)
  • LogicalBounds (418-418)
⏰ 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). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (5)
.claude/settings.local.json (1)

6-7: Allow-list update looks good.

Adding Bash(cargo check:*) keeps the pattern consistent with existing pnpm entries and enables the new workflow without broadening scope unnecessarily.

crates/recording/src/instant_recording.rs (4)

4-4: LGTM! Import changes are correct.

Line 4 removes an unused import (CameraFeedLock), and Line 12 adds the necessary WindowId import for the new exclusion feature.

Also applies to: 12-12


224-224: LGTM! Field addition follows established pattern.

The excluded_windows field is properly initialized to an empty vector (Line 234), matching the implementation in studio_recording.rs per the relevant snippets.

Also applies to: 234-234


248-251: LGTM! Builder method correctly implements the pattern.

The with_excluded_windows method follows the builder pattern and matches the implementation in studio_recording.rs (lines 379-386).


266-266: LGTM! Exclusions correctly propagate through the pipeline.

Line 266 passes excluded_windows into RecordingBaseInputs, and Line 302 correctly borrows it when calling create_screen_capture. The complete flow (builder → RecordingBaseInputs → screen capture) is properly wired.

Also applies to: 302-302

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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)
apps/desktop/src-tauri/src/recording.rs (1)

740-757: Race: deleting recording dir while actor may still be running

Dropping the handle relies on Drop to Stop asynchronously, then removing the dir immediately. This can race with ongoing writes. Explicitly cancel/stop and await before removal.

Apply:

-    if let Some((_, recording_dir, video_id)) = recording_data {
+    if let Some((recording, recording_dir, video_id)) = recording_data {
         CurrentRecordingChanged.emit(&app).ok();
         RecordingStopped {}.emit(&app).ok();
-
-        // let _ = recording.cancel().await;
+        let _ = recording.cancel().await;
♻️ Duplicate comments (1)
apps/desktop/src-tauri/src/general_settings.rs (1)

43-48: Extend defaults to protect all Cap overlays (regression risk)

The default list misses occluders and overlays that were previously protected. Add them to avoid capturing Cap’s own UI on fresh installs.

 const DEFAULT_EXCLUDED_WINDOW_TITLES: &[&str] = &[
     "Cap",
     "Cap Settings",
     "Cap Recording Controls",
     "Cap Camera",
+    "Cap Window Capture Occluder",
+    "Cap Capture Area",
+    "Cap Recordings Overlay",
 ];
🧹 Nitpick comments (4)
apps/desktop/src-tauri/src/window_exclusion.rs (2)

17-64: Match semantics are strict; consider case-insensitive/title contains for robustness

Exact equality on owner/title can be brittle (minor title changes, case). Consider case-insensitive equality or a “contains” option for window_title to reduce false negatives, while keeping bundle_identifier as exact.


66-95: Potential enhancement: deterministic ordering and early-exit

Function is correct. For stability, consider sorting resulting WindowIds to make outputs deterministic. Not required for correctness.

apps/desktop/src-tauri/src/recording.rs (1)

479-488: macOS: exclusion plumbing looks correct; minor clone reductions possible

Logic correctly falls back to defaults and resolves WindowIds; propagation via with_excluded_windows in both Studio/Instant builders is good. Optional: avoid cloning when settings exist by borrowing or using Cow to reduce allocs.

Apply locally, if desired:

-                let excluded = {
-                    let window_exclusions = general_settings
-                        .as_ref()
-                        .map_or_else(general_settings::default_excluded_windows, |s| s.excluded_windows.clone());
-                    crate::window_exclusion::resolve_window_ids(&window_exclusions)
-                };
+                use std::borrow::Cow;
+                let excluded = {
+                    let window_exclusions: Cow<[crate::window_exclusion::WindowExclusion]> =
+                        general_settings
+                            .as_ref()
+                            .map(|s| Cow::Borrowed(&s.excluded_windows))
+                            .unwrap_or_else(|| Cow::Owned(general_settings::default_excluded_windows()));
+                    crate::window_exclusion::resolve_window_ids(&window_exclusions)
+                };

Also applies to: 503-505, 546-548

apps/desktop/src-tauri/src/windows.rs (1)

882-895: Hook refresh into settings updates

The refresh_window_content_protection command is useful. Consider invoking it automatically after saving GeneralSettingsStore to apply changes immediately without requiring a manual refresh.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc9aa2e and b97a3ac.

📒 Files selected for processing (13)
  • apps/desktop/src-tauri/src/general_settings.rs (6 hunks)
  • apps/desktop/src-tauri/src/lib.rs (4 hunks)
  • apps/desktop/src-tauri/src/recording.rs (9 hunks)
  • apps/desktop/src-tauri/src/window_exclusion.rs (1 hunks)
  • apps/desktop/src-tauri/src/windows.rs (19 hunks)
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx (5 hunks)
  • apps/desktop/src/routes/debug.tsx (1 hunks)
  • apps/desktop/src/utils/tauri.ts (4 hunks)
  • crates/recording/src/capture_pipeline.rs (3 hunks)
  • crates/recording/src/instant_recording.rs (6 hunks)
  • crates/recording/src/lib.rs (2 hunks)
  • crates/recording/src/sources/screen_capture/mod.rs (6 hunks)
  • crates/recording/src/studio_recording.rs (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • crates/recording/src/studio_recording.rs
  • crates/recording/src/capture_pipeline.rs
  • crates/recording/src/sources/screen_capture/mod.rs
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx
🧰 Additional context used
📓 Path-based instructions (8)
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/desktop/**/*.{ts,tsx}: Do not manually import icons in the desktop app; rely on auto-imported icons
In the desktop app, use @tanstack/solid-query for server state management

Files:

  • apps/desktop/src/routes/debug.tsx
  • apps/desktop/src/utils/tauri.ts
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • apps/desktop/src/routes/debug.tsx
  • apps/desktop/src-tauri/src/windows.rs
  • crates/recording/src/lib.rs
  • apps/desktop/src-tauri/src/lib.rs
  • apps/desktop/src/utils/tauri.ts
  • apps/desktop/src-tauri/src/recording.rs
  • crates/recording/src/instant_recording.rs
  • apps/desktop/src-tauri/src/general_settings.rs
  • apps/desktop/src-tauri/src/window_exclusion.rs
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; leverage shared types from packages

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Files:

  • apps/desktop/src/routes/debug.tsx
  • apps/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/debug.tsx
  • apps/desktop/src/utils/tauri.ts
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and 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/windows.rs
  • crates/recording/src/lib.rs
  • apps/desktop/src-tauri/src/lib.rs
  • apps/desktop/src-tauri/src/recording.rs
  • crates/recording/src/instant_recording.rs
  • apps/desktop/src-tauri/src/general_settings.rs
  • apps/desktop/src-tauri/src/window_exclusion.rs
crates/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

For desktop IPC, use tauri_specta derive/macros on Rust types/events; do not hand-roll bindings

Files:

  • crates/recording/src/lib.rs
  • crates/recording/src/instant_recording.rs
crates/*/src/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Rust crates should place tests within the src/ and/or a sibling tests/ directory for each crate inside crates/*.

Files:

  • crates/recording/src/lib.rs
  • crates/recording/src/instant_recording.rs
**/tauri.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Never edit auto-generated IPC bindings file tauri.ts

Do not edit auto-generated files named tauri.ts.

Files:

  • apps/desktop/src/utils/tauri.ts
🧬 Code graph analysis (7)
apps/desktop/src-tauri/src/windows.rs (2)
apps/desktop/src-tauri/src/window_exclusion.rs (1)
  • matches (18-63)
apps/desktop/src-tauri/src/general_settings.rs (2)
  • get (205-216)
  • default_excluded_windows (50-59)
crates/recording/src/lib.rs (3)
apps/desktop/src-tauri/src/target_select_overlay.rs (1)
  • scap_targets (49-52)
crates/scap-targets/src/main.rs (1)
  • scap_targets (123-129)
apps/desktop/src/utils/tauri.ts (2)
  • WindowId (487-487)
  • LogicalBounds (415-415)
apps/desktop/src-tauri/src/lib.rs (3)
apps/desktop/src-tauri/src/windows.rs (1)
  • refresh_window_content_protection (884-895)
apps/desktop/src-tauri/src/general_settings.rs (1)
  • get_default_excluded_windows (270-272)
apps/desktop/src/utils/tauri.ts (1)
  • WindowExclusion (486-486)
apps/desktop/src-tauri/src/recording.rs (2)
apps/desktop/src-tauri/src/general_settings.rs (1)
  • default_excluded_windows (50-59)
apps/desktop/src-tauri/src/window_exclusion.rs (1)
  • resolve_window_ids (66-95)
crates/recording/src/instant_recording.rs (3)
crates/recording/src/studio_recording.rs (3)
  • new (353-364)
  • new (591-608)
  • with_excluded_windows (387-390)
apps/desktop/src/utils/tauri.ts (2)
  • ScreenCaptureTarget (459-459)
  • WindowId (487-487)
crates/scap-targets/src/platform/macos.rs (1)
  • Self (286-295)
apps/desktop/src-tauri/src/general_settings.rs (5)
apps/desktop/src/utils/tauri.ts (1)
  • WindowExclusion (486-486)
apps/desktop/src-tauri/src/windows.rs (1)
  • title (121-134)
crates/recording/src/sources/screen_capture/mod.rs (1)
  • title (183-189)
crates/scap-targets/src/platform/macos.rs (2)
  • bundle_identifier (350-378)
  • owner_name (320-332)
crates/scap-targets/src/platform/win.rs (1)
  • owner_name (443-483)
apps/desktop/src-tauri/src/window_exclusion.rs (2)
apps/desktop/src/utils/tauri.ts (2)
  • WindowId (487-487)
  • WindowExclusion (486-486)
crates/scap-targets/src/platform/macos.rs (4)
  • bundle_identifier (350-378)
  • owner_name (320-332)
  • list (32-38)
  • list (237-269)
⏰ 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). (3)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (12)
apps/desktop/src-tauri/src/lib.rs (1)

28-28: Wiring additions look correct and consistent

  • window_exclusion module added
  • Commands exposed: windows::refresh_window_content_protection and general_settings::get_default_excluded_windows
  • Specta types include WindowExclusion and Flags
  • Denylist updated to RecordingControls

LGTM.

Also applies to: 1922-1924, 2023-2025, 2124-2125

apps/desktop/src-tauri/src/general_settings.rs (4)

50-59: Default exclusions builder OK

Mapping titles into WindowExclusion entries is correct and aligns with TS type shape.


121-123: Store field and default initialization look good

New excluded_windows field with sensible default is correctly added to the store and Default.

Also applies to: 186-187


239-248: Exclusion predicate looks good

Delegates to WindowExclusion::matches; no issues.


268-272: Command exposure OK

get_default_excluded_windows properly exposed for clients.

apps/desktop/src/routes/debug.tsx (1)

25-30: Retain InProgressRecording variant
The TS ShowCapWindow union (in apps/desktop/src/utils/tauri.ts) only defines { InProgressRecording: { countdown } }—there is no RecordingControls variant on the TS side.

Likely an incorrect or invalid review comment.

apps/desktop/src-tauri/src/recording.rs (2)

48-50: Grouped general_settings import is fine

Keeps default_excluded_windows reachable without broadening scope. No issues.


598-601: Rename to RecordingControls wired through correctly

Parenting dialogs and close-on-end now reference CapWindowId::RecordingControls consistently. Looks good.

Also applies to: 640-642, 827-829

apps/desktop/src/utils/tauri.ts (1)

47-52: New bindings and types look consistent; confirm backing commands/types exist

These additions align with the Rust side changes. Since this file is auto-generated, ensure it wasn’t edited manually and that:

  • Tauri commands refresh_window_content_protection and get_default_excluded_windows are exported in Rust with specta.
  • CaptureWindow(_WithThumbnail).bundle_identifier and GeneralSettingsStore.excludedWindows map to Rust fields (snake_case to camelCase).

Please verify by re-running codegen (tauri-specta) and checking that apps/desktop/src-tauri/src/lib.rs exports these commands and that general_settings.rs defines excluded_windows accordingly.

Also applies to: 371-373, 396-396, 486-486

crates/recording/src/instant_recording.rs (1)

224-226: macOS exclusions plumbed end-to-end in Instant path

Builder field, with_excluded_windows, RecordingBaseInputs, and create_screen_capture all align. Looks good.

Confirm that scap_targets::WindowId is available on all targets (or gate the import) and that create_screen_capture signature matches on each platform so CI builds for macOS and Windows both pass.

Also applies to: 235-237, 250-254, 268-271, 305-308

apps/desktop/src-tauri/src/windows.rs (2)

47-47: Rename + content protection applied consistently across windows

RecordingControls variant, titles, traffic lights, and content_protected logic are coherent and consistent. Good cross-module alignment.

Also applies to: 63-65, 106-106, 127-127, 157-157, 255-267, 304-309, 319-319, 530-535, 552-552, 570-580, 627-640, 660-673


869-880: Default/fallback semantics: verify excluded_windows defaults

should_protect_window relies on settings.excluded_windows directly. Ensure GeneralSettingsStore.excluded_windows uses serde default = default_excluded_windows (not empty Vec) so first load or missing field preserves defaults; otherwise opening settings could unintentionally disable protection.

If not already, define excluded_windows as Vec with #[serde(default = "default_excluded_windows")] in general_settings.rs. Also verify DEFAULT_EXCLUDED_WINDOW_TITLES exactly match CapWindowId::title() strings to avoid drift.

use cap_media::MediaError;
use feeds::microphone::MicrophoneFeedLock;
use scap_targets::bounds::LogicalBounds;
use scap_targets::{WindowId, bounds::LogicalBounds};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

macOS excluded_windows plumbed into base inputs

Importing WindowId and adding excluded_windows under macOS looks good.

Also applies to: 51-53


🏁 Script executed:

#!/bin/bash
# Verify excluded_windows is propagated through the pipeline.
echo "Sites instantiating RecordingBaseInputs:"
rg -nP -C3 'RecordingBaseInputs\s*\{'
echo
echo "Occurrences of excluded_windows across the repo:"
rg -nP -C2 '\bexcluded_windows\b'

Length of output: 13045


Propagate excluded_windows when constructing RecordingBaseInputs
RecordingBaseInputs (crates/recording/src/lib.rs:52) now includes a macOS-only excluded_windows field but its struct literals in instant_recording and studio_recording omit it. In

  • crates/recording/src/instant_recording.rs at the RecordingBaseInputs { … } (around line 262)
  • crates/recording/src/studio_recording.rs at the RecordingBaseInputs { … } (around line 398)

add:

#[cfg(target_os = "macos")]
excluded_windows: self.excluded_windows,
🤖 Prompt for AI Agents
In crates/recording/src/instant_recording.rs around line 262 and
crates/recording/src/studio_recording.rs around line 398, the
RecordingBaseInputs struct literals omit the new macOS-only excluded_windows
field; add the field guarded by cfg so it propagates the value from self:
#[cfg(target_os = "macos")] excluded_windows: self.excluded_windows, inserting
that line into each RecordingBaseInputs { … } initializer so the macOS-only
field is passed through.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 (2)
apps/desktop/src-tauri/src/recording.rs (1)

479-543: Fix non-mac build for excluded window support

with_excluded_windows (and the corresponding field) only exists behind #[cfg(target_os = "macos")], but we’re calling it unconditionally in both the studio and instant builder chains. That will fail to compile on Windows/Linux. Wrap the resolve_window_ids/with_excluded_windows bits in macOS-only cfg blocks so the other targets keep building. For example:

-                let window_exclusions = general_settings
-                    .as_ref()
-                    .map_or_else(general_settings::default_excluded_windows, |settings| {
-                        settings.excluded_windows.clone()
-                    });
-                let excluded_windows =
-                    crate::window_exclusion::resolve_window_ids(&window_exclusions);
+                #[cfg(target_os = "macos")]
+                let window_exclusions = general_settings
+                    .as_ref()
+                    .map_or_else(general_settings::default_excluded_windows, |settings| {
+                        settings.excluded_windows.clone()
+                    });
+                #[cfg(target_os = "macos")]
+                let excluded_windows =
+                    crate::window_exclusion::resolve_window_ids(&window_exclusions);
@@
-                        let mut builder = studio_recording::Actor::builder(
+                        let mut builder = studio_recording::Actor::builder(
                             recording_dir.clone(),
                             inputs.capture_target.clone(),
                         )
                         .with_system_audio(inputs.capture_system_audio)
-                        .with_custom_cursor(
+                        .with_custom_cursor(
                             general_settings
                                 .map(|s| s.custom_cursor_capture)
                                 .unwrap_or_default(),
-                        )
-                        .with_excluded_windows(excluded_windows.clone());
+                        );
+                        #[cfg(target_os = "macos")]
+                        {
+                            builder =
+                                builder.with_excluded_windows(excluded_windows.clone());
+                        }
@@
-                        let mut builder = instant_recording::Actor::builder(
+                        let mut builder = instant_recording::Actor::builder(
                             recording_dir.clone(),
                             inputs.capture_target.clone(),
-                        )
-                        .with_system_audio(inputs.capture_system_audio)
-                        .with_excluded_windows(excluded_windows.clone());
+                        )
+                        .with_system_audio(inputs.capture_system_audio);
+                        #[cfg(target_os = "macos")]
+                        {
+                            builder =
+                                builder.with_excluded_windows(excluded_windows.clone());
+                        }
crates/recording/src/studio_recording.rs (1)

695-710: Don’t unwrap create_screen_capture; propagate error instead.

unwrap() will panic on capture init failures. Return a proper error with context.

-    let screen_config = create_screen_capture(
+    let screen_config = create_screen_capture(
         &base_inputs.capture_target,
         !custom_cursor_capture,
         120,
         start_time.system_time(),
         base_inputs.capture_system_audio,
         #[cfg(windows)]
         d3d_device,
         #[cfg(target_os = "macos")]
         base_inputs.shareable_content,
         #[cfg(target_os = "macos")]
         base_inputs.excluded_windows,
-    )
-    .await
-    .unwrap();
+    )
+    .await
+    .context("screen capture init")?;
♻️ Duplicate comments (1)
apps/desktop/src-tauri/src/general_settings.rs (1)

43-48: Extend default exclusions to include all Cap overlays.

Defaults omit overlays that were previously protected. Add these to avoid regressions on fresh installs.

 const DEFAULT_EXCLUDED_WINDOW_TITLES: &[&str] = &[
     "Cap",
     "Cap Settings",
     "Cap Recording Controls",
     "Cap Camera",
+    "Cap Window Capture Occluder",
+    "Cap Capture Area",
+    "Cap Recordings Overlay",
 ];

References: apps/desktop/src-tauri/src/windows.rs titles.

🧹 Nitpick comments (6)
apps/desktop/src-tauri/src/windows.rs (1)

204-207: Inconsistent enum variant naming.

The ShowCapWindow::InProgressRecording variant retains the old name while CapWindowId uses the new RecordingControls name. This naming inconsistency could cause confusion for maintainers.

Consider renaming for consistency:

-    InProgressRecording {
+    RecordingControls {
         countdown: Option<u32>,
     },

And update the mapping at line 787:

-            ShowCapWindow::InProgressRecording { .. } => CapWindowId::RecordingControls,
+            ShowCapWindow::RecordingControls { .. } => CapWindowId::RecordingControls,
apps/desktop/src/routes/(window-chrome)/settings/general.tsx (2)

231-237: Clarify the Cap window detection logic.

The isManagedWindowsApp function checks if the bundle identifier contains "so.cap.desktop" or if the owner name includes "cap" (case-insensitive). This might produce false positives for windows from applications with "cap" in their name (e.g., "Capture Pro", "Capital One").

Consider making the check more specific:

 const isManagedWindowsApp = (window: CaptureWindow) => {
   const bundle = window.bundle_identifier?.toLowerCase() ?? "";
   if (bundle.includes("so.cap.desktop")) {
     return true;
   }
-  return window.owner_name.toLowerCase().includes("cap");
+  return window.owner_name.toLowerCase() === "cap";
 };

Or use a more explicit pattern like checking for "cap.so" or the full bundle identifier.


283-295: Consider preserving bundle_identifier when available.

Line 284 sets windowTitle to null when bundle_identifier exists, but this might lose useful information. If a window has both a bundle identifier and a title, both could be preserved for more precise matching.

Consider preserving both fields:

 const handleAddWindow = async (window: CaptureWindow) => {
-  const windowTitle = window.bundle_identifier ? null : window.name;

   const next = [
     ...excludedWindows(),
     {
       bundleIdentifier: window.bundle_identifier ?? null,
       ownerName: window.owner_name ?? null,
-      windowTitle,
+      windowTitle: window.name ?? null,
     },
   ];
   await applyExcludedWindows(next);
 };

This allows the backend's WindowExclusion::matches to use the most specific match available.

crates/recording/src/instant_recording.rs (1)

12-12: Gate WindowId import to macOS to avoid unused-import warnings.

Import is only used behind cfg(target_os = "macos"). Gate it to satisfy clippy.

- use scap_targets::WindowId;
+ #[cfg(target_os = "macos")]
+ use scap_targets::WindowId;

Also applies to: 224-226

crates/recording/src/studio_recording.rs (1)

17-17: Gate WindowId import/usage to macOS to satisfy clippy on other targets.

Avoid unused import warnings on non-mac builds.

- use scap_targets::WindowId;
+ #[cfg(target_os = "macos")]
+ use scap_targets::WindowId;

Also applies to: 348-350

crates/recording/src/sources/screen_capture/mod.rs (1)

475-491: Avoid duplicate owner_name lookup on macOS.

Minor perf/readability: cache owner_name once for filter and struct fill.

-            #[cfg(target_os = "macos")]
-            {
-                if v.raw_handle().level() != Some(0)
-                    || v.owner_name().filter(|v| v == "Window Server").is_some()
-                {
-                    return None;
-                }
-            }
-
-            let owner_name = v.owner_name()?;
+            #[cfg(target_os = "macos")]
+            let owner_name = {
+                let name = v.owner_name()?;
+                if v.raw_handle().level() != Some(0) || name == "Window Server" {
+                    return None;
+                }
+                name
+            };
+            #[cfg(not(target_os = "macos"))]
+            let owner_name = v.owner_name()?;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc9aa2e and b59cfa2.

📒 Files selected for processing (13)
  • apps/desktop/src-tauri/src/general_settings.rs (6 hunks)
  • apps/desktop/src-tauri/src/lib.rs (4 hunks)
  • apps/desktop/src-tauri/src/recording.rs (9 hunks)
  • apps/desktop/src-tauri/src/window_exclusion.rs (1 hunks)
  • apps/desktop/src-tauri/src/windows.rs (19 hunks)
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx (5 hunks)
  • apps/desktop/src/routes/debug.tsx (1 hunks)
  • apps/desktop/src/utils/tauri.ts (4 hunks)
  • crates/recording/src/capture_pipeline.rs (3 hunks)
  • crates/recording/src/instant_recording.rs (6 hunks)
  • crates/recording/src/lib.rs (2 hunks)
  • crates/recording/src/sources/screen_capture/mod.rs (6 hunks)
  • crates/recording/src/studio_recording.rs (6 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/desktop/src/routes/debug.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/desktop/src-tauri/src/window_exclusion.rs
  • apps/desktop/src/utils/tauri.ts
  • crates/recording/src/capture_pipeline.rs
🧰 Additional context used
📓 Path-based instructions (7)
crates/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

For desktop IPC, use tauri_specta derive/macros on Rust types/events; do not hand-roll bindings

Files:

  • crates/recording/src/instant_recording.rs
  • crates/recording/src/lib.rs
  • crates/recording/src/studio_recording.rs
  • crates/recording/src/sources/screen_capture/mod.rs
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • crates/recording/src/instant_recording.rs
  • apps/desktop/src-tauri/src/lib.rs
  • crates/recording/src/lib.rs
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx
  • crates/recording/src/studio_recording.rs
  • crates/recording/src/sources/screen_capture/mod.rs
  • apps/desktop/src-tauri/src/general_settings.rs
  • apps/desktop/src-tauri/src/windows.rs
  • apps/desktop/src-tauri/src/recording.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and 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/instant_recording.rs
  • apps/desktop/src-tauri/src/lib.rs
  • crates/recording/src/lib.rs
  • crates/recording/src/studio_recording.rs
  • crates/recording/src/sources/screen_capture/mod.rs
  • apps/desktop/src-tauri/src/general_settings.rs
  • apps/desktop/src-tauri/src/windows.rs
  • apps/desktop/src-tauri/src/recording.rs
crates/*/src/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Rust crates should place tests within the src/ and/or a sibling tests/ directory for each crate inside crates/*.

Files:

  • crates/recording/src/instant_recording.rs
  • crates/recording/src/lib.rs
  • crates/recording/src/studio_recording.rs
  • crates/recording/src/sources/screen_capture/mod.rs
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/desktop/**/*.{ts,tsx}: Do not manually import icons in the desktop app; rely on auto-imported icons
In the desktop app, use @tanstack/solid-query for server state management

Files:

  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; leverage shared types from packages

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Files:

  • apps/desktop/src/routes/(window-chrome)/settings/general.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/general.tsx
🧬 Code graph analysis (9)
crates/recording/src/instant_recording.rs (3)
crates/recording/src/studio_recording.rs (3)
  • new (353-364)
  • new (591-608)
  • with_excluded_windows (387-390)
apps/desktop/src/utils/tauri.ts (2)
  • ScreenCaptureTarget (459-459)
  • WindowId (487-487)
crates/scap-targets/src/platform/macos.rs (1)
  • Self (286-295)
apps/desktop/src-tauri/src/lib.rs (4)
crates/scap-targets/src/platform/win.rs (2)
  • windows (260-260)
  • windows (1150-1150)
apps/desktop/src-tauri/src/windows.rs (1)
  • refresh_window_content_protection (884-895)
apps/desktop/src-tauri/src/general_settings.rs (1)
  • get_default_excluded_windows (270-272)
apps/desktop/src/utils/tauri.ts (2)
  • Flags (394-394)
  • WindowExclusion (486-486)
crates/recording/src/lib.rs (3)
apps/desktop/src-tauri/src/target_select_overlay.rs (1)
  • scap_targets (49-52)
crates/scap-targets/src/main.rs (1)
  • scap_targets (123-129)
apps/desktop/src/utils/tauri.ts (2)
  • WindowId (487-487)
  • LogicalBounds (415-415)
apps/desktop/src/routes/(window-chrome)/settings/general.tsx (2)
apps/desktop/src/utils/tauri.ts (5)
  • WindowExclusion (486-486)
  • CaptureWindow (371-371)
  • GeneralSettingsStore (396-396)
  • commands (7-284)
  • events (289-333)
apps/desktop/src/store.ts (1)
  • generalSettingsStore (61-62)
crates/recording/src/studio_recording.rs (2)
apps/desktop/src/utils/tauri.ts (1)
  • WindowId (487-487)
crates/recording/src/instant_recording.rs (2)
  • new (229-238)
  • with_excluded_windows (251-254)
crates/recording/src/sources/screen_capture/mod.rs (3)
apps/desktop/src/utils/tauri.ts (2)
  • WindowId (487-487)
  • CaptureWindow (371-371)
crates/scap-targets/src/lib.rs (3)
  • owner_name (134-136)
  • name (40-42)
  • name (150-152)
crates/scap-targets/src/platform/macos.rs (4)
  • owner_name (320-332)
  • bundle_identifier (350-378)
  • name (128-147)
  • name (380-392)
apps/desktop/src-tauri/src/general_settings.rs (6)
apps/desktop/src/utils/tauri.ts (1)
  • WindowExclusion (486-486)
apps/desktop/src-tauri/src/windows.rs (1)
  • title (121-134)
crates/recording/src/sources/screen_capture/mod.rs (1)
  • title (183-189)
crates/scap-targets/src/platform/macos.rs (2)
  • bundle_identifier (350-378)
  • owner_name (320-332)
crates/scap-targets/src/lib.rs (1)
  • owner_name (134-136)
crates/scap-targets/src/platform/win.rs (1)
  • owner_name (443-483)
apps/desktop/src-tauri/src/windows.rs (3)
apps/desktop/src/utils/tauri.ts (3)
  • AppTheme (341-341)
  • GeneralSettingsStore (396-396)
  • WindowExclusion (486-486)
apps/desktop/src-tauri/src/window_exclusion.rs (1)
  • matches (18-63)
apps/desktop/src-tauri/src/general_settings.rs (2)
  • get (205-216)
  • default_excluded_windows (50-59)
apps/desktop/src-tauri/src/recording.rs (2)
apps/desktop/src-tauri/src/general_settings.rs (1)
  • default_excluded_windows (50-59)
apps/desktop/src-tauri/src/window_exclusion.rs (1)
  • resolve_window_ids (66-95)
⏰ 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). (4)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (27)
apps/desktop/src-tauri/src/windows.rs (3)

882-895: LGTM — Well-structured command for refreshing window protection.

The command iterates over all webview windows, derives their IDs, and updates content protection based on current exclusions. The error handling and return type are appropriate.


255-266: Validate content_protected on all platforms. .content_protected(should_protect) is applied unconditionally (in windows.rs and recording.rs); confirm Tauri supports it on Windows/Linux or add platform-specific guards or fallbacks.


869-880: Title-only exclusion matching is correct here. CapWindowId only exposes window titles, so bundle_identifier and owner_name aren’t available in this context.

Likely an incorrect or invalid review comment.

apps/desktop/src/routes/(window-chrome)/settings/general.tsx (7)

203-229: Ensure frontend matching logic mirrors backend WindowExclusion::matches.

The matchesExclusion function duplicates the matching logic from the Rust backend. Any divergence between these implementations could lead to inconsistent behavior where the UI shows different exclusions than what's actually applied.

Compare this logic against apps/desktop/src-tauri/src/window_exclusion.rs lines 17-62 to confirm the matching rules are identical. Pay special attention to:

  • Bundle identifier precedence
  • Owner name + window title combination logic
  • Fallback behavior when fields are null

Based on the relevant code snippet, the Rust implementation returns true immediately if bundle_identifier matches (lines 18-24 in window_exclusion.rs). The TypeScript version also does this (lines 207-210). However, verify that the combined owner + title logic (lines 216-218 TS vs lines 36-48 Rust) matches exactly.


179-188: Good practice: delayed initial fetch to avoid blocking render.

The 100ms delay before fetching windows prevents blocking the initial render, which improves perceived performance.


254-262: Verify error handling doesn't silently mask failures.

The refreshAvailableWindows function logs errors but returns the stale availableWindows() on failure. This could mask issues if window enumeration consistently fails.

Consider whether the UI should display an error state when window refresh fails, rather than silently using stale data. This would help users understand if the exclusion system isn't working correctly.


738-789: Verify menu scroll position restoration works reliably.

Lines 777-785 save and restore the scroll position around the menu popup. This workaround suggests the menu popup might be causing unwanted scroll changes. Verify this is consistently effective across different scroll positions and window sizes.

Test the menu behavior:

  1. Scroll to various positions in the settings panel
  2. Open the "Add" menu
  3. Confirm the scroll position is restored after closing the menu

If the issue persists, consider filing a bug report with the Tauri menu API maintainers.


725-872: LGTM — Well-structured exclusion management UI.

The ExcludedWindowsCard component provides clear controls for adding, removing, and resetting exclusions. The loading states, empty states, and OS-specific messaging are all handled appropriately.


874-892: Good accessibility: skeleton includes aria-hidden.

The loading skeleton correctly uses aria-hidden="true" to prevent screen readers from announcing placeholder content.


264-275: macOS prewarm event with force=true is required to reset ScreenCaptureKit cache so updated exclusions take effect immediately.

apps/desktop/src-tauri/src/general_settings.rs (6)

50-59: LGTM: default_excluded_windows helper.

Clear, simple construction of defaults.


121-122: LGTM: serde default for excluded_windows.

Correctly seeds existing configs with defaults.


186-186: LGTM: Default impl seeds excluded_windows.

Consistent with serde default; good for new configs.


239-248: Confirm matching semantics in WindowExclusion::matches.

Ensure match is robust:

  • Case-insensitive for owner/title?
  • Trims/normalizes whitespace?
  • Treats None and empty strings consistently.

If not, we may miss intended exclusions.


268-272: LGTM: get_default_excluded_windows command.

Properly exposed via specta for the frontend.


1-1: LGTM: WindowExclusion import.

crates/recording/src/instant_recording.rs (4)

235-237: LGTM: initialize excluded_windows on macOS.


250-254: LGTM: with_excluded_windows builder.


268-271: LGTM: propagate excluded_windows into RecordingBaseInputs.


305-308: LGTM: pass excluded_windows into create_screen_capture.

Matches ScreenCaptureConfig::init signature (macOS-gated).

crates/recording/src/studio_recording.rs (2)

386-391: LGTM: with_excluded_windows builder.


405-407: LGTM: propagate excluded_windows into RecordingBaseInputs.

crates/recording/src/sources/screen_capture/mod.rs (5)

35-36: LGTM: CaptureWindow.bundle_identifier added.

Matches frontend type (apps/desktop/src/utils/tauri.ts) and enables bundle-based exclusions.


201-204: LGTM: ScreenCaptureConfig.excluded_windows (macOS).

Properly stored on the config for downstream usage.


240-242: LGTM: clone excluded_windows.


283-284: LGTM: init signature extended with excluded_windows (macOS).


409-412: LGTM: plumb excluded_windows through init.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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-tauri/src/recording.rs (1)

492-506: Immutable builder reassigned on non-macOS → compile error; avoid cfg shadowing and make builder mutable

let builder = ... is immutable, yet later you reassign builder = ... when adding feeds. On macOS you shadow it with a mutable let mut, but on non‑macOS that line is compiled out, leaving an immutable builder and causing a compile error.

Apply:

-                        let builder = studio_recording::Actor::builder(
+                        let mut builder = studio_recording::Actor::builder(
                             recording_dir.clone(),
                             inputs.capture_target.clone(),
                         )
                         .with_system_audio(inputs.capture_system_audio)
                         .with_custom_cursor(
                             general_settings
                                 .map(|s| s.custom_cursor_capture)
                                 .unwrap_or_default(),
                         );
 
-                        #[cfg(target_os = "macos")]
-                        let mut builder = builder.with_excluded_windows(excluded_windows.clone());
+                        #[cfg(target_os = "macos")]
+                        {
+                            builder = builder.with_excluded_windows(excluded_windows.clone());
+                        }

This keeps a single mutable binding across platforms and avoids shadowing under cfg.

🧹 Nitpick comments (6)
crates/recording/src/instant_recording.rs (1)

12-12: Gate WindowId import to macOS to avoid unused-import lint on other OSes

Prevents clippy/rustc warnings when building for non-macOS.

As per coding guidelines

-use scap_targets::WindowId;
+#[cfg(target_os = "macos")]
+use scap_targets::WindowId;
crates/recording/src/sources/screen_capture/mod.rs (3)

203-204: Scope excluded_windows more narrowly (optional)

Consider restricting to pub(crate) to avoid unnecessary public API surface unless external crates need this.

-    pub excluded_windows: Vec<WindowId>,
+    pub(crate) excluded_windows: Vec<WindowId>,

283-285: Accept a slice for excluded_windows for ergonomics and to match callers

Taking &[WindowId] avoids forcing ownership at call sites and aligns with passing &inputs.excluded_windows. Convert to Vec internally.

-        #[cfg(target_os = "macos")] excluded_windows: Vec<WindowId>,
+        #[cfg(target_os = "macos")] excluded_windows: &[WindowId],

And when constructing Self:

-            #[cfg(target_os = "macos")]
-            excluded_windows,
+            #[cfg(target_os = "macos")]
+            excluded_windows: excluded_windows.to_vec(),

475-491: Minor: avoid duplicate owner_name retrieval

You call v.owner_name() in the macOS filter and again to bind owner_name. Retrieve once and reuse to cut duplicate work and syscalls.

apps/desktop/src-tauri/src/recording.rs (1)

546-548: Avoid shadowing builder under cfg in Instant path

Shadowing builder again under macOS is unnecessary. Reassign instead to keep one binding (mirrors Studio fix).

-                        #[cfg(target_os = "macos")]
-                        let mut builder = builder.with_excluded_windows(excluded_windows.clone());
+                        #[cfg(target_os = "macos")]
+                        {
+                            builder = builder.with_excluded_windows(excluded_windows.clone());
+                        }
apps/desktop/src/routes/(window-chrome)/settings/general.tsx (1)

37-38: Do not manually import icons in desktop app

Per guidelines, rely on auto-imported icons instead of manual imports.

Apply:

- import IconLucidePlus from "~icons/lucide/plus";
- import IconLucideX from "~icons/lucide/x";

Keep usages (<IconLucidePlus />, <IconLucideX />) as-is if your icon auto-import plugin is configured.
As per coding guidelines

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc9aa2e and b97a3ac.

📒 Files selected for processing (13)
  • apps/desktop/src-tauri/src/general_settings.rs (6 hunks)
  • apps/desktop/src-tauri/src/lib.rs (4 hunks)
  • apps/desktop/src-tauri/src/recording.rs (9 hunks)
  • apps/desktop/src-tauri/src/window_exclusion.rs (1 hunks)
  • apps/desktop/src-tauri/src/windows.rs (19 hunks)
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx (5 hunks)
  • apps/desktop/src/routes/debug.tsx (1 hunks)
  • apps/desktop/src/utils/tauri.ts (4 hunks)
  • crates/recording/src/capture_pipeline.rs (3 hunks)
  • crates/recording/src/instant_recording.rs (6 hunks)
  • crates/recording/src/lib.rs (2 hunks)
  • crates/recording/src/sources/screen_capture/mod.rs (6 hunks)
  • crates/recording/src/studio_recording.rs (6 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/desktop/src/routes/debug.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/recording/src/capture_pipeline.rs
  • apps/desktop/src-tauri/src/general_settings.rs
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • apps/desktop/src-tauri/src/window_exclusion.rs
  • crates/recording/src/studio_recording.rs
  • apps/desktop/src/utils/tauri.ts
  • crates/recording/src/instant_recording.rs
  • apps/desktop/src-tauri/src/windows.rs
  • crates/recording/src/sources/screen_capture/mod.rs
  • apps/desktop/src-tauri/src/lib.rs
  • apps/desktop/src-tauri/src/recording.rs
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx
  • crates/recording/src/lib.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and 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/window_exclusion.rs
  • crates/recording/src/studio_recording.rs
  • crates/recording/src/instant_recording.rs
  • apps/desktop/src-tauri/src/windows.rs
  • crates/recording/src/sources/screen_capture/mod.rs
  • apps/desktop/src-tauri/src/lib.rs
  • apps/desktop/src-tauri/src/recording.rs
  • crates/recording/src/lib.rs
crates/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

For desktop IPC, use tauri_specta derive/macros on Rust types/events; do not hand-roll bindings

Files:

  • crates/recording/src/studio_recording.rs
  • crates/recording/src/instant_recording.rs
  • crates/recording/src/sources/screen_capture/mod.rs
  • crates/recording/src/lib.rs
crates/*/src/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Rust crates should place tests within the src/ and/or a sibling tests/ directory for each crate inside crates/*.

Files:

  • crates/recording/src/studio_recording.rs
  • crates/recording/src/instant_recording.rs
  • crates/recording/src/sources/screen_capture/mod.rs
  • crates/recording/src/lib.rs
**/tauri.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Never edit auto-generated IPC bindings file tauri.ts

Do not edit auto-generated files named tauri.ts.

Files:

  • apps/desktop/src/utils/tauri.ts
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/desktop/**/*.{ts,tsx}: Do not manually import icons in the desktop app; rely on auto-imported icons
In the desktop app, use @tanstack/solid-query for server state management

Files:

  • apps/desktop/src/utils/tauri.ts
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; leverage shared types from packages

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Files:

  • apps/desktop/src/utils/tauri.ts
  • apps/desktop/src/routes/(window-chrome)/settings/general.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.ts
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx
🧬 Code graph analysis (9)
apps/desktop/src-tauri/src/window_exclusion.rs (2)
apps/desktop/src/utils/tauri.ts (2)
  • WindowId (487-487)
  • WindowExclusion (486-486)
crates/scap-targets/src/platform/macos.rs (4)
  • bundle_identifier (350-378)
  • owner_name (320-332)
  • list (32-38)
  • list (237-269)
crates/recording/src/studio_recording.rs (2)
apps/desktop/src/utils/tauri.ts (1)
  • WindowId (487-487)
crates/recording/src/instant_recording.rs (2)
  • new (229-238)
  • with_excluded_windows (251-254)
crates/recording/src/instant_recording.rs (3)
crates/recording/src/studio_recording.rs (3)
  • new (353-364)
  • new (591-608)
  • with_excluded_windows (387-390)
apps/desktop/src/utils/tauri.ts (2)
  • ScreenCaptureTarget (459-459)
  • WindowId (487-487)
crates/scap-targets/src/platform/macos.rs (1)
  • Self (286-295)
apps/desktop/src-tauri/src/windows.rs (4)
apps/desktop/src/utils/tauri.ts (5)
  • AppTheme (341-341)
  • GeneralSettingsStore (396-396)
  • RecordingTargetMode (449-449)
  • WindowExclusion (486-486)
  • ShowCapWindow (464-464)
apps/desktop/src-tauri/src/target_select_overlay.rs (1)
  • display_id (131-132)
apps/desktop/src-tauri/src/window_exclusion.rs (1)
  • matches (18-63)
apps/desktop/src-tauri/src/general_settings.rs (2)
  • get (205-216)
  • default_excluded_windows (50-59)
crates/recording/src/sources/screen_capture/mod.rs (2)
apps/desktop/src/utils/tauri.ts (2)
  • WindowId (487-487)
  • CaptureWindow (371-371)
crates/scap-targets/src/platform/macos.rs (4)
  • owner_name (320-332)
  • bundle_identifier (350-378)
  • name (128-147)
  • name (380-392)
apps/desktop/src-tauri/src/lib.rs (3)
apps/desktop/src-tauri/src/windows.rs (1)
  • refresh_window_content_protection (884-895)
apps/desktop/src-tauri/src/general_settings.rs (1)
  • get_default_excluded_windows (270-272)
apps/desktop/src/utils/tauri.ts (1)
  • WindowExclusion (486-486)
apps/desktop/src-tauri/src/recording.rs (2)
apps/desktop/src-tauri/src/general_settings.rs (1)
  • default_excluded_windows (50-59)
apps/desktop/src-tauri/src/window_exclusion.rs (1)
  • resolve_window_ids (66-95)
apps/desktop/src/routes/(window-chrome)/settings/general.tsx (3)
apps/desktop/src/utils/tauri.ts (5)
  • WindowExclusion (486-486)
  • CaptureWindow (371-371)
  • GeneralSettingsStore (396-396)
  • commands (7-284)
  • events (289-333)
apps/desktop/src/store.ts (1)
  • generalSettingsStore (61-62)
packages/ui-solid/src/Button.tsx (1)
  • Button (40-50)
crates/recording/src/lib.rs (1)
apps/desktop/src/utils/tauri.ts (2)
  • WindowId (487-487)
  • LogicalBounds (415-415)
⏰ 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). (3)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (19)
crates/recording/src/instant_recording.rs (6)

4-4: Import swap to MicrophoneFeedLock looks correct

Matches usage in create_pipeline; no issues.


224-226: ActorBuilder: macOS-only excluded_windows field is appropriate

Field scoping and visibility look right.


235-237: Good: default excluded_windows to empty vec on macOS

Sane default; aligns with studio_recording builder.


250-254: Builder method with_excluded_windows: API shape LGTM

Fluent builder style; no issues.


268-271: Propagation into RecordingBaseInputs looks correct

Field wired through under macOS as expected.


306-308: Remove suggestion to borrow excluded_windows; Vec is required
create_screen_capture’s signature (and ScreenCaptureConfig::init) takes excluded_windows: Vec by value, and build(self) owns the builder so moving the Vec is correct—using &inputs.excluded_windows would be a type mismatch.

Likely an incorrect or invalid review comment.

crates/recording/src/sources/screen_capture/mod.rs (2)

35-35: CaptureWindow: bundle_identifier field addition looks good

Optional on macOS, null elsewhere; aligns with TS type.


241-242: Clone impl correctly includes excluded_windows

No issues.

crates/recording/src/lib.rs (1)

51-53: macOS exclusion plumbed correctly into inputs

Adding excluded_windows: Vec<WindowId> under macOS in RecordingBaseInputs looks correct and aligns with the capture pipeline changes.

apps/desktop/src-tauri/src/recording.rs (2)

598-601: Dialog parenting updated to RecordingControls

Using CapWindowId::RecordingControls for dialog parent is correct after the rename.


827-829: Close RecordingControls window on finish

Window closure updated to RecordingControls variant looks good.

apps/desktop/src-tauri/src/lib.rs (1)

28-29: Wiring: expose window_exclusion and commands via specta

  • New mod window_exclusion and .typ::<crate::window_exclusion::WindowExclusion>() are correctly added.
  • Commands refresh_window_content_protection and get_default_excluded_windows are exported.

Also applies to: 1922-1924, 2023-2025

apps/desktop/src/utils/tauri.ts (1)

47-52: Auto-generated bindings OK

New commands and types (CaptureWindow with bundle_identifier, WindowExclusion, GeneralSettingsStore.excludedWindows) are present and consistent with Rust types.

Also applies to: 371-373, 396-396, 486-486

apps/desktop/src-tauri/src/windows.rs (1)

869-895: Content protection refresh and matching logic look good

should_protect_window and refresh_window_content_protection integrate settings with a sensible default fallback and update all windows’ protection flags. Title-based matching is appropriate for Cap windows.

If users set only bundleIdentifier/ownerName exclusions, those won’t affect content protection (title-only match). Confirm this is intended; otherwise consider enriching the predicate.

apps/desktop/src-tauri/src/window_exclusion.rs (2)

6-63: WindowExclusion model and matching logic are clear and robust

  • Derives and camelCase serde align with TS.
  • matches handles bundle, owner(+optional title), and title cases.

66-95: Resolving window IDs from exclusions is correct

Enumerating windows and filtering via matches is straightforward. macOS bundle identifier gating is handled.

apps/desktop/src/routes/(window-chrome)/settings/general.tsx (2)

264-275: Good: persist exclusions, refresh protection, and prewarm macOS

Persisting excludedWindows, calling refreshWindowContentProtection, and emitting a prewarm on macOS is the right end-to-end flow.


203-229: Local exclusion matcher mirrors backend logic

Frontend matchesExclusion logic is consistent with Rust WindowExclusion::matches. This prevents drift in UI filtering.

crates/recording/src/studio_recording.rs (1)

695-707: No adjustment needed for excluded_windows argument
create_screen_capture’s signature is excluded_windows: Vec<WindowId>, so passing base_inputs.excluded_windows by value is correct.

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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/window_exclusion.rs (2)

33-54: Simplify the combined owner and title matching logic.

The nested Option handling at lines 38-49 makes the logic harder to follow. Extracting the title matching into a separate binding would improve clarity.

Apply this diff to simplify the logic:

 if let Some(expected_owner) = self.owner_name.as_deref() {
     let owner_matches = owner_name
         .map(|candidate| candidate == expected_owner)
         .unwrap_or(false);

-    if self.window_title.is_some() {
-        return owner_matches
-            && self
-                .window_title
-                .as_deref()
-                .map(|expected_title| {
-                    window_title
-                        .map(|candidate| candidate == expected_title)
-                        .unwrap_or(false)
-                })
-                .unwrap_or(false);
+    if let Some(expected_title) = self.window_title.as_deref() {
+        let title_matches = window_title
+            .map(|candidate| candidate == expected_title)
+            .unwrap_or(false);
+        return owner_matches && title_matches;
     }

     if owner_matches {
         return true;
     }
 }

77-81: Consider consistent typing across platforms.

The code works correctly, but using Option<&str> on non-macOS while macOS returns Option<String> creates a minor type inconsistency. While .as_deref() at line 87 handles both cases, explicit type annotation would make the cross-platform handling clearer.

Apply this diff for consistency:

 #[cfg(target_os = "macos")]
 let bundle_identifier = window.raw_handle().bundle_identifier();

 #[cfg(not(target_os = "macos"))]
-let bundle_identifier = None::<&str>;
+let bundle_identifier: Option<String> = None;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e639c7 and 93fd68f.

📒 Files selected for processing (2)
  • apps/desktop/src-tauri/src/general_settings.rs (5 hunks)
  • apps/desktop/src-tauri/src/window_exclusion.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src-tauri/src/general_settings.rs
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • apps/desktop/src-tauri/src/window_exclusion.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and 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/window_exclusion.rs
🧬 Code graph analysis (1)
apps/desktop/src-tauri/src/window_exclusion.rs (2)
apps/desktop/src/utils/tauri.ts (2)
  • WindowId (487-487)
  • WindowExclusion (486-486)
crates/scap-targets/src/platform/macos.rs (4)
  • bundle_identifier (350-378)
  • owner_name (320-332)
  • list (32-38)
  • list (237-269)
⏰ 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). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (1)
apps/desktop/src-tauri/src/window_exclusion.rs (1)

6-15: LGTM!

The struct definition is well-designed with appropriate derives and serialization attributes. The optional fields provide flexibility for different exclusion strategies.

@Brendonovich Brendonovich merged commit af2c83d into main Oct 13, 2025
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants