From ddfdc409f38d2f3f17e996870dd1f046cff2165d Mon Sep 17 00:00:00 2001 From: kdletters Date: Thu, 18 Jun 2026 21:14:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A1=8C=E9=9D=A2=E5=A3=B3=E8=A1=A5=E9=BD=90?= =?UTF-8?q?=E5=8E=86=E5=8F=B2=E5=AF=BC=E8=88=AA=E8=8F=9C=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为桌面应用菜单增加后退和前进历史动作 补充桌面菜单测试与配置门禁 更新桌面壳菜单导航文档 --- apps/desktop-shell/scripts/check-config.mjs | 8 +++ .../desktop-shell/src-tauri/src/shell/menu.rs | 65 ++++++++++++++++++- .../shared-memory/decision-log.md | 2 +- ...ExpoReactNative与Tauri宿主壳方案-2026-06-17.md | 2 +- 4 files changed, 74 insertions(+), 3 deletions(-) diff --git a/apps/desktop-shell/scripts/check-config.mjs b/apps/desktop-shell/scripts/check-config.mjs index bd8970350..d013d7df3 100644 --- a/apps/desktop-shell/scripts/check-config.mjs +++ b/apps/desktop-shell/scripts/check-config.mjs @@ -1107,8 +1107,16 @@ const requiredRustHostSnippets = [ 'tauri_plugin_clipboard_manager::init()', 'register_desktop_app_menu(app)?', 'DESKTOP_APP_MENU_SHOW', + 'DESKTOP_APP_MENU_BACK', + 'DESKTOP_APP_MENU_FORWARD', 'DESKTOP_APP_MENU_RELOAD', 'DESKTOP_APP_MENU_QUIT', + 'DesktopAppMenuAction::GoBack', + 'DesktopAppMenuAction::GoForward', + 'DESKTOP_HISTORY_BACK_SCRIPT', + 'DESKTOP_HISTORY_FORWARD_SCRIPT', + 'window.history.back(); true;', + 'window.history.forward(); true;', 'resolve_desktop_app_menu_action', 'app.set_menu', 'app.on_menu_event', diff --git a/apps/desktop-shell/src-tauri/src/shell/menu.rs b/apps/desktop-shell/src-tauri/src/shell/menu.rs index 7334c403a..ea603302e 100644 --- a/apps/desktop-shell/src-tauri/src/shell/menu.rs +++ b/apps/desktop-shell/src-tauri/src/shell/menu.rs @@ -1,13 +1,20 @@ use crate::shell::tray::{reload_main_window, show_main_window}; use tauri::menu::{Menu, MenuItem, PredefinedMenuItem, Submenu}; +use tauri::Manager; pub(crate) const DESKTOP_APP_MENU_SHOW: &str = "app-menu-show-main-window"; +pub(crate) const DESKTOP_APP_MENU_BACK: &str = "app-menu-history-back"; +pub(crate) const DESKTOP_APP_MENU_FORWARD: &str = "app-menu-history-forward"; pub(crate) const DESKTOP_APP_MENU_RELOAD: &str = "app-menu-reload-main-window"; pub(crate) const DESKTOP_APP_MENU_QUIT: &str = "app-menu-quit-desktop-shell"; +pub(crate) const DESKTOP_HISTORY_BACK_SCRIPT: &str = "window.history.back(); true;"; +pub(crate) const DESKTOP_HISTORY_FORWARD_SCRIPT: &str = "window.history.forward(); true;"; #[derive(Debug, PartialEq, Eq)] pub(crate) enum DesktopAppMenuAction { ShowMainWindow, + GoBack, + GoForward, ReloadMainWindow, QuitApp, Ignore, @@ -16,17 +23,41 @@ pub(crate) enum DesktopAppMenuAction { pub(crate) fn resolve_desktop_app_menu_action(menu_id: &str) -> DesktopAppMenuAction { match menu_id { DESKTOP_APP_MENU_SHOW => DesktopAppMenuAction::ShowMainWindow, + DESKTOP_APP_MENU_BACK => DesktopAppMenuAction::GoBack, + DESKTOP_APP_MENU_FORWARD => DesktopAppMenuAction::GoForward, DESKTOP_APP_MENU_RELOAD => DesktopAppMenuAction::ReloadMainWindow, DESKTOP_APP_MENU_QUIT => DesktopAppMenuAction::QuitApp, _ => DesktopAppMenuAction::Ignore, } } +fn eval_main_window_history_back(app: &tauri::AppHandle) -> tauri::Result<()> { + if let Some(window) = app.get_webview_window("main") { + window.eval(DESKTOP_HISTORY_BACK_SCRIPT)?; + } + + Ok(()) +} + +fn eval_main_window_history_forward(app: &tauri::AppHandle) -> tauri::Result<()> { + if let Some(window) = app.get_webview_window("main") { + window.eval(DESKTOP_HISTORY_FORWARD_SCRIPT)?; + } + + Ok(()) +} + fn handle_desktop_app_menu_action(app: &tauri::AppHandle, action: DesktopAppMenuAction) { match action { DesktopAppMenuAction::ShowMainWindow => { let _ = show_main_window(app); } + DesktopAppMenuAction::GoBack => { + let _ = eval_main_window_history_back(app); + } + DesktopAppMenuAction::GoForward => { + let _ = eval_main_window_history_forward(app); + } DesktopAppMenuAction::ReloadMainWindow => { let _ = reload_main_window(app); } @@ -38,6 +69,14 @@ fn handle_desktop_app_menu_action(app: &tauri::AppHandle, action: DesktopAppMenu pub(crate) fn register_desktop_app_menu(app: &tauri::App) -> tauri::Result<()> { let show_item = MenuItem::with_id(app, DESKTOP_APP_MENU_SHOW, "显示主窗口", true, None::<&str>)?; + let back_item = MenuItem::with_id(app, DESKTOP_APP_MENU_BACK, "后退", true, Some("Alt+Left"))?; + let forward_item = MenuItem::with_id( + app, + DESKTOP_APP_MENU_FORWARD, + "前进", + true, + Some("Alt+Right"), + )?; let reload_item = MenuItem::with_id( app, DESKTOP_APP_MENU_RELOAD, @@ -57,7 +96,14 @@ pub(crate) fn register_desktop_app_menu(app: &tauri::App) -> tauri::Result<()> { app, "Genarrative", true, - &[&show_item, &reload_item, &app_separator, &quit_item], + &[ + &show_item, + &back_item, + &forward_item, + &reload_item, + &app_separator, + &quit_item, + ], )?; let undo_item = PredefinedMenuItem::undo(app, Some("撤销"))?; @@ -120,6 +166,14 @@ mod tests { resolve_desktop_app_menu_action(DESKTOP_APP_MENU_SHOW), DesktopAppMenuAction::ShowMainWindow ); + assert_eq!( + resolve_desktop_app_menu_action(DESKTOP_APP_MENU_BACK), + DesktopAppMenuAction::GoBack + ); + assert_eq!( + resolve_desktop_app_menu_action(DESKTOP_APP_MENU_FORWARD), + DesktopAppMenuAction::GoForward + ); assert_eq!( resolve_desktop_app_menu_action(DESKTOP_APP_MENU_RELOAD), DesktopAppMenuAction::ReloadMainWindow @@ -133,4 +187,13 @@ mod tests { DesktopAppMenuAction::Ignore ); } + + #[test] + fn desktop_app_menu_history_scripts_are_fixed_browser_history_calls() { + assert_eq!(DESKTOP_HISTORY_BACK_SCRIPT, "window.history.back(); true;"); + assert_eq!( + DESKTOP_HISTORY_FORWARD_SCRIPT, + "window.history.forward(); true;" + ); + } } diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 733991244..6ca9aa19c 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -2506,7 +2506,7 @@ ## 2026-06-18 桌面壳应用菜单 - 背景:桌面壳方案要求 Tauri 承接系统菜单,但当前桌面壳只有系统托盘菜单和 HostBridge 受控能力;可分发桌面包缺少常规应用菜单会让刷新、退出、系统编辑和窗口操作只能依赖 WebView 或托盘。 -- 决策:新增 `apps/desktop-shell/src-tauri/src/shell/menu.rs` 注册 Tauri 应用菜单。应用菜单只复用宿主壳级显示主窗口、刷新主窗口和退出应用动作;编辑菜单和窗口菜单使用 Tauri 原生预定义项承接剪切、复制、粘贴、全选、最小化、最大化和关闭窗口。该能力不进入 HostBridge capability,不开放菜单 API、shell API 或任意窗口控制给 H5;菜单注册失败直接阻断启动,避免生产桌面壳缺少系统菜单仍静默运行。 +- 决策:新增 `apps/desktop-shell/src-tauri/src/shell/menu.rs` 注册 Tauri 应用菜单。应用菜单只复用宿主壳级显示主窗口、后退、前进、刷新主窗口和退出应用动作;后退 / 前进只执行固定 `window.history.back(); true;` 与 `window.history.forward(); true;`,无历史记录时按浏览器 no-op 处理,不新增 HostBridge method,也不接收 H5 payload 或任意脚本;编辑菜单和窗口菜单使用 Tauri 原生预定义项承接剪切、复制、粘贴、全选、最小化、最大化和关闭窗口。该能力不进入 HostBridge capability,不开放菜单 API、shell API 或任意窗口控制给 H5;菜单注册失败直接阻断启动,避免生产桌面壳缺少系统菜单仍静默运行。 - 影响范围:`apps/desktop-shell/src-tauri/src/main.rs`、`apps/desktop-shell/src-tauri/src/shell/menu.rs`、`apps/desktop-shell/src-tauri/src/shell/tray.rs`、`apps/desktop-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、Expo / Tauri HostBridge 方案文档。 - 验证方式:`npm run desktop-shell:typecheck`、`npm run desktop-shell:test`、`npm run desktop-shell:build -- --no-bundle`、`npm run check:native-shells`、`npm run typecheck -- --pretty false`、`npm run check:encoding`、`git diff --check`。 diff --git a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md index c1e603461..5dfc50684 100644 --- a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md +++ b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md @@ -356,7 +356,7 @@ GameBridge 禁止: 2026-06-18 追加:桌面壳启用 Tauri 真实系统托盘,并复用品牌图标。托盘菜单只提供宿主壳级动作:显示主窗口、刷新主窗口和退出应用;左键点击托盘图标恢复并聚焦主窗口。该能力不进入 HostBridge capability 清单,不向 H5 暴露托盘 API、菜单 API、shell API 或任意窗口控制;如果当前桌面环境无法注册托盘,壳会继续启动主窗口。托盘注册成功时,用户点击主窗口关闭按钮会隐藏到托盘,必须通过托盘“退出”动作结束应用;托盘注册失败时不拦截关闭,避免窗口消失后没有恢复入口。 -2026-06-18 追加:桌面壳启用 Tauri 应用菜单。`shell/menu.rs` 注册应用菜单、编辑菜单和窗口菜单,其中应用菜单只复用宿主壳级动作:显示主窗口、刷新主窗口和退出应用;编辑 / 窗口项使用 Tauri 原生预定义菜单项承接系统剪切、复制、粘贴、全选、最小化、最大化和关闭窗口。该能力不进入 HostBridge capability,不向 H5 暴露菜单 API、shell API 或任意窗口控制;应用菜单注册失败视为桌面壳启动失败,避免可分发桌面包缺少系统菜单仍静默运行。 +2026-06-18 追加:桌面壳启用 Tauri 应用菜单。`shell/menu.rs` 注册应用菜单、编辑菜单和窗口菜单,其中应用菜单只复用宿主壳级动作:显示主窗口、后退、前进、刷新主窗口和退出应用;后退 / 前进只执行固定 `window.history.back(); true;` 与 `window.history.forward(); true;`,无历史记录时按浏览器 no-op 处理,不新增 HostBridge method,也不接收 H5 payload 或任意脚本;编辑 / 窗口项使用 Tauri 原生预定义菜单项承接系统剪切、复制、粘贴、全选、最小化、最大化和关闭窗口。该能力不进入 HostBridge capability,不向 H5 暴露菜单 API、shell API 或任意窗口控制;应用菜单注册失败视为桌面壳启动失败,避免可分发桌面包缺少系统菜单仍静默运行。 2026-06-18 追加:桌面壳启用 Tauri 单实例。用户重复启动桌面 App 时,新实例会退出并唤醒已有主窗口;该回调只执行显示、取消最小化和聚焦主窗口,不把第二实例的命令行参数或工作目录作为事件透传给 H5。Windows / Linux 上由单实例插件的 `deep-link` feature 把二次实例 URL 交给 deep-link 插件,仍由 `shell/deep_link.rs` 做受控归一。