feat(ui): Review-Merge-Button — Claude nimmt Waiting-for-Review pro Liste ab

Neuer Button in der virtuellen Review-Liste: öffnet das Merge-Helper-Auswahlmodal
im Review-Modus (alle WaitingForReview-Tasks repo-verknüpfter Listen, gruppiert
unter Listen-Headern, alle vorgetickt) und startet pro gewählter Liste eine
ConPTY-Handler-Session direkt in der Merge-Phase — via bestehendem Handoff-Spec
(nextPhase "merge"), kein Triage/Wait davor. Handler-Host-Task wie beim vollen
"Let Claude handle it"-Lauf (HandlerBaseCommit, IsManual).
This commit is contained in:
mika kuns
2026-08-27 08:44:33 +02:00
parent d0c726baa0
commit 741c7ac58d
11 changed files with 293 additions and 8 deletions
@@ -105,6 +105,25 @@ public sealed partial class ListsIslandViewModel : ViewModelBase, IDisposable
LetClaudeHandleRequested?.Invoke(new MergeHelperRequest(rawId, ids));
}
/// <summary>Raised once per list after the review-scoped selection dialog is confirmed;
/// the shell opens one merge-phase ConPTY tile per request.</summary>
public event Action<MergeHelperRequest>? MergeReviewsRequested;
// Review-list variant of LetClaudeHandleListAsync: offers every WaitingForReview task across
// all repo-linked lists and starts one merge-phase handler session per list picked.
[RelayCommand]
private async Task MergeReviewsAsync()
{
if (Dialogs is null || _services is null) return;
var vm = _services.GetRequiredService<MergeHelperSelectionModalViewModel>();
vm.ConfigureForReview();
await vm.LoadAsync();
var ids = await Dialogs.ShowMergeHelperSelectionAsync(vm);
if (ids is not { Count: > 0 }) return;
foreach (var (listId, taskIds) in vm.GroupSelectionByList(ids))
MergeReviewsRequested?.Invoke(new MergeHelperRequest(listId, taskIds));
}
[RelayCommand]
private void OpenInExplorer(ListNavItemViewModel? row)
{
@@ -124,6 +124,7 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
[NotifyPropertyChangedFor(nameof(IsTasksEmptyRepoHintVisible))]
private bool _isLetClaudeVisible;
[ObservableProperty] private bool _isQuickClaudeVisible;
[ObservableProperty] private bool _isMergeReviewsVisible;
/// <summary>No visible tasks below the add-task row — every item lands in one of
/// Overdue/Open/Completed, so all-false here always means the list has zero items.</summary>
@@ -147,6 +148,13 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
[RelayCommand]
private void LetClaudeHandle() => LetClaudeHandleRequested?.Invoke(this, EventArgs.Empty);
// "Let Claude merge the reviews" on the virtual review list — the shell routes it to the
// review-scoped selection modal, which starts one merge-phase handler session per list.
public event EventHandler? MergeReviewsRequested;
[RelayCommand]
private void MergeReviews() => MergeReviewsRequested?.Invoke(this, EventArgs.Empty);
// Opens a task-less ConPTY session directly in the current list's working dir. The shell owns
// Mission Control, so this just raises an event for it to act on (mirrors OpenConPtySessionRequested).
public event Action<string>? OpenQuickClaudeSessionRequested;
@@ -411,7 +419,7 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
ShowOpenLabel = false;
ShowNotesRow = false;
if (listChanged) SelectFrom(null, "list-change");
if (list is null) { IsLetClaudeVisible = false; IsQuickClaudeVisible = false; LoadTask = Task.CompletedTask; return; }
if (list is null) { IsLetClaudeVisible = false; IsQuickClaudeVisible = false; IsMergeReviewsVisible = false; LoadTask = Task.CompletedTask; return; }
HeaderTitle = list.Name;
HeaderEyebrow = DateTime.Now.ToString("dddd · MMM dd", CultureInfo.InvariantCulture).ToUpperInvariant();
@@ -419,6 +427,7 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable
IsMyDayList = list.Id == "smart:my-day";
IsLetClaudeVisible = list.Kind == ListKind.User && !string.IsNullOrWhiteSpace(list.WorkingDir);
IsQuickClaudeVisible = IsLetClaudeVisible;
IsMergeReviewsVisible = list.Id == "virtual:review";
LoadTask = LoadForListAsync(list, ct, reusable);
}