记录桌面壳宿主动作失败

桌面壳菜单托盘动作失败改为统一日志记录

桌面壳单实例唤醒失败改为统一日志记录

桌面壳配置门禁禁止宿主动作静默吞错

宿主壳文档同步动作失败处理规则
This commit is contained in:
2026-06-20 05:57:05 +08:00
parent 52526bd2ae
commit 8f29ddf8e0
6 changed files with 37 additions and 11 deletions
@@ -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(
+1 -1
View File
@@ -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));
}
}
}))
@@ -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 => {}
@@ -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 => {}
@@ -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 返回栈注入链路需要和扫码链路一样有可执行测试覆盖,不能只靠字符串门禁。
@@ -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 暴露任意窗口状态读写,也不改变托盘关闭隐藏、单实例唤醒和托盘恢复主窗口的既有语义。