锁定桌面窗口状态持久化边界

桌面壳窗口状态 flags 抽成可测试函数并覆盖排除可见性全屏和装饰状态

桌面壳配置门禁反查窗口状态 flags 单测边界

宿主壳方案协议和共享决策同步窗口状态验收口径
This commit is contained in:
2026-06-20 09:16:10 +08:00
parent 11b7694045
commit e4e890a147
5 changed files with 32 additions and 4 deletions
@@ -2122,7 +2122,12 @@ const requiredRustHostSnippets = [
'tauri_plugin_single_instance::init',
'desktop_window_state_plugin()',
'tauri_plugin_window_state::Builder::default()',
'desktop_window_state_flags()',
'StateFlags::SIZE | StateFlags::POSITION | StateFlags::MAXIMIZED',
'fn desktop_window_state_flags_keep_visible_state_out_of_persistence()',
'assert!(!flags.contains(StateFlags::VISIBLE));',
'assert!(!flags.contains(StateFlags::FULLSCREEN));',
'assert!(!flags.contains(StateFlags::DECORATIONS));',
'tauri_plugin_deep_link::init()',
'register_desktop_deep_link_events(app)',
'register_desktop_deep_link_schemes(app)',
@@ -2,8 +2,29 @@ use tauri::plugin::TauriPlugin;
use tauri::Runtime;
use tauri_plugin_window_state::StateFlags;
fn desktop_window_state_flags() -> StateFlags {
StateFlags::SIZE | StateFlags::POSITION | StateFlags::MAXIMIZED
}
pub(crate) fn desktop_window_state_plugin<R: Runtime>() -> TauriPlugin<R> {
tauri_plugin_window_state::Builder::default()
.with_state_flags(StateFlags::SIZE | StateFlags::POSITION | StateFlags::MAXIMIZED)
.with_state_flags(desktop_window_state_flags())
.build()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn desktop_window_state_flags_keep_visible_state_out_of_persistence() {
let flags = desktop_window_state_flags();
assert!(flags.contains(StateFlags::SIZE));
assert!(flags.contains(StateFlags::POSITION));
assert!(flags.contains(StateFlags::MAXIMIZED));
assert!(!flags.contains(StateFlags::VISIBLE));
assert!(!flags.contains(StateFlags::FULLSCREEN));
assert!(!flags.contains(StateFlags::DECORATIONS));
}
}
@@ -2673,7 +2673,7 @@
## 2026-06-18 桌面壳窗口状态持久化
- 背景:Tauri 桌面壳已经具备系统托盘、单实例、深链和受控 HostBridge 能力,但用户调整主窗口尺寸、位置或最大化状态后,重启桌面 App 仍回到固定初始窗口配置;如果直接保存完整窗口状态,又可能把托盘隐藏后的可见性状态带到下次启动。
- 决策:桌面壳接入 `tauri-plugin-window-state`,并把插件配置收口到 `apps/desktop-shell/src-tauri/src/shell/window_state.rs`。只保存 `SIZE``POSITION``MAXIMIZED`,不保存 `VISIBLE``FULLSCREEN``DECORATIONS`;该能力属于宿主壳自身体验,不进入 HostBridge capability,不暴露窗口状态插件 command 给 H5。插件注册顺序固定为 single-instance 优先,其后才是 window-state、deep-link 和其它系统插件。
- 决策:桌面壳接入 `tauri-plugin-window-state`,并把插件配置收口到 `apps/desktop-shell/src-tauri/src/shell/window_state.rs`。只保存 `SIZE``POSITION``MAXIMIZED`,不保存 `VISIBLE``FULLSCREEN``DECORATIONS`;该能力属于宿主壳自身体验,不进入 HostBridge capability,不暴露窗口状态插件 command 给 H5。插件注册顺序固定为 single-instance 优先,其后才是 window-state、deep-link 和其它系统插件。`window_state.rs` 必须保留直接 Rust 单测证明这组 flags 边界,桌面壳配置门禁会反查该测试。
- 影响范围:`apps/desktop-shell/src-tauri/Cargo.toml``apps/desktop-shell/src-tauri/Cargo.lock``apps/desktop-shell/src-tauri/src/main.rs``apps/desktop-shell/src-tauri/src/shell/window_state.rs``apps/desktop-shell/scripts/check-config.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`
@@ -212,7 +212,7 @@ Tauri 壳同样只负责桌面宿主能力,不承接玩法业务。
- 崩溃上报、前端 analytics、桌面遥测日志、自动更新和渠道分发 SDK 都必须等真实端点、采集字段、用户同意、隐私策略、签名和发布流程确定后逐项接入;当前桌面壳不安装 Sentry、Datadog、PostHog、Segment、Amplitude、Bugsnag、OpenTelemetry、Tauri log / updater 等相关依赖。
- 桌面壳和根 H5 包不安装 `@tauri-apps/api``@tauri-apps/plugin-*` JS guest 包;生产 H5 只通过 Tauri 注入的 `window.__TAURI__.core.invoke('host_bridge_request', request)` 进入 HostBridge。opener、clipboard、dialog、notification 等能力只保留 Rust Cargo 插件,由 Rust 内部分发并受 capability 白名单约束。
- 桌面深链只作为宿主启动 / 唤醒入口处理,不进入 HostBridge capability,也不把 deep-link 插件 command 授权给 H5。Tauri 只注册 `genarrative` scheme,并接受同源 `https://app.genarrative.world` URL;壳层会把目标路径归一为带 `native_app``tauri_desktop` 和真实 capability 清单的同源 H5 URL,外域、明文协议和危险协议直接丢弃。`navigation.openNativePage` 的同源主动跳转也必须复用同一宿主上下文补写逻辑,避免新页面按普通浏览器运行态启动。
- 桌面窗口状态持久化属于宿主壳自有体验,不进入 HostBridge capability,也不开放窗口状态插件 command 给 H5。Tauri 壳只保存主窗口大小、位置和最大化状态,不保存可见性、全屏或装饰状态,避免托盘隐藏窗口后下次启动被恢复成隐藏状态。
- 桌面窗口状态持久化属于宿主壳自有体验,不进入 HostBridge capability,也不开放窗口状态插件 command 给 H5。Tauri 壳只保存主窗口大小、位置和最大化状态,不保存可见性、全屏或装饰状态,避免托盘隐藏窗口后下次启动被恢复成隐藏状态`shell/window_state.rs` 必须保留 Rust 单测证明这组 flags 边界
- 桌面壳外链打开、WebView 新窗口外链接管、托盘关闭前生命周期注入和窗口隐藏都属于用户可见宿主动作;这些动作失败必须走统一桌面宿主事件日志,配置检查拒绝 `let _ = ...` 静默吞错。
桌面 release 和 dev 模式:
@@ -427,7 +427,7 @@ GameBridge 禁止:
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 暴露任意窗口状态读写,也不改变托盘关闭隐藏、单实例唤醒和托盘恢复主窗口的既有语义。
2026-06-18 追加:桌面壳启用 Tauri 窗口状态持久化。`shell/window_state.rs` 只配置 `tauri-plugin-window-state` 保存主窗口大小、位置和最大化状态,并排除可见性、全屏和装饰状态;插件注册顺序固定为 single-instance 优先,其后才是窗口状态、deep-link 和其它系统能力插件。该能力不进入 HostBridge 清单,不向 H5 暴露任意窗口状态读写,也不改变托盘关闭隐藏、单实例唤醒和托盘恢复主窗口的既有语义。`apps/desktop-shell/scripts/check-config.mjs` 会反查 `desktop_window_state_flags_keep_visible_state_out_of_persistence` 单测,避免后续把可见性、全屏或装饰状态误纳入持久化。
2026-06-18 追加:桌面壳接入 Tauri deep-link 插件,但不开放插件 JS guest API,也不把 deep-link 命令加入 capability。桌面配置只注册 `genarrative` schemeRust 层只接受 `genarrative://open/...``genarrative://app/...``genarrative://<path>``https://app.genarrative.world/...`,统一跳到同源 H5 路径并补写 `clientRuntime=native_app``hostShell=tauri_desktop`、当前平台、版本与真实 `hostCapabilities`。外域、`http:``mailto:``javascript:``file:` 等来源不进入主 WebView;归一成功后的 `window.navigate(...)` 与主窗口恢复 / 聚焦失败必须记录桌面宿主事件日志,不能静默吞错。
File diff suppressed because one or more lines are too long