Skip to content

Conversation

@daviddprtma
Copy link

@daviddprtma daviddprtma commented Feb 3, 2026

Summary

🎯 Bounty Submission for Issue #1540
/claim #1540

This pull request adds a new Raycast extension for controlling the Cap screen recorder directly from Raycast, and extends the Cap desktop app's deeplink API to support additional recording and device actions. The main changes include new deeplink actions in the desktop app, new Raycast extension commands, and supporting documentation and configuration.

Raycast Extension Integration:

  • Added a new Raycast extension in the raycast-extensions directory, with commands for starting, stopping, pausing, resuming, toggling pause, and taking screenshots in Cap. Each command sends the appropriate deeplink to the Cap desktop app.
  • Included a detailed README.md explaining installation, usage, and the deeplink protocol for the Raycast extension.

Cap Desktop Deeplink API Enhancements:

  • Extended the DeepLinkAction enum and handler in deeplink_actions.rs to support new actions: pause, resume, toggle pause recording, take screenshot, set camera, and set microphone. These are now callable via deeplinks. [1] [2]

Dependency and Configuration Updates:

  • Added the @raycast/api dependency to the main package.json to support Raycast extension development.
  • Created a dedicated package.json for the Raycast extension, specifying commands, dependencies, devDependencies, and scripts for building and publishing the extension.

These changes enable users to control Cap's core recording features from Raycast, improving workflow efficiency and automation.

Greptile Overview

Greptile Summary

This PR adds Raycast extension support to Cap by creating a new extension in raycast-extensions/ and extending the desktop app's deeplink API with 6 new actions (pause, resume, toggle-pause, take-screenshot, set-camera, set-microphone).

Key Changes:

  • Extended DeepLinkAction enum in deeplink_actions.rs with 6 new recording control actions
  • Created Raycast extension with 6 commands that communicate via cap-desktop:// deeplinks
  • Added proper documentation and metadata for Raycast extension publishing

Critical Issues Found:

  • Rust code violates NO COMMENTS policy - lines 35, 168, 172, 176, 180, 184, 190 contain inline comments that must be removed per CLAUDE.md
  • Variable shadowing bug in SetMicrophone handler - label is extracted but mic_label is passed to function (line 192-194)
  • Start recording command broken - opens settings page instead of actually starting recording
  • Monorepo pollution - @raycast/api added to root package.json instead of staying isolated in raycast-extensions/
  • Format inconsistency - pause/resume commands use {} while stop/toggle use null for empty actions

Confidence Score: 2/5

  • Not safe to merge - contains critical logic bug, coding standard violations, and broken start recording functionality
  • Score of 2 reflects multiple critical issues: (1) variable shadowing bug in SetMicrophone that will cause runtime failure, (2) start-recording command doesn't actually start recording, (3) code comment violations that will fail CI linting checks per workspace rules, (4) monorepo dependency pollution
  • apps/desktop/src-tauri/src/deeplink_actions.rs requires immediate fixes for comments and variable bug; raycast-extensions/src/start-recording.tsx needs complete reimplementation

Important Files Changed

Filename Overview
apps/desktop/src-tauri/src/deeplink_actions.rs Added 6 new deeplink actions (pause, resume, toggle-pause, screenshot, set-camera, set-mic) with code comment violations and variable shadowing bug
raycast-extensions/src/start-recording.tsx Start recording command opens settings page instead of starting recording - doesn't match intended functionality
raycast-extensions/src/pause-recording.tsx Sends pause_recording deeplink with inconsistent format ({} vs null)
raycast-extensions/src/resume-recording.tsx Sends resume_recording deeplink with inconsistent format ({} vs null)
package.json Added @raycast/api to root package.json dependencies, should be isolated to raycast-extensions directory

Sequence Diagram

sequenceDiagram
    participant User
    participant Raycast
    participant RaycastExtension
    participant Desktop as Cap Desktop App
    participant DeeplinkHandler as Deeplink Handler
    participant RecordingModule as Recording Module

    User->>Raycast: Execute command (e.g. "Pause Recording")
    Raycast->>RaycastExtension: Trigger command handler
    RaycastExtension->>RaycastExtension: Build deeplink action JSON
    Note over RaycastExtension: Format: {"pause_recording": {}}
    RaycastExtension->>RaycastExtension: Encode to cap-desktop:// URL
    RaycastExtension->>Desktop: Open deeplink URL
    Desktop->>DeeplinkHandler: Parse URL & extract action
    DeeplinkHandler->>DeeplinkHandler: Deserialize JSON to DeepLinkAction enum
    DeeplinkHandler->>RecordingModule: Execute action (e.g. pause_recording)
    RecordingModule->>Desktop: Update recording state
    Desktop-->>User: Recording paused
    RaycastExtension->>User: Show toast notification
Loading

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

Context used:

  • Context from dashboard - CLAUDE.md (source)

@daviddprtma
Copy link
Author

@richiemcilroy I have make the PR for #1570 . Feel free to check my PR and let's discuss if there's something that I can improve from it. thanks

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

5 files reviewed, 7 comments

Edit Code Review Agent Settings | Greptile

OpenSettings {
page: Option<String>,
},
// add Pause Recording, Resume Recording, Toggle Pause Recording, Take Screenshot, SetCamera, & SetMicrophone
Copy link
Contributor

Choose a reason for hiding this comment

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

violates NO COMMENTS policy in CLAUDE.md and AGENTS.md

Suggested change
// add Pause Recording, Resume Recording, Toggle Pause Recording, Take Screenshot, SetCamera, & SetMicrophone
PauseRecording,

Context Used: Context from dashboard - CLAUDE.md (source)

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

Comment:
violates NO COMMENTS policy in CLAUDE.md and AGENTS.md

```suggestion
    PauseRecording,
```

**Context Used:** Context from `dashboard` - CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=9a906542-f1fe-42c1-89a2-9f252d96d9f0))

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

Comment on lines 168 to 195
// Pause Recording
DeepLinkAction::PauseRecording => {
crate::recording::pause_recording(app.clone(), app.state()).await
}
// Resume Recording
DeepLinkAction::ResumeRecording => {
crate::recording::resume_recording(app.clone(), app.state()).await
}
// Toggle Pause Recording
DeepLinkAction::TogglePauseRecording => {
crate::recording::toggle_pause_recording(app.clone(), app.state()).await
}
// Take Screenshot
DeepLinkAction::TakeScreenshot{ target } => {
crate::recording::take_screenshot(app.clone(), app.state(), target).await.map(|_| ())
}
// Set Camera
DeepLinkAction::SetCamera{ camera_id } => {
let camera_id = camera_id.ok_or_else(|| "camera_id is required".to_string())?;
let state = app.state::<ArcLock<App>>();
crate::set_camera_input(app.clone(), state.clone(), Some(camera_id), None).await
}
// Set Microphone
DeepLinkAction::SetMicrophone{ mic_label } => {
let label = mic_label.ok_or_else(|| "mic_label is required".to_string())?;
let state = app.state::<ArcLock<App>>();
crate::set_mic_input(state.clone(), Some(mic_label)).await
}
Copy link
Contributor

Choose a reason for hiding this comment

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

violates NO COMMENTS policy - inline comments on lines 168, 172, 176, 180, 184, and 190 must be removed per CLAUDE.md and AGENTS.md

Suggested change
// Pause Recording
DeepLinkAction::PauseRecording => {
crate::recording::pause_recording(app.clone(), app.state()).await
}
// Resume Recording
DeepLinkAction::ResumeRecording => {
crate::recording::resume_recording(app.clone(), app.state()).await
}
// Toggle Pause Recording
DeepLinkAction::TogglePauseRecording => {
crate::recording::toggle_pause_recording(app.clone(), app.state()).await
}
// Take Screenshot
DeepLinkAction::TakeScreenshot{ target } => {
crate::recording::take_screenshot(app.clone(), app.state(), target).await.map(|_| ())
}
// Set Camera
DeepLinkAction::SetCamera{ camera_id } => {
let camera_id = camera_id.ok_or_else(|| "camera_id is required".to_string())?;
let state = app.state::<ArcLock<App>>();
crate::set_camera_input(app.clone(), state.clone(), Some(camera_id), None).await
}
// Set Microphone
DeepLinkAction::SetMicrophone{ mic_label } => {
let label = mic_label.ok_or_else(|| "mic_label is required".to_string())?;
let state = app.state::<ArcLock<App>>();
crate::set_mic_input(state.clone(), Some(mic_label)).await
}
DeepLinkAction::PauseRecording => {
crate::recording::pause_recording(app.clone(), app.state()).await
}
DeepLinkAction::ResumeRecording => {
crate::recording::resume_recording(app.clone(), app.state()).await
}
DeepLinkAction::TogglePauseRecording => {
crate::recording::toggle_pause_recording(app.clone(), app.state()).await
}
DeepLinkAction::TakeScreenshot{ target } => {
crate::recording::take_screenshot(app.clone(), app.state(), target).await.map(|_| ())
}
DeepLinkAction::SetCamera{ camera_id } => {
let camera_id = camera_id.ok_or_else(|| "camera_id is required".to_string())?;
let state = app.state::<ArcLock<App>>();
crate::set_camera_input(app.clone(), state.clone(), Some(camera_id), None).await
}
DeepLinkAction::SetMicrophone{ mic_label } => {
let label = mic_label.ok_or_else(|| "mic_label is required".to_string())?;
let state = app.state::<ArcLock<App>>();
crate::set_mic_input(state.clone(), Some(mic_label)).await
}

Context Used: Context from dashboard - CLAUDE.md (source)

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/desktop/src-tauri/src/deeplink_actions.rs
Line: 168:195

Comment:
violates NO COMMENTS policy - inline comments on lines 168, 172, 176, 180, 184, and 190 must be removed per CLAUDE.md and AGENTS.md

```suggestion
            DeepLinkAction::PauseRecording => {
                crate::recording::pause_recording(app.clone(), app.state()).await
            }
            DeepLinkAction::ResumeRecording => {
                crate::recording::resume_recording(app.clone(), app.state()).await
            }
            DeepLinkAction::TogglePauseRecording => {
                crate::recording::toggle_pause_recording(app.clone(), app.state()).await
            }
            DeepLinkAction::TakeScreenshot{ target } => {
                crate::recording::take_screenshot(app.clone(), app.state(), target).await.map(|_| ())
            }
            DeepLinkAction::SetCamera{ camera_id } => {
                let camera_id = camera_id.ok_or_else(|| "camera_id is required".to_string())?;
                let state = app.state::<ArcLock<App>>();
                crate::set_camera_input(app.clone(), state.clone(), Some(camera_id), None).await
            }
            DeepLinkAction::SetMicrophone{ mic_label } => {
                let label = mic_label.ok_or_else(|| "mic_label is required".to_string())?;
                let state = app.state::<ArcLock<App>>();
                crate::set_mic_input(state.clone(), Some(mic_label)).await
            }
```

**Context Used:** Context from `dashboard` - CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=9a906542-f1fe-42c1-89a2-9f252d96d9f0))

<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.

Comment on lines 14 to 15
const action = {
open_settings: { page: "recording" }
Copy link
Contributor

Choose a reason for hiding this comment

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

action doesn't trigger recording start - sends open_settings with recording page instead of start_recording action. Doesn't match command name or description

Prompt To Fix With AI
This is a comment left during a code review.
Path: raycast-extensions/src/start-recording.tsx
Line: 14:15

Comment:
action doesn't trigger recording start - sends `open_settings` with recording page instead of `start_recording` action. Doesn't match command name or description

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

package.json Outdated
Comment on lines 63 to 65
"dependencies": {
"@raycast/api": "^1.104.4"
}
Copy link
Contributor

Choose a reason for hiding this comment

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

@raycast/api dependency added to root package.json but Raycast extension has its own package.json in raycast-extensions/. Root dependencies pollute the monorepo workspace

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: package.json
Line: 63:65

Comment:
`@raycast/api` dependency added to root `package.json` but Raycast extension has its own `package.json` in `raycast-extensions/`. Root dependencies pollute the monorepo workspace

<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.


export default async function Command() {
try {
const action = { pause_recording: {} };
Copy link
Contributor

Choose a reason for hiding this comment

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

action format { pause_recording: {} } doesn't match format used in other commands - stop-recording.tsx uses { stop_recording: null } and toggle-pause.tsx uses { toggle_pause_recording: null }

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: raycast-extensions/src/pause-recording.tsx
Line: 5:5

Comment:
action format `{ pause_recording: {} }` doesn't match format used in other commands - `stop-recording.tsx` uses `{ stop_recording: null }` and `toggle-pause.tsx` uses `{ toggle_pause_recording: null }`

<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.


export default async function Command() {
try {
const action = { resume_recording: {} };
Copy link
Contributor

Choose a reason for hiding this comment

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

action format { resume_recording: {} } should be { resume_recording: null } for consistency with stop-recording.tsx and toggle-pause.tsx

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: raycast-extensions/src/resume-recording.tsx
Line: 5:5

Comment:
action format `{ resume_recording: {} }` should be `{ resume_recording: null }` for consistency with `stop-recording.tsx` and `toggle-pause.tsx`

<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.

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