diff --git a/package.json b/package.json index 1874b6b..18e0293 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@raycast-linux/protocol": "workspace:*", "@tauri-apps/api": "^2", "@tauri-apps/plugin-clipboard-manager": "~2.2.2", + "@tauri-apps/plugin-global-shortcut": "~2.2.1", "@tauri-apps/plugin-opener": "~2", "@tauri-apps/plugin-shell": "~2.2.1", "embla-carousel-svelte": "^8.6.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c56af8f..be88285 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@tauri-apps/plugin-clipboard-manager': specifier: ~2.2.2 version: 2.2.2 + '@tauri-apps/plugin-global-shortcut': + specifier: ~2.2.1 + version: 2.2.1 '@tauri-apps/plugin-opener': specifier: ~2 version: 2.2.7 @@ -999,6 +1002,9 @@ packages: '@tauri-apps/plugin-clipboard-manager@2.2.2': resolution: {integrity: sha512-bZvDLMqfcNmsw7Ag8I49jlaCjdpDvvlJHnpp6P+Gg/3xtpSERdwlDxm7cKGbs2mj46dsw4AuG3RoAgcpwgioUA==} + '@tauri-apps/plugin-global-shortcut@2.2.1': + resolution: {integrity: sha512-b64/TI1t5LIi2JY4OWlYjZpPRq60T5GVVL/no27sUuxaNUZY8dVtwsMtDUgxUpln2yR+P2PJsYlqY5V8sLSxEw==} + '@tauri-apps/plugin-opener@2.2.7': resolution: {integrity: sha512-uduEyvOdjpPOEeDRrhwlCspG/f9EQalHumWBtLBnp3fRp++fKGLqDOyUhSIn7PzX45b/rKep//ZQSAQoIxobLA==} @@ -3519,6 +3525,10 @@ snapshots: dependencies: '@tauri-apps/api': 2.5.0 + '@tauri-apps/plugin-global-shortcut@2.2.1': + dependencies: + '@tauri-apps/api': 2.5.0 + '@tauri-apps/plugin-opener@2.2.7': dependencies: '@tauri-apps/api': 2.5.0 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index ae8c0b3..dba9b44 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1742,6 +1742,24 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +[[package]] +name = "global-hotkey" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9247516746aa8e53411a0db9b62b0e24efbcf6a76e0ba73e5a91b512ddabed7" +dependencies = [ + "crossbeam-channel", + "keyboard-types", + "objc2 0.6.1", + "objc2-app-kit", + "once_cell", + "serde", + "thiserror 2.0.12", + "windows-sys 0.59.0", + "x11rb", + "xkeysym", +] + [[package]] name = "gobject-sys" version = "0.18.0" @@ -3595,6 +3613,7 @@ dependencies = [ "tauri", "tauri-build", "tauri-plugin-clipboard-manager", + "tauri-plugin-global-shortcut", "tauri-plugin-opener", "tauri-plugin-shell", "url", @@ -4570,6 +4589,21 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "tauri-plugin-global-shortcut" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31919f3c07bcb585afef217c0c33cde80da9ebccf5b8e2c90e0e0a535b14ab47" +dependencies = [ + "global-hotkey", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.12", +] + [[package]] name = "tauri-plugin-opener" version = "2.2.7" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 72b664e..56ea4b3 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -35,3 +35,6 @@ reqwest = "0.12.20" zip = "4.1.0" bytes = "1.10.1" +[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] +tauri-plugin-global-shortcut = "2" + diff --git a/src-tauri/capabilities/desktop.json b/src-tauri/capabilities/desktop.json new file mode 100644 index 0000000..4aa00ba --- /dev/null +++ b/src-tauri/capabilities/desktop.json @@ -0,0 +1,14 @@ +{ + "identifier": "desktop-capability", + "platforms": [ + "macOS", + "windows", + "linux" + ], + "windows": [ + "main" + ], + "permissions": [ + "global-shortcut:allow-register" + ] +} \ No newline at end of file diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 9fb57fa..96b7acb 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -3,18 +3,18 @@ mod cache; mod desktop; mod error; +use crate::{app::App, cache::AppCache}; #[cfg(target_os = "linux")] use arboard; -use crate::{app::App, cache::AppCache}; use selection::get_text; use std::fs; use std::io::{self, Cursor}; +#[cfg(target_os = "linux")] +use std::path::Path; use std::path::PathBuf; use std::process::Command; use std::thread; use std::time::Duration; -#[cfg(target_os = "linux")] -use std::path::Path; use tauri::Manager; #[cfg(target_os = "linux")] use url::Url; @@ -217,7 +217,7 @@ async fn get_from_file_manager() -> Result, String> { Ok(r) => r, Err(_) => return Ok(vec![]), }; - + let body = response.body(); let windows: Vec = body.deserialize().unwrap_or_default(); @@ -230,14 +230,17 @@ async fn get_from_file_manager() -> Result, String> { window_path, window_interface_ref, ) - .await - { - Ok(p) => p, - Err(_) => continue, - }; + .await + { + Ok(p) => p, + Err(_) => continue, + }; if let Ok(is_active) = window_proxy.get_property::("Active").await { if is_active { - if let Ok(uris) = window_proxy.get_property::>("SelectedUris").await { + if let Ok(uris) = window_proxy + .get_property::>("SelectedUris") + .await + { let paths = uris .iter() .filter_map(|uri_str| Url::parse(uri_str).ok()) @@ -354,7 +357,7 @@ async fn install_extension( .map_err(|e| format!("Failed to read response bytes: {}", e))?; let mut archive = zip::ZipArchive::new(Cursor::new(content)).map_err(|e| e.to_string())?; - + let prefix_to_strip = { let file_names: Vec = archive.file_names().map(PathBuf::from).collect(); @@ -363,7 +366,10 @@ async fn install_extension( } else { let first_path = &file_names[0]; if let Some(first_component) = first_path.components().next() { - if file_names.iter().all(|path| path.starts_with(first_component)) { + if file_names + .iter() + .all(|path| path.starts_with(first_component)) + { Some(PathBuf::from(first_component.as_os_str())) } else { None @@ -425,6 +431,7 @@ async fn install_extension( #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() + .plugin(tauri_plugin_global_shortcut::Builder::new().build()) .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_clipboard_manager::init()) .plugin(tauri_plugin_opener::init()) @@ -435,7 +442,9 @@ pub fn run() { get_selected_finder_items, install_extension ]) - .setup(|_app| { + .setup(|app| { + use tauri_plugin_global_shortcut::{Code, GlobalShortcutExt, Modifiers, Shortcut, ShortcutState}; + thread::spawn(|| { thread::sleep(Duration::from_secs(60)); loop { @@ -443,8 +452,35 @@ pub fn run() { thread::sleep(Duration::from_secs(300)); } }); + + let spotlight_shortcut = Shortcut::new(Some(Modifiers::ALT), Code::Space); + + let handle = app.handle().clone(); + + println!("Spotlight shortcut: {:?}", spotlight_shortcut); + + app.handle().plugin( + tauri_plugin_global_shortcut::Builder::new() + .with_handler(move |_app, shortcut, event| { + println!("Shortcut: {:?}, Event: {:?}", shortcut, event); + if shortcut == &spotlight_shortcut && event.state() == ShortcutState::Pressed { + let spotlight_window = handle.get_webview_window("raycast-linux").unwrap(); + println!("Spotlight window: {:?}", spotlight_window); + if spotlight_window.is_visible().unwrap_or(false) { + spotlight_window.hide().unwrap(); + } else { + spotlight_window.show().unwrap(); + spotlight_window.set_focus().unwrap(); + } + } + }) + .build(), + )?; + + app.global_shortcut().register(spotlight_shortcut)?; + Ok(()) }) .run(tauri::generate_context!()) .expect("error while running tauri application"); -} \ No newline at end of file +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 6fa32e6..8de3dcc 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -13,8 +13,16 @@ "windows": [ { "title": "raycast-linux", - "width": 800, - "height": 600 + "visible": false, + "decorations": false, + "alwaysOnTop": true, + "transparent": true, + "resizable": false, + "skipTaskbar": true, + "fullscreen": false, + "width": 600, + "height": 80, + "center": true } ], "security": {