Skip to content

Extend desktop deeplink actions for recording controls#1794

Open
liameast43345 wants to merge 5 commits into
CapSoftware:mainfrom
liameast43345:cap-deeplink-recording-actions
Open

Extend desktop deeplink actions for recording controls#1794
liameast43345 wants to merge 5 commits into
CapSoftware:mainfrom
liameast43345:cap-deeplink-recording-actions

Conversation

@liameast43345
Copy link
Copy Markdown

@liameast43345 liameast43345 commented May 10, 2026

Summary

  • Adds deeplink actions for restart, explicit pause, explicit resume, pause/resume toggle, microphone selection, camera selection, starting from saved settings, and opening the recording picker.
  • Reuses existing recording, input, and picker functions instead of adding separate recording logic.
  • Adds a Raycast extension under apps/raycast with no-view commands for Cap recording, settings, picker, microphone, and camera deeplinks.
  • Adds parser coverage for the new desktop deeplink payloads.

Refs #1540

/claim #1540

Example deeplinks

The existing parser accepts JSON in the value query parameter on the action host.

Start Studio recording:

cap-desktop://action?value=%7B%22start_recording_from_settings%22%3A%7B%22mode%22%3A%22studio%22%7D%7D

Start Instant recording:

cap-desktop://action?value=%7B%22start_recording_from_settings%22%3A%7B%22mode%22%3A%22instant%22%7D%7D

Pause/resume:

cap-desktop://action?value=%22toggle_pause_recording%22

Pause:

cap-desktop://action?value=%22pause_recording%22

Resume:

cap-desktop://action?value=%22resume_recording%22

Restart:

cap-desktop://action?value=%22restart_recording%22

Open picker:

cap-desktop://action?value=%7B%22open_recording_picker%22%3A%7B%22target_mode%22%3A%22display%22%7D%7D

Clear selected microphone:

cap-desktop://action?value=%7B%22set_microphone%22%3A%7B%22mic_label%22%3Anull%7D%7D

Clear selected camera:

cap-desktop://action?value=%7B%22set_camera%22%3A%7B%22camera%22%3Anull%7D%7D

Set microphone by label:

cap-desktop://action?value=%7B%22set_microphone%22%3A%7B%22mic_label%22%3A%22MacBook%20Pro%20Microphone%22%7D%7D

Set camera by device ID:

cap-desktop://action?value=%7B%22set_camera%22%3A%7B%22camera%22%3A%7B%22DeviceID%22%3A%22camera-device-id%22%7D%7D%7D

Testing

  • pnpm --dir apps/raycast exec tsc --noEmit
  • pnpm --dir apps/raycast build
  • pnpm --dir apps/raycast lint
  • git diff --check

Not run locally: Rust tests, because this environment does not have cargo installed.

Demo video: not attached from this environment because I cannot run the macOS desktop app and Raycast here. The deeplink and Raycast command code paths are included above for maintainer review.

@brin-security-scanner brin-security-scanner Bot added the contributor:verified Contributor passed trust analysis. label May 10, 2026
Comment on lines +185 to +191
None => {
ShowCapWindow::Main {
init_target_mode: None,
}
.show(app)
.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 ShowCapWindow::show returns tauri::Result<WebviewWindow>, and tauri::Error does not implement From<_> for String. Using bare ? here won't compile because the surrounding execute function returns Result<(), String>. Every other call site in this codebase (e.g. show_window in lib.rs) uses .map_err(|e| e.to_string())? to bridge the conversion.

Suggested change
None => {
ShowCapWindow::Main {
init_target_mode: None,
}
.show(app)
.await?;
}
None => {
ShowCapWindow::Main {
init_target_mode: None,
}
.show(app)
.await
.map_err(|e| e.to_string())?;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 185-191

Comment:
`ShowCapWindow::show` returns `tauri::Result<WebviewWindow>`, and `tauri::Error` does not implement `From<_> for String`. Using bare `?` here won't compile because the surrounding `execute` function returns `Result<(), String>`. Every other call site in this codebase (e.g. `show_window` in `lib.rs`) uses `.map_err(|e| e.to_string())?` to bridge the conversion.

```suggestion
                    None => {
                        ShowCapWindow::Main {
                            init_target_mode: None,
                        }
                        .show(app)
                        .await
                        .map_err(|e| e.to_string())?;
                    }
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +234 to +253
#[test]
fn parses_set_microphone_action() {
assert!(matches!(
parse_action("%7B%22set_microphone%22%3A%7B%22mic_label%22%3Anull%7D%7D").unwrap(),
DeepLinkAction::SetMicrophone { mic_label: None }
));
}

#[test]
fn parses_open_recording_picker_action() {
assert!(matches!(
parse_action(
"%7B%22open_recording_picker%22%3A%7B%22target_mode%22%3A%22display%22%7D%7D"
)
.unwrap(),
DeepLinkAction::OpenRecordingPicker {
target_mode: Some(RecordingTargetMode::Display)
}
));
}
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.

P2 Missing test cases for SetCamera and OpenRecordingPicker { target_mode: None }

The new tests cover RestartRecording, TogglePauseRecording, SetMicrophone (null label), and OpenRecordingPicker (with a mode), but there are no round-trip parse tests for SetCamera (both null and non-null camera values) or for OpenRecordingPicker when target_mode is None. Adding these would match the test coverage level of the other new actions.

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

Comment:
**Missing test cases for `SetCamera` and `OpenRecordingPicker { target_mode: None }`**

The new tests cover `RestartRecording`, `TogglePauseRecording`, `SetMicrophone` (null label), and `OpenRecordingPicker` (with a mode), but there are no round-trip parse tests for `SetCamera` (both null and non-null camera values) or for `OpenRecordingPicker` when `target_mode` is `None`. Adding these would match the test coverage level of the other new actions.

How can I resolve this? If you propose a fix, please make it concise.

@brin-security-scanner brin-security-scanner Bot added the pr:verified PR passed security analysis. label May 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🙋 Bounty claim contributor:verified Contributor passed trust analysis. pr:verified PR passed security analysis.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants