From 789094fcd9847cc656949208d2bd25bf1d2f74a9 Mon Sep 17 00:00:00 2001 From: mika kuns Date: Tue, 19 May 2026 09:49:44 +0200 Subject: [PATCH] feat(ui): wire worktree overview modal entry points Add list context-menu command (per-list mode) and Help menu entry (global mode) for the WorktreesOverviewModal; register VM and factory in DI. Co-Authored-By: Claude Sonnet 4.6 --- src/ClaudeDo.App/Program.cs | 2 ++ .../ViewModels/Islands/ListsIslandViewModel.cs | 13 +++++++++++++ .../ViewModels/IslandsShellViewModel.cs | 18 +++++++++++++++++- .../Views/Islands/ListsIslandView.axaml | 3 +++ .../Views/Islands/ListsIslandView.axaml.cs | 18 ++++++++++++++++++ src/ClaudeDo.Ui/Views/MainWindow.axaml | 2 ++ src/ClaudeDo.Ui/Views/MainWindow.axaml.cs | 17 +++++++++++++++++ 7 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/ClaudeDo.App/Program.cs b/src/ClaudeDo.App/Program.cs index 8e3942a..8c0e831 100644 --- a/src/ClaudeDo.App/Program.cs +++ b/src/ClaudeDo.App/Program.cs @@ -95,6 +95,8 @@ sealed class Program // ViewModels sc.AddTransient(); + sc.AddTransient(); + sc.AddTransient>(sp => () => sp.GetRequiredService()); sc.AddSingleton(); sc.AddTransient(); sc.AddTransient(); diff --git a/src/ClaudeDo.Ui/ViewModels/Islands/ListsIslandViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Islands/ListsIslandViewModel.cs index 0bd89d3..fc9880a 100644 --- a/src/ClaudeDo.Ui/ViewModels/Islands/ListsIslandViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/Islands/ListsIslandViewModel.cs @@ -28,6 +28,7 @@ public sealed partial class ListsIslandViewModel : ViewModelBase public Func? ShowSettingsModal { get; set; } public Func? ShowListSettingsModal { get; set; } + public Func? ShowWorktreesOverviewModal { get; set; } [RelayCommand] private async Task OpenSettings() @@ -49,6 +50,18 @@ public sealed partial class ListsIslandViewModel : ViewModelBase await RefreshRowAsync(row.Id); } + [RelayCommand] + private async Task OpenWorktreesOverviewAsync(ListNavItemViewModel? row) + { + if (row is null || ShowWorktreesOverviewModal is null || _services is null) return; + if (row.Kind != ListKind.User) return; + var rawId = row.Id.StartsWith("user:", StringComparison.Ordinal) ? row.Id["user:".Length..] : row.Id; + var vm = _services.GetRequiredService(); + vm.Configure(rawId, row.Name); + await vm.LoadAsync(); + await ShowWorktreesOverviewModal(vm); + } + public ObservableCollection Items { get; } = new(); public ObservableCollection SmartLists { get; } = new(); public ObservableCollection UserLists { get; } = new(); diff --git a/src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs b/src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs index b02a0a6..49e6d6f 100644 --- a/src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/IslandsShellViewModel.cs @@ -32,6 +32,7 @@ public sealed partial class IslandsShellViewModel : ViewModelBase private readonly UpdateCheckService _updateCheck; private readonly InstallerLocator _installerLocator; private readonly IDbContextFactory? _dbFactory; + private readonly Func _worktreesOverviewVmFactory = () => null!; // Set by MainWindow to open the conflict resolution dialog. public Func? ShowConflictDialog { get; set; } @@ -39,6 +40,9 @@ public sealed partial class IslandsShellViewModel : ViewModelBase // Set by MainWindow to open the About dialog. public Func? ShowAboutModal { get; set; } + // Set by MainWindow to open the global worktrees overview dialog. + public Func? ShowWorktreesOverviewModal { get; set; } + [ObservableProperty] private bool _isUpdateBannerVisible; [ObservableProperty] private string? _updateBannerLatestVersion; [ObservableProperty] private string? _inlineUpdateStatus; @@ -159,12 +163,14 @@ public sealed partial class IslandsShellViewModel : ViewModelBase WorkerClient worker, UpdateCheckService updateCheck, InstallerLocator installerLocator, - IDbContextFactory dbFactory) + IDbContextFactory dbFactory, + Func worktreesOverviewVmFactory) { Lists = lists; Tasks = tasks; Details = details; Worker = worker; _updateCheck = updateCheck; _installerLocator = installerLocator; _dbFactory = dbFactory; + _worktreesOverviewVmFactory = worktreesOverviewVmFactory; Lists.SelectionChanged += (_, _) => Tasks.LoadForList(Lists.SelectedList); Tasks.SelectionChanged += (_, _) => Details.Bind(Tasks.SelectedTask); Tasks.TasksChanged += (_, _) => _ = Lists.RefreshCountsAsync(); @@ -249,6 +255,16 @@ public sealed partial class IslandsShellViewModel : ViewModelBase if (ShowAboutModal is not null) await ShowAboutModal(vm); } + [RelayCommand] + private async Task OpenWorktreesOverviewGlobalAsync() + { + if (ShowWorktreesOverviewModal is null) return; + var vm = _worktreesOverviewVmFactory(); + vm.Configure(null, null); + await vm.LoadAsync(); + await ShowWorktreesOverviewModal(vm); + } + [RelayCommand] private async Task CheckForUpdatesAsync() { diff --git a/src/ClaudeDo.Ui/Views/Islands/ListsIslandView.axaml b/src/ClaudeDo.Ui/Views/Islands/ListsIslandView.axaml index 7d34843..0f477aa 100644 --- a/src/ClaudeDo.Ui/Views/Islands/ListsIslandView.axaml +++ b/src/ClaudeDo.Ui/Views/Islands/ListsIslandView.axaml @@ -132,6 +132,9 @@ + diff --git a/src/ClaudeDo.Ui/Views/Islands/ListsIslandView.axaml.cs b/src/ClaudeDo.Ui/Views/Islands/ListsIslandView.axaml.cs index 005f26f..5a18926 100644 --- a/src/ClaudeDo.Ui/Views/Islands/ListsIslandView.axaml.cs +++ b/src/ClaudeDo.Ui/Views/Islands/ListsIslandView.axaml.cs @@ -1,3 +1,4 @@ +using System.Linq; using Avalonia.Controls; using Avalonia.Interactivity; using ClaudeDo.Ui.ViewModels.Islands; @@ -25,6 +26,23 @@ public partial class ListsIslandView : UserControl if (top is null) window.Show(); else await window.ShowDialog(top); }; + vm.ShowWorktreesOverviewModal = async modal => + { + var window = new WorktreesOverviewModalView { DataContext = modal }; + modal.CloseAction = () => window.Close(); + modal.JumpToTaskAction = (listId, _) => + { + if (vm is { } v) + { + var item = v.UserLists.FirstOrDefault(l => l.Id == $"user:{listId}"); + if (item is not null) v.SelectedList = item; + } + }; + // TODO: ShowDiffAction and ConfirmAction not wired in v1 + var top = TopLevel.GetTopLevel(this) as Window; + if (top is null) window.Show(); + else await window.ShowDialog(top); + }; } }; } diff --git a/src/ClaudeDo.Ui/Views/MainWindow.axaml b/src/ClaudeDo.Ui/Views/MainWindow.axaml index 4e599bc..5217ff3 100644 --- a/src/ClaudeDo.Ui/Views/MainWindow.axaml +++ b/src/ClaudeDo.Ui/Views/MainWindow.axaml @@ -69,6 +69,8 @@ Command="{Binding CheckForUpdatesCommand}"/> + diff --git a/src/ClaudeDo.Ui/Views/MainWindow.axaml.cs b/src/ClaudeDo.Ui/Views/MainWindow.axaml.cs index 4ad9502..5221497 100644 --- a/src/ClaudeDo.Ui/Views/MainWindow.axaml.cs +++ b/src/ClaudeDo.Ui/Views/MainWindow.axaml.cs @@ -1,6 +1,8 @@ +using System.Linq; using Avalonia.Controls; using Avalonia.Input; using ClaudeDo.Ui.ViewModels; +using ClaudeDo.Ui.ViewModels.Islands; using ClaudeDo.Ui.Views.Modals; using ClaudeDo.Ui.Views.Planning; @@ -31,6 +33,21 @@ public partial class MainWindow : Window aboutVm.CloseAction = () => { dlg.Close(); tcs.TrySetResult(true); }; await dlg.ShowDialog(this); }; + vm.ShowWorktreesOverviewModal = async (modal) => + { + var dlg = new WorktreesOverviewModalView { DataContext = modal }; + modal.CloseAction = () => dlg.Close(); + modal.JumpToTaskAction = (listId, _) => + { + if (DataContext is IslandsShellViewModel s) + { + var item = s.Lists?.UserLists.FirstOrDefault(l => l.Id == $"user:{listId}"); + if (item is not null && s.Lists is not null) s.Lists.SelectedList = item; + } + }; + // TODO: ShowDiffAction and ConfirmAction not wired in v1 + await dlg.ShowDialog(this); + }; } }