diff --git a/apps/desktop/src/lib/deeplinks.ts b/apps/desktop/src/lib/deeplinks.ts new file mode 100644 index 0000000000..4802e5ec49 --- /dev/null +++ b/apps/desktop/src/lib/deeplinks.ts @@ -0,0 +1,61 @@ +import { onOpenUrl } from "@tauri-apps/plugin-deep-link"; +import { invoke } from "@tauri-apps/api/core"; + +export type DeeplinkAction = + | "start-recording" + | "stop-recording" + | "pause-recording" + | "resume-recording" + | "restart-recording" + | "switch-microphone" + | "switch-camera"; + +export function initDeeplinks() { + onOpenUrl((urls) => { + for (const url of urls) { + handleDeeplink(url); + } + }); +} + +export async function handleDeeplink(url: string) { + try { + const parsed = new URL(url); + if (parsed.protocol !== "cap:") return; + + const action = parsed.hostname as DeeplinkAction; + const params = Object.fromEntries(parsed.searchParams.entries()); + + switch (action) { + case "start-recording": + await invoke("start_recording", params); + break; + case "stop-recording": + await invoke("stop_recording", {}); + break; + case "pause-recording": + await invoke("pause_recording", {}); + break; + case "resume-recording": + await invoke("resume_recording", {}); + break; + case "restart-recording": + await invoke("restart_recording", {}); + break; + case "switch-microphone": { + const deviceId = params["device-id"] ?? null; + await invoke("set_microphone", { deviceId }); + break; + } + case "switch-camera": { + const deviceId = params["device-id"] ?? null; + await invoke("set_camera", { deviceId }); + break; + } + default: + console.warn("Unknown deeplink action:", action); + } + } catch (e) { + console.error("Failed to handle deeplink:", url, e); + } +} diff --git a/apps/raycast-extension/package.json b/apps/raycast-extension/package.json new file mode 100644 index 0000000000..7c05cfd451 --- /dev/null +++ b/apps/raycast-extension/package.json @@ -0,0 +1,60 @@ +{ + "$schema": "https://www.raycast.com/schemas/extension.json", + "name": "cap", + "title": "Cap", + "description": "Control Cap screen recordings from Raycast", + "icon": "cap-icon.png", + "author": "cap", + "categories": ["Applications", "Productivity"], + "license": "MIT", + "commands": [ + { + "name": "start-recording", + "title": "Start Recording", + "description": "Start a new Cap screen recording", + "mode": "no-view" + }, + { + "name": "stop-recording", + "title": "Stop Recording", + "description": "Stop the current Cap screen recording", + "mode": "no-view" + }, + { + "name": "pause-recording", + "title": "Pause Recording", + "description": "Pause the current Cap screen recording", + "mode": "no-view" + }, + { + "name": "resume-recording", + "title": "Resume Recording", + "description": "Resume the paused Cap screen recording", + "mode": "no-view" + }, + { + "name": "restart-recording", + "title": "Restart Recording", + "description": "Restart the current Cap screen recording", + "mode": "no-view" + } + ], + "dependencies": { + "@raycast/api": "^1.79.0" + }, + "devDependencies": { + "@raycast/utils": "^1.17.0", + "@types/node": "20.8.10", + "@types/react": "18.3.3", + "eslint": "^8.57.0", + "prettier": "^3.3.3", + "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" + } +} diff --git a/apps/raycast-extension/src/pause-recording.ts b/apps/raycast-extension/src/pause-recording.ts new file mode 100644 index 0000000000..648324e860 --- /dev/null +++ b/apps/raycast-extension/src/pause-recording.ts @@ -0,0 +1,6 @@ +import { open, showHUD } from "@raycast/api"; + +export default async function Command() { + await open("cap://pause-recording"); + await showHUD("⏸ Pausing Cap recording…"); +} diff --git a/apps/raycast-extension/src/restart-recording.ts b/apps/raycast-extension/src/restart-recording.ts new file mode 100644 index 0000000000..75642a0570 --- /dev/null +++ b/apps/raycast-extension/src/restart-recording.ts @@ -0,0 +1,6 @@ +import { open, showHUD } from "@raycast/api"; + +export default async function Command() { + await open("cap://restart-recording"); + await showHUD("🔄 Restarting Cap recording…"); +} diff --git a/apps/raycast-extension/src/resume-recording.ts b/apps/raycast-extension/src/resume-recording.ts new file mode 100644 index 0000000000..fd0942dd56 --- /dev/null +++ b/apps/raycast-extension/src/resume-recording.ts @@ -0,0 +1,6 @@ +import { open, showHUD } from "@raycast/api"; + +export default async function Command() { + await open("cap://resume-recording"); + await showHUD("▶ Resuming Cap recording…"); +} diff --git a/apps/raycast-extension/src/start-recording.ts b/apps/raycast-extension/src/start-recording.ts new file mode 100644 index 0000000000..9c0560397b --- /dev/null +++ b/apps/raycast-extension/src/start-recording.ts @@ -0,0 +1,6 @@ +import { open, showHUD } from "@raycast/api"; + +export default async function Command() { + await open("cap://start-recording"); + await showHUD("▶ Starting Cap recording…"); +} diff --git a/apps/raycast-extension/src/stop-recording.ts b/apps/raycast-extension/src/stop-recording.ts new file mode 100644 index 0000000000..fa64a7778c --- /dev/null +++ b/apps/raycast-extension/src/stop-recording.ts @@ -0,0 +1,6 @@ +import { open, showHUD } from "@raycast/api"; + +export default async function Command() { + await open("cap://stop-recording"); + await showHUD("⏹ Stopping Cap recording…"); +} diff --git a/apps/raycast-extension/tsconfig.json b/apps/raycast-extension/tsconfig.json new file mode 100644 index 0000000000..b7f27ecc9a --- /dev/null +++ b/apps/raycast-extension/tsconfig.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://www.raycast.com/schemas/extension.json", + "compilerOptions": { + "strict": true, + "module": "CommonJS", + "target": "ES2020", + "jsx": "react-jsx", + "jsxImportSource": "react", + "lib": ["ES2020"], + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "isolatedModules": true + } +}