-
Notifications
You must be signed in to change notification settings - Fork 1.4k
feat: add deep-link support and Raycast extension #1708
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
0d085b3
de021be
7cee5f5
771937e
a6a24ba
fadcaf1
b75aa94
c14a6db
f31341d
7cde3a3
32bc095
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -32,6 +32,9 @@ pub enum DeepLinkAction { | |||||||||||||||||||||||||
| OpenSettings { | ||||||||||||||||||||||||||
| page: Option<String>, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| StartDefaultRecording, | ||||||||||||||||||||||||||
| ResumeRecording, | ||||||||||||||||||||||||||
| TogglePauseRecording, | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| pub fn handle(app_handle: &AppHandle, urls: Vec<Url>) { | ||||||||||||||||||||||||||
|
|
@@ -81,13 +84,27 @@ impl TryFrom<&Url> for DeepLinkAction { | |||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| fn try_from(url: &Url) -> Result<Self, Self::Error> { | ||||||||||||||||||||||||||
| #[cfg(target_os = "macos")] | ||||||||||||||||||||||||||
| if url.scheme() == "file" { | ||||||||||||||||||||||||||
| if url.scheme().eq_ignore_ascii_case("file") { | ||||||||||||||||||||||||||
| return url | ||||||||||||||||||||||||||
| .to_file_path() | ||||||||||||||||||||||||||
| .map(|project_path| Self::OpenEditor { project_path }) | ||||||||||||||||||||||||||
| .map_err(|_| ActionParseFromUrlError::Invalid); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if url.scheme().eq_ignore_ascii_case("cap") { | ||||||||||||||||||||||||||
| if url.path() != "/" { | ||||||||||||||||||||||||||
| return Err(ActionParseFromUrlError::Invalid); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return match url.host_str() { | ||||||||||||||||||||||||||
| Some(h) if h.eq_ignore_ascii_case("record") => Ok(Self::StartDefaultRecording), | ||||||||||||||||||||||||||
| Some(h) if h.eq_ignore_ascii_case("stop") => Ok(Self::StopRecording), | ||||||||||||||||||||||||||
| Some(h) if h.eq_ignore_ascii_case("pause") => Ok(Self::TogglePauseRecording), | ||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor: |
||||||||||||||||||||||||||
| Some(h) if h.eq_ignore_ascii_case("resume") => Ok(Self::ResumeRecording), | ||||||||||||||||||||||||||
| _ => Err(ActionParseFromUrlError::Invalid), | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| match url.domain() { | ||||||||||||||||||||||||||
| Some(v) if v != "action" => Err(ActionParseFromUrlError::NotAction), | ||||||||||||||||||||||||||
| _ => Err(ActionParseFromUrlError::Invalid), | ||||||||||||||||||||||||||
|
|
@@ -107,6 +124,17 @@ impl TryFrom<&Url> for DeepLinkAction { | |||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| impl DeepLinkAction { | ||||||||||||||||||||||||||
| pub async fn execute(self, app: &AppHandle) -> Result<(), String> { | ||||||||||||||||||||||||||
| match &self { | ||||||||||||||||||||||||||
| DeepLinkAction::StartRecording { .. } | ||||||||||||||||||||||||||
| | DeepLinkAction::StopRecording | ||||||||||||||||||||||||||
| | DeepLinkAction::StartDefaultRecording | ||||||||||||||||||||||||||
| | DeepLinkAction::ResumeRecording | ||||||||||||||||||||||||||
| | DeepLinkAction::TogglePauseRecording => { | ||||||||||||||||||||||||||
| crate::notifications::NotificationType::DeepLinkTriggered.send_always(app); | ||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will fire an always-on OS notification for every deep-link action (including pause/resume toggles), which feels like it could get pretty noisy for Raycast/automation and also bypasses the user's notification preference. Consider only forcing the notification for actions that start recording, and let the others respect the setting.
Suggested change
|
||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| _ => {} | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| match self { | ||||||||||||||||||||||||||
| DeepLinkAction::StartRecording { | ||||||||||||||||||||||||||
| capture_mode, | ||||||||||||||||||||||||||
|
|
@@ -153,6 +181,15 @@ impl DeepLinkAction { | |||||||||||||||||||||||||
| DeepLinkAction::OpenSettings { page } => { | ||||||||||||||||||||||||||
| crate::show_window(app.clone(), ShowCapWindow::Settings { page }).await | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| DeepLinkAction::StartDefaultRecording => { | ||||||||||||||||||||||||||
| crate::RequestOpenRecordingPicker { target_mode: None }.emit(app).map_err(|e| e.to_string()) | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| DeepLinkAction::ResumeRecording => { | ||||||||||||||||||||||||||
| crate::recording::resume_recording(app.clone(), app.state()).await | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| DeepLinkAction::TogglePauseRecording => { | ||||||||||||||||||||||||||
| crate::recording::toggle_pause_recording(app.clone(), app.state()).await | ||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -14,6 +14,7 @@ pub enum NotificationType { | |||||||||||||||||||
| ScreenshotCopiedToClipboard, | ||||||||||||||||||||
| ScreenshotSaveFailed, | ||||||||||||||||||||
| ScreenshotCopyFailed, | ||||||||||||||||||||
| DeepLinkTriggered, | ||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since
Suggested change
|
||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| impl NotificationType { | ||||||||||||||||||||
|
|
@@ -62,6 +63,11 @@ impl NotificationType { | |||||||||||||||||||
| "Unable to copy screenshot to clipboard. Please try again", | ||||||||||||||||||||
| true, | ||||||||||||||||||||
| ), | ||||||||||||||||||||
| NotificationType::DeepLinkTriggered => ( | ||||||||||||||||||||
| "Action Triggered", | ||||||||||||||||||||
| "An action was triggered via a deep link", | ||||||||||||||||||||
| false, | ||||||||||||||||||||
| ), | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -84,14 +90,19 @@ impl NotificationType { | |||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| pub fn send(self, app: &tauri::AppHandle) { | ||||||||||||||||||||
| send_notification(app, self); | ||||||||||||||||||||
| send_notification(app, self, false); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| pub fn send_always(self, app: &tauri::AppHandle) { | ||||||||||||||||||||
| send_notification(app, self, true); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| pub fn send_notification(app: &tauri::AppHandle, notification_type: NotificationType) { | ||||||||||||||||||||
| let enable_notifications = GeneralSettingsStore::get(app) | ||||||||||||||||||||
| .map(|settings| settings.is_some_and(|s| s.enable_notifications)) | ||||||||||||||||||||
| .unwrap_or(false); | ||||||||||||||||||||
| pub fn send_notification(app: &tauri::AppHandle, notification_type: NotificationType, always: bool) { | ||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changing |
||||||||||||||||||||
| let enable_notifications = always | ||||||||||||||||||||
| || GeneralSettingsStore::get(app) | ||||||||||||||||||||
| .map(|settings| settings.is_some_and(|s| s.enable_notifications)) | ||||||||||||||||||||
| .unwrap_or(false); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if !enable_notifications { | ||||||||||||||||||||
| return; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,7 +30,7 @@ | |
| "updater": { "active": false, "pubkey": "" }, | ||
| "deep-link": { | ||
| "desktop": { | ||
| "schemes": ["cap-desktop"] | ||
| "schemes": ["cap-desktop", "cap"] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Registering the short |
||
| } | ||
| } | ||
| }, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This currently accepts any path after the host (e.g.
cap://record/anything). If the intent is to support only the exact host actions, consider rejecting non-root paths to avoid surprising matches.