diff --git a/apps/desktop-shell/scripts/check-config.mjs b/apps/desktop-shell/scripts/check-config.mjs index 328cce0c3..899f369e7 100644 --- a/apps/desktop-shell/scripts/check-config.mjs +++ b/apps/desktop-shell/scripts/check-config.mjs @@ -51,6 +51,13 @@ const appPath = new URL('../src-tauri/src/app.rs', import.meta.url); const app = fs.readFileSync(appPath, 'utf8'); const mainPath = new URL('../src-tauri/src/main.rs', import.meta.url); const main = fs.readFileSync(mainPath, 'utf8'); +if ( + !main.includes( + '#![cfg_attr(all(not(dev), target_os = "windows"), windows_subsystem = "windows")]', + ) +) { + throw new Error('desktop shell release builds on Windows must use the GUI subsystem'); +} const rustSourceDir = new URL('../src-tauri/src/', import.meta.url); const desktopHostBridgeAppearancePath = new URL( '../src-tauri/src/host_bridge/appearance.rs', @@ -2791,8 +2798,10 @@ const requiredRustHostSnippets = [ 'path.contains("bridgeVersion=")', 'path.contains("hostCapabilities=")', 'should_allow_desktop_webview_navigation', + 'matches!(url.scheme(), "http" | "https")', 'url.host_str() == Some("127.0.0.1")', 'url.port_or_known_default() == Some(3000)', + 'Url::parse("http://tauri.localhost/index.html")', 'Url::parse("http://127.0.0.1:3000/works/detail?work=PZ-1")', 'desktop_external_navigation_url', 'open_normalized_desktop_external_url', diff --git a/apps/desktop-shell/src-tauri/src/main.rs b/apps/desktop-shell/src-tauri/src/main.rs index cab5f08f7..02f9d2116 100644 --- a/apps/desktop-shell/src-tauri/src/main.rs +++ b/apps/desktop-shell/src-tauri/src/main.rs @@ -1,3 +1,5 @@ +#![cfg_attr(all(not(dev), target_os = "windows"), windows_subsystem = "windows")] + mod app; mod host_bridge; mod shell; diff --git a/apps/desktop-shell/src-tauri/src/shell/navigation.rs b/apps/desktop-shell/src-tauri/src/shell/navigation.rs index facb4f58c..b1b0ffb66 100644 --- a/apps/desktop-shell/src-tauri/src/shell/navigation.rs +++ b/apps/desktop-shell/src-tauri/src/shell/navigation.rs @@ -122,7 +122,7 @@ fn is_desktop_packaged_asset_url(url: &Url) -> bool { return true; } - url.scheme() == "https" + matches!(url.scheme(), "http" | "https") && url .host_str() .map(|host| host.ends_with(".localhost")) @@ -253,6 +253,16 @@ mod tests { )); assert_eq!(desktop_external_navigation_url(&windows_packaged_url), None); + let windows_http_packaged_url = + Url::parse("http://tauri.localhost/index.html").expect("windows http packaged url"); + assert!(should_allow_desktop_webview_navigation( + &windows_http_packaged_url + )); + assert_eq!( + desktop_external_navigation_url(&windows_http_packaged_url), + None + ); + let same_origin_url = Url::parse("https://app.genarrative.world/works/detail?work=PZ-1") .expect("same-origin url"); assert!(should_allow_desktop_webview_navigation(&same_origin_url)); diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index fe49ce155..e9b9184ae 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -3269,5 +3269,6 @@ - 背景:`npm run desktop-shell:dev` 通过 Tauri `devUrl` 固定加载 `http://127.0.0.1:3000/`,但 Linux dev 端口段逻辑会把未显式指定的 `dev:web` 主站端口映射到用户端口段,例如 `10000+`。只在桌面壳 package script 里设置 `WEB_PORT=3000` 不会让 `scripts/dev.mjs` 把 Web 端口视为显式 CLI 参数,结果 Tauri 仍打开 3000,而 Vite 实际监听其它端口。 - 决策:桌面壳 `beforeDevCommand` 必须执行 `npm --prefix ../.. run dev:web -- --web-port 3000 --strict-web-port`,用 CLI 参数锁定主站 Vite 端口并禁止静默漂移;`devUrl` 继续固定 `http://127.0.0.1:3000/`。如果 3000 被占用,应该释放端口后再启动桌面壳,而不是让 Vite 漂移后继续由 Tauri 加载旧端口。由于主窗口设置了 `create=false` 并由 Rust 手动创建,`app.rs` 在 dev build 下必须把主窗口 URL 替换为 `build.devUrl` 后再补写 HostBridge query;release 仍从 `index.html` 打包资源进入。 +- 2026-06-22 调整:release 打包资源在 Windows WebView 内可能以 `http://tauri.localhost/index.html` 出现,这仍是 Tauri 内部资源,不允许被导航拦截交给系统浏览器;`shell/navigation.rs` 必须允许 `http` / `https` 的 `*.localhost` 留在 WebView。Windows release 二进制必须使用 GUI subsystem,避免正式包启动时额外弹出控制台窗口。 - 影响范围:`apps/desktop-shell/src-tauri/tauri.conf.json`、`apps/desktop-shell/scripts/check-config.mjs`、`scripts/dev.test.ts`、Expo / Tauri HostBridge 方案文档。 -- 验证方式:`npm run test -- scripts/dev.test.ts -t "Linux 桌面壳显式指定 web-port"`、`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml desktop_main_window_config_uses_dev_url_in_dev_builds`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。 +- 验证方式:`npm run test -- scripts/dev.test.ts -t "Linux 桌面壳显式指定 web-port"`、`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml desktop_main_window_config_uses_dev_url_in_dev_builds desktop_webview_navigation_stays_on_packaged_or_same_origin_pages`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。 diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index d3f1c493d..ce52d8a3d 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -2214,6 +2214,14 @@ - 验证:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml desktop_main_window_config_uses_dev_url_in_dev_builds desktop_webview_navigation_stays_on_packaged_or_same_origin_pages`,实际启动时窗口应加载 `http://127.0.0.1:3000/...` 而不是 `tauri.localhost/index.html`。 - 关联:`apps/desktop-shell/src-tauri/src/app.rs`、`apps/desktop-shell/src-tauri/src/shell/url.rs`、`apps/desktop-shell/src-tauri/src/shell/navigation.rs`、`apps/desktop-shell/src-tauri/tauri.conf.json`。 +## Tauri release 的 tauri.localhost 不要交给系统浏览器 + +- 现象:Windows / release 包启动桌面壳时,系统默认浏览器被打开到 `http://tauri.localhost/index.html`。 +- 原因:release 打包资源在 WebView 内可能表现为 `tauri://localhost/index.html`、`https://tauri.localhost/index.html` 或 `http://tauri.localhost/index.html`;如果导航白名单只允许 `tauri:` 和 `https://*.localhost`,`http://tauri.localhost` 会被误判成普通外链并交给 `opener.open_url`。 +- 处理:桌面壳导航策略必须把 `http` / `https` 的 `*.localhost` 都视为 Tauri 内部打包资源,只允许真正外部 `http` / `https`、`mailto`、`tel` 走系统浏览器。Windows release 入口还必须使用 `windows_subsystem = "windows"`,避免正式包额外弹出控制台窗口;dev build 保留控制台日志。 +- 验证:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml desktop_webview_navigation_stays_on_packaged_or_same_origin_pages`、`npm run desktop-shell:typecheck`、Windows release 启动时不应打开系统浏览器或控制台窗口。 +- 关联:`apps/desktop-shell/src-tauri/src/main.rs`、`apps/desktop-shell/src-tauri/src/shell/navigation.rs`、`apps/desktop-shell/scripts/check-config.mjs`。 + ## 自动试玩退出不要回到生成页 - 现象:拼图草稿生成完成后自动进入试玩,用户从试玩退出或使用系统返回时落回生成进度页,页面还暴露“重新生成”按钮。