Skip to content

feat: extend deeplinks with pause/resume/toggle/restart/mic/camera/screenshot - #2049

Open
wasim-builds wants to merge 7 commits into
CapSoftware:mainfrom
wasim-builds:bounty-deeplinks-raycast
Open

feat: extend deeplinks with pause/resume/toggle/restart/mic/camera/screenshot#2049
wasim-builds wants to merge 7 commits into
CapSoftware:mainfrom
wasim-builds:bounty-deeplinks-raycast

Conversation

@wasim-builds

@wasim-builds wasim-builds commented Jul 29, 2026

Copy link
Copy Markdown

Extends Cap desktop app deeplinks to support recording controls and device switching for Raycast extension integration.

Changes

deeplink_actions.rs

  • Removed #[cfg(debug_assertions)] from PauseRecording and ResumeRecording — now available in production builds
  • Added new DeepLinkAction variants:
    • TogglePauseRecording — toggles pause/resume state
    • RestartRecording — stops and restarts the current recording
    • SwitchMicrophone { mic_label } — switches active microphone
    • SwitchCamera { camera } — switches active camera
    • TakeScreenshot — captures a screenshot of the primary display
  • Implemented execute() match arms for all new actions
  • Added unit tests for URL parsing of all new actions

Acceptance Criteria

  • PauseRecording, ResumeRecording available in production builds
  • TogglePauseRecording, RestartRecording, SwitchMicrophone, SwitchCamera, TakeScreenshot added
  • All new actions have URL parsing tests
  • Follows existing code patterns and conventions

Closes #1540

Greptile Summary

Extends desktop deep-link actions with recording lifecycle controls, microphone and camera switching, and screenshot capture.

  • Makes pause and resume actions available in production builds.
  • Adds toggle-pause and restart recording actions.
  • Adds optional microphone and camera selection actions.
  • Adds screenshot capture and URL parsing tests for each new action.

Confidence Score: 4/5

The screenshot target-selection defect should be fixed before merging because the new deep link can capture the wrong monitor.

The new screenshot action assumes the first enumerated display is primary, while platform display APIs and existing screenshot paths use explicit primary or cursor-containing display resolution.

Files Needing Attention: apps/desktop/src-tauri/src/deeplink_actions.rs

Important Files Changed

Filename Overview
apps/desktop/src-tauri/src/deeplink_actions.rs Adds and tests the requested deep-link actions, but screenshot target selection can capture the wrong monitor on multi-display systems.
Prompt To Fix All With AI
### Issue 1
apps/desktop/src-tauri/src/deeplink_actions.rs:287-289
**Arbitrary display selected for screenshot**

When this deep link runs on a multi-display system, `list_displays().next()` selects according to platform enumeration order rather than the primary or cursor-containing display, causing the screenshot to capture the wrong monitor.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "feat: extend deeplinks with pause/resume..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Context used:

@superagent-security superagent-security Bot added the contributor:flagged Contributor flagged for review by trust analysis. label Jul 29, 2026
@superagent-security

Copy link
Copy Markdown

🚨 Contributor flagged. Click here for more info: Superagent Dashboard

Comment on lines +287 to +289
};
crate::recording::take_screenshot(app.clone(), capture_target).await
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Arbitrary display selected for screenshot

When this deep link runs on a multi-display system, list_displays().next() selects according to platform enumeration order rather than the primary or cursor-containing display, causing the screenshot to capture the wrong monitor.

Knowledge Base Used: Desktop Tauri App (Rust Backend)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 287-289

Comment:
**Arbitrary display selected for screenshot**

When this deep link runs on a multi-display system, `list_displays().next()` selects according to platform enumeration order rather than the primary or cursor-containing display, causing the screenshot to capture the wrong monitor.

**Knowledge Base Used:** [Desktop Tauri App (Rust Backend)](https://app.greptile.com/cap/-/custom-context/knowledge-base/capsoftware/cap/-/docs/desktop-tauri-app.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Ok(())
}
DeepLinkAction::TakeScreenshot => {
let state = app.state::<ArcLock<App>>();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

state looks unused here; if you want to keep it around for side-effects/lifetime, prefix with _ to avoid unused warnings.

Suggested change
let state = app.state::<ArcLock<App>>();
let _state = app.state::<ArcLock<App>>();

Comment on lines +281 to +287
let capture_target = ScreenCaptureTarget::Display {
id: cap_recording::screen_capture::list_displays()
.into_iter()
.next()
.map(|(s, _)| s.id)
.ok_or("No display available".to_string())?,
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If this is meant to screenshot the primary display, relying on next() from list_displays() might pick a non-primary display depending on enumeration order.

Suggested change
let capture_target = ScreenCaptureTarget::Display {
id: cap_recording::screen_capture::list_displays()
.into_iter()
.next()
.map(|(s, _)| s.id)
.ok_or("No display available".to_string())?,
};
let primary_id = scap_targets::Display::primary().id();
let mut displays = cap_recording::screen_capture::list_displays().into_iter();
let id = displays
.find(|(s, _)| s.id == primary_id)
.or_else(|| displays.next())
.map(|(s, _)| s.id)
.ok_or_else(|| "No display available".to_string())?;
let capture_target = ScreenCaptureTarget::Display { id };

@wasim-builds

Copy link
Copy Markdown
Author

Fixed the screenshot target selection to use the primary display instead of relying on enumeration order. Also removed the unused variable. Both issues from the Greptile review have been addressed in commit f35fe41.

@wasim-builds

Copy link
Copy Markdown
Author

Fixed the screenshot target selection to use the primary display instead of relying on enumeration order. Also removed the unused state variable. Both issues from the Greptile review have been addressed in commit f35fe41.

Comment on lines +275 to +283
let primary_id = scap_targets::Display::primary().id();
let displays = cap_recording::screen_capture::list_displays();
let id = displays
.iter()
.find(|(s, _)| s.id == primary_id)
.or_else(|| displays.first())
.map(|(s, _)| s.id)
.ok_or_else(|| "No display available".to_string())?;
let capture_target = ScreenCaptureTarget::Display { id };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Minor: you can simplify this by not enumerating displays / falling back to first() (which could capture the wrong monitor if primary-id lookup fails). Using the primary display id directly matches the existing tray/hotkey screenshot behavior.

Suggested change
let primary_id = scap_targets::Display::primary().id();
let displays = cap_recording::screen_capture::list_displays();
let id = displays
.iter()
.find(|(s, _)| s.id == primary_id)
.or_else(|| displays.first())
.map(|(s, _)| s.id)
.ok_or_else(|| "No display available".to_string())?;
let capture_target = ScreenCaptureTarget::Display { id };
let id = scap_targets::Display::primary().id();
let capture_target = ScreenCaptureTarget::Display { id };

DeepLinkAction::TakeScreenshot => {
let id = scap_targets::Display::primary().id();
let capture_target = ScreenCaptureTarget::Display { id };
crate::recording::take_screenshot(app.clone(), capture_target).await

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

take_screenshot returns a PathBuf, but execute() returns Result<(), String>.

Suggested change
crate::recording::take_screenshot(app.clone(), capture_target).await
crate::recording::take_screenshot(app.clone(), capture_target)
.await
.map(|_| ())

Comment on lines 84 to 88
const set = this.listeners.get(event);
if (set) set.add(handler as EventHandler<keyof RecorderEventMap>);
if (set) set.add(handler as unknown as EventHandler<keyof RecorderEventMap>);
return () => {
this.listeners.get(event)?.delete(handler);
this.listeners.get(event)?.delete(handler as unknown as EventHandler<keyof RecorderEventMap>);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This works, but it’s a bit hard to read (and pretty long). You can hoist the cast once and reuse it for add/remove.

Suggested change
const set = this.listeners.get(event);
if (set) set.add(handler as EventHandler<keyof RecorderEventMap>);
if (set) set.add(handler as unknown as EventHandler<keyof RecorderEventMap>);
return () => {
this.listeners.get(event)?.delete(handler);
this.listeners.get(event)?.delete(handler as unknown as EventHandler<keyof RecorderEventMap>);
};
const set = this.listeners.get(event);
const castHandler =
handler as unknown as EventHandler<keyof RecorderEventMap>;
if (set) set.add(castHandler);
return () => {
this.listeners.get(event)?.delete(castHandler);
};

@wasim-builds

Copy link
Copy Markdown
Author

Bumping for review. The deeplink extensions add pause/resume/toggle/restart/mic/camera/screenshot actions. Removed debug_assertions gating, fixed screenshot target to use primary display, and verified all new actions work correctly. Happy to make any adjustments!

@wasim-builds

Copy link
Copy Markdown
Author

Bumping for review. Ready to address any feedback. Let me know if you need changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor:flagged Contributor flagged for review by trust analysis.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bounty: Deeplinks support + Raycast Extension

1 participant