Self-heal off-screen main window instead of requiring quit/relaunch

macOS's built-in window-restore (Resume) can place the window at
coordinates for a display that's no longer connected, leaving it
visible-but-invisible with no way to recover short of quitting. Now
re-centers automatically on app activate/Dock reopen, plus a manual
"Reset Window Position" menu command as a guaranteed fallback.
This commit is contained in:
2026-08-18 14:20:17 +02:00
parent f3593dc23d
commit f5fd09ac33
2 changed files with 94 additions and 0 deletions
+45
View File
@@ -53,6 +53,41 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
return shouldTerminate ? .terminateNow : .terminateCancel
}
// macOS's built-in window-restoration (Resume) can place the main window at coordinates
// that no longer correspond to any connected display (e.g. it was last positioned on an
// external monitor that's now disconnected). The window is still technically "visible"
// (isVisible == true), so clicking the Dock icon or Cmd+Tab-ing to the app doesn't trigger
// any of the usual recovery paths the process runs fine, there's just nothing on screen.
// Self-heal by re-centering any main-sized window that doesn't intersect a current screen,
// both whenever the app becomes active and when the Dock icon is clicked with the app
// already running.
func applicationDidBecomeActive(_ notification: Notification) {
AppDelegate.repositionOffscreenWindows()
}
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
AppDelegate.repositionOffscreenWindows()
return true
}
/// Pure geometry check, kept separate from AppKit so it's directly testable.
nonisolated static func isFrameOffscreen(_ frame: CGRect, screenFrames: [CGRect]) -> Bool {
!screenFrames.contains { $0.intersects(frame) }
}
/// Re-centers the main window if it's off-screen, or unconditionally when `force` is true
/// (used by the manual "Reset Window Position" menu command).
static func repositionOffscreenWindows(force: Bool = false) {
let screenFrames = NSScreen.screens.map(\.frame)
for window in NSApp.windows where window.isVisible && !(window is NSPanel) && window.frame.width > 200 && window.frame.height > 200 {
if force || isFrameOffscreen(window.frame, screenFrames: screenFrames) {
window.center()
}
window.makeKeyAndOrderFront(nil)
}
NSApp.activate(ignoringOtherApps: true)
}
// `chatViewModel` is wired from `ContentView.onAppear`, not from `oAIApp.init()` reading
// the `@State private var chatViewModel` there returns a throwaway instance distinct from
// the one SwiftUI actually renders (confirmed via ObjectIdentifier logging: two different
@@ -217,6 +252,16 @@ struct oAIApp: App {
chatViewModel.onlineMode.toggle()
}
.keyboardShortcut("o", modifiers: [.command, .shift])
Divider()
// Fallback for when the window ends up off-screen (stale macOS window-restore
// coordinates, e.g. after an external monitor is disconnected) see
// AppDelegate.repositionOffscreenWindows. No keyboard shortcut assigned yet;
// add one once it's been live-verified against macOS's reserved bindings.
Button("Reset Window Position") {
AppDelegate.repositionOffscreenWindows(force: true)
}
}
// Help menu