diff --git a/apps/desktop/src-tauri/src/deeplink_actions.rs b/apps/desktop/src-tauri/src/deeplink_actions.rs index a117028487..917cef6434 100644 --- a/apps/desktop/src-tauri/src/deeplink_actions.rs +++ b/apps/desktop/src-tauri/src/deeplink_actions.rs @@ -26,6 +26,14 @@ pub enum DeepLinkAction { mode: RecordingMode, }, StopRecording, + PauseRecording, + ResumeRecording, + SwitchMicrophone { + mic_label: String, + }, + SwitchCamera { + camera: DeviceOrModelID, + }, OpenEditor { project_path: PathBuf, }, @@ -49,7 +57,6 @@ pub fn handle(app_handle: &AppHandle, urls: Vec) { ActionParseFromUrlError::Invalid => { eprintln!("Invalid deep link format \"{}\"", &url) } - // Likely login action, not handled here. ActionParseFromUrlError::NotAction => {} }) .ok() @@ -147,6 +154,20 @@ 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::SwitchMicrophone { mic_label } => { + let state = app.state::>(); + crate::set_mic_input(state.clone(), Some(mic_label)).await + } + DeepLinkAction::SwitchCamera { camera } => { + let state = app.state::>(); + crate::set_camera_input(app.clone(), state.clone(), Some(camera), None).await + } DeepLinkAction::OpenEditor { project_path } => { crate::open_project_from_path(Path::new(&project_path), app.clone()) } diff --git a/biome.json b/biome.json index 4b6e2f87ff..c563760b48 100644 --- a/biome.json +++ b/biome.json @@ -48,6 +48,13 @@ "rules": { "a11y": "off" } } }, + { + "includes": ["extensions/raycast/**/*"], + "formatter": { + "indentStyle": "space", + "indentWidth": 2 + } + }, { "includes": ["**/*.css"], "linter": { diff --git a/extensions/raycast/.gitignore b/extensions/raycast/.gitignore new file mode 100644 index 0000000000..8f9d1cfcde --- /dev/null +++ b/extensions/raycast/.gitignore @@ -0,0 +1,5 @@ +node_modules +out +dist +*.log +.DS_Store diff --git a/extensions/raycast/assets/command-icon.png b/extensions/raycast/assets/command-icon.png new file mode 100644 index 0000000000..b1ac1ef7d8 Binary files /dev/null and b/extensions/raycast/assets/command-icon.png differ diff --git a/extensions/raycast/package.json b/extensions/raycast/package.json new file mode 100644 index 0000000000..66087bca4b --- /dev/null +++ b/extensions/raycast/package.json @@ -0,0 +1,80 @@ +{ + "$schema": "https://www.raycast.com/schemas/extension.json", + "name": "cap", + "title": "Cap", + "description": "Control Cap recording via deeplinks", + "icon": "command-icon.png", + "author": "cap", + "categories": [ + "Productivity" + ], + "license": "MIT", + "commands": [ + { + "name": "start-recording", + "title": "Start Recording", + "description": "Start a Cap recording", + "mode": "no-view" + }, + { + "name": "stop-recording", + "title": "Stop Recording", + "description": "Stop the current Cap recording", + "mode": "no-view" + }, + { + "name": "pause-recording", + "title": "Pause Recording", + "description": "Pause the current Cap recording", + "mode": "no-view" + }, + { + "name": "resume-recording", + "title": "Resume Recording", + "description": "Resume the current Cap recording", + "mode": "no-view" + }, + { + "name": "switch-microphone", + "title": "Switch Microphone", + "description": "Switch the active microphone in Cap", + "mode": "no-view", + "arguments": [ + { + "name": "mic", + "placeholder": "Microphone Name", + "type": "text", + "required": true + } + ] + }, + { + "name": "switch-camera", + "title": "Switch Camera", + "description": "Switch the active camera in Cap", + "mode": "no-view", + "arguments": [ + { + "name": "camera", + "placeholder": "Camera Name", + "type": "text", + "required": true + } + ] + } + ], + "dependencies": { + "@raycast/api": "^1.83.1" + }, + "devDependencies": { + "@types/node": "20.8.10", + "@types/react": "18.2.27", + "typescript": "^5.2.2" + }, + "scripts": { + "build": "ray build -e dist", + "dev": "ray develop", + "lint": "ray lint", + "fix-lint": "ray fix-lint" + } +} diff --git a/extensions/raycast/src/pause-recording.tsx b/extensions/raycast/src/pause-recording.tsx new file mode 100644 index 0000000000..b12faa5d6d --- /dev/null +++ b/extensions/raycast/src/pause-recording.tsx @@ -0,0 +1,6 @@ +import { open } from "@raycast/api"; + +export default async function Command() { + const value = JSON.stringify({ PauseRecording: {} }); + await open(`cap://action?value=${encodeURIComponent(value)}`); +} diff --git a/extensions/raycast/src/resume-recording.tsx b/extensions/raycast/src/resume-recording.tsx new file mode 100644 index 0000000000..0c01c54385 --- /dev/null +++ b/extensions/raycast/src/resume-recording.tsx @@ -0,0 +1,6 @@ +import { open } from "@raycast/api"; + +export default async function Command() { + const value = JSON.stringify({ ResumeRecording: {} }); + await open(`cap://action?value=${encodeURIComponent(value)}`); +} diff --git a/extensions/raycast/src/start-recording.tsx b/extensions/raycast/src/start-recording.tsx new file mode 100644 index 0000000000..ff30f0e224 --- /dev/null +++ b/extensions/raycast/src/start-recording.tsx @@ -0,0 +1,14 @@ +import { open } from "@raycast/api"; + +export default async function Command() { + const value = JSON.stringify({ + StartRecording: { + capture_mode: { Screen: "" }, + camera: null, + mic_label: null, + capture_system_audio: false, + mode: "Studio", + }, + }); + await open(`cap://action?value=${encodeURIComponent(value)}`); +} diff --git a/extensions/raycast/src/stop-recording.tsx b/extensions/raycast/src/stop-recording.tsx new file mode 100644 index 0000000000..768529658b --- /dev/null +++ b/extensions/raycast/src/stop-recording.tsx @@ -0,0 +1,6 @@ +import { open } from "@raycast/api"; + +export default async function Command() { + const value = JSON.stringify({ StopRecording: {} }); + await open(`cap://action?value=${encodeURIComponent(value)}`); +} diff --git a/extensions/raycast/src/switch-camera.tsx b/extensions/raycast/src/switch-camera.tsx new file mode 100644 index 0000000000..5113a7eed2 --- /dev/null +++ b/extensions/raycast/src/switch-camera.tsx @@ -0,0 +1,16 @@ +import { open } from "@raycast/api"; + +interface Props { + arguments: { + camera: string; + }; +} + +export default async function Command(props: Props) { + const value = JSON.stringify({ + SwitchCamera: { + camera: props.arguments.camera, + }, + }); + await open(`cap://action?value=${encodeURIComponent(value)}`); +} diff --git a/extensions/raycast/src/switch-microphone.tsx b/extensions/raycast/src/switch-microphone.tsx new file mode 100644 index 0000000000..d27eb331c3 --- /dev/null +++ b/extensions/raycast/src/switch-microphone.tsx @@ -0,0 +1,16 @@ +import { open } from "@raycast/api"; + +interface Props { + arguments: { + mic: string; + }; +} + +export default async function Command(props: Props) { + const value = JSON.stringify({ + SwitchMicrophone: { + mic_label: props.arguments.mic, + }, + }); + await open(`cap://action?value=${encodeURIComponent(value)}`); +} diff --git a/extensions/raycast/tsconfig.json b/extensions/raycast/tsconfig.json new file mode 100644 index 0000000000..37256dd20f --- /dev/null +++ b/extensions/raycast/tsconfig.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Node 20", + "include": ["src/**/*"], + "compilerOptions": { + "lib": ["ES2023"], + "module": "commonjs", + "target": "ES2022", + "strict": true, + "isolatedModules": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react-jsx", + "resolveJsonModule": true, + "declaration": true, + "outDir": "out" + } +}