Skip to content

feat: add Raycast recording controls#1867

Open
tonystark-agent wants to merge 4 commits into
CapSoftware:mainfrom
tonystark-agent:fix/deeplink-raycast-controls
Open

feat: add Raycast recording controls#1867
tonystark-agent wants to merge 4 commits into
CapSoftware:mainfrom
tonystark-agent:fix/deeplink-raycast-controls

Conversation

@tonystark-agent
Copy link
Copy Markdown

@tonystark-agent tonystark-agent commented May 23, 2026

/claim #1540

Summary

  • add deeplink actions for pausing/resuming/toggling recordings, screenshots, and microphone/camera switching
  • add a Raycast extension with commands for recording control, starting recordings, screenshots, and input switching
  • keep non-action deeplinks such as sign-in out of the action parser

Validation

  • cargo fmt --all
  • pnpm exec biome check --write apps/raycast/package.json apps/raycast/tsconfig.json apps/raycast/src apps/raycast/README.md apps/raycast/assets/command-icon.png apps/raycast/.gitignore
  • pnpm --dir apps/raycast exec tsc --noEmit
  • pnpm --dir apps/raycast exec ray build
  • pnpm --dir apps/raycast exec ray lint

cargo check -p cap-desktop is blocked on this machine before reaching this change because cidre requires full Xcode via xcodebuild, while the active developer directory is Command Line Tools only.

Greptile Summary

This PR adds Raycast extension commands for controlling Cap Desktop recordings via deeplinks, and extends the Rust deeplink action handler with pause/resume/toggle, screenshot, and device-switching actions. The url.domain()url.host_str() fix is a correctness improvement that prevents non-domain hostnames from being misclassified.

  • deeplink_actions.rs: Adds six new DeepLinkAction variants, a with_recording_paused_for_input_change helper that correctly brackets input changes with pause/resume, and a test suite covering the new variants.
  • apps/raycast/: New Raycast extension with four commands (control, start recording, screenshot, switch device); the shared deeplinks.ts library mirrors the Rust action enum as TypeScript types and fires deeplinks via open().
  • Minor inconsistency: setMicrophone and setCamera in deeplinks.ts check the raw (untrimmed) value when constructing the toast title, but trim before sending — a whitespace-only input shows a "Switched" toast while actually disabling the device.

Confidence Score: 4/5

Safe to merge; the Rust changes are well-tested and correct, and the Raycast issues are confined to edge-case toast messaging and missing form validation.

The Rust backend changes are clean and well-covered by tests. The Raycast TypeScript layer has two small toast/action mismatches for whitespace-only inputs in setMicrophone and setCamera, and the recording/screenshot forms do not validate the target name field before firing the deeplink.

apps/raycast/src/lib/deeplinks.ts — toast title mismatch for whitespace inputs in setMicrophone and setCamera.

Important Files Changed

Filename Overview
apps/desktop/src-tauri/src/deeplink_actions.rs Adds PauseRecording, ResumeRecording, TogglePauseRecording, TakeScreenshot, SetMicrophone, SetCamera deeplink actions; fixes url.domain() → url.host_str() for correct host matching; introduces with_recording_paused_for_input_change for safe device switching; logic and error handling look correct.
apps/raycast/src/lib/deeplinks.ts Core deeplink URL builder and action helpers; setMicrophone and setCamera have toast/action mismatches for whitespace-only input — the toast says 'Switched' but null (disable) is sent to the desktop.
apps/raycast/src/cap-control.tsx New Raycast command that renders a list of pause/resume/toggle/stop actions; straightforward and correct.
apps/raycast/src/start-recording.tsx Form command for starting a recording; no validation on the required targetName field — an empty submission reaches Cap Desktop where it fails with an opaque error.
apps/raycast/src/take-screenshot.tsx Form command for taking a screenshot; same empty-targetName validation gap as start-recording.tsx.
apps/raycast/src/switch-device.tsx Form command for switching microphone or camera; correctly delegates to setMicrophone/setCamera helpers.
apps/raycast/package.json New Raycast extension package manifest declaring four commands; looks complete and consistent.
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
apps/raycast/src/lib/deeplinks.ts:129
The toast title uses the raw `micLabel` to decide between "Switched" and "Disabled", but the actual value sent to the desktop is `micLabel?.trim() || null`. If `micLabel` is a whitespace-only string (e.g., `" "`), the action sends `null` — disabling the microphone — while the toast claims "Switched microphone". The same pattern affects `setCamera` and `startRecording`.

```suggestion
		micLabel?.trim() ? "Switched microphone" : "Disabled microphone",
```

### Issue 2 of 3
apps/raycast/src/lib/deeplinks.ts:140
Same toast/action mismatch for `setCamera`: a whitespace-only `deviceId` evaluates as truthy here, so the toast says "Switched camera", but the action sends `null` (disabled camera) because `deviceId?.trim()` is falsy.

```suggestion
		deviceId?.trim() ? "Switched camera" : "Disabled camera",
```

### Issue 3 of 3
apps/raycast/src/start-recording.tsx:40
**Missing validation on target name field**

`targetName` has no `placeholder`, `info`, or client-side guard, and submitting an empty string is silently sent to Cap Desktop where it fails with `"No screen/window with name ''"`. The same gap exists in `take-screenshot.tsx`. Adding a validation callback on the form would surface the problem before the deeplink fires.

Reviews (1): Last reviewed commit: "feat: add Raycast recording controls" | Re-trigger Greptile

Greptile also left 3 inline comments on this PR.

@superagent-security superagent-security Bot added contributor:verified Contributor passed trust analysis. pr:flagged PR flagged for review by security analysis. labels May 23, 2026
Copy link
Copy Markdown

@superagent-security superagent-security Bot left a comment

Choose a reason for hiding this comment

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

Superagent found 1 security concern(s).

mic_label: micLabel?.trim() || null,
},
},
micLabel ? "Switched microphone" : "Disabled microphone",
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 The toast title uses the raw micLabel to decide between "Switched" and "Disabled", but the actual value sent to the desktop is micLabel?.trim() || null. If micLabel is a whitespace-only string (e.g., " "), the action sends null — disabling the microphone — while the toast claims "Switched microphone". The same pattern affects setCamera and startRecording.

Suggested change
micLabel ? "Switched microphone" : "Disabled microphone",
micLabel?.trim() ? "Switched microphone" : "Disabled microphone",
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/raycast/src/lib/deeplinks.ts
Line: 129

Comment:
The toast title uses the raw `micLabel` to decide between "Switched" and "Disabled", but the actual value sent to the desktop is `micLabel?.trim() || null`. If `micLabel` is a whitespace-only string (e.g., `" "`), the action sends `null` — disabling the microphone — while the toast claims "Switched microphone". The same pattern affects `setCamera` and `startRecording`.

```suggestion
		micLabel?.trim() ? "Switched microphone" : "Disabled microphone",
```

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

camera: deviceId?.trim() ? { DeviceID: deviceId.trim() } : null,
},
},
deviceId ? "Switched camera" : "Disabled camera",
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 Same toast/action mismatch for setCamera: a whitespace-only deviceId evaluates as truthy here, so the toast says "Switched camera", but the action sends null (disabled camera) because deviceId?.trim() is falsy.

Suggested change
deviceId ? "Switched camera" : "Disabled camera",
deviceId?.trim() ? "Switched camera" : "Disabled camera",
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/raycast/src/lib/deeplinks.ts
Line: 140

Comment:
Same toast/action mismatch for `setCamera`: a whitespace-only `deviceId` evaluates as truthy here, so the toast says "Switched camera", but the action sends `null` (disabled camera) because `deviceId?.trim()` is falsy.

```suggestion
		deviceId?.trim() ? "Switched camera" : "Disabled camera",
```

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

<Form.Dropdown.Item value="screen" title="Screen" />
<Form.Dropdown.Item value="window" title="Window" />
</Form.Dropdown>
<Form.TextField id="targetName" title="Target Name" />
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 target name field

targetName has no placeholder, info, or client-side guard, and submitting an empty string is silently sent to Cap Desktop where it fails with "No screen/window with name ''". The same gap exists in take-screenshot.tsx. Adding a validation callback on the form would surface the problem before the deeplink fires.

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

Comment:
**Missing validation on target name field**

`targetName` has no `placeholder`, `info`, or client-side guard, and submitting an empty string is silently sent to Cap Desktop where it fails with `"No screen/window with name ''"`. The same gap exists in `take-screenshot.tsx`. Adding a validation callback on the form would surface the problem before the deeplink fires.

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

Copy link
Copy Markdown

@superagent-security superagent-security Bot left a comment

Choose a reason for hiding this comment

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

Superagent found 1 security concern(s).

@superagent-security superagent-security Bot added pr:verified PR passed security analysis. and removed pr:flagged PR flagged for review by security analysis. labels May 23, 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. pr:verified PR passed security analysis.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant