From 0993a57f821f92b68fd1feb727c6c504f15d5657 Mon Sep 17 00:00:00 2001 From: Openclaw Date: Wed, 13 May 2026 18:03:54 +0000 Subject: [PATCH 1/3] Add Raycast deep link controls --- .../desktop/src-tauri/src/deeplink_actions.rs | 133 ++++++++++++++++-- apps/raycast/command-icon.png | Bin 0 -> 122 bytes apps/raycast/package.json | 75 ++++++++++ apps/raycast/src/cap.ts | 21 +++ apps/raycast/src/open-settings.tsx | 30 ++++ apps/raycast/src/pause-recording.ts | 4 + apps/raycast/src/resume-recording.ts | 4 + apps/raycast/src/set-camera.tsx | 32 +++++ apps/raycast/src/set-microphone.tsx | 32 +++++ apps/raycast/src/start-recording.tsx | 76 ++++++++++ apps/raycast/src/stop-recording.ts | 4 + apps/raycast/src/toggle-pause-recording.ts | 4 + apps/raycast/tsconfig.json | 15 ++ 13 files changed, 417 insertions(+), 13 deletions(-) create mode 100644 apps/raycast/command-icon.png create mode 100644 apps/raycast/package.json create mode 100644 apps/raycast/src/cap.ts create mode 100644 apps/raycast/src/open-settings.tsx create mode 100644 apps/raycast/src/pause-recording.ts create mode 100644 apps/raycast/src/resume-recording.ts create mode 100644 apps/raycast/src/set-camera.tsx create mode 100644 apps/raycast/src/set-microphone.tsx create mode 100644 apps/raycast/src/start-recording.tsx create mode 100644 apps/raycast/src/stop-recording.ts create mode 100644 apps/raycast/src/toggle-pause-recording.ts create mode 100644 apps/raycast/tsconfig.json diff --git a/apps/desktop/src-tauri/src/deeplink_actions.rs b/apps/desktop/src-tauri/src/deeplink_actions.rs index a1170284877..23e6818add2 100644 --- a/apps/desktop/src-tauri/src/deeplink_actions.rs +++ b/apps/desktop/src-tauri/src/deeplink_actions.rs @@ -26,6 +26,15 @@ pub enum DeepLinkAction { mode: RecordingMode, }, StopRecording, + PauseRecording, + ResumeRecording, + TogglePauseRecording, + ToggleMicrophone { + mic_label: Option, + }, + ToggleCamera { + camera: Option, + }, OpenEditor { project_path: PathBuf, }, @@ -89,19 +98,106 @@ impl TryFrom<&Url> for DeepLinkAction { } match url.domain() { + Some("action") => parse_action_url(url), Some(v) if v != "action" => Err(ActionParseFromUrlError::NotAction), _ => Err(ActionParseFromUrlError::Invalid), - }?; - - let params = url - .query_pairs() - .collect::>(); - let json_value = params - .get("value") - .ok_or(ActionParseFromUrlError::Invalid)?; - let action: Self = serde_json::from_str(json_value) - .map_err(|e| ActionParseFromUrlError::ParseFailed(e.to_string()))?; - Ok(action) + } + } +} + +fn parse_action_url(url: &Url) -> Result { + let params = url + .query_pairs() + .collect::>(); + + if let Some(action) = params.get("action").or_else(|| params.get("value")) { + return serde_json::from_str::(action) + .map_err(|e| ActionParseFromUrlError::ParseFailed(e.to_string())); + } + + let command = url + .path_segments() + .and_then(|mut segments| segments.next()) + .filter(|segment| !segment.is_empty()) + .ok_or(ActionParseFromUrlError::Invalid)?; + + match command { + "record" | "start-recording" | "start_recording" => Ok(DeepLinkAction::StartRecording { + capture_mode: parse_capture_mode(¶ms)?, + camera: parse_camera(¶ms)?, + mic_label: params.get("mic").or_else(|| params.get("mic_label")).map(|v| v.to_string()), + capture_system_audio: parse_bool_param(¶ms, "system_audio", false), + mode: parse_recording_mode(¶ms)?, + }), + "stop" | "stop-recording" | "stop_recording" => Ok(DeepLinkAction::StopRecording), + "pause" | "pause-recording" | "pause_recording" => Ok(DeepLinkAction::PauseRecording), + "resume" | "resume-recording" | "resume_recording" => Ok(DeepLinkAction::ResumeRecording), + "toggle-pause" | "toggle_pause" | "toggle-pause-recording" | "toggle_pause_recording" => { + Ok(DeepLinkAction::TogglePauseRecording) + } + "toggle-microphone" | "toggle_microphone" | "mic" => Ok(DeepLinkAction::ToggleMicrophone { + mic_label: params.get("mic").or_else(|| params.get("mic_label")).map(|v| v.to_string()), + }), + "toggle-camera" | "toggle_camera" | "camera" => Ok(DeepLinkAction::ToggleCamera { + camera: parse_camera(¶ms)?, + }), + "settings" | "open-settings" | "open_settings" => Ok(DeepLinkAction::OpenSettings { + page: params.get("page").map(|v| v.to_string()), + }), + _ => Err(ActionParseFromUrlError::Invalid), + } +} + +fn parse_capture_mode( + params: &std::collections::HashMap, std::borrow::Cow<'_, str>>, +) -> Result { + if let Some(display) = params.get("display").or_else(|| params.get("screen")) { + return Ok(CaptureMode::Screen(display.to_string())); + } + + if let Some(window) = params.get("window") { + return Ok(CaptureMode::Window(window.to_string())); + } + + Err(ActionParseFromUrlError::Invalid) +} + +fn parse_camera( + params: &std::collections::HashMap, std::borrow::Cow<'_, str>>, +) -> Result, ActionParseFromUrlError> { + if let Some(device_id) = params.get("camera_device_id").or_else(|| params.get("camera")) { + return Ok(Some(DeviceOrModelID::DeviceID(device_id.to_string()))); + } + + if let Some(model_id) = params.get("camera_model_id") { + return serde_json::from_value(serde_json::json!({ "ModelID": model_id.to_string() })) + .map(Some) + .map_err(|e| ActionParseFromUrlError::ParseFailed(e.to_string())); + } + + Ok(None) +} + +fn parse_bool_param( + params: &std::collections::HashMap, std::borrow::Cow<'_, str>>, + key: &str, + default: bool, +) -> bool { + params + .get(key) + .and_then(|value| value.parse::().ok()) + .unwrap_or(default) +} + +fn parse_recording_mode( + params: &std::collections::HashMap, std::borrow::Cow<'_, str>>, +) -> Result { + match params.get("mode").map(|v| v.as_ref()) { + Some("instant") => Ok(RecordingMode::Instant), + Some("studio") | None => Ok(RecordingMode::Studio), + Some(value) => Err(ActionParseFromUrlError::ParseFailed(format!( + "Unsupported recording mode: {value}" + ))), } } @@ -121,12 +217,12 @@ impl DeepLinkAction { crate::set_mic_input(state.clone(), mic_label).await?; let capture_target: ScreenCaptureTarget = match capture_mode { - CaptureMode::Screen(name) => cap_recording::screen_capture::list_displays() + CaptureMode::Screen(name) => cap_recording::sources::screen_capture::list_displays() .into_iter() .find(|(s, _)| s.name == name) .map(|(s, _)| ScreenCaptureTarget::Display { id: s.id }) .ok_or(format!("No screen with name \"{}\"", &name))?, - CaptureMode::Window(name) => cap_recording::screen_capture::list_windows() + CaptureMode::Window(name) => cap_recording::sources::screen_capture::list_windows() .into_iter() .find(|(w, _)| w.name == name) .map(|(w, _)| ScreenCaptureTarget::Window { id: w.id }) @@ -147,6 +243,17 @@ impl DeepLinkAction { DeepLinkAction::StopRecording => { crate::recording::stop_recording(app.clone(), app.state()).await } + DeepLinkAction::PauseRecording => crate::recording::pause_recording(app.state()).await, + DeepLinkAction::ResumeRecording => crate::recording::resume_recording(app.state()).await, + DeepLinkAction::TogglePauseRecording => { + crate::recording::toggle_pause_recording(app.state()).await + } + DeepLinkAction::ToggleMicrophone { mic_label } => { + crate::set_mic_input(app.state(), mic_label).await + } + DeepLinkAction::ToggleCamera { camera } => { + crate::set_camera_input(app.clone(), app.state(), camera, None).await + } DeepLinkAction::OpenEditor { project_path } => { crate::open_project_from_path(Path::new(&project_path), app.clone()) } diff --git a/apps/raycast/command-icon.png b/apps/raycast/command-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bcebc9dbce7d282435c3ac5be48c825298230c9c GIT binary patch literal 122 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=E}kxqAr-gYo@eA`VBla@xNiR? k_TfWD74MrffwDw^2hu4FjPJr7r-Rgby85}Sb4q9e0H$gd^#A|> literal 0 HcmV?d00001 diff --git a/apps/raycast/package.json b/apps/raycast/package.json new file mode 100644 index 00000000000..3a3415fb0e2 --- /dev/null +++ b/apps/raycast/package.json @@ -0,0 +1,75 @@ +{ + "$schema": "https://www.raycast.com/schemas/extension.json", + "name": "cap-raycast", + "title": "Cap", + "description": "Control Cap recordings with deeplinks.", + "icon": "command-icon.png", + "author": "Cap", + "license": "AGPL-3.0-or-later", + "commands": [ + { + "name": "start-recording", + "title": "Start Recording", + "description": "Start a Cap recording with a display or window target.", + "mode": "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": "toggle-pause-recording", + "title": "Toggle Pause Recording", + "description": "Toggle pause for the current Cap recording.", + "mode": "no-view" + }, + { + "name": "set-microphone", + "title": "Set Microphone", + "description": "Set or clear Cap's microphone input.", + "mode": "view" + }, + { + "name": "set-camera", + "title": "Set Camera", + "description": "Set or clear Cap's camera input.", + "mode": "view" + }, + { + "name": "open-settings", + "title": "Open Settings", + "description": "Open Cap settings.", + "mode": "view" + } + ], + "dependencies": { + "@raycast/api": "^1.83.2" + }, + "devDependencies": { + "@raycast/eslint-config": "^1.0.11", + "@types/node": "^20.17.6", + "eslint": "^8.57.1", + "prettier": "^3.3.3", + "typescript": "^5.6.3" + }, + "scripts": { + "build": "ray build -e dist", + "dev": "ray develop", + "lint": "ray lint", + "fix-lint": "ray lint --fix" + } +} diff --git a/apps/raycast/src/cap.ts b/apps/raycast/src/cap.ts new file mode 100644 index 00000000000..106358c280e --- /dev/null +++ b/apps/raycast/src/cap.ts @@ -0,0 +1,21 @@ +import { open, showHUD } from "@raycast/api"; + +const SCHEME = "cap-desktop://action"; + +export type QueryParams = Record; + +export async function openCapAction(action: string, params: QueryParams = {}) { + const url = new URL(`${SCHEME}/${action}`); + + for (const [key, value] of Object.entries(params)) { + if (value !== undefined && value !== "") + url.searchParams.set(key, String(value)); + } + + await open(url.toString()); +} + +export async function runNoViewAction(action: string, hudTitle: string) { + await openCapAction(action); + await showHUD(hudTitle); +} diff --git a/apps/raycast/src/open-settings.tsx b/apps/raycast/src/open-settings.tsx new file mode 100644 index 00000000000..bd52b4390e9 --- /dev/null +++ b/apps/raycast/src/open-settings.tsx @@ -0,0 +1,30 @@ +import { Action, ActionPanel, Form, showToast, Toast } from "@raycast/api"; +import { openCapAction } from "./cap"; + +type Values = { page?: string }; + +export default function Command() { + async function submit(values: Values) { + await openCapAction("settings", { page: values.page?.trim() }); + await showToast({ + style: Toast.Style.Success, + title: "Opening Cap settings", + }); + } + + return ( +
+ + + } + > + + + ); +} diff --git a/apps/raycast/src/pause-recording.ts b/apps/raycast/src/pause-recording.ts new file mode 100644 index 00000000000..a63e4f15d94 --- /dev/null +++ b/apps/raycast/src/pause-recording.ts @@ -0,0 +1,4 @@ +import { runNoViewAction } from "./cap"; +export default async function Command() { + await runNoViewAction("pause", "Pausing Cap recording"); +} diff --git a/apps/raycast/src/resume-recording.ts b/apps/raycast/src/resume-recording.ts new file mode 100644 index 00000000000..ff5499042f9 --- /dev/null +++ b/apps/raycast/src/resume-recording.ts @@ -0,0 +1,4 @@ +import { runNoViewAction } from "./cap"; +export default async function Command() { + await runNoViewAction("resume", "Resuming Cap recording"); +} diff --git a/apps/raycast/src/set-camera.tsx b/apps/raycast/src/set-camera.tsx new file mode 100644 index 00000000000..027b8476acc --- /dev/null +++ b/apps/raycast/src/set-camera.tsx @@ -0,0 +1,32 @@ +import { Action, ActionPanel, Form, showToast, Toast } from "@raycast/api"; +import { openCapAction } from "./cap"; + +type Values = { cameraDeviceId?: string }; + +export default function Command() { + async function submit(values: Values) { + await openCapAction("toggle-camera", { + camera_device_id: values.cameraDeviceId?.trim(), + }); + await showToast({ + style: Toast.Style.Success, + title: values.cameraDeviceId ? "Setting camera" : "Clearing camera", + }); + } + + return ( +
+ + + } + > + + + ); +} diff --git a/apps/raycast/src/set-microphone.tsx b/apps/raycast/src/set-microphone.tsx new file mode 100644 index 00000000000..629287efd1d --- /dev/null +++ b/apps/raycast/src/set-microphone.tsx @@ -0,0 +1,32 @@ +import { Action, ActionPanel, Form, showToast, Toast } from "@raycast/api"; +import { openCapAction } from "./cap"; + +type Values = { microphone?: string }; + +export default function Command() { + async function submit(values: Values) { + await openCapAction("toggle-microphone", { + mic: values.microphone?.trim(), + }); + await showToast({ + style: Toast.Style.Success, + title: values.microphone ? "Setting microphone" : "Clearing microphone", + }); + } + + return ( +
+ + + } + > + + + ); +} diff --git a/apps/raycast/src/start-recording.tsx b/apps/raycast/src/start-recording.tsx new file mode 100644 index 00000000000..141e92d11e3 --- /dev/null +++ b/apps/raycast/src/start-recording.tsx @@ -0,0 +1,76 @@ +import { Action, ActionPanel, Form, showToast, Toast } from "@raycast/api"; +import { openCapAction } from "./cap"; + +type Values = { + mode: "studio" | "instant"; + targetType: "display" | "window"; + targetName: string; + microphone?: string; + cameraDeviceId?: string; + systemAudio: boolean; +}; + +export default function Command() { + async function submit(values: Values) { + if (!values.targetName.trim()) { + await showToast({ + style: Toast.Style.Failure, + title: "Target name is required", + }); + return; + } + + await openCapAction("record", { + mode: values.mode, + [values.targetType]: values.targetName.trim(), + mic: values.microphone?.trim(), + camera_device_id: values.cameraDeviceId?.trim(), + system_audio: values.systemAudio, + }); + + await showToast({ + style: Toast.Style.Success, + title: "Starting Cap recording", + }); + } + + return ( +
+ + + } + > + + + + + + + + + + + + + + ); +} diff --git a/apps/raycast/src/stop-recording.ts b/apps/raycast/src/stop-recording.ts new file mode 100644 index 00000000000..7afa7584134 --- /dev/null +++ b/apps/raycast/src/stop-recording.ts @@ -0,0 +1,4 @@ +import { runNoViewAction } from "./cap"; +export default async function Command() { + await runNoViewAction("stop", "Stopping Cap recording"); +} diff --git a/apps/raycast/src/toggle-pause-recording.ts b/apps/raycast/src/toggle-pause-recording.ts new file mode 100644 index 00000000000..3b752edd784 --- /dev/null +++ b/apps/raycast/src/toggle-pause-recording.ts @@ -0,0 +1,4 @@ +import { runNoViewAction } from "./cap"; +export default async function Command() { + await runNoViewAction("toggle-pause", "Toggling Cap pause"); +} diff --git a/apps/raycast/tsconfig.json b/apps/raycast/tsconfig.json new file mode 100644 index 00000000000..ebccd5359e0 --- /dev/null +++ b/apps/raycast/tsconfig.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "lib": ["ES2023"], + "module": "commonjs", + "target": "ES2022", + "strict": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true + } +} From c9a5de65a8510c3b21027a8ae3a7af14a6e5e440 Mon Sep 17 00:00:00 2001 From: Openclaw Date: Wed, 13 May 2026 19:15:42 +0000 Subject: [PATCH 2/3] Fix Raycast extension validation --- apps/raycast/.eslintrc.json | 3 ++ apps/raycast/assets/command-icon.png | Bin 0 -> 3647 bytes apps/raycast/command-icon.png | Bin 122 -> 0 bytes apps/raycast/package.json | 2 +- apps/raycast/raycast-env.d.ts | 52 +++++++++++++++++++++++++++ apps/raycast/tsconfig.json | 6 ++-- 6 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 apps/raycast/.eslintrc.json create mode 100644 apps/raycast/assets/command-icon.png delete mode 100644 apps/raycast/command-icon.png create mode 100644 apps/raycast/raycast-env.d.ts diff --git a/apps/raycast/.eslintrc.json b/apps/raycast/.eslintrc.json new file mode 100644 index 00000000000..0373a3c4747 --- /dev/null +++ b/apps/raycast/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": ["@raycast"] +} diff --git a/apps/raycast/assets/command-icon.png b/apps/raycast/assets/command-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a3749e041118dd5fb72939d9b4af776530045906 GIT binary patch literal 3647 zcmd^C`CAj`8hz(uAQNOsz#<^B$*NJqqe8GW83YB?0(C`-I=GdpC zb5na$05A^=4W15wxLX8-33usAxzP(?VG} zf~M|VrdbAaJq*qt?{X=&z2puP=W5u0O?1uyV&PMxHv%I&oP;c=l+A*uVu&iZVCfJ@ z91a7~r50Ya04*27po;Ev$BnE#&LWYV!d=9EnAj)Fsf;9)^)xTQyTPgKTtLxIYo

ziMhPnL|MycgGIDrLeetFaPUy2qou9m6uzEtVXl@vtUP)LCu1EQskQA<^X6lNThp=G zt-Huk;qdPYP5P|DRVN5$Vt@nttFmeWP6{1Ooj0Y<+3*s~+5;iCXFe+l2a{u(^yGGx z1W^LNPuP*K6U;C%Q#?$ys(m4i6l4AT*){Ep79hoNKR@=ivR;mpo-3CzeFgbx1Y>bm z2{}G~&DOB3YAaTNzT@m^ zdZ2HAmbvM=R!d9ioa!wHdh06S+p@3pC==S zBIcNgX4alY3WqEw#V4Nd^}-&bE*J2sRr8*NKx9UnWm7&-k_IP*3}YknWUMV>V!2ni zk%G8^7xCf8;~MOuH6$ErEZ-XovO>70%7fK?8Gf6`2zT@AM?;+vnYuF9@9i@J03k*-wc zq}wF#zeD>>d;W*<3{TBpE5aZ?UZ=VKVu)pSMr)xv;OxU(C$G4@Pp{RuyP4!Z-UR~4 zoGMqSyn^zyqXyNH>eE($R0;K#gF9v|dqw|`BHS8BV$M($Dlb#n?H{5G8MKL1ALc-c zt*x1+jy^YAbaCl|(2`l&VNfKP-FfD_eBINoy9;Fd%rJ7SE37P*_tXfn>uOUrciD)Z z6!Rlu2Iv&(E%6k_x6UT3-feCg<@lj z9OBB%Ol;6?BfKJ@X&d4EX@?#*g%fA<^J8>=t_l%G&I!jtsUF%HO|gPxPZU+yMsFaz zLO{Hc0Nbe`&L*hw(tf(uoAgG^-e5RXu6gN(E(^fS6BX=f(>V(xGeCh0fs-bMnUKw1 z-%IDtU>@SIe+w{DPUBWId*B5b5xS54jcunuWF7%=oR}36iV>w+%a*~EEk>r!=YvB8 zh|OIfVRtuF#*F@UOge8XOit3!<$*`BYme#mAoe_nL7#@x-SWCVBJ^k~6C%MVyKv*U z_-5Za-G95A;T|Wg{^7=w+_J^95VJqc;eH15GOBx@0D`N0?@kIF-B7X^sk(jyvy2#R zhf7Z98+VfF>-i@7b9wc=_ncxuwE+Fo5}0+ThoGope_f_Q2=(S+FePcHoS;5!?|Xm) zdq&=GpJu%L_3P(T4CG7Lw8-^u_OvoG3zmx;k8+A$Fa1}3qQkfl;OCUEi%410lh2uDbXX44^?*)9Di|@i z(t8cxSDRLRs*lfXsI*HjzUIoROEW;Squ8p_+Z~B?UlPE~pPj?pr0J|w|I)^qkOk+L zos1uv#wXXv=o>!VL<4!U#ka^h`ASik^IjX_h>B_o~{N%Lgg3VC@vk-T~fac0W)U=(&L&wH5boR9=z`2s-H{2 z_WTu#&dUJ~L15N;fOoBM!=r8sQdm6%+$y^3l2GKvOe{3^{!M2veH96Z#y;i@_d<$< z(I-C+7|Q_UG zxDhi&r0tPl1z%^H|B*(GCFU@>y!){S*tp`1qewfRV2t^g!Z?c_g{atxAj!B; diff --git a/apps/raycast/package.json b/apps/raycast/package.json index 3a3415fb0e2..bf065e9c4ba 100644 --- a/apps/raycast/package.json +++ b/apps/raycast/package.json @@ -5,7 +5,7 @@ "description": "Control Cap recordings with deeplinks.", "icon": "command-icon.png", "author": "Cap", - "license": "AGPL-3.0-or-later", + "license": "MIT", "commands": [ { "name": "start-recording", diff --git a/apps/raycast/raycast-env.d.ts b/apps/raycast/raycast-env.d.ts new file mode 100644 index 00000000000..98de326395a --- /dev/null +++ b/apps/raycast/raycast-env.d.ts @@ -0,0 +1,52 @@ +/// + +/* 🚧 🚧 🚧 + * This file is auto-generated from the extension's manifest. + * Do not modify manually. Instead, update the `package.json` file. + * 🚧 🚧 🚧 */ + +/* eslint-disable @typescript-eslint/ban-types */ + +type ExtensionPreferences = {} + +/** Preferences accessible in all the extension's commands */ +declare type Preferences = ExtensionPreferences + +declare namespace Preferences { + /** Preferences accessible in the `start-recording` command */ + export type StartRecording = ExtensionPreferences & {} + /** Preferences accessible in the `stop-recording` command */ + export type StopRecording = ExtensionPreferences & {} + /** Preferences accessible in the `pause-recording` command */ + export type PauseRecording = ExtensionPreferences & {} + /** Preferences accessible in the `resume-recording` command */ + export type ResumeRecording = ExtensionPreferences & {} + /** Preferences accessible in the `toggle-pause-recording` command */ + export type TogglePauseRecording = ExtensionPreferences & {} + /** Preferences accessible in the `set-microphone` command */ + export type SetMicrophone = ExtensionPreferences & {} + /** Preferences accessible in the `set-camera` command */ + export type SetCamera = ExtensionPreferences & {} + /** Preferences accessible in the `open-settings` command */ + export type OpenSettings = ExtensionPreferences & {} +} + +declare namespace Arguments { + /** Arguments passed to the `start-recording` command */ + export type StartRecording = {} + /** Arguments passed to the `stop-recording` command */ + export type StopRecording = {} + /** Arguments passed to the `pause-recording` command */ + export type PauseRecording = {} + /** Arguments passed to the `resume-recording` command */ + export type ResumeRecording = {} + /** Arguments passed to the `toggle-pause-recording` command */ + export type TogglePauseRecording = {} + /** Arguments passed to the `set-microphone` command */ + export type SetMicrophone = {} + /** Arguments passed to the `set-camera` command */ + export type SetCamera = {} + /** Arguments passed to the `open-settings` command */ + export type OpenSettings = {} +} + diff --git a/apps/raycast/tsconfig.json b/apps/raycast/tsconfig.json index ebccd5359e0..106063101e4 100644 --- a/apps/raycast/tsconfig.json +++ b/apps/raycast/tsconfig.json @@ -1,11 +1,13 @@ { "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { - "lib": ["ES2023"], + "lib": [ + "ES2023" + ], "module": "commonjs", "target": "ES2022", "strict": true, - "jsx": "react-jsx", + "jsx": "preserve", "moduleResolution": "node", "esModuleInterop": true, "skipLibCheck": true, From f0a97414bcc7338a851daf4201485b61bec59f6c Mon Sep 17 00:00:00 2001 From: Openclaw Date: Wed, 13 May 2026 21:51:03 +0000 Subject: [PATCH 3/3] Fix Raycast extension validation --- .../desktop/src-tauri/src/deeplink_actions.rs | 43 ++++-- apps/raycast/.eslintrc.json | 2 +- apps/raycast/package.json | 146 +++++++++--------- apps/raycast/tsconfig.json | 28 ++-- 4 files changed, 116 insertions(+), 103 deletions(-) diff --git a/apps/desktop/src-tauri/src/deeplink_actions.rs b/apps/desktop/src-tauri/src/deeplink_actions.rs index 23e6818add2..b0500d14ad7 100644 --- a/apps/desktop/src-tauri/src/deeplink_actions.rs +++ b/apps/desktop/src-tauri/src/deeplink_actions.rs @@ -125,7 +125,10 @@ fn parse_action_url(url: &Url) -> Result Ok(DeepLinkAction::StartRecording { capture_mode: parse_capture_mode(¶ms)?, camera: parse_camera(¶ms)?, - mic_label: params.get("mic").or_else(|| params.get("mic_label")).map(|v| v.to_string()), + mic_label: params + .get("mic") + .or_else(|| params.get("mic_label")) + .map(|v| v.to_string()), capture_system_audio: parse_bool_param(¶ms, "system_audio", false), mode: parse_recording_mode(¶ms)?, }), @@ -136,7 +139,10 @@ fn parse_action_url(url: &Url) -> Result Ok(DeepLinkAction::ToggleMicrophone { - mic_label: params.get("mic").or_else(|| params.get("mic_label")).map(|v| v.to_string()), + mic_label: params + .get("mic") + .or_else(|| params.get("mic_label")) + .map(|v| v.to_string()), }), "toggle-camera" | "toggle_camera" | "camera" => Ok(DeepLinkAction::ToggleCamera { camera: parse_camera(¶ms)?, @@ -165,7 +171,10 @@ fn parse_capture_mode( fn parse_camera( params: &std::collections::HashMap, std::borrow::Cow<'_, str>>, ) -> Result, ActionParseFromUrlError> { - if let Some(device_id) = params.get("camera_device_id").or_else(|| params.get("camera")) { + if let Some(device_id) = params + .get("camera_device_id") + .or_else(|| params.get("camera")) + { return Ok(Some(DeviceOrModelID::DeviceID(device_id.to_string()))); } @@ -217,16 +226,20 @@ impl DeepLinkAction { crate::set_mic_input(state.clone(), mic_label).await?; let capture_target: ScreenCaptureTarget = match capture_mode { - CaptureMode::Screen(name) => cap_recording::sources::screen_capture::list_displays() - .into_iter() - .find(|(s, _)| s.name == name) - .map(|(s, _)| ScreenCaptureTarget::Display { id: s.id }) - .ok_or(format!("No screen with name \"{}\"", &name))?, - CaptureMode::Window(name) => cap_recording::sources::screen_capture::list_windows() - .into_iter() - .find(|(w, _)| w.name == name) - .map(|(w, _)| ScreenCaptureTarget::Window { id: w.id }) - .ok_or(format!("No window with name \"{}\"", &name))?, + CaptureMode::Screen(name) => { + cap_recording::sources::screen_capture::list_displays() + .into_iter() + .find(|(s, _)| s.name == name) + .map(|(s, _)| ScreenCaptureTarget::Display { id: s.id }) + .ok_or(format!("No screen with name \"{}\"", &name))? + } + CaptureMode::Window(name) => { + cap_recording::sources::screen_capture::list_windows() + .into_iter() + .find(|(w, _)| w.name == name) + .map(|(w, _)| ScreenCaptureTarget::Window { id: w.id }) + .ok_or(format!("No window with name \"{}\"", &name))? + } }; let inputs = StartRecordingInputs { @@ -244,7 +257,9 @@ impl DeepLinkAction { crate::recording::stop_recording(app.clone(), app.state()).await } DeepLinkAction::PauseRecording => crate::recording::pause_recording(app.state()).await, - DeepLinkAction::ResumeRecording => crate::recording::resume_recording(app.state()).await, + DeepLinkAction::ResumeRecording => { + crate::recording::resume_recording(app.state()).await + } DeepLinkAction::TogglePauseRecording => { crate::recording::toggle_pause_recording(app.state()).await } diff --git a/apps/raycast/.eslintrc.json b/apps/raycast/.eslintrc.json index 0373a3c4747..bcf78d698a6 100644 --- a/apps/raycast/.eslintrc.json +++ b/apps/raycast/.eslintrc.json @@ -1,3 +1,3 @@ { - "extends": ["@raycast"] + "extends": ["@raycast"] } diff --git a/apps/raycast/package.json b/apps/raycast/package.json index bf065e9c4ba..cc0682eb052 100644 --- a/apps/raycast/package.json +++ b/apps/raycast/package.json @@ -1,75 +1,75 @@ { - "$schema": "https://www.raycast.com/schemas/extension.json", - "name": "cap-raycast", - "title": "Cap", - "description": "Control Cap recordings with deeplinks.", - "icon": "command-icon.png", - "author": "Cap", - "license": "MIT", - "commands": [ - { - "name": "start-recording", - "title": "Start Recording", - "description": "Start a Cap recording with a display or window target.", - "mode": "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": "toggle-pause-recording", - "title": "Toggle Pause Recording", - "description": "Toggle pause for the current Cap recording.", - "mode": "no-view" - }, - { - "name": "set-microphone", - "title": "Set Microphone", - "description": "Set or clear Cap's microphone input.", - "mode": "view" - }, - { - "name": "set-camera", - "title": "Set Camera", - "description": "Set or clear Cap's camera input.", - "mode": "view" - }, - { - "name": "open-settings", - "title": "Open Settings", - "description": "Open Cap settings.", - "mode": "view" - } - ], - "dependencies": { - "@raycast/api": "^1.83.2" - }, - "devDependencies": { - "@raycast/eslint-config": "^1.0.11", - "@types/node": "^20.17.6", - "eslint": "^8.57.1", - "prettier": "^3.3.3", - "typescript": "^5.6.3" - }, - "scripts": { - "build": "ray build -e dist", - "dev": "ray develop", - "lint": "ray lint", - "fix-lint": "ray lint --fix" - } + "$schema": "https://www.raycast.com/schemas/extension.json", + "name": "cap-raycast", + "title": "Cap", + "description": "Control Cap recordings with deeplinks.", + "icon": "command-icon.png", + "author": "Cap", + "license": "MIT", + "commands": [ + { + "name": "start-recording", + "title": "Start Recording", + "description": "Start a Cap recording with a display or window target.", + "mode": "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": "toggle-pause-recording", + "title": "Toggle Pause Recording", + "description": "Toggle pause for the current Cap recording.", + "mode": "no-view" + }, + { + "name": "set-microphone", + "title": "Set Microphone", + "description": "Set or clear Cap's microphone input.", + "mode": "view" + }, + { + "name": "set-camera", + "title": "Set Camera", + "description": "Set or clear Cap's camera input.", + "mode": "view" + }, + { + "name": "open-settings", + "title": "Open Settings", + "description": "Open Cap settings.", + "mode": "view" + } + ], + "dependencies": { + "@raycast/api": "^1.83.2" + }, + "devDependencies": { + "@raycast/eslint-config": "^1.0.11", + "@types/node": "^20.17.6", + "eslint": "^8.57.1", + "prettier": "^3.3.3", + "typescript": "^5.6.3" + }, + "scripts": { + "build": "ray build -e dist", + "dev": "ray develop", + "lint": "ray lint", + "fix-lint": "ray lint --fix" + } } diff --git a/apps/raycast/tsconfig.json b/apps/raycast/tsconfig.json index 106063101e4..89da8c37ff9 100644 --- a/apps/raycast/tsconfig.json +++ b/apps/raycast/tsconfig.json @@ -1,17 +1,15 @@ { - "$schema": "https://json.schemastore.org/tsconfig", - "compilerOptions": { - "lib": [ - "ES2023" - ], - "module": "commonjs", - "target": "ES2022", - "strict": true, - "jsx": "preserve", - "moduleResolution": "node", - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true - } + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "lib": ["ES2023"], + "module": "commonjs", + "target": "ES2022", + "strict": true, + "jsx": "preserve", + "moduleResolution": "node", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true + } }