Skip to content

fix(desktop): show main window when ready-to-show never fires#377

Open
kzikit wants to merge 1 commit into
OpenCoworkAI:mainfrom
kzikit:fix/main-window-show-fallback
Open

fix(desktop): show main window when ready-to-show never fires#377
kzikit wants to merge 1 commit into
OpenCoworkAI:mainfrom
kzikit:fix/main-window-show-fallback

Conversation

@kzikit
Copy link
Copy Markdown

@kzikit kzikit commented Jun 6, 2026

Summary

On software-rendered systems — e.g. a VMware Fusion VM with the VMware SVGA II virtual GPU, where Chromium falls back to SwiftShader (gpu_compositing: disabled_software) — the compositor never produces a first frame for a hidden window. The main window is created with show: false and only shown from ready-to-show (apps/desktop/src/main/index.ts), and that event is driven by the first frame. Result: chicken-and-egg — the window waits for a frame, the frame waits for a visible window. The app boots completely healthy underneath (single-instance lock acquired, snapshots DB up, renderer DOM fully loaded — verified via CDP) but the window stays invisible forever. --disable-gpu and --ozone-platform=wayland do not help.

This PR adds a 2s fallback timer that shows the window when ready-to-show never fires. On healthy systems ready-to-show fires well within 2s, cancels the timer, and startup stays flicker-free — no behavior change there.

Diagnosis evidence: with the window hidden, Page.captureScreenshot over CDP hangs indefinitely (no frames produced); with the window shown, the same call returns the fully rendered UI immediately.

Type of change

  • Bug fix
  • New feature
  • Refactor (no behavior change)
  • Documentation
  • Build / CI / tooling
  • Breaking change

Linked issue

None — happy to open one with the full diagnosis log if you prefer tracking it that way.

Checklist

  • I checked the linked issue / relevant context before starting
  • pnpm lint && pnpm typecheck && pnpm test passes locally (1366 tests, 111 files)
  • Added/updated tests for the change — createWindow() is not exported and is all side effects; covering a window-show timing fallback would require refactoring it for testability, which felt out of proportion for this fix. Glad to add one if you want the refactor.
  • Added a changeset (pnpm changeset) if user-visible
  • Updated docs if behavior changed — n/a, no documented behavior changed

PRINCIPLES §5b

  • Compatibility — no behavior change on systems where ready-to-show fires; fallback only triggers where the window previously never appeared at all
  • Upgradeability — no new APIs, config, or on-disk state
  • No bloat — no new dependencies; +13 lines in one file
  • Elegance — keeps the flicker-free show: false + ready-to-show path as the primary mechanism; the timer is a guarded escape hatch with the why documented inline

🤖 Generated with Claude Code

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) <noreply@anthropic.com>
@github-actions github-actions Bot added docs Documentation area:desktop apps/desktop (Electron shell, renderer) labels Jun 6, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review mode: initial

Findings

  • [Minor] Missing tests for the window-show fallback behavior. The PR author acknowledges this, citing that createWindow() is not exported and is all side effects. While the change is small and the testability gap is real (refactoring would be out of proportion for this fix), the project's checklist requires tests for changes. Consider either exporting a testable helper or accepting this as a documented gap for a hotfix-level change.

  • [Nit] Hardcoded 2-second fallback timeout (apps/desktop/src/main/index.ts:119). On extremely constrained systems or under high load, 2s might still not be enough. Consider making the timeout a module-level constant (e.g. WINDOW_SHOW_FALLBACK_MS = 2000) with a brief inline comment explaining the choice, rather than a magic number.

Summary

This PR adds a 2s fallback timer that shows the main window when ready-to-show never fires — a bug affecting software-rendered systems (VMware/VirtualBox VMs where Chromium falls back to SwiftShader). The change is minimal (+21 lines, -1 line), well-documented, and includes a changeset. The existing ready-to-show path remains primary and is preserved with a clearTimeout guard. The closed event cleanup prevents timer leaks. No dependencies added, no security concerns, no architecture impact. A test is missing (acknowledged with a reasonable justification). The PR is directionally sound and safe to merge.

Testing

  • Suggested: Manual testing on a VMware Fusion or VirtualBox VM to confirm the window now appears. Can also be tested by artificially delaying/faking ready-to-show in a dedicated test build (e.g., overriding the event emitter in a preload script).
  • Not run: No automated test added (see Minor finding above).

Open-CoDesign Bot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:desktop apps/desktop (Electron shell, renderer) docs Documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant