From a3332aedae4ee806208958c777b4ffc94bd54b56 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Thu, 9 Jul 2026 00:47:08 +0200 Subject: [PATCH] fix(clients/apple): return to windowed after a stream error, not just an active disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A session error tears down the same as a deliberate disconnect (connection → nil flips FullscreenController's `active` off), but ALSO sets `errorMessage`, raising the "Connection failed" alert. On macOS that alert is a window sheet, and AppKit drops `-toggleFullScreen:` while a sheet is attached — so the exit was swallowed and the window stayed fullscreen on the home screen (an active disconnect sets no error, so it never stuck). Defer the alert while a forced-fullscreen exit is pending: no sheet attaches during the exit, the window returns to windowed, then the alert presents over it. Not gated when fullscreen is the user's own manual choice (opt-out setting), where nothing is auto-exiting. Co-Authored-By: Claude Opus 4.8 --- .../Sources/PunktfunkClient/ContentView.swift | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/clients/apple/Sources/PunktfunkClient/ContentView.swift b/clients/apple/Sources/PunktfunkClient/ContentView.swift index 9f0b8c56..1e6f36e9 100644 --- a/clients/apple/Sources/PunktfunkClient/ContentView.swift +++ b/clients/apple/Sources/PunktfunkClient/ContentView.swift @@ -208,7 +208,21 @@ struct ContentView: View { .alert( "Connection failed", isPresented: Binding( - get: { model.errorMessage != nil }, + get: { + guard model.errorMessage != nil else { return false } + #if os(macOS) + // Defer the alert while a forced-fullscreen exit is still pending: a sheet + // attached to a fullscreen window makes AppKit drop `-toggleFullScreen:`, so + // presenting it now strands the window fullscreen on the home screen after a + // session error (a deliberate disconnect sets no `errorMessage`, which is why + // it never stuck). Tearing the session down already flipped `active`→false; + // once the window leaves fullscreen and `isFullscreen` flips, the alert shows + // over the windowed home UI. Not gated when fullscreen is the user's own manual + // choice (opt-out setting) — nothing is auto-exiting there to conflict with. + if fullscreenWhileStreaming && isFullscreen { return false } + #endif + return true + }, set: { if !$0 { model.errorMessage = nil } }) ) { Button("OK", role: .cancel) {}