From fe97e9a51a61a62112ebb16720a5cb0ee0f50b6e Mon Sep 17 00:00:00 2001 From: "Mars.P" Date: Tue, 28 Jul 2026 08:29:33 +0800 Subject: [PATCH] Fix unresponsive Windows white screen --- .github/workflows/release.yml | 2 +- .github/workflows/windows-vulkan.yml | 2 +- app/src-tauri/src/lib.rs | 16 ++++++++++++++- app/src-tauri/tauri.conf.json | 6 +++++- app/src/routes/+page.svelte | 16 ++++++++++++++- scripts/smoke-windows-installer.ps1 | 30 +++++++++++++++++++++++++--- 6 files changed, 64 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 55ebeb2..6c49815 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -168,7 +168,7 @@ jobs: shell: bash run: ./scripts/verify-windows-runtime.sh D:/b/release/bundle/nsis/Wisp_*-setup.exe --require-vulkan - - name: Smoke-test the packaged Windows app + - name: Smoke-test the packaged Windows frontend and close behavior if: runner.os == 'Windows' shell: pwsh run: | diff --git a/.github/workflows/windows-vulkan.yml b/.github/workflows/windows-vulkan.yml index eeedf29..a78ada5 100644 --- a/.github/workflows/windows-vulkan.yml +++ b/.github/workflows/windows-vulkan.yml @@ -78,7 +78,7 @@ jobs: - name: Verify the installer runtime shell: bash run: ./scripts/verify-windows-runtime.sh D:/b/release/bundle/nsis/Wisp_*-setup.exe --require-vulkan - - name: Smoke-test the packaged app + - name: Smoke-test the packaged frontend and close behavior shell: pwsh run: | $installer = Get-ChildItem 'D:\b\release\bundle\nsis\Wisp_*-setup.exe' | Select-Object -First 1 diff --git a/app/src-tauri/src/lib.rs b/app/src-tauri/src/lib.rs index 5de41ab..939397e 100644 --- a/app/src-tauri/src/lib.rs +++ b/app/src-tauri/src/lib.rs @@ -4508,6 +4508,19 @@ fn set_search_mode(state: State<'_, AppState>, mode: String) -> Result<(), Strin Ok(()) } +/// Marks the first rendered frontend frame for the packaged Windows smoke test. Normal users keep +/// the regular title; CI opts in with an environment variable so it can distinguish a working UI +/// from a white WebView process that merely stayed alive. +#[tauri::command] +fn frontend_ready(window: tauri::WebviewWindow) -> Result<(), String> { + if std::env::var_os("WISP_SMOKE_TEST").is_some() { + window + .set_title("Wisp [ready]") + .map_err(|e| format!("could not mark the frontend ready: {e}"))?; + } + Ok(()) +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() @@ -4694,7 +4707,8 @@ pub fn run() { set_embedding_model, delete_embedding_model, search_mode, - set_search_mode + set_search_mode, + frontend_ready ]) .build(tauri::generate_context!()) .expect("error while building tauri application") diff --git a/app/src-tauri/tauri.conf.json b/app/src-tauri/tauri.conf.json index 1a70e37..0a60e8c 100644 --- a/app/src-tauri/tauri.conf.json +++ b/app/src-tauri/tauri.conf.json @@ -17,7 +17,8 @@ "width": 1040, "height": 740, "minWidth": 820, - "minHeight": 640 + "minHeight": 640, + "additionalBrowserArgs": "--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection --disable-gpu" } ], "security": { @@ -27,6 +28,9 @@ "bundle": { "active": true, "targets": ["nsis", "dmg"], + "windows": { + "minimumWebview2Version": "120.0.2210.61" + }, "resources": [ "resources/silero_vad.onnx" ], diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte index e15b9b3..167f24d 100644 --- a/app/src/routes/+page.svelte +++ b/app/src/routes/+page.svelte @@ -1359,10 +1359,24 @@ ensureProgressListener(); ensureFileListeners(); syncRunning(); + // The packaged Windows test waits for this native title marker before it accepts startup. Two + // animation frames prove Svelte mounted and WebView2 completed a real paint; a white/hung + // renderer never reaches the command. Outside that opt-in test the backend leaves the title + // unchanged. + let readyFrame = requestAnimationFrame(() => { + readyFrame = requestAnimationFrame(() => { + void invoke("frontend_ready").catch(() => { + /* diagnostic-only handshake; never disrupt the real UI */ + }); + }); + }); // Re-check when the window regains focus, so granting in System Settings clears the banner. const onFocus = () => checkPermissions(); window.addEventListener("focus", onFocus); - return () => window.removeEventListener("focus", onFocus); + return () => { + cancelAnimationFrame(readyFrame); + window.removeEventListener("focus", onFocus); + }; }); onDestroy(() => { unlisten?.(); diff --git a/scripts/smoke-windows-installer.ps1 b/scripts/smoke-windows-installer.ps1 index 13aeeca..bcc40a6 100644 --- a/scripts/smoke-windows-installer.ps1 +++ b/scripts/smoke-windows-installer.ps1 @@ -4,6 +4,7 @@ param( ) $ErrorActionPreference = "Stop" +$readyTitle = "Wisp [ready]" if (-not (Test-Path -LiteralPath $Installer -PathType Leaf)) { throw "Windows installer not found: $Installer" @@ -11,6 +12,7 @@ if (-not (Test-Path -LiteralPath $Installer -PathType Leaf)) { $extractDirectory = Join-Path ([System.IO.Path]::GetTempPath()) "wisp-windows-smoke-$([guid]::NewGuid())" $process = $null +$previousSmokeTest = $env:WISP_SMOKE_TEST try { New-Item -ItemType Directory -Path $extractDirectory | Out-Null @@ -20,14 +22,36 @@ try { } $executable = Join-Path $extractDirectory "Wisp.exe" + $env:WISP_SMOKE_TEST = "1" $process = Start-Process -FilePath $executable -WorkingDirectory $extractDirectory -PassThru - if ($process.WaitForExit(15000)) { - throw "Packaged Wisp.exe exited during startup with code $($process.ExitCode)" + + $readyDeadline = [DateTime]::UtcNow.AddSeconds(30) + do { + if ($process.HasExited) { + throw "Packaged Wisp.exe exited during startup with code $($process.ExitCode)" + } + $process.Refresh() + if ($process.MainWindowTitle -eq $readyTitle -and $process.MainWindowHandle -ne [IntPtr]::Zero) { + break + } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $readyDeadline) + + if ($process.MainWindowTitle -ne $readyTitle -or $process.MainWindowHandle -eq [IntPtr]::Zero) { + throw "Packaged Wisp.exe did not render its frontend within 30 seconds" + } + + if (-not $process.CloseMainWindow()) { + throw "Packaged Wisp.exe did not accept a close request" + } + if (-not $process.WaitForExit(10000)) { + throw "Packaged Wisp.exe rendered but its window became unresponsive while closing" } - Write-Host "Packaged Wisp.exe remained running for 15 seconds" + Write-Host "Packaged Wisp.exe rendered its frontend and closed responsively" } finally { + $env:WISP_SMOKE_TEST = $previousSmokeTest if ($null -ne $process -and -not $process.HasExited) { Stop-Process -Id $process.Id -Force $process.WaitForExit()