-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: Add Raycast extension for controlling Cap recordings #1554
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b07c94f
53c7186
892e336
f6a365d
ed2badf
9733c40
8260798
4181c3f
e2dd0a1
caa1bdc
c68fc52
e16033e
0f75938
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| # Cap Raycast Extension | ||
|
|
||
| Control Cap screen recording directly from Raycast. | ||
|
|
||
| ## Features | ||
|
|
||
| - **Start Recording**: Choose between screen or window capture with Studio or Instant mode | ||
| - **Stop Recording**: Quickly stop your current recording | ||
| - **Pause Recording**: Pause the recording without stopping | ||
| - **Resume Recording**: Resume a paused recording | ||
| - **Toggle Pause**: Toggle between pause and resume states | ||
| - **Switch Camera**: Change the camera being used during recording | ||
| - **Switch Microphone**: Change the microphone being used during recording | ||
|
|
||
| ## Installation | ||
|
|
||
| ### From Source | ||
|
|
||
| 1. Clone the Cap repository | ||
| 2. Navigate to the extension directory: | ||
| ```bash | ||
| cd extensions/raycast | ||
| ``` | ||
| 3. Install dependencies: | ||
| ```bash | ||
| npm install | ||
| ``` | ||
| 4. Build and import to Raycast: | ||
| ```bash | ||
| npm run dev | ||
| ``` | ||
|
|
||
| ## Usage | ||
|
|
||
| Once installed, you can access all Cap commands from Raycast: | ||
|
|
||
| - Type "Cap" in Raycast to see all available commands | ||
| - Use keyboard shortcuts to quickly control your recordings | ||
| - Commands execute instantly without opening the Cap UI | ||
|
|
||
| ## Requirements | ||
|
|
||
| - Cap desktop application must be installed and running | ||
| - macOS (Cap is currently macOS-only) | ||
| - Raycast | ||
|
|
||
| ## Deep Link Protocol | ||
|
|
||
| This extension uses Cap's deep link protocol (`cap://action`) to communicate with the desktop application. All commands are executed via URL schemes that trigger the corresponding actions in Cap. | ||
|
|
||
| ## Development | ||
|
|
||
| ```bash | ||
| # Install dependencies | ||
| npm install | ||
|
|
||
| # Run in development mode | ||
| npm run dev | ||
|
|
||
| # Build for production | ||
| npm run build | ||
|
|
||
| # Lint code | ||
| npm run lint | ||
|
|
||
| # Fix linting issues | ||
| npm run fix-lint | ||
| ``` | ||
|
|
||
| ## License | ||
|
|
||
| MIT |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| { | ||
| "$schema": "https://www.raycast.com/schemas/extension.json", | ||
| "name": "cap", | ||
| "title": "Cap", | ||
| "description": "Control Cap screen recording from Raycast", | ||
| "icon": "cap-icon.png", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Icon file |
||
| "author": "cap", | ||
| "categories": [ | ||
| "Productivity", | ||
| "Media" | ||
| ], | ||
| "license": "MIT", | ||
| "commands": [ | ||
| { | ||
| "name": "start-recording", | ||
| "title": "Start Recording", | ||
| "description": "Start a new Cap 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 Recording", | ||
| "description": "Toggle pause/resume for the current recording", | ||
| "mode": "no-view" | ||
| }, | ||
| { | ||
| "name": "switch-camera", | ||
| "title": "Switch Camera", | ||
| "description": "Switch to a different camera", | ||
| "mode": "view" | ||
| }, | ||
| { | ||
| "name": "switch-microphone", | ||
| "title": "Switch Microphone", | ||
| "description": "Switch to a different microphone", | ||
| "mode": "view" | ||
| } | ||
| ], | ||
| "dependencies": { | ||
| "@raycast/api": "^1.48.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/node": "18.8.3", | ||
| "@types/react": "18.0.9", | ||
| "@typescript-eslint/eslint-plugin": "^5.0.0", | ||
| "@typescript-eslint/parser": "^5.0.0", | ||
| "eslint": "^8.0.0", | ||
| "eslint-config-prettier": "^8.3.0", | ||
| "prettier": "^2.5.1", | ||
| "typescript": "^4.4.3" | ||
| }, | ||
| "scripts": { | ||
| "build": "ray build -e dist", | ||
| "dev": "ray develop", | ||
| "fix-lint": "ray lint --fix", | ||
| "lint": "ray lint", | ||
| "publish": "npx @raycast/api@latest publish" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,26 @@ | ||||||
| import { showToast, Toast, closeMainWindow } from "@raycast/api"; | ||||||
| import { executeCapAction } from "./utils"; | ||||||
|
|
||||||
| export default async function Command() { | ||||||
| try { | ||||||
| await closeMainWindow(); | ||||||
|
|
||||||
| await showToast({ | ||||||
| style: Toast.Style.Animated, | ||||||
| title: "Pausing recording...", | ||||||
| }); | ||||||
|
|
||||||
| await executeCapAction({ pause_recording: {} }); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the enum serialization, this should be a JSON string action.
Suggested change
|
||||||
|
|
||||||
| await showToast({ | ||||||
| style: Toast.Style.Success, | ||||||
| title: "Recording paused", | ||||||
| }); | ||||||
| } catch (error) { | ||||||
| await showToast({ | ||||||
| style: Toast.Style.Failure, | ||||||
| title: "Failed to pause recording", | ||||||
| message: String(error), | ||||||
| }); | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,26 @@ | ||||||
| import { showToast, Toast, closeMainWindow } from "@raycast/api"; | ||||||
| import { executeCapAction } from "./utils"; | ||||||
|
|
||||||
| export default async function Command() { | ||||||
| try { | ||||||
| await closeMainWindow(); | ||||||
|
|
||||||
| await showToast({ | ||||||
| style: Toast.Style.Animated, | ||||||
| title: "Resuming recording...", | ||||||
| }); | ||||||
|
|
||||||
| await executeCapAction({ resume_recording: {} }); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the enum serialization, this should be a JSON string action.
Suggested change
|
||||||
|
|
||||||
| await showToast({ | ||||||
| style: Toast.Style.Success, | ||||||
| title: "Recording resumed", | ||||||
| }); | ||||||
| } catch (error) { | ||||||
| await showToast({ | ||||||
| style: Toast.Style.Failure, | ||||||
| title: "Failed to resume recording", | ||||||
| message: String(error), | ||||||
| }); | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,90 @@ | ||||||
| import { ActionPanel, Action, List, showToast, Toast } from "@raycast/api"; | ||||||
| import { executeCapAction, RecordingMode } from "./utils"; | ||||||
|
|
||||||
| export default function Command() { | ||||||
| const captureOptions = [ | ||||||
| { title: "Full Screen", value: "screen" }, | ||||||
| { title: "Window", value: "window" }, | ||||||
| ]; | ||||||
|
|
||||||
| const recordingModes: { title: string; value: RecordingMode }[] = [ | ||||||
| { title: "Studio Mode", value: "studio" }, | ||||||
| { title: "Instant Mode", value: "instant" }, | ||||||
| ]; | ||||||
|
|
||||||
| async function startRecording( | ||||||
| captureType: "screen" | "window", | ||||||
| mode: RecordingMode, | ||||||
| captureSystemAudio: boolean | ||||||
| ) { | ||||||
| try { | ||||||
| await showToast({ | ||||||
| style: Toast.Style.Animated, | ||||||
| title: "Starting recording...", | ||||||
| }); | ||||||
|
|
||||||
| // For simplicity, using default screen/window | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor: this repo avoids code comments; you can drop these without losing clarity.
Suggested change
|
||||||
| // In a real implementation, you'd want to list available screens/windows | ||||||
| const capture_mode = | ||||||
| captureType === "screen" | ||||||
| ? { screen: "Default Screen" } | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
| : { window: "Default Window" }; | ||||||
1234-ad marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
|
||||||
| await executeCapAction({ | ||||||
| start_recording: { | ||||||
| capture_mode, | ||||||
| capture_system_audio: captureSystemAudio, | ||||||
| mode, | ||||||
| }, | ||||||
| }); | ||||||
|
|
||||||
| await showToast({ | ||||||
| style: Toast.Style.Success, | ||||||
| title: "Recording started", | ||||||
| }); | ||||||
| } catch (error) { | ||||||
| await showToast({ | ||||||
| style: Toast.Style.Failure, | ||||||
| title: "Failed to start recording", | ||||||
| message: String(error), | ||||||
| }); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| return ( | ||||||
| <List> | ||||||
| {captureOptions.map((captureOption) => | ||||||
| recordingModes.map((mode) => ( | ||||||
| <List.Item | ||||||
| key={`${captureOption.value}-${mode.value}`} | ||||||
| title={`${captureOption.title} - ${mode.title}`} | ||||||
| actions={ | ||||||
| <ActionPanel> | ||||||
| <Action | ||||||
| title="Start Recording" | ||||||
| onAction={() => | ||||||
| startRecording( | ||||||
| captureOption.value as "screen" | "window", | ||||||
| mode.value, | ||||||
| false | ||||||
| ) | ||||||
| } | ||||||
| /> | ||||||
| <Action | ||||||
| title="Start Recording (with System Audio)" | ||||||
| onAction={() => | ||||||
| startRecording( | ||||||
| captureOption.value as "screen" | "window", | ||||||
| mode.value, | ||||||
| true | ||||||
| ) | ||||||
| } | ||||||
| /> | ||||||
| </ActionPanel> | ||||||
| } | ||||||
| /> | ||||||
| )) | ||||||
| )} | ||||||
| </List> | ||||||
| ); | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,26 @@ | ||||||
| import { showToast, Toast, closeMainWindow } from "@raycast/api"; | ||||||
| import { executeCapAction } from "./utils"; | ||||||
|
|
||||||
| export default async function Command() { | ||||||
| try { | ||||||
| await closeMainWindow(); | ||||||
|
|
||||||
| await showToast({ | ||||||
| style: Toast.Style.Animated, | ||||||
| title: "Stopping recording...", | ||||||
| }); | ||||||
|
|
||||||
| await executeCapAction({ stop_recording: {} }); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the enum serialization, this should be a JSON string action.
Suggested change
|
||||||
|
|
||||||
| await showToast({ | ||||||
| style: Toast.Style.Success, | ||||||
| title: "Recording stopped", | ||||||
| }); | ||||||
| } catch (error) { | ||||||
| await showToast({ | ||||||
| style: Toast.Style.Failure, | ||||||
| title: "Failed to stop recording", | ||||||
| message: String(error), | ||||||
| }); | ||||||
| } | ||||||
| } | ||||||
Uh oh!
There was an error while loading. Please reload this page.