feat: Raycast Deeplink Extension (#1540) - #1727
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.fb7b04f to
6e55010
Compare
|
Hi team! Just checking in to see if you've had a chance to review this PR, or if there's any feedback or further changes needed to merge this. Thanks! |
|
We are closing the deeplinks and Raycast bounty (#1540) without awarding it, and closing the pull requests opened against it. The full reasoning is on that issue: the remaining actions it asked for are deliberately limited to debug builds because deeplinks are a remote control surface any web page can invoke, and widening that in production is a security decision we do not want to take through a bounty. This is not a reflection on your work. Leaving it unreviewed for this long is our failure, and we are sorry for the wasted effort. If you want to contribute again, the open bug reports are the most useful place and we will review promptly. |
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!
/claim #1540