Skip to content

feat: Raycast extension and deep link support#1743

Open
webhop123 wants to merge 2 commits intoCapSoftware:mainfrom
webhop123:feat/raycast-extension-and-deep-links
Open

feat: Raycast extension and deep link support#1743
webhop123 wants to merge 2 commits intoCapSoftware:mainfrom
webhop123:feat/raycast-extension-and-deep-links

Conversation

@webhop123
Copy link
Copy Markdown

@webhop123 webhop123 commented Apr 20, 2026

Description

This PR implements comprehensive deep link support and a dedicated Raycast extension to allow external control of the Cap recorder.

Key Features:

  • Backend (Rust):
    • Implemented Mute, Unmute, and ToggleMute logic in the MicrophoneFeed actor to surgically drop audio samples when requested.
    • Added a robust deep link handler in deeplink_actions.rs supporting the cap:// scheme.
    • Integrated desktop notifications for all external actions (Start, Stop, Pause, Resume, Mute, Screenshot) to provide immediate user feedback.
    • Added unit tests for deep link parsing to ensure stability.
  • Raycast Extension:
    • Created a full extension in extensions/raycast with 9 commands:
      • Start/Stop Recording
      • Pause/Resume/Toggle Recording
      • Mute/Unmute/Toggle Microphone
      • Take Screenshot
    • Integrated official branding and provided a detailed README.
  • Monorepo Integration:
    • Added extensions/* to pnpm-workspace.yaml.
    • Formatted all changes with Biome to match project standards.

Testing:

  • Verified deep link parsing via new Rust unit tests.
  • Manually checked actor-level mute logic.

/claim #bounty

Greptile Summary

This PR adds a Raycast extension and expands deep link handling so Cap can be controlled externally via cap:// URLs, adding start/stop/pause/resume/mute/screenshot actions backed by new Rust actor messages and desktop notifications.

  • P0 – compile failure: Message<Mute>, Message<Unmute>, and Message<ToggleMute> are each implemented twice for MicrophoneFeed in microphone.rs (lines 768–791 and 950–973). Rust will reject this with error[E0119], so the crate won't build until the duplicate impls are removed.

Confidence Score: 4/5

Not safe to merge until the duplicate Message trait implementations in microphone.rs are removed — they will prevent the crate from compiling.

A single P0 finding (duplicate trait impls causing a hard compile error) blocks merge. Once removed the rest of the implementation is straightforward and the logic is sound.

crates/recording/src/feeds/microphone.rs — duplicate Message<Mute/Unmute/ToggleMute> implementations at lines 950–973 must be deleted.

Important Files Changed

Filename Overview
crates/recording/src/feeds/microphone.rs Adds muted field and Mute/Unmute/ToggleMute message handlers, but the three Message impls are duplicated (lines 768–791 and 950–973), which is a hard compile error.
apps/desktop/src-tauri/src/deeplink_actions.rs Expands deep link routing to support simple host-based URLs, adds pause/resume/mute/screenshot execution with desktop notifications, and includes unit tests. Minor unused variable in test.
extensions/raycast/package.json New Raycast extension manifest declaring 9 no-view commands; dependencies look correct.
extensions/raycast/src/start-recording.tsx Minimal Raycast command that fires cap://start-recording deep link; no error handling or HUD feedback for the user (applies to all 9 command files).
pnpm-workspace.yaml Adds extensions/* to pnpm workspace so the Raycast extension is managed by the monorepo.

Sequence Diagram

sequenceDiagram
    participant R as Raycast Extension
    participant OS as macOS URL Dispatcher
    participant T as Tauri deep-link plugin
    participant H as deeplink_actions::handle()
    participant A as App / Recording State
    participant M as MicrophoneFeed Actor
    participant N as Desktop Notification

    R->>OS: open("cap://mute-recording")
    OS->>T: cap:// URL event
    T->>H: handle(urls)
    H->>H: parse URL host → DeepLinkAction
    H->>A: acquire read lock on ArcLock<App>
    A-->>H: app_state
    H->>M: tell(Mute) / ask(ToggleMute)
    M-->>H: () / bool
    H->>N: emit NewNotification
    N-->>R: (system notification shown to user)
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: crates/recording/src/feeds/microphone.rs
Line: 950-973

Comment:
**Duplicate `Message` trait implementations will cause a compile error**

`Message<Mute>`, `Message<Unmute>`, and `Message<ToggleMute>` are each implemented twice for `MicrophoneFeed`. The first set appears at lines 768–791 and the second set at lines 950–973 (added by this PR). Rust's orphan/coherence rules forbid conflicting trait implementations for the same type, so this will produce `error[E0119]` and prevent the crate from building at all.

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: 331

Comment:
**Unused variable `expected_host` in test loop**

The tuple variable `expected_host` is bound in the `for` loop but never read. Rust will emit an `unused variable` warning. Either use it in an assertion or prefix it with `_`.

```suggestion
        for (url_str, _expected_host) in urls {
```

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

---

This is a comment left during a code review.
Path: extensions/raycast/src/start-recording.tsx
Line: 1-5

Comment:
**No user feedback when Cap is not running**

All nine Raycast commands simply fire `open()` and return immediately. If the Cap app is not installed or is not running to handle the `cap://` deep link, the command completes silently with no indication to the user that the action failed. Consider wrapping in a try/catch and using `showHUD` or `showToast` to surface an error — e.g.:

```ts
import { open, showHUD } from "@raycast/api";

export default async function Command() {
  try {
    await open("cap://start-recording");
    await showHUD("Starting recording…");
  } catch {
    await showHUD("Failed to open Cap");
  }
}
```

This applies to all nine command files.

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

Reviews (1): Last reviewed commit: "feat: add Raycast extension and deep lin..." | Re-trigger Greptile

Greptile also left 3 inline comments on this PR.

This commit adds:
- Comprehensive deep link support via the 'cap://' scheme.
- Mute/Unmute functionality in the microphone feed actor.
- Desktop notifications for all deep link actions.
- A full-featured Raycast extension in extensions/raycast.
- Rust unit tests for deep link parsing.
Comment on lines +950 to +973
impl Message<Mute> for MicrophoneFeed {
type Reply = ();

async fn handle(&mut self, _: Mute, _: &mut Context<Self, Self::Reply>) -> Self::Reply {
self.muted = true;
}
}

impl Message<Unmute> for MicrophoneFeed {
type Reply = ();

async fn handle(&mut self, _: Unmute, _: &mut Context<Self, Self::Reply>) -> Self::Reply {
self.muted = false;
}
}

impl Message<ToggleMute> for MicrophoneFeed {
type Reply = bool;

async fn handle(&mut self, _: ToggleMute, _: &mut Context<Self, Self::Reply>) -> Self::Reply {
self.muted = !self.muted;
self.muted
}
}
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 Duplicate Message trait implementations will cause a compile error

Message<Mute>, Message<Unmute>, and Message<ToggleMute> are each implemented twice for MicrophoneFeed. The first set appears at lines 768–791 and the second set at lines 950–973 (added by this PR). Rust's orphan/coherence rules forbid conflicting trait implementations for the same type, so this will produce error[E0119] and prevent the crate from building at all.

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/recording/src/feeds/microphone.rs
Line: 950-973

Comment:
**Duplicate `Message` trait implementations will cause a compile error**

`Message<Mute>`, `Message<Unmute>`, and `Message<ToggleMute>` are each implemented twice for `MicrophoneFeed`. The first set appears at lines 768–791 and the second set at lines 950–973 (added by this PR). Rust's orphan/coherence rules forbid conflicting trait implementations for the same type, so this will produce `error[E0119]` and prevent the crate from building at all.

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

Comment thread apps/desktop/src-tauri/src/deeplink_actions.rs Outdated
Comment on lines +1 to +5
import { open } from "@raycast/api";

export default async function Command() {
await open("cap://start-recording");
}
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 No user feedback when Cap is not running

All nine Raycast commands simply fire open() and return immediately. If the Cap app is not installed or is not running to handle the cap:// deep link, the command completes silently with no indication to the user that the action failed. Consider wrapping in a try/catch and using showHUD or showToast to surface an error — e.g.:

import { open, showHUD } from "@raycast/api";

export default async function Command() {
  try {
    await open("cap://start-recording");
    await showHUD("Starting recording…");
  } catch {
    await showHUD("Failed to open Cap");
  }
}

This applies to all nine command files.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/raycast/src/start-recording.tsx
Line: 1-5

Comment:
**No user feedback when Cap is not running**

All nine Raycast commands simply fire `open()` and return immediately. If the Cap app is not installed or is not running to handle the `cap://` deep link, the command completes silently with no indication to the user that the action failed. Consider wrapping in a try/catch and using `showHUD` or `showToast` to surface an error — e.g.:

```ts
import { open, showHUD } from "@raycast/api";

export default async function Command() {
  try {
    await open("cap://start-recording");
    await showHUD("Starting recording…");
  } catch {
    await showHUD("Failed to open Cap");
  }
}
```

This applies to all nine command files.

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

@webhop123
Copy link
Copy Markdown
Author

/claim #1540

- Remove duplicate Message implementations in microphone.rs.
- Fix unused variable warning in deeplink_actions.rs test.
- Add HUD feedback and error handling to all Raycast commands.
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.

1 participant