修复桌面壳开发入口地址

桌面壳 dev 模式手动创建主窗口时改用 Tauri devUrl

桌面导航策略允许本机 Vite devUrl 留在 WebView 内

补充桌面壳 devUrl 回归测试和文档记忆
This commit is contained in:
2026-06-22 11:35:29 +08:00
parent 15623a9eff
commit f0b9f3f5c2
6 changed files with 82 additions and 4 deletions
@@ -2772,12 +2772,16 @@ const requiredRustHostSnippets = [
'desktop_h5_url_with_host_context(target_url)',
'desktop_h5_url_with_host_context(normalized_url)',
'desktop_window_config_with_runtime_platform',
'apply_desktop_dev_url_to_main_window_config',
'DESKTOP_MAIN_WINDOW_LABEL: &str = "main"',
'desktop_main_window_config_from_windows',
'.find(|window| window.label == DESKTOP_MAIN_WINDOW_LABEL)',
'tauri::Error::WindowNotFound',
'desktop_main_window_config(app)?',
'desktop_main_window_config_uses_labeled_main_window',
'desktop_main_window_config_uses_dev_url_in_dev_builds',
'config.url = tauri::WebviewUrl::External(dev_url.clone())',
'assert_eq!(url.origin().ascii_serialization(), "http://127.0.0.1:3000")',
'path.starts_with("index.html?")',
'path.contains("clientRuntime=native_app")',
'path.contains("clientType=native_app")',
@@ -2787,6 +2791,9 @@ const requiredRustHostSnippets = [
'path.contains("bridgeVersion=")',
'path.contains("hostCapabilities=")',
'should_allow_desktop_webview_navigation',
'url.host_str() == Some("127.0.0.1")',
'url.port_or_known_default() == Some(3000)',
'Url::parse("http://127.0.0.1:3000/works/detail?work=PZ-1")',
'desktop_external_navigation_url',
'open_normalized_desktop_external_url',
'open_desktop_external_navigation',
+48 -1
View File
@@ -32,10 +32,24 @@ fn desktop_main_window_config_from_windows(
.ok_or(tauri::Error::WindowNotFound)
}
#[cfg(dev)]
fn apply_desktop_dev_url_to_main_window_config(
mut config: tauri::utils::config::WindowConfig,
dev_url: &tauri::Url,
) -> tauri::utils::config::WindowConfig {
config.url = tauri::WebviewUrl::External(dev_url.clone());
desktop_window_config_with_runtime_platform(config)
}
fn desktop_main_window_config(
app: &tauri::App,
) -> tauri::Result<tauri::utils::config::WindowConfig> {
desktop_main_window_config_from_windows(&app.config().app.windows)
let config = desktop_main_window_config_from_windows(&app.config().app.windows)?;
#[cfg(dev)]
if let Some(dev_url) = app.config().build.dev_url.as_ref() {
return Ok(apply_desktop_dev_url_to_main_window_config(config, dev_url));
}
Ok(config)
}
fn desktop_new_window_external_url(url: &tauri::Url) -> Option<String> {
@@ -157,6 +171,39 @@ mod tests {
}
}
#[cfg(dev)]
#[test]
fn desktop_main_window_config_uses_dev_url_in_dev_builds() {
let mut main_window = tauri::utils::config::WindowConfig::default();
main_window.label = "main".to_string();
main_window.url = tauri::WebviewUrl::App(PathBuf::from("index.html"));
let mut config = tauri::utils::config::Config::default();
config.app.windows = vec![main_window];
config.build.dev_url =
Some(tauri::Url::parse("http://127.0.0.1:3000/").expect("desktop dev url"));
let mut resolved = desktop_main_window_config_from_windows(&config.app.windows)
.expect("main window config");
if let Some(dev_url) = config.build.dev_url.as_ref() {
resolved = apply_desktop_dev_url_to_main_window_config(resolved, dev_url);
}
match resolved.url {
tauri::WebviewUrl::External(url) => {
assert_eq!(url.origin().ascii_serialization(), "http://127.0.0.1:3000");
assert_eq!(url.path(), "/");
assert_eq!(
url.query_pairs()
.find(|(key, _)| key == "hostShell")
.map(|(_, value)| value.into_owned()),
Some("tauri_desktop".to_string())
);
}
other => panic!("unexpected dev main window url {other:?}"),
}
}
#[test]
fn desktop_main_window_config_rejects_missing_main_window() {
let mut secondary_window = tauri::utils::config::WindowConfig::default();
@@ -134,6 +134,14 @@ pub(crate) fn should_allow_desktop_webview_navigation(url: &Url) -> bool {
return true;
}
#[cfg(dev)]
if url.scheme() == "http"
&& url.host_str() == Some("127.0.0.1")
&& url.port_or_known_default() == Some(3000)
{
return true;
}
if url.scheme() == "https" {
let base_url = Url::parse(WEB_APP_ORIGIN).ok();
return base_url
@@ -249,6 +257,14 @@ mod tests {
.expect("same-origin url");
assert!(should_allow_desktop_webview_navigation(&same_origin_url));
assert_eq!(desktop_external_navigation_url(&same_origin_url), None);
#[cfg(dev)]
{
let dev_url =
Url::parse("http://127.0.0.1:3000/works/detail?work=PZ-1").expect("dev url");
assert!(should_allow_desktop_webview_navigation(&dev_url));
assert_eq!(desktop_external_navigation_url(&dev_url), None);
}
}
#[test]
@@ -3268,6 +3268,6 @@
## 2026-06-21 Tauri devUrl 与 Vite 端口必须显式对齐
- 背景:`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 加载旧端口。
- 决策:桌面壳 `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` 打包资源进入。
- 影响范围:`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"`、`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`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
@@ -2206,6 +2206,14 @@
- 验证:`npm run test -- scripts/dev.test.ts -t "Linux 桌面壳显式指定 web-port"`、`npm run desktop-shell:typecheck`、实际启动时终端应显示 `[dev] web: http://127.0.0.1:3000`。
- 关联:`apps/desktop-shell/src-tauri/tauri.conf.json`、`apps/desktop-shell/scripts/check-config.mjs`、`scripts/dev.mjs`。
## Tauri 手动创建主窗口时 devUrl 不会自动套到 index.html
- 现象:`npm run desktop-shell:dev` 启动后窗口地址显示 `http://tauri.localhost/index.html` 或 `tauri://localhost/index.html`,即使 Vite 已经在 `http://127.0.0.1:3000/` 正常监听。
- 原因:桌面壳为了注册导航、下载、生命周期和托盘行为,把 Tauri 配置里的主窗口设为 `create=false`,再在 Rust `app.rs` 中用 `WebviewWindowBuilder::from_config(...)` 手动创建窗口。此时如果只读取 `app.windows[].url = index.html` 并补 HostBridge query,手动窗口会沿 release 入口走打包资源协议;Tauri CLI 的 `build.devUrl` 不会自动替换这份手动克隆后的窗口 URL。
- 处理:`app.rs` 在 dev build 下必须先把主窗口 URL 替换为 `config.build.dev_url`,再调用 `desktop_window_config_with_runtime_platform(...)` 补写宿主上下文;`shell/navigation.rs` 也必须允许 dev build 下的 `http://127.0.0.1:3000` 留在 WebView 内,不要把自己的 Vite 首页当外链交给系统浏览器。release build 保持 `index.html` 打包入口。
- 验证:`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`。
## 自动试玩退出不要回到生成页
- 现象:拼图草稿生成完成后自动进入试玩,用户从试玩退出或使用系统返回时落回生成进度页,页面还暴露“重新生成”按钮。
@@ -560,7 +560,7 @@ GameBridge 禁止:
2026-06-18 追加:桌面壳 bundle 图标集从现有真实品牌 PNG `apps/desktop-shell/src-tauri/icons/icon.png` 派生,补齐 `32x32.png`、`128x128.png`、`128x128@2x.png`、`icon.ico` 和 `icon.icns`,Tauri `bundle.icon` 同时声明这些平台图标。没有引入外部素材或占位图;`apps/desktop-shell/scripts/check-config.mjs` 会校验 PNG 尺寸、ICO 多尺寸头部、ICNS 容器长度和 bundle 图标列表,避免后续退回单图标或替换为非品牌素材。
2026-06-21 追加:桌面壳本地开发入口必须让 Tauri `devUrl` 和主站 Vite 实际监听端口保持一致。`apps/desktop-shell/src-tauri/tauri.conf.json` 的 `devUrl` 固定为 `http://127.0.0.1:3000/`,`beforeDevCommand` 必须执行 `npm --prefix ../.. run dev:web -- --web-port 3000 --strict-web-port`,不能只依赖 `WEB_PORT=3000` 环境变量;Linux 用户端口段分配会改写未显式指定的 Web 端口,裸 `dev:web` 可能漂移到 `10000+`,导致 Tauri 窗口加载 3000 时白屏或连接失败。3000 被占用时必须直接失败并提示释放端口,不能静默漂移。`apps/desktop-shell/scripts/check-config.mjs` 和 `scripts/dev.test.ts` 会反查该口径。
2026-06-21 追加:桌面壳本地开发入口必须让 Tauri `devUrl` 和主站 Vite 实际监听端口保持一致。`apps/desktop-shell/src-tauri/tauri.conf.json` 的 `devUrl` 固定为 `http://127.0.0.1:3000/`,`beforeDevCommand` 必须执行 `npm --prefix ../.. run dev:web -- --web-port 3000 --strict-web-port`,不能只依赖 `WEB_PORT=3000` 环境变量;Linux 用户端口段分配会改写未显式指定的 Web 端口,裸 `dev:web` 可能漂移到 `10000+`,导致 Tauri 窗口加载 3000 时白屏或连接失败。3000 被占用时必须直接失败并提示释放端口,不能静默漂移。由于桌面壳主窗口设置了 `create=false` 并由 Rust 手动创建,`apps/desktop-shell/src-tauri/src/app.rs` 在 dev build 下必须把主窗口 URL 替换为 `build.devUrl` 后再补写 HostBridge query,不能继续用 release 的 `index.html`,否则 WebView 会落到 `tauri.localhost/index.html`。`apps/desktop-shell/scripts/check-config.mjs`、`scripts/dev.test.ts` 和桌面壳 Rust 单测会反查该口径。
2026-06-18 追加:移动壳生产包网络安全元数据显式收紧。Android `usesCleartextTraffic=false`,iOS `NSAppTransportSecurity.NSAllowsArbitraryLoads=false`,并设置 `ITSAppUsesNonExemptEncryption=false` 作为当前未接入自定义加密能力的出口合规声明;开发联调本机 Vite 仍通过显式 `EXPO_PUBLIC_GENARRATIVE_WEB_URL=http://127.0.0.1:3000/` 进入 development build,不把任意明文流量开关带进默认包配置。`apps/mobile-shell/scripts/check-config.mjs` 会校验这些网络安全字段。