//! Tauri 命令:快速面板模块 use tauri::AppHandle; use super::popup::{self, QuickPanelSettings}; use super::{file_index, app_scanner, icon_extractor}; /// 读取快速面板设置(快捷键等) #[tauri::command] pub async fn quickpanel_get_settings(app: AppHandle) -> Result { Ok(popup::load_settings(&app)) } /// 保存快速面板设置;快捷键变化时自动重新注册 + 预创建窗口 #[tauri::command] pub async fn quickpanel_save_settings( settings: QuickPanelSettings, app: AppHandle, ) -> Result<(), String> { let prev_shortcut = popup::load_settings(&app).shortcut; popup::save_settings(&app, &settings)?; // 快捷键变化时重新注册 if settings.shortcut != prev_shortcut { popup::register_shortcut(&app, &settings.shortcut)?; // 新快捷键非空时确保弹窗窗口已预创建 if !settings.shortcut.trim().is_empty() { popup::ensure_window(&app); } } Ok(()) } /// 注册(或切换)快速面板全局快捷键 #[tauri::command] pub async fn quickpanel_register_shortcut( shortcut: String, app: AppHandle, ) -> Result<(), String> { popup::register_shortcut(&app, &shortcut) } /// 注销快速面板全局快捷键 #[tauri::command] pub async fn quickpanel_unregister_shortcut(app: AppHandle) -> Result<(), String> { popup::unregister_shortcut(&app); Ok(()) } /// 手动触发显示快速面板(供 UI 按钮调用) #[tauri::command] pub async fn quickpanel_show_popup(app: AppHandle) -> Result<(), String> { popup::show_popup(&app); Ok(()) } /// 隐藏快速面板 #[tauri::command] pub async fn quickpanel_hide_popup(app: AppHandle) -> Result<(), String> { popup::hide_popup(&app); Ok(()) } /// 显示已创建的弹窗窗口(前端 onMounted 后调用) #[tauri::command] pub async fn quickpanel_show_window(app: AppHandle) -> Result<(), String> { popup::show_window(&app); Ok(()) } /// 锁定屏幕(Windows: rundll32 user32.dll,LockWorkStation,CREATE_NO_WINDOW 避免黑窗) #[tauri::command] pub fn quickpanel_lock_screen() -> Result<(), String> { #[cfg(windows)] { use crate::process_manager::setup_creation_flags; let mut cmd = std::process::Command::new("rundll32.exe"); cmd.arg("user32.dll,LockWorkStation"); setup_creation_flags(&mut cmd); cmd.spawn().map_err(|e| format!("锁屏失败: {}", e))?; } #[cfg(not(windows))] { return Err("当前平台不支持锁屏".into()); } Ok(()) } /// 初始化文件索引数据库(应用启动时调用) #[tauri::command] pub fn quickpanel_init_file_index(app: AppHandle) -> Result<(), String> { file_index::init(&app); Ok(()) } /// 构建文件索引(全量重建,阻塞操作建议在 spawn_blocking 调用) #[tauri::command] pub async fn quickpanel_build_file_index(app: AppHandle) -> Result { let settings = popup::load_settings(&app); let dirs = if settings.index_dirs.is_empty() { popup::QuickPanelSettings::default().index_dirs } else { settings.index_dirs }; // 阻塞操作放到 spawn_blocking tauri::async_runtime::spawn_blocking(move || file_index::build_index(&dirs)) .await .map_err(|e| format!("索引任务失败: {}", e))? } /// 搜索文件索引 #[tauri::command] pub fn quickpanel_search_files(query: String, limit: Option) -> Vec { file_index::search(&query, limit.unwrap_or(50)) } /// 获取索引状态 #[tauri::command] pub fn quickpanel_file_index_stats() -> file_index::IndexStats { file_index::stats() } /// 扫描已安装应用 #[tauri::command] pub fn quickpanel_scan_apps() -> Vec { app_scanner::scan_apps() } /// 获取应用图标(data URL)。命中内存/磁盘缓存时零 Windows API 调用。 /// 前端按需为可见项调用,避免一次性加载全部图标。 #[tauri::command] pub fn quickpanel_get_app_icon(app: AppHandle, path: String) -> Option { icon_extractor::get_icon_data_url(&app, &path) } /// 清理图标缓存(磁盘 + 内存) #[tauri::command] pub fn quickpanel_clear_app_icon_cache(app: AppHandle) -> Result<(), String> { icon_extractor::clear_cache(&app); Ok(()) } /// 在资源管理器中显示文件(选中) #[tauri::command] pub fn quickpanel_reveal_in_explorer(path: String) -> Result<(), String> { #[cfg(windows)] { use std::ffi::OsStr; use std::os::windows::ffi::OsStrExt; use windows_sys::Win32::Foundation::HWND; use windows_sys::Win32::UI::Shell::ShellExecuteW; use windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL; // 规范化路径分隔符 let normalized = path.replace('/', "\\"); // explorer.exe /select,"path" — 用 ShellExecuteW 直接传参, // 避免 std::process::Command 的 arg 转义破坏 /select 语法。 // 对 .lnk 文件也能正确选中(explorer 直接选中 .lnk 文件本身)。 let params = format!("/select,\"{}\"", normalized); let wide_exe: Vec = OsStr::new("explorer.exe") .encode_wide() .chain(std::iter::once(0)) .collect(); let wide_params: Vec = OsStr::new(¶ms) .encode_wide() .chain(std::iter::once(0)) .collect(); unsafe { let hinst = ShellExecuteW( 0 as HWND, std::ptr::null(), wide_exe.as_ptr(), wide_params.as_ptr(), std::ptr::null(), SW_SHOWNORMAL, ); // ShellExecuteW 返回值 <= 32 表示错误 if hinst <= 32 { return Err(format!("打开资源管理器失败 (code: {})", hinst as i32)); } } } #[cfg(not(windows))] { let _ = path; return Err("当前平台不支持".into()); } Ok(()) } /// 用系统默认程序打开文件/文件夹。 /// - 目录:explorer.exe 直接打开(修复索引目录点击后未打开的问题) /// - 文件:ShellExecuteW open,无关联应用时自动 fallback 到「打开方式」对话框(verb: openas) #[tauri::command] pub fn quickpanel_open_file(path: String) -> Result<(), String> { super::special_locations::open_path(&path) } /// 获取 Windows 常用快捷位置(hosts、回收站、此电脑、用户目录、系统管理工具等) #[tauri::command] pub fn quickpanel_get_special_locations() -> Vec { super::special_locations::get_special_locations() } /// 打开快捷位置(kind: file | shell | cmd) #[tauri::command] pub fn quickpanel_open_special( kind: String, target: String, args: Vec, ) -> Result<(), String> { match kind.as_str() { "shell" => super::special_locations::open_shell(&target), "cmd" => super::special_locations::open_cmd(&target, &args), _ => super::special_locations::open_path(&target), } } /// 删除文件(移到回收站) #[tauri::command] pub fn quickpanel_delete_file(path: String) -> Result<(), String> { #[cfg(windows)] { use crate::process_manager::setup_creation_flags; let p = std::path::Path::new(&path); let is_dir = p.is_dir(); // 用 PowerShell + Microsoft.VisualBasic 移到回收站 let script = if is_dir { format!( "Add-Type -AssemblyName Microsoft.VisualBasic; [Microsoft.VisualBasic.FileIO.FileSystem]::DeleteDirectory('{}','OnlyErrorDialogs','SendToRecycleBin')", path.replace('\'', "''") ) } else { format!( "Add-Type -AssemblyName Microsoft.VisualBasic; [Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile('{}','OnlyErrorDialogs','SendToRecycleBin')", path.replace('\'', "''") ) }; let mut cmd = std::process::Command::new("powershell.exe"); cmd.args(["-NoProfile", "-NonInteractive", "-Command", &script]); setup_creation_flags(&mut cmd); let output = cmd.output().map_err(|e| format!("删除失败: {}", e))?; if !output.status.success() { return Err(format!( "删除失败: {}", String::from_utf8_lossy(&output.stderr) )); } } #[cfg(not(windows))] { let _ = path; return Err("当前平台不支持".into()); } Ok(()) } /// 运行自定义命令(执行可执行文件 + 参数) /// .lnk 快捷方式不能直接 spawn(os error 193),需通过 cmd /C 启动 #[tauri::command] pub fn quickpanel_run_custom_command(command: String, args: Vec) -> Result<(), String> { use crate::process_manager::setup_creation_flags; let is_lnk = command .to_lowercase() .ends_with(".lnk"); let mut cmd = if is_lnk { // cmd /C start "" "path.lnk" arg1 arg2 let mut c = std::process::Command::new("cmd"); c.args(["/C", "start", "", &command]); c.args(&args); c } else { let mut c = std::process::Command::new(&command); c.args(&args); c }; setup_creation_flags(&mut cmd); cmd.spawn().map_err(|e| format!("运行命令失败: {}", e))?; Ok(()) } /// 运行系统命令(不设置 CREATE_NO_WINDOW,使 cmd/powershell/regedit 等显示自身窗口) /// 适用于内置系统工具:regedit、shutdown、cmd、powershell、taskmgr 等。 #[tauri::command] pub fn quickpanel_run_system_command(command: String, args: Vec) -> Result<(), String> { let mut cmd = std::process::Command::new(&command); cmd.args(&args); cmd.spawn().map_err(|e| format!("运行系统命令失败: {}", e))?; Ok(()) }