fix(ui): diff viewer opens non-modal and maximizable

The diff viewer used ShowDialog at both call sites (task details, worktrees
overview), blocking navigation of the rest of the app while open. Switch both
to Window.Show(owner) and reuse the one open window (swap DataContext,
Activate()) instead of stacking a second one on re-open.

Add an opt-in maximize/restore button + double-click-titlebar toggle to
ModalShell (ShowMaximizeButton, default false so other modals are untouched),
wired on for DiffViewerView only. Reuses the existing Icon.WinMax/WinRestore
geometry and OffScreenMargin inset from MainWindow's own maximize handling.

The review gate (Approve & Merge locked until the diff is opened) already
fires DiffViewed before the dialog/window is shown, so it is unaffected by
the modal-to-non-modal change.
This commit is contained in:
mika kuns
2026-08-10 13:46:56 +02:00
parent 6a2a19cc9e
commit f74c7dd70b
7 changed files with 95 additions and 11 deletions
@@ -18,6 +18,7 @@ public partial class DetailsIslandView : UserControl
// never dragged stay dynamic (Auto-sized description).
private readonly Dictionary<string, double> _descriptionHeights = new();
private DetailsIslandViewModel? _vm;
private DiffViewerView? _diffViewerWindow;
public DetailsIslandView()
{
@@ -142,12 +143,26 @@ public partial class DetailsIslandView : UserControl
vm.PropertyChanged += OnViewModelPropertyChanged;
ApplyResizeStateForCurrentTask();
vm.Merge.ShowDiffViewer = async (diffVm) =>
// Non-modal: the diff viewer must not block navigating the rest of the app.
// Reuse the one open window (swap its DataContext + bring to front) instead of
// stacking a second one when the user re-opens the diff for another task.
vm.Merge.ShowDiffViewer = (diffVm) =>
{
var owner = TopLevel.GetTopLevel(this) as Window;
if (owner == null) return;
var modal = new DiffViewerView { DataContext = diffVm };
await modal.ShowDialog(owner);
if (owner == null) return System.Threading.Tasks.Task.CompletedTask;
if (_diffViewerWindow is { } existing)
{
existing.DataContext = diffVm;
if (existing.WindowState == WindowState.Minimized)
existing.WindowState = WindowState.Normal;
existing.Activate();
return System.Threading.Tasks.Task.CompletedTask;
}
var window = new DiffViewerView { DataContext = diffVm };
window.Closed += (_, _) => _diffViewerWindow = null;
_diffViewerWindow = window;
window.Show(owner);
return System.Threading.Tasks.Task.CompletedTask;
};
vm.Merge.ShowMergeModal = async (mergeVm) =>