Skip to content

Add Raycast extension for Cap recording controls#1825

Open
xiongbear01 wants to merge 3 commits into
CapSoftware:mainfrom
xiongbear01:bounty-1540-raycast-deeplinks
Open

Add Raycast extension for Cap recording controls#1825
xiongbear01 wants to merge 3 commits into
CapSoftware:mainfrom
xiongbear01:bounty-1540-raycast-deeplinks

Conversation

@xiongbear01
Copy link
Copy Markdown

@xiongbear01 xiongbear01 commented May 16, 2026

This PR adds Raycast support for controlling Cap via the existing cap://action deep link flow.

Changes:

Extends desktop deep link actions for:
starting a recording with saved settings
opening display/window/area recording pickers
pausing, resuming, toggling pause, and stopping recordings
setting or clearing microphone input
setting or clearing camera input
Adds a standalone Raycast extension under extensions/raycast.
Adds Raycast commands for Studio/Instant recording, target pickers, pause/resume/stop, and microphone/camera switching.
Validation:

pnpm dlx @biomejs/biome@2.2.0 check --write extensions/raycast/package.json extensions/raycast/tsconfig.json extensions/raycast/README.md extensions/raycast/src/.ts extensions/raycast/src/.tsx
pnpm --dir extensions/raycast exec tsc --noEmit
pnpm --dir extensions/raycast run build
Not run:

Rust formatting/checks, because this local machine does not have cargo, rustc, or rustfmt installed.

Greptile Summary

This PR adds a Raycast extension for controlling Cap recordings via cap://action deep links, and extends the desktop backend with new deep link actions (pause, resume, toggle-pause, set-mic, set-camera, open-picker, start-with-settings).

  • The Rust backend additions in deeplink_actions.rs are clean and consistent with existing patterns.
  • The Raycast extension's cap.ts serializes DeviceOrModelID with snake_case keys (device_id, model_id) that don't match the PascalCase keys (DeviceID, ModelID) the Rust enum serializes to, causing the set-camera command to silently fail on the backend.

Confidence Score: 3/5

Safe to merge for most commands, but the set-camera command will not work as written.

The camera ID serialization in cap.ts uses the wrong key casing, so any attempt to set a camera from Raycast will send a payload that the Rust serde deserializer rejects and silently drops. All other commands are unaffected.

extensions/raycast/src/cap.ts — the DeviceOrModelID type and its two helper functions need the key names corrected before the set-camera command will work.

Important Files Changed

Filename Overview
extensions/raycast/src/cap.ts Core deep-link helper — has a serialization mismatch for DeviceOrModelID keys (snake_case vs the PascalCase the Rust backend expects), breaking the set-camera command.
apps/desktop/src-tauri/src/deeplink_actions.rs Adds PauseRecording, ResumeRecording, TogglePauseRecording, SetMicInput, SetCameraInput, OpenRecordingPicker, and StartRecordingWithSettings variants and their execute arms; logic looks correct and consistent with existing patterns.
extensions/raycast/src/set-camera.tsx Form UI for camera selection; validates non-empty input and delegates to cap.ts helpers — will work correctly once the DeviceOrModelID key casing is fixed.
extensions/raycast/src/set-microphone.tsx Form UI for microphone label input; validates non-empty label and fires the set_mic_input deep link action correctly.
extensions/raycast/package.json Raycast extension manifest declaring all 14 commands with correct modes; dependencies look reasonable.
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
extensions/raycast/src/cap.ts:5-11
The `DeviceOrModelID` field names are snake_case (`device_id`, `model_id`) but the Rust enum `DeviceOrModelID` serializes without any `rename_all` attribute, defaulting to PascalCase — `{"DeviceID": "..."}` and `{"ModelID": "..."}`. The existing TypeScript type in `apps/desktop/src/utils/tauri.ts` confirms this: `{ DeviceID: string } | { ModelID: ModelIDType }`. As written, `set-camera` will send a payload that fails serde deserialization on the Rust side and the action will silently be dropped.

```suggestion
type DeviceOrModelID =
	| {
			DeviceID: string;
	  }
	| {
			ModelID: string;
	  };
```

### Issue 2 of 2
extensions/raycast/src/cap.ts:50-56
The `deviceId` and `modelId` helpers produce objects with snake_case keys (`device_id`, `model_id`), which won't match the expected PascalCase keys after fixing the type definition above.

```suggestion
export function deviceId(value: string): DeviceOrModelID {
	return { DeviceID: value };
}

export function modelId(value: string): DeviceOrModelID {
	return { ModelID: value };
}
```

Reviews (1): Last reviewed commit: "feat: add Raycast recording controls" | 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:flagged PR flagged for review by security analysis. labels May 16, 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).

Comment on lines +5 to +11
type DeviceOrModelID =
| {
device_id: string;
}
| {
model_id: string;
};
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.

P1 The DeviceOrModelID field names are snake_case (device_id, model_id) but the Rust enum DeviceOrModelID serializes without any rename_all attribute, defaulting to PascalCase — {"DeviceID": "..."} and {"ModelID": "..."}. The existing TypeScript type in apps/desktop/src/utils/tauri.ts confirms this: { DeviceID: string } | { ModelID: ModelIDType }. As written, set-camera will send a payload that fails serde deserialization on the Rust side and the action will silently be dropped.

Suggested change
type DeviceOrModelID =
| {
device_id: string;
}
| {
model_id: string;
};
type DeviceOrModelID =
| {
DeviceID: string;
}
| {
ModelID: string;
};
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/raycast/src/cap.ts
Line: 5-11

Comment:
The `DeviceOrModelID` field names are snake_case (`device_id`, `model_id`) but the Rust enum `DeviceOrModelID` serializes without any `rename_all` attribute, defaulting to PascalCase — `{"DeviceID": "..."}` and `{"ModelID": "..."}`. The existing TypeScript type in `apps/desktop/src/utils/tauri.ts` confirms this: `{ DeviceID: string } | { ModelID: ModelIDType }`. As written, `set-camera` will send a payload that fails serde deserialization on the Rust side and the action will silently be dropped.

```suggestion
type DeviceOrModelID =
	| {
			DeviceID: string;
	  }
	| {
			ModelID: string;
	  };
```

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

Comment on lines +50 to +56
export function deviceId(value: string): DeviceOrModelID {
return { device_id: value };
}

export function modelId(value: string): DeviceOrModelID {
return { model_id: value };
}
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.

P1 The deviceId and modelId helpers produce objects with snake_case keys (device_id, model_id), which won't match the expected PascalCase keys after fixing the type definition above.

Suggested change
export function deviceId(value: string): DeviceOrModelID {
return { device_id: value };
}
export function modelId(value: string): DeviceOrModelID {
return { model_id: value };
}
export function deviceId(value: string): DeviceOrModelID {
return { DeviceID: value };
}
export function modelId(value: string): DeviceOrModelID {
return { ModelID: value };
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/raycast/src/cap.ts
Line: 50-56

Comment:
The `deviceId` and `modelId` helpers produce objects with snake_case keys (`device_id`, `model_id`), which won't match the expected PascalCase keys after fixing the type definition above.

```suggestion
export function deviceId(value: string): DeviceOrModelID {
	return { DeviceID: value };
}

export function modelId(value: string): DeviceOrModelID {
	return { ModelID: value };
}
```

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 16, 2026
@xiongbear01
Copy link
Copy Markdown
Author

Updated the PR to address the review comments: fixed DeviceOrModelID serialization, renamed the Raycast publish script, and added confirmation for sensitive deep-link actions.

@xiongbear01
Copy link
Copy Markdown
Author

/claim #1540

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