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 <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-05-19 09:49:44 +02:00
parent 9f70f6747e
commit 789094fcd9
7 changed files with 72 additions and 1 deletions

View File

@@ -95,6 +95,8 @@ sealed class Program
// ViewModels // ViewModels
sc.AddTransient<WorktreeModalViewModel>(); sc.AddTransient<WorktreeModalViewModel>();
sc.AddTransient<WorktreesOverviewModalViewModel>();
sc.AddTransient<Func<WorktreesOverviewModalViewModel>>(sp => () => sp.GetRequiredService<WorktreesOverviewModalViewModel>());
sc.AddSingleton<IPrimeScheduleApi, WorkerPrimeScheduleApi>(); sc.AddSingleton<IPrimeScheduleApi, WorkerPrimeScheduleApi>();
sc.AddTransient<PrimeClaudeTabViewModel>(); sc.AddTransient<PrimeClaudeTabViewModel>();
sc.AddTransient<SettingsModalViewModel>(); sc.AddTransient<SettingsModalViewModel>();

View File

@@ -28,6 +28,7 @@ public sealed partial class ListsIslandViewModel : ViewModelBase
public Func<SettingsModalViewModel, Task>? ShowSettingsModal { get; set; } public Func<SettingsModalViewModel, Task>? ShowSettingsModal { get; set; }
public Func<ListSettingsModalViewModel, System.Threading.Tasks.Task>? ShowListSettingsModal { get; set; } public Func<ListSettingsModalViewModel, System.Threading.Tasks.Task>? ShowListSettingsModal { get; set; }
public Func<WorktreesOverviewModalViewModel, Task>? ShowWorktreesOverviewModal { get; set; }
[RelayCommand] [RelayCommand]
private async Task OpenSettings() private async Task OpenSettings()
@@ -49,6 +50,18 @@ public sealed partial class ListsIslandViewModel : ViewModelBase
await RefreshRowAsync(row.Id); 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<WorktreesOverviewModalViewModel>();
vm.Configure(rawId, row.Name);
await vm.LoadAsync();
await ShowWorktreesOverviewModal(vm);
}
public ObservableCollection<ListNavItemViewModel> Items { get; } = new(); public ObservableCollection<ListNavItemViewModel> Items { get; } = new();
public ObservableCollection<ListNavItemViewModel> SmartLists { get; } = new(); public ObservableCollection<ListNavItemViewModel> SmartLists { get; } = new();
public ObservableCollection<ListNavItemViewModel> UserLists { get; } = new(); public ObservableCollection<ListNavItemViewModel> UserLists { get; } = new();

View File

@@ -32,6 +32,7 @@ public sealed partial class IslandsShellViewModel : ViewModelBase
private readonly UpdateCheckService _updateCheck; private readonly UpdateCheckService _updateCheck;
private readonly InstallerLocator _installerLocator; private readonly InstallerLocator _installerLocator;
private readonly IDbContextFactory<ClaudeDoDbContext>? _dbFactory; private readonly IDbContextFactory<ClaudeDoDbContext>? _dbFactory;
private readonly Func<WorktreesOverviewModalViewModel> _worktreesOverviewVmFactory = () => null!;
// Set by MainWindow to open the conflict resolution dialog. // Set by MainWindow to open the conflict resolution dialog.
public Func<ConflictResolutionViewModel, Task>? ShowConflictDialog { get; set; } public Func<ConflictResolutionViewModel, Task>? ShowConflictDialog { get; set; }
@@ -39,6 +40,9 @@ public sealed partial class IslandsShellViewModel : ViewModelBase
// Set by MainWindow to open the About dialog. // Set by MainWindow to open the About dialog.
public Func<AboutModalViewModel, Task>? ShowAboutModal { get; set; } public Func<AboutModalViewModel, Task>? ShowAboutModal { get; set; }
// Set by MainWindow to open the global worktrees overview dialog.
public Func<WorktreesOverviewModalViewModel, Task>? ShowWorktreesOverviewModal { get; set; }
[ObservableProperty] private bool _isUpdateBannerVisible; [ObservableProperty] private bool _isUpdateBannerVisible;
[ObservableProperty] private string? _updateBannerLatestVersion; [ObservableProperty] private string? _updateBannerLatestVersion;
[ObservableProperty] private string? _inlineUpdateStatus; [ObservableProperty] private string? _inlineUpdateStatus;
@@ -159,12 +163,14 @@ public sealed partial class IslandsShellViewModel : ViewModelBase
WorkerClient worker, WorkerClient worker,
UpdateCheckService updateCheck, UpdateCheckService updateCheck,
InstallerLocator installerLocator, InstallerLocator installerLocator,
IDbContextFactory<ClaudeDoDbContext> dbFactory) IDbContextFactory<ClaudeDoDbContext> dbFactory,
Func<WorktreesOverviewModalViewModel> worktreesOverviewVmFactory)
{ {
Lists = lists; Tasks = tasks; Details = details; Worker = worker; Lists = lists; Tasks = tasks; Details = details; Worker = worker;
_updateCheck = updateCheck; _updateCheck = updateCheck;
_installerLocator = installerLocator; _installerLocator = installerLocator;
_dbFactory = dbFactory; _dbFactory = dbFactory;
_worktreesOverviewVmFactory = worktreesOverviewVmFactory;
Lists.SelectionChanged += (_, _) => Tasks.LoadForList(Lists.SelectedList); Lists.SelectionChanged += (_, _) => Tasks.LoadForList(Lists.SelectedList);
Tasks.SelectionChanged += (_, _) => Details.Bind(Tasks.SelectedTask); Tasks.SelectionChanged += (_, _) => Details.Bind(Tasks.SelectedTask);
Tasks.TasksChanged += (_, _) => _ = Lists.RefreshCountsAsync(); Tasks.TasksChanged += (_, _) => _ = Lists.RefreshCountsAsync();
@@ -249,6 +255,16 @@ public sealed partial class IslandsShellViewModel : ViewModelBase
if (ShowAboutModal is not null) await ShowAboutModal(vm); 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] [RelayCommand]
private async Task CheckForUpdatesAsync() private async Task CheckForUpdatesAsync()
{ {

View File

@@ -132,6 +132,9 @@
<MenuItem Header="Settings..." <MenuItem Header="Settings..."
Command="{Binding $parent[UserControl].((vm:ListsIslandViewModel)DataContext).OpenListSettingsCommand}" Command="{Binding $parent[UserControl].((vm:ListsIslandViewModel)DataContext).OpenListSettingsCommand}"
CommandParameter="{Binding}"/> CommandParameter="{Binding}"/>
<MenuItem Header="Worktrees anzeigen…"
Command="{Binding $parent[UserControl].((vm:ListsIslandViewModel)DataContext).OpenWorktreesOverviewCommand}"
CommandParameter="{Binding}"/>
</ContextMenu> </ContextMenu>
</Border.ContextMenu> </Border.ContextMenu>
<Grid ColumnDefinitions="20,*,Auto"> <Grid ColumnDefinitions="20,*,Auto">

View File

@@ -1,3 +1,4 @@
using System.Linq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using ClaudeDo.Ui.ViewModels.Islands; using ClaudeDo.Ui.ViewModels.Islands;
@@ -25,6 +26,23 @@ public partial class ListsIslandView : UserControl
if (top is null) window.Show(); if (top is null) window.Show();
else await window.ShowDialog(top); 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);
};
} }
}; };
} }

View File

@@ -69,6 +69,8 @@
Command="{Binding CheckForUpdatesCommand}"/> Command="{Binding CheckForUpdatesCommand}"/>
<MenuItem Header="Restart worker" <MenuItem Header="Restart worker"
Command="{Binding RestartWorkerCommand}"/> Command="{Binding RestartWorkerCommand}"/>
<MenuItem Header="Worktrees…"
Command="{Binding OpenWorktreesOverviewGlobalCommand}"/>
<MenuItem Header="About…" Command="{Binding OpenAboutCommand}"/> <MenuItem Header="About…" Command="{Binding OpenAboutCommand}"/>
</MenuItem> </MenuItem>
</Menu> </Menu>

View File

@@ -1,6 +1,8 @@
using System.Linq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using ClaudeDo.Ui.ViewModels; using ClaudeDo.Ui.ViewModels;
using ClaudeDo.Ui.ViewModels.Islands;
using ClaudeDo.Ui.Views.Modals; using ClaudeDo.Ui.Views.Modals;
using ClaudeDo.Ui.Views.Planning; using ClaudeDo.Ui.Views.Planning;
@@ -31,6 +33,21 @@ public partial class MainWindow : Window
aboutVm.CloseAction = () => { dlg.Close(); tcs.TrySetResult(true); }; aboutVm.CloseAction = () => { dlg.Close(); tcs.TrySetResult(true); };
await dlg.ShowDialog(this); 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);
};
} }
} }