From 103eb9a687f8c22dee02ebdd29df6bc1c568b98d Mon Sep 17 00:00:00 2001 From: Mika Kuns Date: Wed, 16 Sep 2026 11:29:44 +0200 Subject: [PATCH] fix(ui): Shutdown terminates the app even with secondary windows open ShutdownWorkerAndCloseApp only closed MainWindow via CloseWindow, relying on ShutdownMode.OnMainWindowClose to bring the process down. That mode backs off if any window cancels its own Closing event, and MissionControlWindow always does (it hides instead of closing) - so the process never actually exited while Mission Control, Cheat Sheets, or the diff viewer were still open. Call IClassicDesktopStyleApplicationLifetime .Shutdown() directly, which force-closes every window regardless of per-window cancellation. --- .../ViewModels/IslandsShellViewModel.cs | 15 ++++++++++----- src/ClaudeDo.Ui/Views/MainWindow.axaml.cs | 5 ----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs b/src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs index e879fd4e..93837d53 100644 --- a/src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs @@ -1,3 +1,5 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Threading; using System; using System.Threading; @@ -686,10 +688,6 @@ public sealed partial class IslandsShellViewModel : ViewModelBase, IDisposable } } - // Set by MainWindow: force-closes the window without re-triggering its own close - // confirmation (the shutdown dialog above already confirmed). - public Action? CloseWindow { get; set; } - // Test seam: overrides the real kill-worker + close-window body. internal Action? ShutdownOperation { get; set; } @@ -706,7 +704,14 @@ public sealed partial class IslandsShellViewModel : ViewModelBase, IDisposable { var exe = _workerLocator.Find(); if (exe is not null) KillMatchingWorkerProcesses(exe); - CloseWindow?.Invoke(); + + // Closing just the MainWindow relies on ShutdownMode.OnMainWindowClose, which asks + // every open window to close and backs off if any of them cancels — and + // MissionControlWindow always cancels its own close (it hides instead, see + // MissionControlWindow.OnClosing), so the process never actually exited. Shutdown() + // force-closes every window, ignoring that cancellation. + if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + desktop.Shutdown(); } private bool _updatingClaudeCli; diff --git a/src/ClaudeDo.Ui/Views/MainWindow.axaml.cs b/src/ClaudeDo.Ui/Views/MainWindow.axaml.cs index ba65fb72..b1633f86 100644 --- a/src/ClaudeDo.Ui/Views/MainWindow.axaml.cs +++ b/src/ClaudeDo.Ui/Views/MainWindow.axaml.cs @@ -80,11 +80,6 @@ public partial class MainWindow : Window WindowState = WindowState.Normal; Activate(); }; - vm.CloseWindow = () => - { - _forceClose = true; - Close(); - }; if (vm.Tasks is not null) vm.Tasks.IsWindowVisible = WindowState != WindowState.Minimized; }