diff --git a/src-tauri/capabilities/screenshot.json b/src-tauri/capabilities/screenshot.json index 3b119a0..d9da867 100644 --- a/src-tauri/capabilities/screenshot.json +++ b/src-tauri/capabilities/screenshot.json @@ -1,8 +1,8 @@ { "$schema": "../gen/schemas/desktop-schema.json", "identifier": "screenshot", - "description": "Capability for screenshot overlay and editor windows", - "windows": ["screenshot-overlay*", "screenshot-editor-*"], + "description": "Capability for screenshot overlay, editor and pin windows", + "windows": ["screenshot-overlay*", "screenshot-editor-*", "screenshot-pin"], "permissions": [ "core:default", "core:window:allow-hide", @@ -11,6 +11,7 @@ "core:window:allow-start-dragging", "core:window:allow-set-position", "core:window:allow-set-size", + "core:window:allow-set-resizable", "core:window:allow-set-always-on-top", "core:window:allow-set-skip-taskbar", "core:window:allow-set-decorations", diff --git a/src-tauri/src/constants.rs b/src-tauri/src/constants.rs index 926ca01..4860c21 100644 --- a/src-tauri/src/constants.rs +++ b/src-tauri/src/constants.rs @@ -10,6 +10,8 @@ pub mod windows { pub const OSD_OVERLAY: &str = "osd-overlay"; #[allow(dead_code)] pub const SCREENSHOT_OVERLAY: &str = "screenshot-overlay"; + #[allow(dead_code)] + pub const SCREENSHOT_PIN: &str = "screenshot-pin"; } /// Tauri 事件名(与前端 constants::EVENTS 对应) @@ -41,6 +43,7 @@ pub mod events { pub const OSD_END_DRAG: &str = "osd-end-drag"; // 截图 pub const SCREENSHOT_SHORTCUT: &str = "screenshot-shortcut"; + pub const SCREENSHOT_PIN_SHORTCUT: &str = "screenshot-pin-shortcut"; // 内核安装进度 pub const KERNEL_INSTALL_PROGRESS: &str = "kernel-install-progress"; // 进程与下载 diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 2b8a0c5..7ded8a3 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -34,10 +34,10 @@ use mihomo_manager::{ proxy_test_delay, proxy_update_kernel, proxy_update_profile, proxy_version, MihomoManager, }; use monitor_kernel::{ - monitor_elevate_self, monitor_get_elevate_on_launch, monitor_get_hardware_config, - monitor_get_snapshot, monitor_get_status, monitor_kernel_info, monitor_set_elevate_on_launch, - monitor_set_hardware_config, monitor_start, monitor_start_elevated, monitor_status, - monitor_stop, MonitorKernel, + monitor_elevate_self, monitor_get_auto_start, monitor_get_elevate_on_launch, + monitor_get_hardware_config, monitor_get_snapshot, monitor_get_status, monitor_kernel_info, + monitor_set_auto_start, monitor_set_elevate_on_launch, monitor_set_hardware_config, + monitor_start, monitor_start_elevated, monitor_status, monitor_stop, MonitorKernel, }; use network_monitor::network_status; use osd_window::{ @@ -54,8 +54,9 @@ use screenshot::commands::{ screenshot_crop_stored, screenshot_cursor_pos, screenshot_delete_cache, screenshot_disable_transitions, screenshot_enum_windows, screenshot_fullscreen_png, screenshot_get_editor_image, screenshot_get_fullscreen_bmp, screenshot_load_cache, - screenshot_register_shortcut, screenshot_save_cache, screenshot_save_png, - screenshot_set_editor_image, screenshot_unregister_shortcut, screenshot_window_from_point, + screenshot_register_pin_shortcut, screenshot_register_shortcut, screenshot_save_cache, + screenshot_save_png, screenshot_set_editor_image, screenshot_unregister_pin_shortcut, + screenshot_unregister_shortcut, screenshot_window_from_point, }; use clipboard::{ ClipboardManager, @@ -127,13 +128,14 @@ fn export_bindings() { downloader_get_tasks, downloader_check_url, downloader_add_task, downloader_pause_task, downloader_resume_task, downloader_remove_task, downloader_get_settings, downloader_save_settings, downloader_open_dir, downloader_open_url, - // screenshot(19,豁免 2:get_fullscreen_bmp 返回 ipc::Response、compose_copy 接收 ipc::Request) + // screenshot(21,豁免 2:get_fullscreen_bmp 返回 ipc::Response、compose_copy 接收 ipc::Request) screenshot_disable_transitions, screenshot_register_shortcut, - screenshot_unregister_shortcut, screenshot_capture_fullscreen, screenshot_fullscreen_png, - screenshot_clear_fullscreen, screenshot_crop_stored, screenshot_crop_copy_stored, - screenshot_window_from_point, screenshot_cursor_pos, screenshot_enum_windows, - screenshot_capture_window, screenshot_set_editor_image, screenshot_get_editor_image, - screenshot_copy_image, screenshot_save_png, + screenshot_unregister_shortcut, screenshot_register_pin_shortcut, + screenshot_unregister_pin_shortcut, screenshot_capture_fullscreen, + screenshot_fullscreen_png, screenshot_clear_fullscreen, screenshot_crop_stored, + screenshot_crop_copy_stored, screenshot_window_from_point, screenshot_cursor_pos, + screenshot_enum_windows, screenshot_capture_window, screenshot_set_editor_image, + screenshot_get_editor_image, screenshot_copy_image, screenshot_save_png, screenshot_save_cache, screenshot_load_cache, screenshot_delete_cache, ]) .export(Typescript::default(), "../src/lib/bindings.ts") @@ -204,6 +206,8 @@ pub fn run() { monitor_get_snapshot, monitor_get_elevate_on_launch, monitor_set_elevate_on_launch, + monitor_get_auto_start, + monitor_set_auto_start, monitor_get_hardware_config, monitor_set_hardware_config, network_status, @@ -291,6 +295,8 @@ pub fn run() { screenshot_delete_cache, screenshot_register_shortcut, screenshot_unregister_shortcut, + screenshot_register_pin_shortcut, + screenshot_unregister_pin_shortcut, screenshot_disable_transitions, screenshot_compose_copy ]) diff --git a/src-tauri/src/mihomo_manager/mod.rs b/src-tauri/src/mihomo_manager/mod.rs index 75e56bf..de0fa0a 100644 --- a/src-tauri/src/mihomo_manager/mod.rs +++ b/src-tauri/src/mihomo_manager/mod.rs @@ -14,6 +14,7 @@ mod system_proxy; mod types; pub use pseudo::is_pseudo_node; +pub use system_proxy::get_system_proxy_windows; pub use types::{InstallProgress, KernelInfo, KernelUpdateInfo, ProfileMeta, ProxySettings, ProxyStatus}; pub use commands::{ proxy_activate_profile, proxy_check_kernel_update, proxy_clear_system_proxy, proxy_close_connection, diff --git a/src-tauri/src/mihomo_manager/system_proxy.rs b/src-tauri/src/mihomo_manager/system_proxy.rs index 241ec4b..1d8cf2e 100644 --- a/src-tauri/src/mihomo_manager/system_proxy.rs +++ b/src-tauri/src/mihomo_manager/system_proxy.rs @@ -38,7 +38,7 @@ pub(crate) fn clear_system_proxy_windows() -> Result<(), String> { } #[cfg(windows)] -pub(crate) fn get_system_proxy_windows() -> bool { +pub fn get_system_proxy_windows() -> bool { use winreg::enums::*; use winreg::RegKey; let hkcu = RegKey::predef(HKEY_CURRENT_USER); @@ -67,7 +67,7 @@ pub(crate) fn clear_system_proxy_windows() -> Result<(), String> { Err("系统代理仅支持 Windows".into()) } #[cfg(not(windows))] -pub(crate) fn get_system_proxy_windows() -> bool { +pub fn get_system_proxy_windows() -> bool { false } diff --git a/src-tauri/src/monitor_kernel.rs b/src-tauri/src/monitor_kernel.rs index 2902207..dbd2257 100644 --- a/src-tauri/src/monitor_kernel.rs +++ b/src-tauri/src/monitor_kernel.rs @@ -132,6 +132,41 @@ pub fn is_thing_elevated() -> bool { false } +// ===================== 监控设置持久化 ===================== + +/// 监控模块设置(存储于 {app_data_dir}/monitor/settings.json) +/// 参照代理模块 ProxySettings 的模式,仅包含需要持久化的运行时开关。 +#[derive(Serialize, Deserialize, Clone, Default)] +#[serde(rename_all = "camelCase")] +pub struct MonitorSettings { + /// 应用启动时自动启动监控内核(默认 false) + #[serde(default)] + pub auto_start: bool, +} + +/// 监控设置文件路径: {app_data_dir}/monitor/settings.json +fn settings_path(app_data_dir: &std::path::Path) -> PathBuf { + app_data_dir.join("monitor").join("settings.json") +} + +/// 读取监控设置(文件不存在或解析失败时返回默认值) +pub fn load_monitor_settings(app_data_dir: &std::path::Path) -> MonitorSettings { + fs::read_to_string(settings_path(app_data_dir)) + .ok() + .and_then(|s| serde_json::from_str::(&s).ok()) + .unwrap_or_default() +} + +/// 写入监控设置 +pub fn save_monitor_settings(app_data_dir: &std::path::Path, settings: &MonitorSettings) -> Result<(), String> { + let path = settings_path(app_data_dir); + if let Some(parent) = path.parent() { + fs::create_dir_all(parent).map_err(|e| format!("创建目录失败: {}", e))?; + } + let content = serde_json::to_string_pretty(settings).map_err(|e| format!("序列化设置失败: {}", e))?; + fs::write(path, content).map_err(|e| format!("写入设置失败: {}", e)) +} + // ===================== 永久提权标志持久化 ===================== /// 永久提权标志文件路径: {app_data_dir}/monitor/elevate.json @@ -295,6 +330,32 @@ impl MonitorKernel { write_elevate_flag(&self.root.join("elevate.json"), enabled) } + /// 读取监控设置(auto_start 等) + pub fn load_settings(&self) -> MonitorSettings { + load_monitor_settings(&self.root.parent().unwrap_or(&self.root).to_path_buf()) + } + + /// 写入监控设置 + pub fn save_settings(&self, settings: &MonitorSettings) -> Result<(), String> { + save_monitor_settings(&self.root.parent().unwrap_or(&self.root).to_path_buf(), settings) + } + + /// 应用启动时检查是否需要自动启动监控内核 + pub fn auto_start_on_launch(&self, app: &AppHandle) { + let settings = self.load_settings(); + if !settings.auto_start { + return; + } + let monitor = self.clone(); + let app_handle = app.clone(); + tauri::async_runtime::spawn(async move { + match monitor.start_with_subscription(&app_handle).await { + Ok(info) => crate::logger::log_info("monitor", &format!("自动启动成功, pid={:?}", info.pid)), + Err(e) => crate::logger::log_warn("monitor", &format!("自动启动跳过: {}", e)), + } + }); + } + fn cores_dir(&self) -> PathBuf { self.root.join("cores") } @@ -950,6 +1011,25 @@ pub fn monitor_set_elevate_on_launch( state.set_elevate_on_launch(enabled) } +/// 查询"应用启动时自动启动监控内核"是否已启用 +#[tauri::command] +pub fn monitor_get_auto_start( + state: tauri::State<'_, MonitorKernel>, +) -> bool { + state.load_settings().auto_start +} + +/// 设置/清除"应用启动时自动启动监控内核"开关 +#[tauri::command] +pub fn monitor_set_auto_start( + state: tauri::State<'_, MonitorKernel>, + enabled: bool, +) -> Result<(), String> { + let mut settings = state.load_settings(); + settings.auto_start = enabled; + state.save_settings(&settings) +} + /// 查询硬件监控配置(透传 Kernel GET /config/hardware)。 /// 返回当前配置 + 可用硬件/传感器类型清单,供前端 Dialog 渲染。 #[tauri::command] diff --git a/src-tauri/src/quickpanel/commands.rs b/src-tauri/src/quickpanel/commands.rs index 5c32e48..5f0ca89 100644 --- a/src-tauri/src/quickpanel/commands.rs +++ b/src-tauri/src/quickpanel/commands.rs @@ -114,6 +114,8 @@ pub async fn quickpanel_build_file_index(app: AppHandle) -> Result } else { settings.index_dirs }; + // 懒加载:首次构建时自动初始化 DB 连接 + file_index::ensure_initialized(&app); // 阻塞操作放到 spawn_blocking tauri::async_runtime::spawn_blocking(move || file_index::build_index(&dirs)) .await @@ -126,7 +128,10 @@ pub async fn quickpanel_build_file_index(app: AppHandle) -> Result pub async fn quickpanel_search_files( query: String, limit: Option, + app: AppHandle, ) -> Result, String> { + // 懒加载:首次搜索时自动初始化 DB 连接 + file_index::ensure_initialized(&app); tauri::async_runtime::spawn_blocking(move || file_index::search(&query, limit.unwrap_or(50))) .await .map_err(|e| format!("搜索任务失败: {}", e)) @@ -135,7 +140,9 @@ pub async fn quickpanel_search_files( /// 获取索引状态 #[tauri::command] #[specta::specta] -pub fn quickpanel_file_index_stats() -> file_index::IndexStats { +pub async fn quickpanel_file_index_stats(app: AppHandle) -> file_index::IndexStats { + // 懒加载:查询状态前确保 DB 已初始化(未初始化时 stats 返回全 0) + file_index::ensure_initialized(&app); file_index::stats() } diff --git a/src-tauri/src/quickpanel/file_index.rs b/src-tauri/src/quickpanel/file_index.rs index bd22194..2243daa 100644 --- a/src-tauri/src/quickpanel/file_index.rs +++ b/src-tauri/src/quickpanel/file_index.rs @@ -93,6 +93,17 @@ pub fn init(app: &AppHandle) { crate::logger::log_info("quickpanel", &format!("文件索引 DB 已就绪: {}", path.display())); } +/// 懒加载:首次访问文件索引时自动初始化(若尚未初始化)。 +/// 避免应用启动时即打开 SQLite 连接,降低启动 IO 开销。 +pub fn ensure_initialized(app: &AppHandle) { + let guard = index_slot().lock().unwrap_or_else(|e| e.into_inner()); + if guard.is_some() { + return; + } + drop(guard); + init(app); +} + /// 判断索引是否已初始化 fn with_conn(f: F) -> Option where diff --git a/src-tauri/src/screenshot/commands.rs b/src-tauri/src/screenshot/commands.rs index ba01dfb..ecc19ec 100644 --- a/src-tauri/src/screenshot/commands.rs +++ b/src-tauri/src/screenshot/commands.rs @@ -67,6 +67,27 @@ pub async fn screenshot_unregister_shortcut(app: tauri::AppHandle) -> Result<(), Ok(()) } +/// 注册(或切换)贴图全局快捷键。传入空字符串则禁用快捷键。 +/// 按下时 emit 'screenshot-pin-shortcut',由前端切换贴图窗口显示/隐藏。 +#[tauri::command] +#[specta::specta] +pub async fn screenshot_register_pin_shortcut( + app: tauri::AppHandle, + shortcut: String, +) -> Result<(), String> { + crate::shortcut::register_shortcut(&app, "贴图", &shortcut, |a| { + let _ = a.emit(crate::constants::events::SCREENSHOT_PIN_SHORTCUT, ()); + }) +} + +/// 注销贴图全局快捷键 +#[tauri::command] +#[specta::specta] +pub async fn screenshot_unregister_pin_shortcut(app: tauri::AppHandle) -> Result<(), String> { + crate::shortcut::unregister_shortcut(&app, "贴图"); + Ok(()) +} + /// 捕获整个虚拟屏(多显示器拼接)存入静态,不做 PNG 编码 #[tauri::command] #[specta::specta] @@ -349,17 +370,17 @@ pub async fn screenshot_save_png(png_base64: String, path: String) -> Result<(), .map_err(|e| format!("保存任务失败: {}", e))? } -// ===== 截图历史缓存:完整 PNG 落盘缓存目录,内存只保留缩略图 ===== +// ===== 截图历史缓存:完整 PNG 落盘到应用数据目录(持久化,随历史保留数量清理),内存只保留缩略图 ===== -/// 历史缓存根目录(app_cache_dir/screenshot/history) +/// 历史根目录(app_data_dir/screenshot/history) fn history_cache_dir(app: &tauri::AppHandle) -> Result { let dir = app .path() - .app_cache_dir() - .map_err(|e| format!("获取缓存目录失败: {}", e))? + .app_data_dir() + .map_err(|e| format!("获取应用数据目录失败: {}", e))? .join("screenshot") .join("history"); - std::fs::create_dir_all(&dir).map_err(|e| format!("创建缓存目录失败: {}", e))?; + std::fs::create_dir_all(&dir).map_err(|e| format!("创建历史目录失败: {}", e))?; Ok(dir) } diff --git a/src-tauri/src/setup.rs b/src-tauri/src/setup.rs index b8bd8b1..8f76e6c 100644 --- a/src-tauri/src/setup.rs +++ b/src-tauri/src/setup.rs @@ -90,8 +90,10 @@ pub fn init(app: &mut App) -> Result<(), Box> { } app.manage(clipboard); - // ===== 快速面板:快捷键 + 预创建弹窗 + 文件索引 ===== + // ===== 快速面板:快捷键 + 预创建弹窗 ===== // defaultEnabled:true 假设启用;用户在设置页禁用模块时由前端 onDisable 钩子注销快捷键。 + // 文件索引 DB 连接改为懒加载(首次搜索/构建时由 commands 中的 ensure_initialized 触发), + // 避免应用启动时即打开 SQLite 连接,降低启动 IO 开销。 let qp_settings = crate::quickpanel::load_settings(&app.handle()); if !qp_settings.shortcut.trim().is_empty() { let app_handle = app.handle().clone(); @@ -106,8 +108,6 @@ pub fn init(app: &mut App) -> Result<(), Box> { // 预创建弹窗窗口(隐藏),首次按快捷键时直接 show,避免首次创建时序问题 crate::quickpanel::ensure_window(&app_handle); } - // 初始化文件索引数据库(不立即构建,由前端设置页或首次唤起时触发) - crate::quickpanel::file_index::init(&app.handle()); // ===== 托盘菜单 ===== crate::tray_menu::create_tray_menu(app.handle())?; @@ -123,16 +123,9 @@ pub fn init(app: &mut App) -> Result<(), Box> { } } - // monitor Kernel:硬件监控默认启用,被动读取无副作用 + // monitor Kernel:用户在设置中开启"自动启动"时随应用启动 if let Some(monitor) = app.try_state::() { - let monitor = monitor.inner().clone(); - let app_handle = app.handle().clone(); - tauri::async_runtime::spawn(async move { - match monitor.start_with_subscription(&app_handle).await { - Ok(info) => crate::logger::log_info("monitor", &format!("自动启动成功, pid={:?}", info.pid)), - Err(e) => crate::logger::log_warn("monitor", &format!("自动启动跳过: {}", e)), - } - }); + monitor.auto_start_on_launch(app.handle()); } // 截图快捷键由前端 screenshotStore 启动时调用 screenshot_register_shortcut 注册 diff --git a/src-tauri/src/tray_menu.rs b/src-tauri/src/tray_menu.rs index 6e0c1bf..60addac 100644 --- a/src-tauri/src/tray_menu.rs +++ b/src-tauri/src/tray_menu.rs @@ -46,6 +46,7 @@ pub struct ProxyNodeInfo { #[serde(rename_all = "camelCase")] pub struct TrayMenuState { pub proxy_running: bool, + pub system_proxy: bool, pub monitor_running: bool, pub proxy_group: Option, pub proxy_nodes: Vec, @@ -150,6 +151,8 @@ async fn fetch_proxy_nodes(app: &AppHandle) -> Option<(String, Vec<(String, Opti pub async fn get_tray_menu_state(app: &AppHandle) -> TrayMenuState { let proxy_running = is_proxy_running(app); let monitor_running = is_monitor_running(app); + // 读取 Windows 注册表中的真实系统代理状态(不依赖 settings.json 缓存) + let system_proxy = crate::mihomo_manager::get_system_proxy_windows(); let (proxy_group, proxy_nodes, proxy_current) = if proxy_running { match fetch_proxy_nodes(app).await { @@ -172,6 +175,7 @@ pub async fn get_tray_menu_state(app: &AppHandle) -> TrayMenuState { TrayMenuState { proxy_running, + system_proxy, monitor_running, proxy_group, proxy_nodes, @@ -338,6 +342,21 @@ pub async fn tray_menu_action( } } } + "system_proxy_toggle" => { + // 根据注册表当前真实状态切换(不依赖 settings.json 缓存,避免与主界面不同步) + let mihomo = app.state::(); + if crate::mihomo_manager::get_system_proxy_windows() { + if let Err(e) = mihomo.disable_system_proxy() { + crate::logger::log_error("tray", &format!("关闭系统代理失败: {}", e)); + send_notification(&app, "系统代理关闭失败", &e); + } + } else { + if let Err(e) = mihomo.enable_system_proxy() { + crate::logger::log_error("tray", &format!("开启系统代理失败: {}", e)); + send_notification(&app, "系统代理开启失败", &e); + } + } + } "osd_toggle" => { let _ = app.emit(crate::constants::events::TRAY_TOGGLE_OSD, ()); } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 134c5c0..8348778 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -37,6 +37,11 @@ "icons/icon.icns", "icons/icon.ico" ], - "resources": ["binaries/*", "resources/thing-extension/**/*"] + "resources": ["binaries/*", "resources/thing-extension/**/*"], + "windows": { + "nsis": { + "compression": "lzma" + } + } } } diff --git a/src/App.vue b/src/App.vue index f810baf..f7631d0 100644 --- a/src/App.vue +++ b/src/App.vue @@ -10,6 +10,7 @@ import { useAppStore } from '@/stores/appStore' import { useScreenshotStore } from '@/stores/screenshotStore' import { useQuickPanelStore } from '@/stores/quickpanelStore' import { useMonitorStore } from '@/stores/monitorStore' +import { useProcessStore } from '@/stores/processStore' import { TooltipProvider } from '@/components/ui/tooltip' import { moduleRegistry } from '@/modules/registry' import type { ModuleMeta } from '@/types/module' @@ -20,6 +21,7 @@ const appStore = useAppStore() const screenshotStore = useScreenshotStore() const quickpanelStore = useQuickPanelStore() const monitorStore = useMonitorStore() +const processStore = useProcessStore() /** 侧边栏 / 标题栏需要的模块信息(id + name + icon) */ interface NavModule { @@ -193,6 +195,8 @@ onUnmounted(() => { screenshotStore.destroyExportListener() // 释放 OSD 事件监听与配置 watcher monitorStore.disposeOsd() + // 释放进程状态事件监听 + processStore.destroyListener() }) diff --git a/src/components/layout/Sidebar.vue b/src/components/layout/Sidebar.vue index fd6e691..2097f48 100644 --- a/src/components/layout/Sidebar.vue +++ b/src/components/layout/Sidebar.vue @@ -31,14 +31,14 @@ const displayModules = computed(() => { @@ -55,14 +55,14 @@ const displayModules = computed(() => { diff --git a/src/components/layout/TitleBar.vue b/src/components/layout/TitleBar.vue index 5980483..8cd5c34 100644 --- a/src/components/layout/TitleBar.vue +++ b/src/components/layout/TitleBar.vue @@ -2,6 +2,7 @@ import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue' import { Search, Settings, ChevronRight, ArrowUp, Check, Loader2 } from '@lucide/vue' import { Input } from '@/components/ui/input' +import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip' import { invoke } from '@tauri-apps/api/core' import { getCurrentWindow } from '@tauri-apps/api/window' import { useSearchStore, type SearchItem } from '@/stores/searchStore' @@ -257,37 +258,43 @@ const handleBlur = () => { - + + + + + 回到顶部 +
- + + + + + 保存当前模块设置 +
{ class="w-full px-3 py-2 text-left text-sm hover:bg-accent transition-colors flex items-center gap-2" @click="handleSettingSelect(item)" > - +
{{ item.title }} {{ item.description }}
- + @@ -347,7 +354,7 @@ const handleBlur = () => {
@@ -372,7 +373,7 @@ onUnmounted(() => { v-if="!historyList.length" class="flex flex-col items-center justify-center text-muted-foreground py-12" > - +

暂无历史记录,复制内容后将自动收录

{ class="group hover:shadow-md transition-shadow py-0" > - +
-

{{ item.preview }}

+

{{ item.preview }}

{{ kindLabel(item.kind) }} {{ formatTime(item.createdAt) }} @@ -391,15 +392,30 @@ onUnmounted(() => {
- - - + + + + + 复制 + + + + + + {{ item.pinned ? '取消固定' : '固定' }} + + + + + + 删除 +
@@ -414,15 +430,15 @@ onUnmounted(() => { v-if="!pinnedList.length" class="flex flex-col items-center justify-center h-full text-muted-foreground" > - +

暂无固定条目

鼠标悬停历史条目,点击图钉按钮即可固定

- +
-

{{ item.preview }}

+

{{ item.preview }}

{{ kindLabel(item.kind) }} {{ formatTime(item.createdAt) }} @@ -430,15 +446,30 @@ onUnmounted(() => {
- - - + + + + + 复制 + + + + + + 取消固定 + + + + + + 删除 +
@@ -450,7 +481,7 @@ onUnmounted(() => {

- 基本设置 + 基本设置

@@ -519,7 +550,7 @@ onUnmounted(() => {

全局快捷键触发鼠标位置历史弹窗,留空禁用

@@ -563,7 +594,7 @@ onUnmounted(() => {
- +