From 3bd73e648520ecf424af277356e46c442a182209 Mon Sep 17 00:00:00 2001 From: Wasim Date: Wed, 29 Jul 2026 12:58:48 +0530 Subject: [PATCH 1/7] feat: extend deeplinks with pause/resume/toggle/restart/mic/camera/screenshot actions --- .../desktop/src-tauri/src/deeplink_actions.rs | 110 +++++++++++++++++- 1 file changed, 105 insertions(+), 5 deletions(-) diff --git a/apps/desktop/src-tauri/src/deeplink_actions.rs b/apps/desktop/src-tauri/src/deeplink_actions.rs index 6eb7f4c893..93efe83be5 100644 --- a/apps/desktop/src-tauri/src/deeplink_actions.rs +++ b/apps/desktop/src-tauri/src/deeplink_actions.rs @@ -46,10 +46,17 @@ pub enum DeepLinkAction { mode: RecordingMode, }, StopRecording, - #[cfg(debug_assertions)] PauseRecording, - #[cfg(debug_assertions)] ResumeRecording, + TogglePauseRecording, + RestartRecording, + SwitchMicrophone { + mic_label: Option, + }, + SwitchCamera { + camera: Option, + }, + TakeScreenshot, #[cfg(debug_assertions)] OpenCamera { camera: DeviceOrModelID, @@ -244,14 +251,42 @@ impl DeepLinkAction { DeepLinkAction::StopRecording => { crate::recording::stop_recording(app.clone(), app.state()).await } - #[cfg(debug_assertions)] DeepLinkAction::PauseRecording => { crate::recording::pause_recording(app.clone(), app.state()).await } - #[cfg(debug_assertions)] DeepLinkAction::ResumeRecording => { crate::recording::resume_recording(app.clone(), app.state()).await } + DeepLinkAction::TogglePauseRecording => { + crate::recording::toggle_pause_recording(app.clone(), app.state()).await + } + DeepLinkAction::RestartRecording => { + crate::recording::restart_recording(app.clone(), app.state()).await + } + DeepLinkAction::SwitchMicrophone { mic_label } => { + crate::set_mic_input(app.state(), mic_label).await + } + DeepLinkAction::SwitchCamera { camera } => { + crate::set_camera_input( + app.clone(), + app.state::>(), + camera, + None, + ) + .await?; + Ok(()) + } + DeepLinkAction::TakeScreenshot => { + let state = app.state::>(); + let capture_target = ScreenCaptureTarget::Display { + id: cap_recording::screen_capture::list_displays() + .into_iter() + .next() + .map(|(s, _)| s.id) + .ok_or("No display available".to_string())?, + }; + crate::recording::take_screenshot(app.clone(), capture_target).await + } #[cfg(debug_assertions)] DeepLinkAction::OpenCamera { camera } => { crate::set_camera_input( @@ -358,7 +393,6 @@ mod tests { ); } - #[cfg(debug_assertions)] #[test] fn parses_pause_and_resume_action_urls() { let pause_url = Url::parse("cap-desktop://action?value=%22pause_recording%22").unwrap(); @@ -374,6 +408,72 @@ mod tests { ); } + #[test] + fn parses_toggle_pause_action_url() { + let url = Url::parse("cap-desktop://action?value=%22toggle_pause_recording%22").unwrap(); + + assert_eq!( + DeepLinkAction::try_from(&url), + Ok(DeepLinkAction::TogglePauseRecording) + ); + } + + #[test] + fn parses_restart_action_url() { + let url = Url::parse("cap-desktop://action?value=%22restart_recording%22").unwrap(); + + assert_eq!( + DeepLinkAction::try_from(&url), + Ok(DeepLinkAction::RestartRecording) + ); + } + + #[test] + fn parses_switch_microphone_action_url() { + let value = serde_json::json!({ + "switch_microphone": { + "mic_label": "Built-in Microphone" + } + }) + .to_string(); + let url = Url::parse_with_params("cap-desktop://action", &[("value", value)]).unwrap(); + + assert_eq!( + DeepLinkAction::try_from(&url), + Ok(DeepLinkAction::SwitchMicrophone { + mic_label: Some("Built-in Microphone".to_string()) + }) + ); + } + + #[test] + fn parses_switch_camera_action_url() { + let value = serde_json::json!({ + "switch_camera": { + "camera": { "DeviceID": "camera-1" } + } + }) + .to_string(); + let url = Url::parse_with_params("cap-desktop://action", &[("value", value)]).unwrap(); + + assert_eq!( + DeepLinkAction::try_from(&url), + Ok(DeepLinkAction::SwitchCamera { + camera: Some(DeviceOrModelID::DeviceID("camera-1".to_string())) + }) + ); + } + + #[test] + fn parses_take_screenshot_action_url() { + let url = Url::parse("cap-desktop://action?value=%22take_screenshot%22").unwrap(); + + assert_eq!( + DeepLinkAction::try_from(&url), + Ok(DeepLinkAction::TakeScreenshot) + ); + } + #[cfg(debug_assertions)] #[test] fn parses_area_recording_action_url() { From f35fe4100e34b252ec0176b32192af87603f427d Mon Sep 17 00:00:00 2001 From: Wasim Date: Wed, 29 Jul 2026 13:10:29 +0530 Subject: [PATCH 2/7] fix: use primary display for screenshot target, prefix unused state --- apps/desktop/src-tauri/src/deeplink_actions.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/desktop/src-tauri/src/deeplink_actions.rs b/apps/desktop/src-tauri/src/deeplink_actions.rs index 93efe83be5..6ea3f2b9da 100644 --- a/apps/desktop/src-tauri/src/deeplink_actions.rs +++ b/apps/desktop/src-tauri/src/deeplink_actions.rs @@ -277,14 +277,14 @@ impl DeepLinkAction { Ok(()) } DeepLinkAction::TakeScreenshot => { - let state = app.state::>(); - let capture_target = ScreenCaptureTarget::Display { - id: cap_recording::screen_capture::list_displays() - .into_iter() - .next() - .map(|(s, _)| s.id) - .ok_or("No display available".to_string())?, - }; + let primary_id = scap_targets::Display::primary().id(); + let mut displays = cap_recording::screen_capture::list_displays().into_iter(); + let id = displays + .find(|(s, _)| s.id == primary_id) + .or_else(|| displays.next()) + .map(|(s, _)| s.id) + .ok_or_else(|| "No display available".to_string())?; + let capture_target = ScreenCaptureTarget::Display { id }; crate::recording::take_screenshot(app.clone(), capture_target).await } #[cfg(debug_assertions)] From ef79d2dd2174fe2cde17a335a08564a131913b9e Mon Sep 17 00:00:00 2001 From: Wasim Date: Wed, 29 Jul 2026 18:41:53 +0530 Subject: [PATCH 3/7] fix(deeplinks): handle iterator exhaustion in screenshot and run cargo fmt --- apps/desktop/src-tauri/src/deeplink_actions.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/apps/desktop/src-tauri/src/deeplink_actions.rs b/apps/desktop/src-tauri/src/deeplink_actions.rs index 6ea3f2b9da..0963b8dba3 100644 --- a/apps/desktop/src-tauri/src/deeplink_actions.rs +++ b/apps/desktop/src-tauri/src/deeplink_actions.rs @@ -267,21 +267,17 @@ impl DeepLinkAction { crate::set_mic_input(app.state(), mic_label).await } DeepLinkAction::SwitchCamera { camera } => { - crate::set_camera_input( - app.clone(), - app.state::>(), - camera, - None, - ) - .await?; + crate::set_camera_input(app.clone(), app.state::>(), camera, None) + .await?; Ok(()) } DeepLinkAction::TakeScreenshot => { let primary_id = scap_targets::Display::primary().id(); - let mut displays = cap_recording::screen_capture::list_displays().into_iter(); + let displays = cap_recording::screen_capture::list_displays(); let id = displays + .iter() .find(|(s, _)| s.id == primary_id) - .or_else(|| displays.next()) + .or_else(|| displays.first()) .map(|(s, _)| s.id) .ok_or_else(|| "No display available".to_string())?; let capture_target = ScreenCaptureTarget::Display { id }; From 5fb6e9a2daae28c2e4a30ae3d25e4b3c313679d5 Mon Sep 17 00:00:00 2001 From: Wasim Date: Wed, 29 Jul 2026 18:55:12 +0530 Subject: [PATCH 4/7] refactor: simplify display lookup per review --- apps/desktop/src-tauri/src/deeplink_actions.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/apps/desktop/src-tauri/src/deeplink_actions.rs b/apps/desktop/src-tauri/src/deeplink_actions.rs index 0963b8dba3..0a85e08bc1 100644 --- a/apps/desktop/src-tauri/src/deeplink_actions.rs +++ b/apps/desktop/src-tauri/src/deeplink_actions.rs @@ -272,14 +272,7 @@ impl DeepLinkAction { Ok(()) } DeepLinkAction::TakeScreenshot => { - let primary_id = scap_targets::Display::primary().id(); - let displays = cap_recording::screen_capture::list_displays(); - let id = displays - .iter() - .find(|(s, _)| s.id == primary_id) - .or_else(|| displays.first()) - .map(|(s, _)| s.id) - .ok_or_else(|| "No display available".to_string())?; + let id = scap_targets::Display::primary().id(); let capture_target = ScreenCaptureTarget::Display { id }; crate::recording::take_screenshot(app.clone(), capture_target).await } From f43c2180f425034963cfa68e51209881952eb2f2 Mon Sep 17 00:00:00 2001 From: Wasim Date: Thu, 30 Jul 2026 09:47:44 +0530 Subject: [PATCH 5/7] fix: Resolve TS error in sdk-recorder --- packages/sdk-recorder/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sdk-recorder/src/index.ts b/packages/sdk-recorder/src/index.ts index 0f7eb10e1f..a468dcf8ce 100644 --- a/packages/sdk-recorder/src/index.ts +++ b/packages/sdk-recorder/src/index.ts @@ -82,9 +82,9 @@ export class CapRecorder { this.listeners.set(event, new Set()); } const set = this.listeners.get(event); - if (set) set.add(handler as EventHandler); + if (set) set.add(handler as unknown as EventHandler); return () => { - this.listeners.get(event)?.delete(handler); + this.listeners.get(event)?.delete(handler as unknown as EventHandler); }; } From 34d607ea6b3270b4fd92e825011e277c20f5ca06 Mon Sep 17 00:00:00 2001 From: Wasim Date: Thu, 30 Jul 2026 09:55:55 +0530 Subject: [PATCH 6/7] refactor: Hoist type cast as suggested --- packages/sdk-recorder/src/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/sdk-recorder/src/index.ts b/packages/sdk-recorder/src/index.ts index a468dcf8ce..72757aeba6 100644 --- a/packages/sdk-recorder/src/index.ts +++ b/packages/sdk-recorder/src/index.ts @@ -82,9 +82,10 @@ export class CapRecorder { this.listeners.set(event, new Set()); } const set = this.listeners.get(event); - if (set) set.add(handler as unknown as EventHandler); + const castHandler = handler as unknown as EventHandler; + if (set) set.add(castHandler); return () => { - this.listeners.get(event)?.delete(handler as unknown as EventHandler); + this.listeners.get(event)?.delete(castHandler); }; } From fcca574311920e9b564410960de73da7ade1c77d Mon Sep 17 00:00:00 2001 From: Wasim Date: Thu, 30 Jul 2026 21:34:59 +0530 Subject: [PATCH 7/7] Fix type mismatches in deeplink actions --- apps/desktop/src-tauri/src/deeplink_actions.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src-tauri/src/deeplink_actions.rs b/apps/desktop/src-tauri/src/deeplink_actions.rs index 0a85e08bc1..919b3e18e2 100644 --- a/apps/desktop/src-tauri/src/deeplink_actions.rs +++ b/apps/desktop/src-tauri/src/deeplink_actions.rs @@ -261,7 +261,9 @@ impl DeepLinkAction { crate::recording::toggle_pause_recording(app.clone(), app.state()).await } DeepLinkAction::RestartRecording => { - crate::recording::restart_recording(app.clone(), app.state()).await + crate::recording::restart_recording(app.clone(), app.state()) + .await + .map(|_| ()) } DeepLinkAction::SwitchMicrophone { mic_label } => { crate::set_mic_input(app.state(), mic_label).await @@ -274,7 +276,9 @@ impl DeepLinkAction { DeepLinkAction::TakeScreenshot => { let id = scap_targets::Display::primary().id(); let capture_target = ScreenCaptureTarget::Display { id }; - crate::recording::take_screenshot(app.clone(), capture_target).await + crate::recording::take_screenshot(app.clone(), capture_target) + .await + .map(|_| ()) } #[cfg(debug_assertions)] DeepLinkAction::OpenCamera { camera } => {