Skip to content

feat: add raycast extension and deeplink support#1700

Open
Chronolapse411 wants to merge 2 commits intoCapSoftware:mainfrom
Chronolapse411:fix-raycast-extension-1540
Open

feat: add raycast extension and deeplink support#1700
Chronolapse411 wants to merge 2 commits intoCapSoftware:mainfrom
Chronolapse411:fix-raycast-extension-1540

Conversation

@Chronolapse411
Copy link
Copy Markdown

@Chronolapse411 Chronolapse411 commented Mar 31, 2026

Allows starting/stopping recording via deeplinks and adds a Raycast extension for ease of use. Fixes #1540

Greptile Summary

This PR adds a Raycast extension and a new StartDefaultRecording deeplink action to allow users to start and stop Cap recordings from Raycast without opening the app. The Raycast side is clean and the deeplink URL encoding is correct, but the Rust StartDefaultRecording implementation contains a critical compile-time error that will prevent the desktop app from building.

Key changes:

  • New DeepLinkAction::StartDefaultRecording variant in deeplink_actions.rs that reads the user's stored recording settings (mic, camera, capture target, system audio, org ID) and starts a recording using sensible defaults
  • New apps/raycast-extension with two no-view commands (start-recording, stop-recording) that fire the corresponding deeplinks via open()

Issues found:

  • P0 — Compile error: crate::recording::RecordingMode::Screen on line 168 of deeplink_actions.rs references a variant that does not exist. cap_recording::RecordingMode only has Studio, Instant, and Screenshot. The fix is to use settings.mode.unwrap_or_default()
  • P2 — Optimistic HUD messages: Both Raycast commands show a success HUD immediately after open() returns, before Cap has confirmed the operation
  • P2 — author field: \"Cap\" in package.json may not match a registered Raycast developer handle, blocking Raycast Store publishing
  • P2 — Pre-existing code comment: The // Likely login action, not handled here. comment was re-introduced when the file was rewritten, violating the project's strict no-comments convention

Confidence Score: 4/5

Not safe to merge — the RecordingMode::Screen compile error will break the desktop app build.

A confirmed P0 compile error in deeplink_actions.rs (non-existent RecordingMode::Screen variant) will prevent cap-desktop from building. This must be fixed before merge. All other findings are P2 style/UX concerns that don't block correctness.

apps/desktop/src-tauri/src/deeplink_actions.rs — the StartDefaultRecording arm uses a non-existent enum variant and needs the mode fixed to settings.mode.unwrap_or_default().

Important Files Changed

Filename Overview
apps/desktop/src-tauri/src/deeplink_actions.rs Adds StartDefaultRecording variant that reads settings and triggers recording — but uses the non-existent RecordingMode::Screen variant, causing a compile error; also re-introduces a pre-existing code comment that violates project conventions.
apps/raycast-extension/src/start-recording.tsx Triggers cap://action?value="start_default_recording" deeplink and shows a HUD; deeplink encoding is correct but the HUD fires optimistically before Cap confirms recording has started.
apps/raycast-extension/src/stop-recording.tsx Triggers cap://action?value="stop_recording" deeplink; deeplink encoding is correct but the success HUD fires optimistically before stop is confirmed.
apps/raycast-extension/package.json Declares the Raycast extension manifest with two no-view commands; author: "Cap" may not be a valid Raycast store handle, which could block publishing.
apps/raycast-extension/tsconfig.json Standard Raycast extension TypeScript config; no issues.
Cargo.lock Version bump for cap-desktop from 0.4.81 to 0.4.82; expected change for this feature addition.

Sequence Diagram

sequenceDiagram
    participant User
    participant Raycast
    participant OS
    participant CapDesktop
    participant RecordingSettingsStore

    User->>Raycast: Trigger "Start Default Recording"
    Raycast->>OS: open("cap://action?value=%22start_default_recording%22")
    Raycast->>Raycast: closeMainWindow()
    Raycast->>Raycast: showHUD("Starting default recording in Cap")
    OS->>CapDesktop: Deep link event (cap://action?value=...)
    CapDesktop->>CapDesktop: handle() → parse DeepLinkAction
    CapDesktop->>RecordingSettingsStore: RecordingSettingsStore::get(app)
    RecordingSettingsStore-->>CapDesktop: settings (mic, camera, target, system_audio, org_id)
    CapDesktop->>CapDesktop: set_mic_input (errors silenced)
    CapDesktop->>CapDesktop: set_camera_input (errors silenced)
    CapDesktop->>CapDesktop: start_recording(StartRecordingInputs)
    Note over CapDesktop: mode: RecordingMode::Screen ← COMPILE ERROR

    User->>Raycast: Trigger "Stop Recording"
    Raycast->>OS: open("cap://action?value=%22stop_recording%22")
    Raycast->>Raycast: closeMainWindow()
    Raycast->>Raycast: showHUD("Stopped recording in Cap")
    OS->>CapDesktop: Deep link event
    CapDesktop->>CapDesktop: stop_recording()
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 168

Comment:
**`RecordingMode::Screen` does not exist — compile error**

`crate::recording::RecordingMode` is `cap_recording::RecordingMode`, whose only variants are `Studio`, `Instant`, and `Screenshot`. There is no `Screen` variant. This line will cause a compilation failure.

Additionally, `RecordingMode` is not re-exported from `crate::recording` (the `use cap_recording::RecordingMode` in `recording.rs` is not `pub use`), so the qualified path `crate::recording::RecordingMode` won't resolve either.

For a "start default recording" action, the correct approach is to read the user's configured mode from settings (which `RecordingSettingsStore` already exposes as `mode: Option<RecordingMode>`) or fall back to the default:

```suggestion
                let inputs = StartRecordingInputs {
                    mode: settings.mode.unwrap_or_default(),
```

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

---

This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 53

Comment:
**Code comment violates the NO CODE COMMENTS convention**

Per the project's CLAUDE.md and AGENTS.md, no comments of any kind are permitted in code. The line `// Likely login action, not handled here.` is a pre-existing violation that was re-introduced by this PR when the file was rewritten. Code must be self-explanatory through naming and types.

```suggestion
                    ActionParseFromUrlError::NotAction => {}
```

**Context Used:** AGENTS.md ([source](https://app.greptile.com/review/custom-context?memory=27801409-c24c-4476-9c6c-180f1ef0a7f2))

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

---

This is a comment left during a code review.
Path: apps/raycast-extension/src/start-recording.tsx
Line: 7-8

Comment:
**Optimistic HUD message fires before the operation completes**

`open()` only triggers the deeplink (launching the URL handler in the native app) and returns immediately — it does not wait for Cap to actually begin recording. `showHUD("Starting default recording in Cap")` therefore fires regardless of whether recording actually starts successfully (e.g., Cap isn't running, permissions are missing, or the deeplink returns an error). Consider wording the message to reflect that the request was *sent* rather than that recording has *started*, e.g. `"Recording requested in Cap"`, to avoid giving users false confidence.

The same pattern applies in `stop-recording.tsx` line 8 (`"Stopped recording in Cap"`).

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

---

This is a comment left during a code review.
Path: apps/raycast-extension/package.json
Line: 8

Comment:
**`author` field may not match a valid Raycast handle**

Raycast requires the `author` field to be the publisher's Raycast developer username, not a display name. If this extension is intended for Raycast Store publishing (the `package.json` includes a `publish` script), `"Cap"` may not match an actual registered account and will cause the publish command to fail. Confirm the correct Raycast username or document that this is internal-only.

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

Reviews (1): Last reviewed commit: "feat: add raycast extension and deeplink..." | Re-trigger Greptile

Greptile also left 4 inline comments on this PR.

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

Context used:

  • Context used - AGENTS.md (source)
  • Context used - CLAUDE.md (source)

let _ = crate::set_camera_input(app.clone(), state.clone(), settings.camera_id, None).await;

let inputs = StartRecordingInputs {
mode: crate::recording::RecordingMode::Screen,
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.

P0 RecordingMode::Screen does not exist — compile error

crate::recording::RecordingMode is cap_recording::RecordingMode, whose only variants are Studio, Instant, and Screenshot. There is no Screen variant. This line will cause a compilation failure.

Additionally, RecordingMode is not re-exported from crate::recording (the use cap_recording::RecordingMode in recording.rs is not pub use), so the qualified path crate::recording::RecordingMode won't resolve either.

For a "start default recording" action, the correct approach is to read the user's configured mode from settings (which RecordingSettingsStore already exposes as mode: Option<RecordingMode>) or fall back to the default:

Suggested change
mode: crate::recording::RecordingMode::Screen,
let inputs = StartRecordingInputs {
mode: settings.mode.unwrap_or_default(),
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 168

Comment:
**`RecordingMode::Screen` does not exist — compile error**

`crate::recording::RecordingMode` is `cap_recording::RecordingMode`, whose only variants are `Studio`, `Instant`, and `Screenshot`. There is no `Screen` variant. This line will cause a compilation failure.

Additionally, `RecordingMode` is not re-exported from `crate::recording` (the `use cap_recording::RecordingMode` in `recording.rs` is not `pub use`), so the qualified path `crate::recording::RecordingMode` won't resolve either.

For a "start default recording" action, the correct approach is to read the user's configured mode from settings (which `RecordingSettingsStore` already exposes as `mode: Option<RecordingMode>`) or fall back to the default:

```suggestion
                let inputs = StartRecordingInputs {
                    mode: settings.mode.unwrap_or_default(),
```

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

ActionParseFromUrlError::Invalid => {
eprintln!("Invalid deep link format \"{}\"", &url)
}
// Likely login action, not handled here.
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 Code comment violates the NO CODE COMMENTS convention

Per the project's CLAUDE.md and AGENTS.md, no comments of any kind are permitted in code. The line // Likely login action, not handled here. is a pre-existing violation that was re-introduced by this PR when the file was rewritten. Code must be self-explanatory through naming and types.

Suggested change
// Likely login action, not handled here.
ActionParseFromUrlError::NotAction => {}

Context Used: AGENTS.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src-tauri/src/deeplink_actions.rs
Line: 53

Comment:
**Code comment violates the NO CODE COMMENTS convention**

Per the project's CLAUDE.md and AGENTS.md, no comments of any kind are permitted in code. The line `// Likely login action, not handled here.` is a pre-existing violation that was re-introduced by this PR when the file was rewritten. Code must be self-explanatory through naming and types.

```suggestion
                    ActionParseFromUrlError::NotAction => {}
```

**Context Used:** AGENTS.md ([source](https://app.greptile.com/review/custom-context?memory=27801409-c24c-4476-9c6c-180f1ef0a7f2))

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!

Comment on lines +7 to +8
await closeMainWindow();
await showHUD("Starting default recording in Cap");
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 Optimistic HUD message fires before the operation completes

open() only triggers the deeplink (launching the URL handler in the native app) and returns immediately — it does not wait for Cap to actually begin recording. showHUD("Starting default recording in Cap") therefore fires regardless of whether recording actually starts successfully (e.g., Cap isn't running, permissions are missing, or the deeplink returns an error). Consider wording the message to reflect that the request was sent rather than that recording has started, e.g. "Recording requested in Cap", to avoid giving users false confidence.

The same pattern applies in stop-recording.tsx line 8 ("Stopped recording in Cap").

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

Comment:
**Optimistic HUD message fires before the operation completes**

`open()` only triggers the deeplink (launching the URL handler in the native app) and returns immediately — it does not wait for Cap to actually begin recording. `showHUD("Starting default recording in Cap")` therefore fires regardless of whether recording actually starts successfully (e.g., Cap isn't running, permissions are missing, or the deeplink returns an error). Consider wording the message to reflect that the request was *sent* rather than that recording has *started*, e.g. `"Recording requested in Cap"`, to avoid giving users false confidence.

The same pattern applies in `stop-recording.tsx` line 8 (`"Stopped recording in Cap"`).

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

"description": "Control Cap Screen Recorder from Raycast",
"icon": "icon.png",
"author": "Cap",
"categories": [
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 author field may not match a valid Raycast handle

Raycast requires the author field to be the publisher's Raycast developer username, not a display name. If this extension is intended for Raycast Store publishing (the package.json includes a publish script), "Cap" may not match an actual registered account and will cause the publish command to fail. Confirm the correct Raycast username or document that this is internal-only. Is this extension intended for Raycast Store publishing? If so, does "Cap" match the registered Raycast developer username?

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/raycast-extension/package.json
Line: 8

Comment:
**`author` field may not match a valid Raycast handle**

Raycast requires the `author` field to be the publisher's Raycast developer username, not a display name. If this extension is intended for Raycast Store publishing (the `package.json` includes a `publish` script), `"Cap"` may not match an actual registered account and will cause the publish command to fail. Confirm the correct Raycast username or document that this is internal-only. Is this extension intended for Raycast Store publishing? If so, does `"Cap"` match the registered Raycast developer username?

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