Skip to content

feat(desktop): deeplink recording controls + Raycast extension (#1540)#1741

Open
Leviann wants to merge 5 commits intoCapSoftware:mainfrom
Leviann:feat/deeplinks-raycast-1540
Open

feat(desktop): deeplink recording controls + Raycast extension (#1540)#1741
Leviann wants to merge 5 commits intoCapSoftware:mainfrom
Leviann:feat/deeplinks-raycast-1540

Conversation

@Leviann
Copy link
Copy Markdown

@Leviann Leviann commented Apr 19, 2026

Summary

Extends cap-desktop://action deeplinks for recording controls and adds a Raycast extension under extensions/cap/.

Deeplinks (Rust, deeplink_actions.rs)

  • pause_recording, resume_recording, toggle_pause_recording
  • take_screenshot (same capture_mode shape as start_recording)
  • set_microphone, set_camera
  • refresh_raycast_device_cache writes raycast-device-cache.json in Cap app data (displays, windows, cameras, mics)

Fix

cap-desktop://action?... was incorrectly rejected when the host was action (fall-through to Invalid). Parsing now requires host_str() == "action".

Raycast

Commands call open(cap-desktop://action?value=...) with snake_case JSON matching DeepLinkAction. Includes start/stop/pause/resume/toggle, refresh cache, screenshot, set mic/camera.

Testing

  • npm install + tsc --noEmit in extensions/cap (Windows).
  • Full cargo check -p cap-desktop was not completed on this Windows agent (no MSVC link.exe; GNU path hits ffmpeg pkg-config). Expect macOS / project CI to validate the Rust crate.

Related: #1540

/claim #1540

Greptile Summary

This PR extends cap-desktop://action deeplinks with pause/resume/toggle-pause, take-screenshot, set-microphone, set-camera, and a device-cache refresh action on the Rust side, and adds a companion Raycast extension (extensions/cap/) with a command for each action. It also fixes a pre-existing URL-parsing bug where url.domain() with Some("action") fell through to the _ => Invalid arm, silently dropping every action deeplink.

Confidence Score: 5/5

Safe to merge; all remaining findings are P2 style suggestions with no correctness impact.

The Rust handler implementations correctly delegate to existing recording functions, the URL host-check fix is clearly correct, and the Raycast extension is straightforward. The two open items (blocking std::fs I/O in async, missing error surface in the deeplink helper) are minor best-practice notes that don't affect correctness or data integrity.

No files require special attention; the blocking I/O note in deeplink_actions.rs is low-risk for a cache-write path.

Important Files Changed

Filename Overview
apps/desktop/src-tauri/src/deeplink_actions.rs Adds pause/resume/toggle-pause, take-screenshot, set-mic, set-camera, and refresh-device-cache deeplink handlers; fixes the critical host-check bug that was silently dropping all action deeplinks; uses blocking std::fs I/O inside async context in the RefreshRaycastDeviceCache branch.
extensions/cap/src/deeplink.ts Thin helper that JSON-encodes the action body and fires a cap-desktop:// deeplink; no error handling around the open() call.
extensions/cap/src/start-recording.tsx Validates target/mode/camera arguments and fires start_recording deeplink; input validation is thorough.
extensions/cap/src/take-screenshot.tsx Parses screen:/window: prefix and sends take_screenshot deeplink; validation is correct.
extensions/cap/src/set-camera.tsx Accepts raw JSON for DeviceOrModelID and delegates to set_camera deeplink; handles empty input by passing null to disable camera.
extensions/cap/src/refresh-device-cache.tsx Single-shot command that fires refresh_raycast_device_cache; straightforward and correct.
extensions/cap/package.json Raycast extension manifest declaring all 9 commands with their arguments; correctly structured.

Sequence Diagram

sequenceDiagram
    participant R as Raycast Extension
    participant OS as macOS open()
    participant T as Tauri deeplink handler
    participant DA as deeplink_actions.rs
    participant Rec as recording.rs

    R->>OS: open(cap-desktop://action?value=JSON)
    OS->>T: URL event
    T->>DA: handle(urls)
    DA->>DA: host_str() == "action"? parse ?value= JSON
    alt pause / resume / toggle
        DA->>Rec: pause_recording / resume_recording / toggle_pause_recording
    else take_screenshot
        DA->>DA: capture_target_from_mode()
        DA->>Rec: take_screenshot(app, target)
    else set_microphone / set_camera
        DA->>Rec: set_mic_input / set_camera_input
    else refresh_raycast_device_cache
        DA->>Rec: list_capture_displays / list_capture_windows / list_cameras
        DA->>DA: MicrophoneFeed::list()
        DA->>DA: std::fs::write(raycast-device-cache.json)
    end
    Rec-->>DA: Result
    DA-->>R: (fire-and-forget)
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 248-253

Comment:
**Blocking I/O inside async executor**

`std::fs::create_dir_all` (called inside `raycast_device_cache_path`) and `std::fs::write` are synchronous and will block the Tokio worker thread for the duration of the disk operation. For small files this is unlikely to matter in practice, but the pattern is inconsistent with the rest of the `execute` method. Consider `tokio::fs::create_dir_all` / `tokio::fs::write` (or wrapping with `tokio::task::spawn_blocking`) to keep the async context non-blocking.

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

---

This is a comment left during a code review.
Path: extensions/cap/src/deeplink.ts
Line: 3-6

Comment:
**No error handling on deeplink open**

`open(...)` is awaited but any error it throws (e.g. Cap is not installed, or the OS refuses to handle the scheme) will propagate as an unhandled rejection to the caller. Every command that calls `runCapDeeplink` shows a success HUD immediately after — so a user whose Cap install is broken would see "Cap: pause recording" even though nothing happened. Wrapping in a try/catch and surfacing a `Toast.Style.Failure` would give actionable feedback.

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

Reviews (1): Last reviewed commit: "feat(desktop): deeplink recording contro..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

Comment thread apps/desktop/src-tauri/src/deeplink_actions.rs Outdated
Comment thread extensions/cap/src/deeplink.ts
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Leviann added 2 commits April 19, 2026 22:26
…eplink parse logging

- Use rundll32 url.dll,FileProtocolHandler on Windows (cmd start mangles % in query)\n- Move extension icon to assets/ (512px from app icon); README note\n- Refresh command toast with dev vs prod cache paths\n- Log parse/execute failures with tracing::warn for cap-desktop.log
@Leviann
Copy link
Copy Markdown
Author

Leviann commented Apr 19, 2026

Update: pushed \

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants