Skip to content

Conversation

@dxverm23
Copy link

@dxverm23 dxverm23 commented Jan 31, 2026

Add deeplink actions for recording control + Raycast extension

Closes #1540

Summary

This PR extends Cap's deeplink support to enable full recording control via URL schemes, and includes a complete Raycast extension for quick access.

New Deeplink Actions

Added to deeplink_actions.rs:

Action Description URL Example
pause_recording Pause current recording cap-desktop://action?value={"pause_recording":null}
resume_recording Resume paused recording cap-desktop://action?value={"resume_recording":null}
toggle_pause_recording Toggle pause/resume cap-desktop://action?value={"toggle_pause_recording":null}
set_microphone Switch microphone cap-desktop://action?value={"set_microphone":{"label":"MacBook Pro Microphone"}}
set_camera Switch camera cap-desktop://action?value={"set_camera":{"device_id":"..."}}}

All new actions call the existing internal functions (crate::recording::pause_recording, etc.), following the same pattern as StartRecording and StopRecording.

Raycast Extension

Located in extensions/raycast/ with commands:

  • Start Recording
  • Stop Recording
  • Pause Recording
  • Resume Recording
  • Toggle Pause
  • Open Settings

Testing

# Test pause/resume
open "cap-desktop://action?value={\"toggle_pause_recording\":null}"

# Test open settings  
open "cap-desktop://action?value={\"open_settings\":{\"page\":null}}"

Checklist

  • Extended DeepLinkAction enum with new variants
  • Implemented execute() for each new action
  • Created Raycast extension with all commands
  • Added documentation

Demo

[Demo video will be added after testing on macOS]

Greptile Overview

Greptile Summary

This PR extends Cap's deeplink support to enable comprehensive recording control via URL schemes and adds a complete Raycast extension for quick access to recording commands.

Key Changes

  • New Deeplink Actions: Added 5 new variants to the DeepLinkAction enum in deeplink_actions.rs:

    • PauseRecording, ResumeRecording, TogglePauseRecording for recording state control
    • SetMicrophone, SetCamera for device switching during recording
  • Implementation Pattern: All new actions follow the established pattern by delegating to existing internal functions (crate::recording::pause_recording, crate::set_mic_input, etc.), ensuring consistency with the codebase

  • Raycast Extension: Complete TypeScript extension with 6 commands (start, stop, pause, resume, toggle pause, open settings) that trigger deeplink actions via URL schemes

  • URL Scheme Format: Actions use the format cap-desktop://action?value={JSON} where JSON is the serialized action object (e.g., {"pause_recording":null})

Code Quality

  • Follows existing code patterns and conventions from StartRecording and StopRecording actions
  • Proper error handling with Result<(), String> returns
  • Async execution model consistent with the codebase
  • TypeScript extension has proper Cap installation checks and user feedback

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The implementation follows established patterns perfectly, delegates to existing tested functions, and adds no complex logic or security concerns. The Raycast extension is straightforward with proper error handling.
  • No files require special attention

Important Files Changed

Filename Overview
apps/desktop/src-tauri/src/deeplink_actions.rs Added 5 new deeplink actions (pause, resume, toggle pause, set microphone, set camera) that delegate to existing internal functions following established patterns
extensions/raycast/package.json Raycast extension manifest with 6 commands for controlling Cap recording
extensions/raycast/src/utils.ts Helper utilities for checking Cap installation and triggering deeplink actions

Sequence Diagram

sequenceDiagram
    participant User
    participant Raycast
    participant DeepLink as cap-desktop://
    participant DeepLinkHandler as deeplink_actions.rs
    participant RecordingModule as recording.rs
    participant App as Cap Application

    User->>Raycast: Execute command (e.g., "Pause Recording")
    Raycast->>Raycast: Check if Cap is installed
    alt Cap not installed
        Raycast->>User: Show error toast
    else Cap installed
        Raycast->>DeepLink: Open URL with action payload
        Note over DeepLink: cap-desktop://action?value={"pause_recording":null}
        DeepLink->>DeepLinkHandler: Parse URL and extract action
        DeepLinkHandler->>DeepLinkHandler: Deserialize JSON to DeepLinkAction enum
        DeepLinkHandler->>DeepLinkHandler: Execute action asynchronously
        
        alt Recording Control Actions
            DeepLinkHandler->>RecordingModule: Call pause_recording/resume_recording/toggle_pause_recording
            RecordingModule->>App: Update recording state
            App-->>User: Recording paused/resumed
        else Device Control Actions
            DeepLinkHandler->>App: Call set_mic_input/set_camera_input
            App->>App: Switch audio/video device
            App-->>User: Device switched
        else Navigation Actions
            DeepLinkHandler->>App: Call show_window/open_project_from_path
            App-->>User: Window opened
        end
        
        DeepLinkHandler-->>Raycast: Action completed
        Raycast->>User: Close Raycast window
    end
Loading

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

Closes CapSoftware#1540

New deeplink actions:
- pause_recording
- resume_recording
- toggle_pause_recording
- set_microphone
- set_camera

Raycast extension with commands for all recording controls.
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DeepLinkAction {
// Recording controls
Copy link

Choose a reason for hiding this comment

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

Minor: this repo avoids code comments; can we drop the new // ... / /// ... in DeepLinkAction (and the whitespace-only lines) so this stays consistent with the rest of the codebase?

}

await closeMainWindow();
// Open Cap app - it will show the recording interface
Copy link

Choose a reason for hiding this comment

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

Nit: same here—can we remove the new inline comment to stay consistent with the no-comments policy?

return apps.some(app => app.bundleId === CAP_BUNDLE_ID);
}

export async function triggerCapAction(action: object): Promise<void> {
Copy link

Choose a reason for hiding this comment

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

Small typing improvement: object is very broad; Record<string, unknown> avoids accidentally accepting arrays/functions.

Suggested change
export async function triggerCapAction(action: object): Promise<void> {
export async function triggerCapAction(action: Record<string, unknown>): Promise<void> {

@@ -0,0 +1 @@
Placeholder - replace with actual Cap icon
Copy link

Choose a reason for hiding this comment

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

extensions/raycast/assets/command-icon.png is currently a text placeholder, so Raycast won’t be able to load it as an icon. Either commit an actual PNG here (or point package.json’s icon to a real asset).

| `resume_recording` | Resume paused recording | `cap-desktop://action?value={"resume_recording":null}` |
| `toggle_pause_recording` | Toggle pause/resume | `cap-desktop://action?value={"toggle_pause_recording":null}` |
| `set_microphone` | Switch microphone | `cap-desktop://action?value={"set_microphone":{"label":"MacBook Pro Microphone"}}` |
| `set_camera` | Switch camera | `cap-desktop://action?value={"set_camera":{"device_id":"..."}}}` |
Copy link

Choose a reason for hiding this comment

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

Docs: extra } in the set_camera example URL.

Suggested change
| `set_camera` | Switch camera | `cap-desktop://action?value={"set_camera":{"device_id":"..."}}}` |
| `set_camera` | Switch camera | `cap-desktop://action?value={"set_camera":{"device_id":"..."}}` |

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.

Bounty: Deeplinks support + Raycast Extension

1 participant