Skip to content

Comments

feat(desktop): extend deeplinks + add Raycast extension scaffold#1620

Open
cococlaw wants to merge 2 commits intoCapSoftware:mainfrom
cococlaw:feat/algora-1540-deeplinks-raycast
Open

feat(desktop): extend deeplinks + add Raycast extension scaffold#1620
cococlaw wants to merge 2 commits intoCapSoftware:mainfrom
cococlaw:feat/algora-1540-deeplinks-raycast

Conversation

@cococlaw
Copy link

@cococlaw cococlaw commented Feb 20, 2026

Implements #1540 by extending desktop deeplink actions and adding an initial Raycast extension scaffold.

/claim #1540

What’s included

  • Extended deeplink actions for recording controls and device switching:
    • start_current_recording
    • pause_recording
    • resume_recording
    • switch_microphone (optional label, rotate when null)
    • switch_camera (optional selector, rotate when null)
  • Fixed action URL parsing for cap-desktop://action?value=... host handling.
  • Added deeplink parsing unit tests in deeplink_actions.rs.
  • Added desktop deeplink docs: apps/desktop/src-tauri/DEEPLINKS.md.
  • Added Raycast extension scaffold under extensions/raycast with commands:
    • start/stop/pause/resume recording
    • switch microphone
    • switch camera
  • Wired workspace to include extensions/*.

Notes

  • Typecheck verified for Raycast extension (pnpm --dir extensions/raycast typecheck).
  • Rust tests/build were attempted but may be environment-dependent due to native desktop deps.

Greptile Summary

Extended desktop deeplink actions to support recording controls (pause/resume) and device switching (microphone/camera with rotation), and added a Raycast extension scaffold with 6 commands that invoke these deeplinks.

  • Fixed URL parsing to properly handle cap-desktop://action?value=... using host_str() instead of only domain()
  • Added start_current_recording action that uses saved recording settings from RecordingSettingsStore
  • Implemented device rotation logic via next_item() helper that cycles through available devices when no specific device is provided
  • Added find_camera_by_selector() to match cameras by display name, device ID, or model ID
  • Comprehensive unit tests verify action parsing for all new deeplink variants
  • Raycast extension uses shared runDeepLinkAction() utility that encodes JSON actions as URL query params

Confidence Score: 5/5

  • This PR is safe to merge with no issues found
  • Well-structured implementation with proper error handling, comprehensive unit tests for Rust deeplink parsing, clean TypeScript implementation following Raycast patterns, and thorough documentation. The URL parsing fix properly handles the action host, device rotation logic is sound, and all commands follow consistent patterns.
  • No files require special attention

Important Files Changed

Filename Overview
apps/desktop/src-tauri/src/deeplink_actions.rs Extended deeplink actions with recording controls, device switching, rotation logic, and comprehensive unit tests
apps/desktop/src-tauri/DEEPLINKS.md Added comprehensive deeplink documentation with examples for all new actions
extensions/raycast/src/lib/deeplink.ts Added reusable deeplink utility for Raycast commands with proper URL encoding
extensions/raycast/package.json Added Raycast extension manifest with 6 commands and proper dependencies
pnpm-workspace.yaml Added extensions/* to workspace packages

Sequence Diagram

sequenceDiagram
    participant User
    participant Raycast
    participant Desktop as Cap Desktop
    participant Recording as Recording Module
    
    User->>Raycast: Execute command
    Raycast->>Raycast: Build deeplink action JSON
    Raycast->>Raycast: URL encode as cap-desktop://action?value=...
    Raycast->>Desktop: Open deeplink URL
    Desktop->>Desktop: Parse URL and deserialize action
    
    alt Recording Control
        Desktop->>Recording: pause_recording() / resume_recording()
        Recording-->>Desktop: Result
    else Device Switching
        Desktop->>Desktop: Get current device
        Desktop->>Desktop: List available devices
        Desktop->>Desktop: Calculate next_item() with rotation
        Desktop->>Recording: set_mic_input() / set_camera_input()
        Recording-->>Desktop: Result
    else Start Current Recording
        Desktop->>Desktop: Load RecordingSettingsStore
        Desktop->>Recording: set_mic_input() + set_camera_input()
        Desktop->>Recording: start_recording() with stored settings
        Recording-->>Desktop: Result
    end
    
    Desktop-->>Raycast: Success/Error
    Raycast->>User: Show HUD notification
Loading

Last reviewed commit: 112bf3f

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

.flatten()
.unwrap_or_default();

crate::set_mic_input(app.state(), settings.mic_name).await?;
Copy link

Choose a reason for hiding this comment

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

settings.mic_name / settings.camera_id move out of settings, so using settings.mode/target/... afterwards becomes a partial-move and won’t compile.

Suggested change
crate::set_mic_input(app.state(), settings.mic_name).await?;
let settings = crate::recording_settings::RecordingSettingsStore::get(app)
.ok()
.flatten()
.unwrap_or_default();
let crate::recording_settings::RecordingSettingsStore {
target,
mic_name,
camera_id,
mode: saved_mode,
system_audio,
organization_id,
} = settings;
crate::set_mic_input(app.state(), mic_name).await?;
crate::set_camera_input(app.clone(), app.state(), camera_id, None).await?;
let inputs = StartRecordingInputs {
mode: mode.or(saved_mode).unwrap_or(RecordingMode::Studio),
capture_target: target.unwrap_or_else(|| {
ScreenCaptureTarget::Display {
id: Display::primary().id(),
}
}),
capture_system_audio: system_audio,
organization_id,
};


type DeepLinkAction = string | Record<string, unknown>;

export async function runDeepLinkAction(action: DeepLinkAction, successMessage: string) {
Copy link

Choose a reason for hiding this comment

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

open() can fail (e.g., scheme not registered / Cap not installed), but we always show a success HUD. Maybe wrap in a try/catch and show a failure message.

Suggested change
export async function runDeepLinkAction(action: DeepLinkAction, successMessage: string) {
export async function runDeepLinkAction(action: DeepLinkAction, successMessage: string) {
const value = JSON.stringify(action);
const deeplink = `cap-desktop://action?value=${encodeURIComponent(value)}`;
await closeMainWindow();
try {
await open(deeplink);
await showHUD(successMessage);
} catch {
await showHUD("Failed to open Cap");
}
}

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.

1 participant