From 8f29ddf8e077e443c2d9e14925969a66227c6630 Mon Sep 17 00:00:00 2001 From: kdletters Date: Sat, 20 Jun 2026 05:57:05 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=B0=E5=BD=95=E6=A1=8C=E9=9D=A2=E5=A3=B3?= =?UTF-8?q?=E5=AE=BF=E4=B8=BB=E5=8A=A8=E4=BD=9C=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 桌面壳菜单托盘动作失败改为统一日志记录 桌面壳单实例唤醒失败改为统一日志记录 桌面壳配置门禁禁止宿主动作静默吞错 宿主壳文档同步动作失败处理规则 --- apps/desktop-shell/scripts/check-config.mjs | 12 ++++++++++++ apps/desktop-shell/src-tauri/src/app.rs | 2 +- apps/desktop-shell/src-tauri/src/shell/menu.rs | 12 ++++++++---- apps/desktop-shell/src-tauri/src/shell/tray.rs | 9 ++++++--- docs/project-memory/shared-memory/decision-log.md | 7 +++++++ ...架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md | 6 +++--- 6 files changed, 37 insertions(+), 11 deletions(-) diff --git a/apps/desktop-shell/scripts/check-config.mjs b/apps/desktop-shell/scripts/check-config.mjs index 15decce01..1fb796814 100644 --- a/apps/desktop-shell/scripts/check-config.mjs +++ b/apps/desktop-shell/scripts/check-config.mjs @@ -2108,6 +2108,14 @@ const requiredRustHostSnippets = [ 'open_desktop_deep_link_url(window: &WebviewWindow, url: &Url) -> tauri::Result<()>', 'window.navigate(target_url)?', 'show_main_window(window.app_handle())', + 'log_desktop_host_event_result("app_menu.show", show_main_window(app))', + 'log_desktop_host_event_result("app_menu.back", eval_main_window_history_back(app))', + '"app_menu.forward"', + 'eval_main_window_history_forward(app)', + 'log_desktop_host_event_result("app_menu.reload", reload_main_window(app))', + 'log_desktop_host_event_result("tray.show", show_main_window(app))', + 'log_desktop_host_event_result("tray.reload", reload_main_window(app))', + 'log_desktop_host_event_result("single_instance.show", show_main_window(app))', 'DESKTOP_DEEP_LINK_HOSTS', 'resolve_desktop_single_instance_action', 'tauri_plugin_clipboard_manager::init()', @@ -2715,6 +2723,10 @@ for (const blockedLifecycleSnippet of [ 'let _ = emit_desktop_image_drop_event(&drop_window', 'let _ = window.navigate(target_url)', 'let _ = show_main_window(window.app_handle())', + 'let _ = show_main_window(app)', + 'let _ = eval_main_window_history_back(app)', + 'let _ = eval_main_window_history_forward(app)', + 'let _ = reload_main_window(app)', ]) { if (rustHostSource.includes(blockedLifecycleSnippet)) { throw new Error( diff --git a/apps/desktop-shell/src-tauri/src/app.rs b/apps/desktop-shell/src-tauri/src/app.rs index fcbcde11d..250441e94 100644 --- a/apps/desktop-shell/src-tauri/src/app.rs +++ b/apps/desktop-shell/src-tauri/src/app.rs @@ -54,7 +54,7 @@ pub(crate) fn run() { .plugin(tauri_plugin_single_instance::init(|app, _argv, _cwd| { match resolve_desktop_single_instance_action() { DesktopSingleInstanceAction::ShowMainWindow => { - let _ = show_main_window(app); + log_desktop_host_event_result("single_instance.show", show_main_window(app)); } } })) diff --git a/apps/desktop-shell/src-tauri/src/shell/menu.rs b/apps/desktop-shell/src-tauri/src/shell/menu.rs index ea603302e..7f7a41715 100644 --- a/apps/desktop-shell/src-tauri/src/shell/menu.rs +++ b/apps/desktop-shell/src-tauri/src/shell/menu.rs @@ -1,3 +1,4 @@ +use crate::shell::lifecycle::log_desktop_host_event_result; use crate::shell::tray::{reload_main_window, show_main_window}; use tauri::menu::{Menu, MenuItem, PredefinedMenuItem, Submenu}; use tauri::Manager; @@ -50,16 +51,19 @@ fn eval_main_window_history_forward(app: &tauri::AppHandle) -> tauri::Result<()> fn handle_desktop_app_menu_action(app: &tauri::AppHandle, action: DesktopAppMenuAction) { match action { DesktopAppMenuAction::ShowMainWindow => { - let _ = show_main_window(app); + log_desktop_host_event_result("app_menu.show", show_main_window(app)); } DesktopAppMenuAction::GoBack => { - let _ = eval_main_window_history_back(app); + log_desktop_host_event_result("app_menu.back", eval_main_window_history_back(app)); } DesktopAppMenuAction::GoForward => { - let _ = eval_main_window_history_forward(app); + log_desktop_host_event_result( + "app_menu.forward", + eval_main_window_history_forward(app), + ); } DesktopAppMenuAction::ReloadMainWindow => { - let _ = reload_main_window(app); + log_desktop_host_event_result("app_menu.reload", reload_main_window(app)); } DesktopAppMenuAction::QuitApp => app.exit(0), DesktopAppMenuAction::Ignore => {} diff --git a/apps/desktop-shell/src-tauri/src/shell/tray.rs b/apps/desktop-shell/src-tauri/src/shell/tray.rs index 5d4db09b3..70cd6a1c9 100644 --- a/apps/desktop-shell/src-tauri/src/shell/tray.rs +++ b/apps/desktop-shell/src-tauri/src/shell/tray.rs @@ -1,4 +1,7 @@ -use crate::shell::lifecycle::{emit_current_desktop_lifecycle_event, emit_desktop_lifecycle_event}; +use crate::shell::lifecycle::{ + emit_current_desktop_lifecycle_event, emit_desktop_lifecycle_event, + log_desktop_host_event_result, +}; use tauri::menu::{Menu, MenuItem}; use tauri::tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent}; use tauri::{Manager, WebviewWindow, WindowEvent}; @@ -81,10 +84,10 @@ pub(crate) fn reload_main_window(app: &tauri::AppHandle) -> tauri::Result<()> { fn handle_desktop_tray_action(app: &tauri::AppHandle, action: DesktopTrayAction) { match action { DesktopTrayAction::ShowMainWindow => { - let _ = show_main_window(app); + log_desktop_host_event_result("tray.show", show_main_window(app)); } DesktopTrayAction::ReloadMainWindow => { - let _ = reload_main_window(app); + log_desktop_host_event_result("tray.reload", reload_main_window(app)); } DesktopTrayAction::QuitApp => app.exit(0), DesktopTrayAction::Ignore => {} diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 8cf55e6cc..82a2d1097 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -2866,6 +2866,13 @@ - 影响范围:`apps/desktop-shell/src-tauri/src/shell/deep_link.rs`、`apps/desktop-shell/scripts/check-config.mjs`、宿主壳能力统一协议文档。 - 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。 +## 2026-06-20 桌面壳菜单托盘与单实例动作失败不可静默 + +- 背景:桌面壳应用菜单、托盘菜单和单实例唤醒是生产用户恢复、刷新和导航主窗口的宿主入口;如果这些动作失败仍被 `let _ = ...` 静默忽略,用户会看到菜单、托盘或二次启动无反应且无法排查。 +- 决策:应用菜单显示、后退、前进、刷新主窗口,托盘显示、刷新主窗口,以及单实例唤醒主窗口动作失败必须走统一桌面宿主事件日志;配置检查拒绝这些入口继续对主窗口动作使用 `let _ = ...` 静默吞错。 +- 影响范围:`apps/desktop-shell/src-tauri/src/app.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`、宿主壳方案文档。 +- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。 + ## 2026-06-20 移动壳 ShellApp HostBridge 事件注入必须可执行覆盖 - 背景:Expo 移动壳声明 `host.events`、`app.lifecycle`、`network.statusChanged` 和 `navigation.canGoBack`,但 ShellApp 真实 AppState、Network 和 WebView 返回栈注入链路需要和扫码链路一样有可执行测试覆盖,不能只靠字符串门禁。 diff --git a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md index 6780103c9..0e0f438c0 100644 --- a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md +++ b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md @@ -419,11 +419,11 @@ GameBridge 禁止: 2026-06-18 追加:桌面壳 WebView 每次页面加载完成后都会回放当前 `app.lifecycle` 并重新安装 `network.statusChanged` 监听脚本,覆盖托盘刷新、`app.reloadWebView` 和 H5 自刷新后的新 JS 上下文。回放会读取主窗口当前 `is_visible()`、`is_minimized()` 和 `is_focused()`,隐藏或最小化状态下不得误报 active;事件仍只通过 HostBridge message 注入,不开放 Tauri event 插件或额外 command。 -2026-06-18 追加:桌面壳启用 Tauri 真实系统托盘,并复用品牌图标。托盘菜单只提供宿主壳级动作:显示主窗口、刷新主窗口和退出应用;左键点击托盘图标恢复并聚焦主窗口。该能力不进入 HostBridge capability 清单,不向 H5 暴露托盘 API、菜单 API、shell API 或任意窗口控制;如果当前桌面环境无法注册托盘,壳会继续启动主窗口。托盘注册成功时,用户点击主窗口关闭按钮会先通过 `app.lifecycle` 注入 `background` / `hidden`,再隐藏到托盘,必须通过托盘“退出”动作结束应用;托盘恢复、应用菜单显示、单实例唤醒和 deep link 唤醒都会重新显示、取消最小化、聚焦主窗口并回放当前生命周期状态。托盘注册失败时不拦截关闭,避免窗口消失后没有恢复入口。 +2026-06-18 追加:桌面壳启用 Tauri 真实系统托盘,并复用品牌图标。托盘菜单只提供宿主壳级动作:显示主窗口、刷新主窗口和退出应用;左键点击托盘图标恢复并聚焦主窗口。该能力不进入 HostBridge capability 清单,不向 H5 暴露托盘 API、菜单 API、shell API 或任意窗口控制;如果当前桌面环境无法注册托盘,壳会继续启动主窗口。托盘注册成功时,用户点击主窗口关闭按钮会先通过 `app.lifecycle` 注入 `background` / `hidden`,再隐藏到托盘,必须通过托盘“退出”动作结束应用;托盘恢复、应用菜单显示、单实例唤醒和 deep link 唤醒都会重新显示、取消最小化、聚焦主窗口并回放当前生命周期状态。托盘注册失败时不拦截关闭,避免窗口消失后没有恢复入口;托盘显示和刷新主窗口动作失败必须记录桌面宿主事件日志,不能静默吞错。 -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 应用菜单。`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` 做受控归一。 +2026-06-18 追加:桌面壳启用 Tauri 单实例。用户重复启动桌面 App 时,新实例会退出并唤醒已有主窗口;该回调只执行显示、取消最小化和聚焦主窗口,不把第二实例的命令行参数或工作目录作为事件透传给 H5。Windows / Linux 上由单实例插件的 `deep-link` feature 把二次实例 URL 交给 deep-link 插件,仍由 `shell/deep_link.rs` 做受控归一;单实例唤醒主窗口失败必须记录桌面宿主事件日志,不能静默吞错。 2026-06-18 追加:桌面壳启用 Tauri 窗口状态持久化。`shell/window_state.rs` 只配置 `tauri-plugin-window-state` 保存主窗口大小、位置和最大化状态,并排除可见性、全屏和装饰状态;插件注册顺序固定为 single-instance 优先,其后才是窗口状态、deep-link 和其它系统能力插件。该能力不进入 HostBridge 清单,不向 H5 暴露任意窗口状态读写,也不改变托盘关闭隐藏、单实例唤醒和托盘恢复主窗口的既有语义。