diff --git a/apps/desktop/src-tauri/src/deeplink_actions.rs b/apps/desktop/src-tauri/src/deeplink_actions.rs index dbd90f667f..f7cb836286 100644 --- a/apps/desktop/src-tauri/src/deeplink_actions.rs +++ b/apps/desktop/src-tauri/src/deeplink_actions.rs @@ -26,6 +26,18 @@ pub enum DeepLinkAction { mode: RecordingMode, }, StopRecording, + PauseRecording, + ResumeRecording, + TogglePauseRecording, + TakeScreenshot { + target: ScreenCaptureTarget, + }, + SetCamera { + camera_id: Option, + }, + SetMicrophone { + mic_label: Option, + }, OpenEditor { project_path: PathBuf, }, @@ -146,6 +158,28 @@ 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 { target } => { + crate::recording::take_screenshot(app.clone(), target) + .await + .map(|_| ()) + } + DeepLinkAction::SetCamera { camera_id } => { + let state = app.state::>(); + crate::set_camera_input(app.clone(), state, camera_id).await + } + DeepLinkAction::SetMicrophone { mic_label } => { + let state = app.state::>(); + crate::set_mic_input(state, mic_label).await + } DeepLinkAction::OpenEditor { project_path } => { crate::open_project_from_path(Path::new(&project_path), app.clone()) } diff --git a/raycast-cap-extension/README.md b/raycast-cap-extension/README.md new file mode 100644 index 0000000000..5b68703752 --- /dev/null +++ b/raycast-cap-extension/README.md @@ -0,0 +1,33 @@ +# Cap Raycast Extension + +Control Cap screen recording directly from Raycast. + +## Features + +- **Start Recording**: Start a new Cap recording +- **Stop Recording**: Stop the current recording +- **Pause/Resume**: Pause and resume recordings +- **Toggle Pause**: Toggle pause state with one command +- **Take Screenshot**: Capture screenshots with Cap + +## Installation + +1. Make sure you have [Cap](https://cap.so) installed +2. Install this extension in Raycast +3. Use the commands to control Cap + +## Usage + +All commands use Cap's deeplink protocol to communicate with the desktop app. Make sure Cap is running before using these commands. + +## Development + +```bash +npm install +npm run dev +``` + +## License + +MIT + diff --git a/raycast-cap-extension/icon.png b/raycast-cap-extension/icon.png new file mode 100644 index 0000000000..718226caf2 Binary files /dev/null and b/raycast-cap-extension/icon.png differ diff --git a/raycast-cap-extension/package.json b/raycast-cap-extension/package.json new file mode 100644 index 0000000000..4a90997cf6 --- /dev/null +++ b/raycast-cap-extension/package.json @@ -0,0 +1,70 @@ +{ + "$schema": "https://www.raycast.com/schemas/extension.json", + "name": "cap", + "title": "Cap", + "description": "Control Cap screen recording from Raycast", + "icon": "icon.png", + "author": "bikramadhikari001", + "categories": [ + "Productivity", + "Media" + ], + "license": "MIT", + "commands": [ + { + "name": "start-recording", + "title": "Start Recording", + "description": "Start a new Cap recording", + "mode": "no-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-recording", + "title": "Toggle Pause Recording", + "description": "Toggle pause/resume for the current recording", + "mode": "no-view" + }, + { + "name": "take-screenshot", + "title": "Take Screenshot", + "description": "Take a screenshot with Cap", + "mode": "no-view" + } + ], + "dependencies": { + "@raycast/api": "^1.87.0", + "@raycast/utils": "^1.19.0" + }, + "devDependencies": { + "@raycast/eslint-config": "^1.0.11", + "@types/node": "20.8.10", + "@types/react": "18.3.3", + "eslint": "^8.57.0", + "prettier": "^3.2.5", + "typescript": "^5.4.5" + }, + "scripts": { + "build": "ray build -e dist", + "dev": "ray develop", + "fix-lint": "ray lint --fix", + "lint": "ray lint", + "publish": "npx @raycast/api@latest publish" + } +} \ No newline at end of file diff --git a/raycast-cap-extension/src/pause-recording.tsx b/raycast-cap-extension/src/pause-recording.tsx new file mode 100644 index 0000000000..cea1db8154 --- /dev/null +++ b/raycast-cap-extension/src/pause-recording.tsx @@ -0,0 +1,13 @@ +import { open, showToast, Toast } from "@raycast/api"; + +export default async function Command() { + try { + const action = { pause_recording: {} }; + const url = `cap-desktop://action?value=${encodeURIComponent(JSON.stringify(action))}`; + await open(url); + await showToast({ style: Toast.Style.Success, title: "Pause recording requested" }); + } catch (error) { + await showToast({ style: Toast.Style.Failure, title: "Failed to pause recording", message: String(error) }); + } +} + diff --git a/raycast-cap-extension/src/resume-recording.tsx b/raycast-cap-extension/src/resume-recording.tsx new file mode 100644 index 0000000000..5da696c9a2 --- /dev/null +++ b/raycast-cap-extension/src/resume-recording.tsx @@ -0,0 +1,13 @@ +import { open, showToast, Toast } from "@raycast/api"; + +export default async function Command() { + try { + const action = { resume_recording: {} }; + const url = `cap-desktop://action?value=${encodeURIComponent(JSON.stringify(action))}`; + await open(url); + await showToast({ style: Toast.Style.Success, title: "Resume recording requested" }); + } catch (error) { + await showToast({ style: Toast.Style.Failure, title: "Failed to resume recording", message: String(error) }); + } +} + diff --git a/raycast-cap-extension/src/start-recording.tsx b/raycast-cap-extension/src/start-recording.tsx new file mode 100644 index 0000000000..ca8da6713a --- /dev/null +++ b/raycast-cap-extension/src/start-recording.tsx @@ -0,0 +1,21 @@ +import { open, showToast, Toast } from "@raycast/api"; + +export default async function Command() { + try { + const action = { + start_recording: { + capture_mode: { screen: "" }, + camera: null, + mic_label: null, + capture_system_audio: true, + mode: "studio" + } + }; + const url = `cap-desktop://action?value=${encodeURIComponent(JSON.stringify(action))}`; + await open(url); + await showToast({ style: Toast.Style.Success, title: "Start recording requested" }); + } catch (error) { + await showToast({ style: Toast.Style.Failure, title: "Failed to start recording", message: String(error) }); + } +} + diff --git a/raycast-cap-extension/src/stop-recording.tsx b/raycast-cap-extension/src/stop-recording.tsx new file mode 100644 index 0000000000..fc04f09af9 --- /dev/null +++ b/raycast-cap-extension/src/stop-recording.tsx @@ -0,0 +1,13 @@ +import { open, showToast, Toast } from "@raycast/api"; + +export default async function Command() { + try { + const action = { stop_recording: {} }; + const url = `cap-desktop://action?value=${encodeURIComponent(JSON.stringify(action))}`; + await open(url); + await showToast({ style: Toast.Style.Success, title: "Stop recording requested" }); + } catch (error) { + await showToast({ style: Toast.Style.Failure, title: "Failed to stop recording", message: String(error) }); + } +} + diff --git a/raycast-cap-extension/src/take-screenshot.tsx b/raycast-cap-extension/src/take-screenshot.tsx new file mode 100644 index 0000000000..eb073c726f --- /dev/null +++ b/raycast-cap-extension/src/take-screenshot.tsx @@ -0,0 +1,17 @@ +import { open, showToast, Toast } from "@raycast/api"; + +export default async function Command() { + try { + const action = { + take_screenshot: { + target: { variant: "display", id: "0" } + } + }; + const url = `cap-desktop://action?value=${encodeURIComponent(JSON.stringify(action))}`; + await open(url); + await showToast({ style: Toast.Style.Success, title: "Screenshot requested" }); + } catch (error) { + await showToast({ style: Toast.Style.Failure, title: "Failed to take screenshot", message: String(error) }); + } +} + diff --git a/raycast-cap-extension/src/toggle-pause-recording.tsx b/raycast-cap-extension/src/toggle-pause-recording.tsx new file mode 100644 index 0000000000..3568bc9b72 --- /dev/null +++ b/raycast-cap-extension/src/toggle-pause-recording.tsx @@ -0,0 +1,13 @@ +import { open, showToast, Toast } from "@raycast/api"; + +export default async function Command() { + try { + const action = { toggle_pause_recording: {} }; + const url = `cap-desktop://action?value=${encodeURIComponent(JSON.stringify(action))}`; + await open(url); + await showToast({ style: Toast.Style.Success, title: "Toggle pause requested" }); + } catch (error) { + await showToast({ style: Toast.Style.Failure, title: "Failed to toggle pause", message: String(error) }); + } +} + diff --git a/raycast-cap-extension/tsconfig.json b/raycast-cap-extension/tsconfig.json new file mode 100644 index 0000000000..91a8c5fc74 --- /dev/null +++ b/raycast-cap-extension/tsconfig.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2022"], + "module": "commonjs", + "jsx": "react", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "moduleResolution": "node" + }, + "include": ["src/**/*"] +} +