mod ai; mod app; mod browser_extension; mod cache; mod clipboard; pub mod clipboard_history; mod desktop; mod error; mod extensions; mod file_search; mod filesystem; mod frecency; mod oauth; mod quicklinks; mod snippets; mod soulver; mod store; mod system; use crate::snippets::input_manager::{EvdevInputManager, InputManager}; use crate::{app::App, cache::AppCache}; use ai::AiUsageManager; use browser_extension::WsState; use frecency::FrecencyManager; use gtk::glib::{ControlFlow, MainContext, Priority, Sender}; use gtk::prelude::{GtkWindowExt, WidgetExt}; use quicklinks::QuicklinkManager; use selection::get_text; use snippets::engine::ExpansionEngine; use snippets::manager::SnippetManager; use std::process::Command; use std::sync::Arc; use std::thread; use std::time::Duration; use tauri::{Emitter, Manager}; #[derive(Clone)] struct GtkWindowHandle(Sender); enum GtkCommand { Show, Hide, } #[tauri::command] fn get_installed_apps() -> Vec { match AppCache::get_apps() { Ok(apps) => apps, Err(e) => { eprintln!("Failed to get apps: {:?}", e); Vec::new() } } } #[tauri::command] fn launch_app(exec: String) -> Result<(), String> { let exec_parts: Vec<&str> = exec.split_whitespace().collect(); if exec_parts.is_empty() { return Err("Empty exec command".to_string()); } let mut command = Command::new(exec_parts[0]); for arg in &exec_parts[1..] { if !arg.starts_with('%') { command.arg(arg); } } command .spawn() .map_err(|e| format!("Failed to launch app: {}", e))?; Ok(()) } #[tauri::command] fn get_selected_text() -> String { get_text() } #[tauri::command] async fn show_hud(app: tauri::AppHandle, title: String) -> Result<(), String> { let hud_window = match app.get_webview_window("hud") { Some(window) => window, None => { tauri::WebviewWindowBuilder::new(&app, "hud", tauri::WebviewUrl::App("/hud".into())) .decorations(false) .transparent(true) .always_on_top(true) .skip_taskbar(true) .center() .min_inner_size(300.0, 80.0) .max_inner_size(300.0, 80.0) .inner_size(300.0, 80.0) .build() .map_err(|e| e.to_string())? } }; let window_clone = hud_window.clone(); window_clone.show().map_err(|e| e.to_string())?; window_clone .emit("hud-message", &title) .map_err(|e| e.to_string())?; window_clone .set_ignore_cursor_events(true) .map_err(|e| e.to_string())?; window_clone.set_focus().map_err(|e| e.to_string())?; tauri::async_runtime::spawn(async move { tokio::time::sleep(std::time::Duration::from_secs(2)).await; let _ = window_clone.hide(); }); Ok(()) } #[tauri::command] fn record_usage(app: tauri::AppHandle, item_id: String) -> Result<(), String> { app.state::() .record_usage(item_id) .map_err(|e| e.to_string()) } #[tauri::command] fn get_frecency_data(app: tauri::AppHandle) -> Result, String> { app.state::() .get_frecency_data() .map_err(|e| e.to_string()) } #[tauri::command] fn delete_frecency_entry(app: tauri::AppHandle, item_id: String) -> Result<(), String> { app.state::() .delete_frecency_entry(item_id) .map_err(|e| e.to_string()) } #[tauri::command] fn hide_item(app: tauri::AppHandle, item_id: String) -> Result<(), String> { app.state::() .hide_item(item_id) .map_err(|e| e.to_string()) } #[tauri::command] fn get_hidden_item_ids(app: tauri::AppHandle) -> Result, String> { app.state::() .get_hidden_item_ids() .map_err(|e| e.to_string()) } fn setup_background_refresh() { thread::spawn(|| { thread::sleep(Duration::from_secs(60)); loop { AppCache::refresh_background(); thread::sleep(Duration::from_secs(300)); } }); } fn setup_global_shortcut(app: &mut tauri::App) -> Result<(), Box> { use tauri_plugin_global_shortcut::{ Code, GlobalShortcutExt, Modifiers, Shortcut, ShortcutState, }; let spotlight_shortcut = Shortcut::new(Some(Modifiers::ALT), Code::Space); app.handle().plugin( tauri_plugin_global_shortcut::Builder::new() .with_handler(move |app_handle, shortcut, event| { if shortcut == &spotlight_shortcut && event.state() == ShortcutState::Pressed { let spotlight_window = app_handle .get_webview_window("main") .expect("Main window should exist"); if spotlight_window.is_visible().unwrap_or(false) { let _ = hide_window(app_handle.clone(), spotlight_window.clone()); } else { let _ = show_window(app_handle.clone(), spotlight_window.clone()); let _ = spotlight_window.set_focus(); } } }) .build(), )?; app.global_shortcut().register(spotlight_shortcut)?; Ok(()) } fn setup_input_listener(app: &tauri::AppHandle) { let snippet_manager = app.state::().inner().clone(); let snippet_manager_arc = Arc::new(snippet_manager); let input_manager = EvdevInputManager::new().unwrap(); let input_manager_arc: Arc = Arc::new(input_manager); app.manage(input_manager_arc.clone()); let engine = ExpansionEngine::new(snippet_manager_arc, input_manager_arc); thread::spawn(move || { if let Err(e) = engine.start_listening() { eprintln!("[ExpansionEngine] Failed to start: {}", e); } }); } #[tauri::command] fn hide_window(app: tauri::AppHandle, window: tauri::WebviewWindow) -> Result<(), String> { if let Some(gtk_handle) = app.try_state::() { gtk_handle .0 .send(GtkCommand::Hide) .map_err(|e| e.to_string())?; return Ok(()); } window.hide().map_err(|e| e.to_string()) } #[tauri::command] fn show_window(app: tauri::AppHandle, window: tauri::WebviewWindow) -> Result<(), String> { if let Some(gtk_handle) = app.try_state::() { gtk_handle .0 .send(GtkCommand::Show) .map_err(|e| e.to_string())?; return Ok(()); } window.show().map_err(|e| e.to_string()) } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_http::init()) .manage(WsState::default()) .plugin(tauri_plugin_single_instance::init(|app, args, _cwd| { if args.len() > 1 && args[1].starts_with("raycast://") { if let Some(window) = app.get_webview_window("main") { let _ = window.emit("deep-link", args[1].to_string()); let _ = show_window(app.clone(), window.clone()); let _ = window.set_focus(); } return; } if let Some(window) = app.get_webview_window("main") { if let Ok(true) = window.is_visible() { let _ = hide_window(app.clone(), window.clone()); } else { let _ = show_window(app.clone(), window.clone()); let _ = window.set_focus(); } } })) .plugin(tauri_plugin_deep_link::init()) .plugin(tauri_plugin_global_shortcut::Builder::new().build()) .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_clipboard_manager::init()) .plugin(tauri_plugin_opener::init()) .invoke_handler(tauri::generate_handler![ get_installed_apps, launch_app, get_selected_text, show_hud, filesystem::get_selected_finder_items, extensions::install_extension, browser_extension::browser_extension_check_connection, browser_extension::browser_extension_request, clipboard::clipboard_read_text, clipboard::clipboard_read, clipboard::clipboard_copy, clipboard::clipboard_paste, clipboard::clipboard_clear, oauth::oauth_set_tokens, oauth::oauth_get_tokens, oauth::oauth_remove_tokens, clipboard_history::history_get_items, clipboard_history::history_get_item_content, clipboard_history::history_delete_item, clipboard_history::history_toggle_pin, clipboard_history::history_clear_all, clipboard_history::history_item_was_copied, quicklinks::create_quicklink, quicklinks::list_quicklinks, quicklinks::update_quicklink, quicklinks::delete_quicklink, quicklinks::execute_quicklink, system::get_applications, system::get_default_application, system::get_frontmost_application, system::show_in_finder, system::trash, record_usage, get_frecency_data, delete_frecency_entry, hide_item, get_hidden_item_ids, snippets::create_snippet, snippets::list_snippets, snippets::update_snippet, snippets::delete_snippet, snippets::import_snippets, snippets::paste_snippet_content, snippets::snippet_was_used, file_search::search_files, ai::set_ai_api_key, ai::is_ai_api_key_set, ai::clear_ai_api_key, ai::ai_ask_stream, ai::get_ai_usage_history, ai::get_ai_settings, ai::set_ai_settings, ai::ai_can_access, hide_window, show_window, soulver::calculate_soulver ]) .setup(|app| { let app_handle = app.handle().clone(); tauri::async_runtime::spawn(browser_extension::run_server(app_handle)); clipboard_history::init(app.handle().clone()); file_search::init(app.handle().clone()); app.manage(QuicklinkManager::new(app.handle())?); app.manage(FrecencyManager::new(app.handle())?); app.manage(SnippetManager::new(app.handle())?); app.manage(AiUsageManager::new(app.handle())?); setup_background_refresh(); setup_global_shortcut(app)?; setup_input_listener(app.handle()); let soulver_core_path = app .path() .resource_dir() .unwrap() .join("SoulverWrapper/Vendor/SoulverCore-linux"); soulver::initialize(soulver_core_path.to_str().unwrap()); #[cfg(target_os = "linux")] { use gtk::prelude::ContainerExt; use gtk_layer_shell::{Edge, Layer, LayerShell}; let webview_window = app.get_webview_window("main").unwrap(); webview_window.hide().unwrap(); webview_window.set_decorations(false).unwrap(); let window = gtk::ApplicationWindow::new( &webview_window.gtk_window().unwrap().application().unwrap(), ); window.set_app_paintable(true); window.set_decorated(false); window.stick(); webview_window .gtk_window() .unwrap() .remove(&webview_window.default_vbox().unwrap()); window.add(&webview_window.default_vbox().unwrap()); window.init_layer_shell(); window.set_layer(Layer::Overlay); window.set_anchor(Edge::Top, true); window.set_width_request(400); window.set_height_request(400); if let Some(monitor) = window.display().monitor(0) { window.set_monitor(&monitor); } window.set_keyboard_mode(gtk_layer_shell::KeyboardMode::Exclusive); let (sender, receiver) = MainContext::channel(Priority::DEFAULT); app.manage(GtkWindowHandle(sender)); let main_window_clone = window.clone(); receiver.attach(None, move |msg| { match msg { GtkCommand::Show => main_window_clone.show(), GtkCommand::Hide => main_window_clone.hide(), } ControlFlow::Continue }); window.show_all(); } Ok(()) }) .build(tauri::generate_context!()) .expect("error while building tauri application") .run(|app, event| { if let tauri::RunEvent::WindowEvent { label, event, .. } = event { if label == "main" { match event { tauri::WindowEvent::CloseRequested { api, .. } => { api.prevent_close(); if let Some(window) = app.get_webview_window("main") { let _ = hide_window(app.clone(), window); } } tauri::WindowEvent::Focused(false) => { if let Some(window) = app.get_webview_window("main") { if !cfg!(debug_assertions) { let _ = hide_window(app.clone(), window); } } } _ => {} } } } }); }