Skip to content

[Fix] Add Raycast recording controls via deeplinks#1813

Open
adminlip wants to merge 3 commits into
CapSoftware:mainfrom
adminlip:fix/deeplinks-raycast
Open

[Fix] Add Raycast recording controls via deeplinks#1813
adminlip wants to merge 3 commits into
CapSoftware:mainfrom
adminlip:fix/deeplinks-raycast

Conversation

@adminlip
Copy link
Copy Markdown

@adminlip adminlip commented May 13, 2026

Summary

  • add desktop deeplink actions for pause/resume recording and switching microphone/camera inputs
  • add a Raycast extension with commands to start/stop/pause/resume recordings and switch inputs through Cap deeplinks
  • include Raycast extension configuration, generated types, README, and lockfile entries

Validation

  • pnpm --dir apps/raycast lint
  • pnpm --dir apps/raycast exec tsc --noEmit
  • pnpm --dir apps/raycast build

Note: Rust tooling (cargo/rustfmt) was not available in this environment, so desktop Rust changes were statically reviewed and covered with parser unit tests.

Greptile Summary

This PR adds PauseRecording, ResumeRecording, SetMicrophone, and SetCamera deeplink actions to the Tauri desktop backend and introduces a new Raycast extension that exposes all recording controls as Raycast commands via those deeplinks. The Rust additions are small, cleanly delegating to existing functions, and are covered by new parser unit tests.

  • The deeplink actions in deeplink_actions.rs are well-integrated — they reuse the existing pause_recording, resume_recording, set_mic_input, and set_camera_input helpers and the JSON shape matches the TypeScript type definitions in cap.ts.
  • The switch-microphone and switch-camera commands in package.json copy the full start-recording preference block (including unrelated defaultScreenName, defaultWindowName, recordingMode, captureSystemAudio), none of which those commands read; this creates unnecessary clutter in the Raycast preferences UI.
  • start-recording.tsx has no form validation on the targetName field, so a blank submission silently fails inside Cap without surfacing an error to the user.

Confidence Score: 4/5

The Rust backend changes are safe to merge; the Raycast extension has minor UX rough edges but no breaking issues.

The core deeplink dispatch and the TypeScript-to-Rust JSON contract are correct. The two findings — extraneous preferences on switch commands and the missing target-name validation — affect usability rather than correctness, and neither blocks the primary recording control flows.

apps/raycast/package.json (preference bloat on switch-input commands) and apps/raycast/src/start-recording.tsx (missing field validation).

Important Files Changed

Filename Overview
apps/desktop/src-tauri/src/deeplink_actions.rs Adds PauseRecording, ResumeRecording, SetMicrophone, and SetCamera variants to DeepLinkAction; delegates to existing recording/input functions; includes parser unit tests.
apps/raycast/src/cap.ts Core utility module defining CapAction types, URL builder, runAction dispatcher, and preference helpers; types align with Rust serde layout.
apps/raycast/package.json Defines 5 Raycast commands; switch-microphone and switch-camera commands copy all start-recording preferences that are unrelated to input-switching and never read by those commands.
apps/raycast/src/start-recording.tsx Form-based start recording command; no validation on targetName so an empty/whitespace submission silently fails inside Cap with no user feedback.
apps/raycast/src/switch-camera.tsx Simple form that reads cameraDeviceId and dispatches set_camera deeplink; correctly allows empty value to disable the camera.
apps/raycast/src/switch-microphone.tsx Simple form that reads microphoneLabel and dispatches set_microphone deeplink; correctly allows empty value to disable the mic.
apps/raycast/src/pause-recording.ts Minimal no-view command that dispatches pause_recording deeplink.
apps/raycast/src/resume-recording.ts Minimal no-view command that dispatches resume_recording deeplink.
apps/raycast/src/stop-recording.ts Minimal no-view command that dispatches stop_recording deeplink.
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
apps/raycast/package.json:101-180
**Irrelevant preferences copied into switch-input commands**

The `switch-microphone` and `switch-camera` commands inherit the full `start-recording` preference block — `defaultScreenName`, `defaultWindowName`, `recordingMode`, and `captureSystemAudio` — but neither command's source (`switch-microphone.tsx`, `switch-camera.tsx`) reads any of those values. Raycast will surface all six preferences to users under these commands when only `microphoneLabel` (or `cameraDeviceId`) is actually needed, which creates a confusing settings experience.

### Issue 2 of 2
apps/raycast/src/start-recording.tsx:63-68
**Missing validation on `targetName`** — an empty or whitespace-only submission is allowed by the form but will always fail inside Cap (the Rust side returns `"No screen/window with name \"\""`) with no visible error feedback to the user. Adding a `validation` prop ensures Cap only receives a meaningful name.

```suggestion
      <Form.TextField
        id="targetName"
        title="Target Name"
        defaultValue={defaultTarget}
        placeholder="Display or window name from Cap"
        validation={(v) => (!v?.trim() ? "Target name is required" : undefined)}
      />
```

Reviews (1): Last reviewed commit: "feat: add recording deeplinks and raycas..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

@superagent-security superagent-security Bot added contributor:verified Contributor passed trust analysis. pr:verified PR passed security analysis. labels May 13, 2026
@socket-security
Copy link
Copy Markdown

socket-security Bot commented May 13, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​raycast/​eslint-config@​1.0.11821005987100
Added@​raycast/​api@​1.104.169610084100100

View full report

@socket-security
Copy link
Copy Markdown

socket-security Bot commented May 13, 2026

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report

Comment thread apps/raycast/package.json Outdated
Comment on lines +101 to +180
"name": "switch-microphone",
"title": "Switch Microphone",
"description": "Select the microphone Cap should use",
"mode": "view",
"preferences": [
{
"name": "defaultScreenName",
"description": "Screen display name used by Start Recording when Capture Source is Screen.",
"type": "textfield",
"required": false,
"title": "Default Screen Name",
"label": "Default Screen Name"
},
{
"name": "defaultWindowName",
"description": "Window title used by Start Recording when Capture Source is Window.",
"type": "textfield",
"required": false,
"title": "Default Window Name",
"label": "Default Window Name"
},
{
"name": "microphoneLabel",
"description": "Microphone label Cap should select before recording.",
"type": "textfield",
"required": false,
"title": "Microphone Label",
"label": "Microphone Label"
},
{
"name": "cameraDeviceId",
"description": "Camera device ID Cap should select before recording.",
"type": "textfield",
"required": false,
"title": "Camera Device ID",
"label": "Camera Device ID"
},
{
"name": "recordingMode",
"description": "Default Cap recording mode.",
"type": "dropdown",
"required": false,
"default": "studio",
"data": [
{
"title": "Studio",
"value": "studio"
},
{
"title": "Instant",
"value": "instant"
},
{
"title": "Screenshot",
"value": "screenshot"
}
],
"title": "Recording Mode",
"label": "Recording Mode"
},
{
"name": "captureSystemAudio",
"description": "Capture system audio by default.",
"type": "checkbox",
"required": false,
"default": true,
"title": "Capture System Audio",
"label": "Capture System Audio"
}
]
},
{
"name": "switch-camera",
"title": "Switch Camera",
"description": "Select the camera Cap should use",
"mode": "view",
"preferences": [
{
"name": "defaultScreenName",
"description": "Screen display name used by Start Recording when Capture Source is Screen.",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Irrelevant preferences copied into switch-input commands

The switch-microphone and switch-camera commands inherit the full start-recording preference block — defaultScreenName, defaultWindowName, recordingMode, and captureSystemAudio — but neither command's source (switch-microphone.tsx, switch-camera.tsx) reads any of those values. Raycast will surface all six preferences to users under these commands when only microphoneLabel (or cameraDeviceId) is actually needed, which creates a confusing settings experience.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/raycast/package.json
Line: 101-180

Comment:
**Irrelevant preferences copied into switch-input commands**

The `switch-microphone` and `switch-camera` commands inherit the full `start-recording` preference block — `defaultScreenName`, `defaultWindowName`, `recordingMode`, and `captureSystemAudio` — but neither command's source (`switch-microphone.tsx`, `switch-camera.tsx`) reads any of those values. Raycast will surface all six preferences to users under these commands when only `microphoneLabel` (or `cameraDeviceId`) is actually needed, which creates a confusing settings experience.

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

Comment on lines +63 to +68
<Form.TextField
id="targetName"
title="Target Name"
defaultValue={defaultTarget}
placeholder="Display or window name from Cap"
/>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Missing validation on targetName — an empty or whitespace-only submission is allowed by the form but will always fail inside Cap (the Rust side returns "No screen/window with name \"\"") with no visible error feedback to the user. Adding a validation prop ensures Cap only receives a meaningful name.

Suggested change
<Form.TextField
id="targetName"
title="Target Name"
defaultValue={defaultTarget}
placeholder="Display or window name from Cap"
/>
<Form.TextField
id="targetName"
title="Target Name"
defaultValue={defaultTarget}
placeholder="Display or window name from Cap"
validation={(v) => (!v?.trim() ? "Target name is required" : undefined)}
/>
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/raycast/src/start-recording.tsx
Line: 63-68

Comment:
**Missing validation on `targetName`** — an empty or whitespace-only submission is allowed by the form but will always fail inside Cap (the Rust side returns `"No screen/window with name \"\""`) with no visible error feedback to the user. Adding a `validation` prop ensures Cap only receives a meaningful name.

```suggestion
      <Form.TextField
        id="targetName"
        title="Target Name"
        defaultValue={defaultTarget}
        placeholder="Display or window name from Cap"
        validation={(v) => (!v?.trim() ? "Target name is required" : undefined)}
      />
```

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

@superagent-security superagent-security Bot removed the pr:verified PR passed security analysis. label May 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor:verified Contributor passed trust analysis.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant