From 85fe81a0a797721f66bb15624ce525d6d5db5b3e Mon Sep 17 00:00:00 2001 From: kzikit Date: Sat, 6 Jun 2026 16:29:27 +0200 Subject: [PATCH] fix(desktop): show main window when ready-to-show never fires On software-rendered systems (e.g. VMware SVGA where Chromium falls back to SwiftShader) the compositor never produces a first frame for a hidden window, so 'ready-to-show' never fires and the main window stays invisible forever while the app boots healthy underneath (DOM complete, no errors). Add a 2s fallback timer that shows the window anyway; on healthy systems ready-to-show fires first and startup stays flicker-free. Co-Authored-By: Claude Opus 4.8 (1M context) --- .changeset/main-window-show-fallback.md | 5 +++++ apps/desktop/src/main/index.ts | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 .changeset/main-window-show-fallback.md diff --git a/.changeset/main-window-show-fallback.md b/.changeset/main-window-show-fallback.md new file mode 100644 index 00000000..e21b06ba --- /dev/null +++ b/.changeset/main-window-show-fallback.md @@ -0,0 +1,5 @@ +--- +"@open-codesign/desktop": patch +--- + +Show the main window via a 2s fallback timer when `ready-to-show` never fires. On software-rendered systems (e.g. VMware/VirtualBox VMs where Chromium falls back to SwiftShader), the compositor never produces a first frame for a hidden window, so the window stayed invisible forever while the app ran healthy underneath. diff --git a/apps/desktop/src/main/index.ts b/apps/desktop/src/main/index.ts index 4d28c823..1cbf083a 100644 --- a/apps/desktop/src/main/index.ts +++ b/apps/desktop/src/main/index.ts @@ -103,7 +103,22 @@ function createWindow(): void { }, }); - mainWindow.on('ready-to-show', () => mainWindow?.show()); + // On software-rendered systems (e.g. VMware SVGA → SwiftShader fallback) + // the compositor never produces a first frame for a hidden window, so + // ready-to-show never fires and the window stays invisible forever while + // the app runs healthy underneath. The fallback timer shows the window + // anyway; on healthy systems ready-to-show fires well within 2s and + // startup stays flicker-free. + const showFallback = setTimeout(() => { + if (mainWindow !== null && !mainWindow.isDestroyed() && !mainWindow.isVisible()) { + mainWindow.show(); + } + }, 2000); + mainWindow.on('ready-to-show', () => { + clearTimeout(showFallback); + mainWindow?.show(); + }); + mainWindow.on('closed', () => clearTimeout(showFallback)); // Null the reference on close so stale IPC sends from async emitters // (autoUpdater, long-running generate runs) become clean no-ops rather // than throwing "Object has been destroyed" on a discarded webContents.