Bounty: Deeplinks support + Raycast Extension#1628
Bounty: Deeplinks support + Raycast Extension#1628SolariSystems wants to merge 5 commits intoCapSoftware:mainfrom
Conversation
| camera: null, | ||
| mic_label: null, | ||
| capture_system_audio: prefs.captureSystemAudio ?? false, | ||
| mode: prefs.recordingMode ?? "instant", |
There was a problem hiding this comment.
instant_capture doesn't match the Rust RecordingMode enum values (studio or instant)
| mode: prefs.recordingMode ?? "instant", | |
| mode: prefs.recordingMode ?? "instant", |
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/raycast-extension/src/start-recording.tsx
Line: 25
Comment:
`instant_capture` doesn't match the Rust `RecordingMode` enum values (`studio` or `instant`)
```suggestion
mode: prefs.recordingMode ?? "instant",
```
How can I resolve this? If you propose a fix, please make it concise.| pub fn register(app: &tauri::App) -> Result<(), Box<dyn std::error::Error>> { | ||
| use tauri_plugin_deep_link::DeepLinkExt; | ||
| let app_handle = app.handle().clone(); | ||
| app.deep_link().on_open_urls(move |event| { | ||
| handle(&app_handle, event.urls().to_vec()); | ||
| })?; | ||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
The register function is defined but never called. Deeplinks are already handled via app.deep_link().on_open_url() in lib.rs:3390.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 45-52
Comment:
The `register` function is defined but never called. Deeplinks are already handled via `app.deep_link().on_open_url()` in `lib.rs:3390`.
How can I resolve this? If you propose a fix, please make it concise.
apps/raycast-extension/package.json
Outdated
| "description": "Instant shares immediately; Studio lets you edit first", | ||
| "type": "dropdown", | ||
| "required": false, | ||
| "default": "instant", |
There was a problem hiding this comment.
Consider using studio as default to match the description on line 15 and provide consistency with the desktop app defaults
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/raycast-extension/package.json
Line: 53
Comment:
Consider using `studio` as default to match the description on line 15 and provide consistency with the desktop app defaults
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.
.gitignore
Outdated
| analysis/plans/ | ||
| .ralphy No newline at end of file | ||
| .ralphy | ||
| # Auto-added by Solari to prevent build artifact commits |
There was a problem hiding this comment.
This looks like it accidentally includes contributor-specific ignores (and the attribution line). I’d keep the generic ones, but drop the Solari-specific patterns.
| # Auto-added by Solari to prevent build artifact commits | |
| .venv/ | |
| venv/ | |
| node_modules/ | |
| __pycache__/ | |
| *.pyc |
.gitignore
Outdated
| # Auto-added by Solari to prevent build artifact commits | ||
| .venv/ | ||
| venv/ | ||
| node_modules/ | ||
| __pycache__/ | ||
| *.pyc | ||
| .solari_* | ||
| .solari_deps_installed |
There was a problem hiding this comment.
Edit: suggestion meant to cover the whole new block here (drop the attribution + .solari_* entries).
| # Auto-added by Solari to prevent build artifact commits | |
| .venv/ | |
| venv/ | |
| node_modules/ | |
| __pycache__/ | |
| *.pyc | |
| .solari_* | |
| .solari_deps_installed | |
| .venv/ | |
| venv/ | |
| node_modules/ | |
| __pycache__/ | |
| *.pyc |
package-lock.json
Outdated
| @@ -0,0 +1,1455 @@ | |||
| { | |||
There was a problem hiding this comment.
This repo is pnpm-based; committing a root package-lock.json usually means someone ran npm install and it’ll fight pnpm-lock.yaml. I’d drop this file from the PR and stick with pnpm’s lockfile.
| let raw = v.as_ref(); | ||
| serde_json::from_str::<DeviceOrModelID>(raw) | ||
| .or_else(|_| { | ||
| serde_json::from_str::<DeviceOrModelID>(&format!(r#""{}""#, raw)) |
There was a problem hiding this comment.
The fallback format!(r#"\"{}\""#, raw) will break if raw contains quotes/backslashes. Using serde_json::Value::String avoids manual escaping.
| serde_json::from_str::<DeviceOrModelID>(&format!(r#""{}""#, raw)) | |
| serde_json::from_str::<DeviceOrModelID>(raw) | |
| .or_else(|_| { | |
| serde_json::from_value::<DeviceOrModelID>(serde_json::Value::String( | |
| raw.to_string(), | |
| )) | |
| }) | |
| .ok() |
| serde_json::from_str::<DeviceOrModelID>(raw) | ||
| .or_else(|_| { | ||
| serde_json::from_str::<DeviceOrModelID>(&format!(r#""{}""#, raw)) | ||
| }) |
There was a problem hiding this comment.
Edit: suggestion intended to replace the whole parse block.
| serde_json::from_str::<DeviceOrModelID>(raw) | |
| .or_else(|_| { | |
| serde_json::from_str::<DeviceOrModelID>(&format!(r#""{}""#, raw)) | |
| }) | |
| serde_json::from_str::<DeviceOrModelID>(raw) | |
| .or_else(|_| { | |
| serde_json::from_value::<DeviceOrModelID>(serde_json::Value::String( | |
| raw.to_string(), | |
| )) | |
| }) | |
| .ok() |
| } | ||
| } | ||
|
|
||
| { |
There was a problem hiding this comment.
Looks like apps/raycast-extension content (extension schema / tsconfig / TS commands) got pasted into this Rust file after the impl DeepLinkAction block. I’d remove everything from here to EOF so deeplink_actions.rs stays valid Rust (the Raycast extension files are already added under apps/raycast-extension/).
apps/raycast-extension/package.json
Outdated
| { | ||
| "name": "captureName", | ||
| "title": "Screen or Window Name", | ||
| "description": "Exact name of the screen or window to capture (leave blank for first available)", |
There was a problem hiding this comment.
Cap currently matches by exact name; leaving this blank won’t select “first available” (it’ll send an empty string). Either implement empty-as-default on the app side, or tweak this description so it doesn’t promise that behavior.
| "description": "Exact name of the screen or window to capture (leave blank for first available)", | |
| "description": "Exact name of the screen or window to capture", |
| const captureName = prefs.captureName?.trim() || ""; | ||
| const captureMode = | ||
| prefs.captureType === "window" | ||
| ? { window: captureName } | ||
| : { screen: captureName }; |
There was a problem hiding this comment.
If captureName is empty, the desktop side will treat it as an exact match and error. Consider failing fast in the command so users get immediate feedback.
| const captureName = prefs.captureName?.trim() || ""; | |
| const captureMode = | |
| prefs.captureType === "window" | |
| ? { window: captureName } | |
| : { screen: captureName }; | |
| const captureName = prefs.captureName?.trim(); | |
| if (!captureName) { | |
| await showHUD("Set a screen/window name in Raycast preferences first"); | |
| return; | |
| } | |
| const captureMode = | |
| prefs.captureType === "window" | |
| ? { window: captureName } | |
| : { screen: captureName }; |
| "deep-link": { | ||
| "desktop": { | ||
| "schemes": ["cap-desktop"] | ||
| "schemes": ["cap-desktop", "cap"] |
There was a problem hiding this comment.
Adding the global cap:// scheme means any app/site can trigger these actions once Cap is installed (e.g. starting/stopping recordings). Might be worth gating “remote control” deeplinks behind an explicit user setting, or at least limiting which actions are allowed without user confirmation.
<sub>[Edit Code Review Agent Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile; This looks like it accidentally includes contrib
| let id = params.get("id").and_then(|v| { | ||
| let raw = v.as_ref(); | ||
| serde_json::from_str::<DeviceOrModelID>(raw) | ||
| .or_else(|_| { | ||
| serde_json::from_str::<DeviceOrModelID>(&format!(r#""{}""#, raw)) | ||
| }) | ||
| .ok() | ||
| }); |
There was a problem hiding this comment.
DeviceOrModelID is an externally-tagged enum (DeviceID/ModelID), so deserializing from a plain string will always fail. As-is, cap://switch-camera?id=<some id> likely resolves to id: None and disables the camera instead of switching.
| let id = params.get("id").and_then(|v| { | |
| let raw = v.as_ref(); | |
| serde_json::from_str::<DeviceOrModelID>(raw) | |
| .or_else(|_| { | |
| serde_json::from_str::<DeviceOrModelID>(&format!(r#""{}""#, raw)) | |
| }) | |
| .ok() | |
| }); | |
| let id = params.get("id").and_then(|v| { | |
| let raw = v.as_ref(); | |
| if raw.is_empty() { | |
| return None; | |
| } | |
| serde_json::from_str::<DeviceOrModelID>(raw) | |
| .ok() | |
| .or_else(|| Some(DeviceOrModelID::DeviceID(raw.to_string()))) | |
| }); |
<sub>[Edit Code Review Agent Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile; This looks like it accidentally includes contrib
<sub>[Edit Code Review Agent Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile; This looks like it accidentally includes contrib
Summary
Bounty: Deeplinks support + Raycast Extension.
Changes
apps/desktop/src-tauri/src/deeplink_actions.rsapps/desktop/src-tauri/tauri.conf.jsonTesting
Note: Some tests may need attention. See CI results.
/claim #1540
Greptile Summary
Added deeplink support for controlling Cap recordings via URL schemes (
cap://andcap-desktop://) and implemented a new Raycast extension for remote control functionality. The implementation includes new deeplink actions for pause, resume, and switching audio/video devices.deeplink_actions.rswithPauseRecording,ResumeRecording,SwitchMic, andSwitchCameraactionscap://pause-recording) alongside legacy JSON-based format for backward compatibilitycapandcap-desktopURL schemes in systemIssues Found:
start-recording.tsxwill cause runtime failures (instant_capturedoesn't exist in Rust enum)registerfunction indeeplink_actions.rs(deeplinks already configured inlib.rs)Confidence Score: 3/5
instant_captureis used instead ofinstant, causing the deeplink action to fail. The Rust deeplink implementation is solid and properly integrated, but the TypeScript code will produce invalid deeplink URLs that won't deserialize correctly on the Rust side.apps/raycast-extension/src/start-recording.tsx- the recording mode bug must be fixed before mergeImportant Files Changed
capandcap-desktopURL schemesSequence Diagram
sequenceDiagram participant Raycast as Raycast Extension participant OS as Operating System participant Tauri as Tauri Deep Link Plugin participant Handler as deeplink_actions::handle participant Action as DeepLinkAction participant Recording as recording module Raycast->>OS: open("cap://pause-recording") OS->>Tauri: URL scheme handler triggered Tauri->>Handler: on_open_url event with URL Handler->>Action: TryFrom<&Url> parse URL alt Simple path-based (new) Action-->>Action: Match domain "pause-recording" Action-->>Handler: DeepLinkAction::PauseRecording else Legacy JSON-based Action-->>Action: Parse ?value=<json> Action-->>Handler: Deserialized action end Handler->>Action: execute(&app_handle) Action->>Recording: pause_recording(app, state) Recording-->>Action: Result<(), String> Action-->>Handler: Result Handler-->>Tauri: Async task completeLast reviewed commit: 0ec5e67