Skip to content

feat(desktop): extend deeplinks + add Raycast extension scaffold#1633

Open
sungdark wants to merge 11 commits intoCapSoftware:mainfrom
sungdark:feat/issue-1540-deeplink-raycast
Open

feat(desktop): extend deeplinks + add Raycast extension scaffold#1633
sungdark wants to merge 11 commits intoCapSoftware:mainfrom
sungdark:feat/issue-1540-deeplink-raycast

Conversation

@sungdark
Copy link

@sungdark sungdark commented Feb 26, 2026

Summary

  • extend desktop deeplink actions with pause/resume/toggle pause
  • add deeplink actions for switching microphone and camera
  • add a Raycast extension scaffold in apps/raycast that dispatches Cap deeplinks for recording controls

Deeplink actions added

  • pause_recording
  • resume_recording
  • toggle_pause_recording
  • switch_microphone
  • switch_camera

Closes #1540

Greptile Summary

Extended desktop deeplink actions to support pause/resume/toggle recording and device switching (microphone/camera), then added a Raycast extension scaffold to control Cap via these deeplinks.

  • Added 5 new DeepLinkAction variants in Rust: PauseRecording, ResumeRecording, TogglePauseRecording, SwitchMicrophone, SwitchCamera
  • Created new Raycast extension at apps/raycast with 7 commands that dispatch Cap deeplinks
  • Critical issue: fireSimpleAction in cap.ts incorrectly wraps unit variants in { type: action } instead of passing the plain string, which will cause deserialization failures for pause/resume/stop/toggle commands

Confidence Score: 2/5

  • Critical bug in fireSimpleAction will break 4 of 7 Raycast commands
  • The Rust backend implementation is solid, but the TypeScript deeplink dispatch function has a critical serialization bug that breaks unit variant deserialization. Commands using struct variants (start_recording, switch_camera, switch_microphone) will work, but commands using unit variants (pause_recording, resume_recording, stop_recording, toggle_pause_recording) will fail due to incorrect JSON format
  • apps/raycast/src/lib/cap.ts requires immediate fix before merge

Important Files Changed

Filename Overview
apps/desktop/src-tauri/src/deeplink_actions.rs Added new deeplink action variants and handlers for pause/resume/toggle and device switching
apps/raycast/src/lib/cap.ts Core deeplink dispatch logic - contains critical bug in fireSimpleAction that will break unit variant actions
apps/raycast/src/pause-recording.ts Pause recording command - affected by fireSimpleAction bug
apps/raycast/src/resume-recording.ts Resume recording command - affected by fireSimpleAction bug
apps/raycast/src/stop-recording.ts Stop recording command - affected by fireSimpleAction bug
apps/raycast/src/toggle-pause-recording.ts Toggle pause command - affected by fireSimpleAction bug

Sequence Diagram

sequenceDiagram
    participant User
    participant Raycast
    participant cap.ts
    participant Desktop App
    participant Rust Handler
    
    User->>Raycast: Trigger command
    Raycast->>cap.ts: fireSimpleAction("pause_recording")
    cap.ts->>cap.ts: JSON.stringify({ type: "pause_recording" })
    cap.ts->>Desktop App: cap-desktop://action?value=...
    Desktop App->>Rust Handler: Parse deeplink
    Rust Handler->>Rust Handler: serde_json::from_str<DeepLinkAction>
    Note over Rust Handler: FAILS - expects "pause_recording"<br/>but receives {"type":"pause_recording"}
    Rust Handler-->>User: Error: Failed to parse deeplink
    
    Note over cap.ts,Rust Handler: FIX: Pass action string directly,<br/>not wrapped in object
Loading

Last reviewed commit: a595e0e

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

}

export async function fireSimpleAction(action: string) {
await dispatchAction({ type: action });
Copy link

Choose a reason for hiding this comment

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

DeepLinkAction uses Serde's default external tagging, so unit variants need to be JSON strings (e.g. "pause_recording"), not { type: "pause_recording" }.

Suggested change
await dispatchAction({ type: action });
await dispatchAction(action);

Comment on lines +38 to +42
async function onSubmit(values: Values) {
const captureMode =
values.captureType === "screen"
? { screen: values.captureName }
: { window: values.captureName };
Copy link

Choose a reason for hiding this comment

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

If captureName is empty, this will dispatch an action that the desktop app can't resolve (and the user gets a pretty opaque failure). Consider validating before dispatch.

Suggested change
async function onSubmit(values: Values) {
const captureMode =
values.captureType === "screen"
? { screen: values.captureName }
: { window: values.captureName };
async function onSubmit(values: Values) {
const captureName = values.captureName.trim();
if (!captureName) {
await showHUD("Cap: capture name required");
return;
}
const captureMode =
values.captureType === "screen" ? { screen: captureName } : { window: captureName };

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.

11 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +13 to +15
export async function fireSimpleAction(action: string) {
await dispatchAction({ type: action });
await showHUD(`Cap: ${action}`);
Copy link
Contributor

Choose a reason for hiding this comment

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

Serde deserializes unit enum variants from plain JSON strings, not objects. { type: action } should be just action

Suggested change
export async function fireSimpleAction(action: string) {
await dispatchAction({ type: action });
await showHUD(`Cap: ${action}`);
export async function fireSimpleAction(action: string) {
await dispatchAction(action);
await showHUD(`Cap: ${action}`);
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/raycast/src/lib/cap.ts
Line: 13-15

Comment:
Serde deserializes unit enum variants from plain JSON strings, not objects. `{ type: action }` should be just `action`

```suggestion
export async function fireSimpleAction(action: string) {
  await dispatchAction(action);
  await showHUD(`Cap: ${action}`);
}
```

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bounty: Deeplinks support + Raycast Extension

1 participant