feat: Raycast Deeplink Extension (#1540)#1727
feat: Raycast Deeplink Extension (#1540)#1727Ojas2095 wants to merge 1 commit intoCapSoftware:mainfrom
Conversation
| // We will assume "camera" in JSON is null to disable, or an object if specific. | ||
| // But for simple use, setting to an exact string from args. | ||
| await sendCapCommand("switch_camera", { | ||
| camera: props.arguments.cameraId === "none" ? null : props.arguments.cameraId |
There was a problem hiding this comment.
Wrong serialization format for
DeviceOrModelID
A plain string will always fail to deserialize on the Rust side. DeviceOrModelID is a Rust enum with two tuple variants — serde serializes it as {"DeviceID": "..."} or {"ModelID": {...}}, not as a bare string. Passing props.arguments.cameraId directly means any non-"none" input will produce a ParseFailed deep-link error and the camera switch will silently do nothing.
camera: props.arguments.cameraId === "none" ? null : { DeviceID: props.arguments.cameraId }Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/raycast/src/switch-camera.ts
Line: 10
Comment:
**Wrong serialization format for `DeviceOrModelID`**
A plain string will always fail to deserialize on the Rust side. `DeviceOrModelID` is a Rust enum with two tuple variants — serde serializes it as `{"DeviceID": "..."}` or `{"ModelID": {...}}`, not as a bare string. Passing `props.arguments.cameraId` directly means any non-`"none"` input will produce a `ParseFailed` deep-link error and the camera switch will silently do nothing.
```ts
camera: props.arguments.cameraId === "none" ? null : { DeviceID: props.arguments.cameraId }
```
How can I resolve this? If you propose a fix, please make it concise.|
|
||
| export default async function Command() { | ||
| await sendCapCommand("start_recording", { | ||
| capture_mode: { screen: "Built-in Retina Display" }, // Fallback default or need to handle better |
There was a problem hiding this comment.
Hardcoded macOS-only display name
"Built-in Retina Display" is only a valid screen name on Apple Retina Macs. On non-Retina Macs the name differs (e.g. "Built-in Display"), on external-only setups it won't exist at all, and on Windows the display name format is entirely different. The StartRecording handler does an exact string match against cap_recording::screen_capture::list_displays(), so this will produce a "No screen with name" error on most machines.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/raycast/src/start.ts
Line: 5
Comment:
**Hardcoded macOS-only display name**
`"Built-in Retina Display"` is only a valid screen name on Apple Retina Macs. On non-Retina Macs the name differs (e.g. `"Built-in Display"`), on external-only setups it won't exist at all, and on Windows the display name format is entirely different. The `StartRecording` handler does an exact string match against `cap_recording::screen_capture::list_displays()`, so this will produce a `"No screen with name"` error on most machines.
How can I resolve this? If you propose a fix, please make it concise.| // If no object value is passed, it represents a simple unit enum variant. | ||
| // Rust expects: "stop_recording" | ||
| // If an object is passed, Rust expects: {"switch_microphone": {"mic_label": "mic"}} | ||
| const valuePayload = objectValue | ||
| ? JSON.stringify({ [action]: objectValue }) |
There was a problem hiding this comment.
Code comments violate project convention
The project prohibits all code comments (single-line //, multi-line /* */, JSDoc, etc.) — code should be self-explanatory through naming and types. These comment blocks should be removed. The same pattern appears in apps/raycast/src/start.ts (line 5) and apps/raycast/src/switch-camera.ts (lines 5–8).
Context Used: CLAUDE.md (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/raycast/src/utils.ts
Line: 4-8
Comment:
**Code comments violate project convention**
The project prohibits all code comments (single-line `//`, multi-line `/* */`, JSDoc, etc.) — code should be self-explanatory through naming and types. These comment blocks should be removed. The same pattern appears in `apps/raycast/src/start.ts` (line 5) and `apps/raycast/src/switch-camera.ts` (lines 5–8).
**Context Used:** 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.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!
| const url = `cap://action?value=${encodeURIComponent(valuePayload)}`; | ||
| try { | ||
| await open(url); | ||
| await showHUD(`Cap: Action Executed`); |
There was a problem hiding this comment.
showHUD("Cap: Action Executed") fires immediately after open(url) resolves, which only confirms the URL was handed to the OS — not that Cap received or processed it. A recording may be in the wrong state, or Cap may not even be running. A more accurate message like "Sent to Cap" would prevent users from assuming the action succeeded when it may not have.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/raycast/src/utils.ts
Line: 14
Comment:
**Misleading success HUD**
`showHUD("Cap: Action Executed")` fires immediately after `open(url)` resolves, which only confirms the URL was handed to the OS — not that Cap received or processed it. A recording may be in the wrong state, or Cap may not even be running. A more accurate message like `"Sent to Cap"` would prevent users from assuming the action succeeded when it may not have.
How can I resolve this? If you propose a fix, please make it concise.
Fixes #1540. Adds support for Pause, Resume, Switch Microphone and Camera deeplink commands. Includes official Raycast extension package inside apps/raycast mapped to these endpoints.
Greptile Summary
This PR adds a Raycast extension (
apps/raycast) for controlling Cap recordings via deeplinks, and extends the desktop'sdeeplink_actions.rswith four new action variants:PauseRecording,ResumeRecording,SwitchMicrophone, andSwitchCamera. The Rust side is implemented correctly, but two TypeScript commands have bugs that will cause silent failures at runtime.switch-camera.ts: Sends the camera ID as a bare string, but Rust'sDeviceOrModelIDenum requires the tagged-object format{\"DeviceID\": \"...\"}— any non-\"none\"input will always fail to deserialize.start.ts: Hardcodes\"Built-in Retina Display\"which only matches a specific subset of Apple displays; this will fail on most machines.Confidence Score: 4/5
Not safe to merge as-is — two Raycast commands will silently fail at runtime due to incorrect JSON serialization and a hardcoded display name.
Two P1 findings: switch_camera sends the wrong JSON format for DeviceOrModelID, and start_recording hardcodes a display name that won't resolve on most machines. The Rust desktop changes are clean and correct. Fixing the two TypeScript issues is required before this is usable.
apps/raycast/src/switch-camera.ts and apps/raycast/src/start.ts require fixes before merge.
Important Files Changed
Sequence Diagram
sequenceDiagram participant R as Raycast Command participant U as utils.sendCapCommand participant OS as macOS URL Handler participant T as Tauri deeplink_actions participant RC as Recording Core R->>U: sendCapCommand("pause_recording") U->>U: Build valuePayload = '"pause_recording"' U->>OS: open("cap://action?value=...") OS->>T: handle(urls) T->>T: DeepLinkAction::try_from(url) → serde_json::from_str T->>RC: pause_recording(app, state) RC-->>T: Ok(()) T-->>OS: done U->>R: showHUD("Cap: Action Executed") Note over R,U: switch-camera path (broken) R->>U: sendCapCommand("switch_camera", {camera: "raw_string"}) U->>OS: open("cap://action?value=...") OS->>T: handle(urls) T->>T: serde_json::from_str → ParseFailed (expects {"DeviceID":"..."}) T-->>OS: eprintln error, silent failurePrompt To Fix All With AI
Reviews (1): Last reviewed commit: "feat: implement deep links for Pause/Res..." | Re-trigger Greptile
(2/5) Greptile learns from your feedback when you react with thumbs up/down!