Skip to content

fix(launch): keep Welcome visible until a main window is on-screen#1027

Closed
datlechin wants to merge 2 commits intomainfrom
fix/launch-no-window-race
Closed

fix(launch): keep Welcome visible until a main window is on-screen#1027
datlechin wants to merge 2 commits intomainfrom
fix/launch-no-window-race

Conversation

@datlechin
Copy link
Copy Markdown
Member

@datlechin datlechin commented May 6, 2026

Summary

Two related fixes for the "app launches with no visible window" failure mode.

1. attemptAutoReconnect no longer destroys Welcome before main window is visible

AppLaunchCoordinator.attemptAutoReconnect would orderOut the Welcome window upfront, then asynchronously try to reconnect saved connections, then close() Welcome unconditionally, then try to reshow Welcome only if no main window came up. Failure modes:

  • ensureConnected hangs (e.g., SSH tunnel never returns) → Welcome stays hidden, no main window painted, user sees nothing
  • ensureConnected throws after Welcome was already close()d → reshow attempt depends on the bridge in the destroyed Welcome scene's view tree
  • Race with windowWillClose observer in AppDelegate that also tries to reopen Welcome

Fix: Apple-pattern graceful degradation — never destroy fallback UI (Welcome) until the replacement (main window) is confirmed on-screen. Welcome stays visible during reconnect (briefly overlapping with the new main window). Welcome only closes once a main window is confirmed visible. If reconnect hangs or all connections fail, Welcome was never hidden — user has UI to act on.

2. applicationDidBecomeActive safety net

Whenever the app becomes active (whether from launch, relaunch, or click-to-front from the dock), check if any main or Welcome window is visible. If none, call WindowOpener.shared.openWelcome(). The call works as long as the bridge has been wired at least once during this session.

This catches the residual cases where Welcome was rendered then closed (so the bridge is wired) but no main window came up — e.g., a delayed connection failure, a user closing the only main window, or any future code path that leaves the app in a no-window state.

Known limitation

Neither fix solves the first-launch race seen with xctrace --launch and (rarely) Xcode runs, where SwiftUI never instantiates any scene's body, so the bridge is never wired. The proper fix for that would require an imperative NSWindow + NSHostingController fallback (essentially the pre-PR-988 WelcomeWindowFactory pattern, kept as last-resort fallback). Out of scope for this PR. Workaround: launch the app normally before profiling, then xctrace record --attach TablePro instead of --launch.

Test plan

  • Cold launch with startupBehavior=.reopenLast and 1 valid saved connection: main window appears, Welcome closes after connect completes
  • Cold launch with startupBehavior=.reopenLast and 1 invalid saved connection (deleted/wrong host): main window opens with error, Welcome stays visible
  • Cold launch with no lastOpenConnectionIds saved: Welcome auto-launches normally, no Task runs
  • Close all windows via Cmd+W → Welcome reopens automatically (existing behavior, regression check)
  • Open and close the only main window via attemptAutoReconnect failure path → safety net brings Welcome back on next activation
  • swiftlint --strict on changed files: 0 violations
  • xcodebuild Debug arm64: BUILD SUCCEEDED

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@datlechin datlechin closed this May 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant