Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions apps/desktop/src-tauri/src/deeplink_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,22 @@ pub enum DeepLinkAction {
mode: RecordingMode,
},
StopRecording,
PauseRecording,
ResumeRecording,
TogglePauseRecording,
TakeScreenshot {
capture_target: ScreenCaptureTarget,
},
SetCamera {
id: Option<DeviceOrModelID>,
},
SetMicrophone {
label: Option<String>,
},
ListCameras,
ListMicrophones,
ListDisplays,
ListWindows,
OpenEditor {
project_path: PathBuf,
},
Expand Down Expand Up @@ -146,6 +162,73 @@ impl DeepLinkAction {
DeepLinkAction::StopRecording => {
crate::recording::stop_recording(app.clone(), app.state()).await
}
DeepLinkAction::PauseRecording => {
crate::recording::pause_recording(app.clone(), app.state()).await
}
DeepLinkAction::ResumeRecording => {
crate::recording::resume_recording(app.clone(), app.state()).await
}
DeepLinkAction::TogglePauseRecording => {
crate::recording::toggle_pause_recording(app.clone(), app.state()).await
}
DeepLinkAction::TakeScreenshot { capture_target } => {
crate::recording::take_screenshot(app.clone(), capture_target)
.await
.map(|_| ())
}
DeepLinkAction::SetCamera { id } => {
crate::set_camera_input(app.clone(), app.state(), id).await
Comment on lines +179 to +180
Copy link
Contributor

Choose a reason for hiding this comment

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

crate::set_camera_input and crate::set_mic_input functions in lib.rs are not pub or pub(crate), so this will fail to compile.

Check apps/desktop/src-tauri/src/lib.rs:385 and apps/desktop/src-tauri/src/lib.rs:471 - both functions need pub(crate) visibility to be accessible from this module.

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

Comment:
`crate::set_camera_input` and `crate::set_mic_input` functions in `lib.rs` are not `pub` or `pub(crate)`, so this will fail to compile.

Check `apps/desktop/src-tauri/src/lib.rs:385` and `apps/desktop/src-tauri/src/lib.rs:471` - both functions need `pub(crate)` visibility to be accessible from this module.

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

}
DeepLinkAction::SetMicrophone { label } => {
crate::set_mic_input(app.state(), label).await
}
DeepLinkAction::ListCameras => {
let cameras = crate::recording::list_cameras();
let cameras_json = serde_json::to_string(&cameras)
.map_err(|e| format!("Failed to serialize cameras: {}", e))?;
tracing::info!("Available cameras: {}", cameras_json);
Ok(())
}
DeepLinkAction::ListMicrophones => {
use cap_recording::feeds::microphone::MicrophoneFeed;
let microphones: Vec<String> = MicrophoneFeed::list().keys().cloned().collect();
let mics_json = serde_json::to_string(&microphones)
.map_err(|e| format!("Failed to serialize microphones: {}", e))?;
tracing::info!("Available microphones: {}", mics_json);
Ok(())
}
DeepLinkAction::ListDisplays => {
let displays = cap_recording::screen_capture::list_displays();
let displays_data: Vec<_> = displays
.into_iter()
.map(|(capture_display, _)| {
serde_json::json!({
"id": capture_display.id,
"name": capture_display.name,
})
})
.collect();
let displays_json = serde_json::to_string(&displays_data)
.map_err(|e| format!("Failed to serialize displays: {}", e))?;
tracing::info!("Available displays: {}", displays_json);
Ok(())
}
DeepLinkAction::ListWindows => {
let windows = cap_recording::screen_capture::list_windows();
let windows_data: Vec<_> = windows
.into_iter()
.map(|(capture_window, _)| {
serde_json::json!({
"id": capture_window.id,
"name": capture_window.name,
})
})
.collect();
let windows_json = serde_json::to_string(&windows_data)
.map_err(|e| format!("Failed to serialize windows: {}", e))?;
tracing::info!("Available windows: {}", windows_json);
Ok(())
}
DeepLinkAction::OpenEditor { project_path } => {
crate::open_project_from_path(Path::new(&project_path), app.clone())
}
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ impl App {
#[tauri::command]
#[specta::specta]
#[instrument(skip(state))]
async fn set_mic_input(state: MutableState<'_, App>, label: Option<String>) -> Result<(), String> {
pub(crate) async fn set_mic_input(state: MutableState<'_, App>, label: Option<String>) -> Result<(), String> {
let (mic_feed, studio_handle, current_label) = {
let app = state.read().await;
let handle = match app.current_recording() {
Expand Down Expand Up @@ -468,7 +468,7 @@ fn get_system_diagnostics() -> cap_recording::diagnostics::SystemDiagnostics {
#[specta::specta]
#[instrument(skip(app_handle, state))]
#[allow(unused_mut)]
async fn set_camera_input(
pub(crate) async fn set_camera_input(
app_handle: AppHandle,
state: MutableState<'_, App>,
id: Option<DeviceOrModelID>,
Expand Down
177 changes: 177 additions & 0 deletions apps/raycast-extension/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# Cap Raycast Extension

Control [Cap](https://cap.so) screen recording directly from Raycast!

## Features

This extension provides quick access to Cap's recording functionality through Raycast commands:

### Recording Controls
- **Start Recording** - Start a new screen or window recording with customizable options
- **Stop Recording** - Stop the current recording
- **Pause Recording** - Pause the active recording
- **Resume Recording** - Resume a paused recording
- **Toggle Pause** - Toggle between paused and active recording states

### Capture
- **Take Screenshot** - Capture a screenshot of a specific display or window

### Hardware Management
- **Switch Camera** - Change the active camera input or disable camera
- **Switch Microphone** - Change the active microphone input or mute

## Requirements

- [Cap](https://cap.so) desktop application (v0.3.0 or later) must be installed
- macOS (Raycast is macOS-only)
- Cap must be running to respond to commands

## Installation

### From Raycast Store (Coming Soon)
1. Open Raycast
2. Search for "Cap"
3. Click "Install Extension"

### Manual Installation (Development)
1. Clone the repository:
```bash
git clone https://github.com/CapSoftware/Cap.git
cd Cap/apps/raycast-extension
```

2. Install dependencies:
```bash
npm install
```

3. Build the extension:
```bash
npm run dev
```

4. The extension will automatically be available in Raycast during development

## Usage

### Starting a Recording
1. Open Raycast (⌘ + Space)
2. Type "Start Recording"
3. Fill in the form:
- **Capture Type**: Choose Screen or Window
- **Target Name**: Enter the display/window name
- **Recording Mode**: Studio (editable) or Instant (immediately uploaded)
- **Enable Camera**: Toggle camera on/off
- **Enable Microphone**: Toggle microphone on/off
- **Capture System Audio**: Include system audio in recording
4. Press Enter to start recording

### Finding Display/Window Names
Use the built-in Cap commands to list available targets:
- In your terminal, run Cap with `--list-displays` or `--list-windows` flags
- Or check the Cap UI for display/window names

### Quick Actions
All other commands are instant actions:
- **Stop Recording**: Simply run the command
- **Pause/Resume**: Run the respective command while recording
- **Toggle Pause**: Quick shortcut to toggle pause state
- **Take Screenshot**: Fill in the target and capture instantly

### Hardware Switching
1. Run "Switch Camera" or "Switch Microphone"
2. Enter the device ID or name
3. Toggle enable/disable as needed
4. Press Enter to switch

**Tip**: Use the "List Cameras" and "List Microphones" Cap commands to see available devices

## Commands Reference

| Command | Shortcut | Description |
|---------|----------|-------------|
| Start Recording | - | Start a new recording with options |
| Stop Recording | - | Stop the current recording |
| Pause Recording | - | Pause the active recording |
| Resume Recording | - | Resume a paused recording |
| Toggle Pause | - | Toggle pause state |
| Take Screenshot | - | Capture a screenshot |
| Switch Camera | - | Change camera input |
| Switch Microphone | - | Change microphone input |

## Troubleshooting

### Command Not Working
- Ensure Cap is running
- Check that Cap has necessary permissions (Screen Recording, Camera, Microphone)
- Verify you're running Cap v0.3.0 or later with deeplink support

### "Failed to Start Recording"
- Double-check the display/window name is correct
- Ensure the target display/window exists and is accessible
- Check Cap's permissions in System Settings > Privacy & Security

### Camera/Microphone Not Switching
- Verify the device ID/name is correct
- Check that the device is connected and recognized by your system
- Ensure Cap has permission to access camera/microphone

## Development

### Project Structure
```
src/
├── utils/
│ └── deeplink.ts # Deeplink utility functions
├── start-recording.tsx # Start recording command
├── stop-recording.tsx # Stop recording command
├── pause-recording.tsx # Pause command
├── resume-recording.tsx # Resume command
├── toggle-pause.tsx # Toggle pause command
├── take-screenshot.tsx # Screenshot command
├── switch-camera.tsx # Camera switching command
└── switch-microphone.tsx # Microphone switching command
```

### Building
```bash
npm run build
```

### Linting
```bash
npm run lint
npm run fix-lint
```

## How It Works

This extension communicates with Cap using the `cap-desktop://` URL scheme. Each command constructs a deeplink URL with JSON-encoded actions and opens it, which Cap intercepts and executes.

Example deeplink:
```
cap-desktop://action?value={"pauseRecording":{}}
```

## Contributing

Contributions are welcome! Please:
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Submit a pull request

## License

MIT License - see LICENSE file for details

## Links

- [Cap Website](https://cap.so)
- [Cap GitHub](https://github.com/CapSoftware/Cap)
- [Report Issues](https://github.com/CapSoftware/Cap/issues)
- [Raycast](https://raycast.com)

## Credits

Created for the Cap deeplinks bounty (#1540)
78 changes: 78 additions & 0 deletions apps/raycast-extension/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"$schema": "https://www.raycast.com/schemas/extension.json",
"name": "cap",
"title": "Cap",
"description": "Control Cap screen recording from Raycast",
"icon": "icon.png",
"author": "cap",
"license": "MIT",
"commands": [
{
"name": "start-recording",
"title": "Start Recording",
"description": "Start a new screen recording",
"mode": "view"
},
{
"name": "stop-recording",
"title": "Stop Recording",
"description": "Stop the current recording",
"mode": "no-view"
},
{
"name": "pause-recording",
"title": "Pause Recording",
"description": "Pause the current recording",
"mode": "no-view"
},
{
"name": "resume-recording",
"title": "Resume Recording",
"description": "Resume the paused recording",
"mode": "no-view"
},
{
"name": "toggle-pause",
"title": "Toggle Pause",
"description": "Toggle recording pause state",
"mode": "no-view"
},
{
"name": "take-screenshot",
"title": "Take Screenshot",
"description": "Capture a screenshot",
"mode": "view"
},
{
"name": "switch-camera",
"title": "Switch Camera",
"description": "Change camera input",
"mode": "view"
},
{
"name": "switch-microphone",
"title": "Switch Microphone",
"description": "Change microphone input",
"mode": "view"
}
],
"dependencies": {
"@raycast/api": "^1.83.2",
"@raycast/utils": "^1.19.0"
},
"devDependencies": {
"@raycast/eslint-config": "^1.0.11",
"@types/node": "^20.16.5",
"@types/react": "^18.3.3",
"eslint": "^8.57.0",
"prettier": "^3.3.3",
"typescript": "^5.5.4"
},
"scripts": {
"build": "ray build -e dist",
"dev": "ray develop",
"fix-lint": "ray lint --fix",
"lint": "ray lint",
"publish": "npx @raycast/api@latest publish"
}
}
19 changes: 19 additions & 0 deletions apps/raycast-extension/src/pause-recording.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { showToast, Toast } from "@raycast/api";
import * as deeplink from "./utils/deeplink";

export default async function Command() {
try {
await deeplink.pauseRecording();
await showToast({
style: Toast.Style.Success,
title: "Recording Paused",
message: "Cap recording has been paused",
});
} catch (error) {
await showToast({
style: Toast.Style.Failure,
title: "Failed to Pause Recording",
message: error instanceof Error ? error.message : "Unknown error occurred",
});
}
}
Loading