From b03e858a8f32ad86dfe4c07ddccb9e7479f94002 Mon Sep 17 00:00:00 2001 From: Mika Kuns Date: Tue, 21 Apr 2026 17:40:32 +0200 Subject: [PATCH] feat(ui): add Continue and Reset commands to DetailsIslandViewModel --- .../Islands/DetailsIslandViewModel.cs | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs index 8440a70..d737a98 100644 --- a/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs @@ -39,11 +39,21 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase public bool IsDone => AgentStatusLabel == "Done"; public bool IsFailed => AgentStatusLabel == "Failed"; + [ObservableProperty] + [NotifyCanExecuteChangedFor(nameof(ContinueCommand))] + [NotifyCanExecuteChangedFor(nameof(ResetCommand))] + private bool _showFailedActions; + + [ObservableProperty] + [NotifyCanExecuteChangedFor(nameof(ContinueCommand))] + private string? _latestRunSessionId; + partial void OnAgentStatusLabelChanged(string value) { OnPropertyChanged(nameof(IsRunning)); OnPropertyChanged(nameof(IsDone)); OnPropertyChanged(nameof(IsFailed)); + ShowFailedActions = value == "Failed"; } [ObservableProperty] private string? _model; [ObservableProperty] private string? _worktreePath; @@ -108,11 +118,15 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase // Subscribe once; filter by current task id inside the handler _worker.TaskMessageEvent += OnTaskMessage; - // Re-evaluate RunNow CanExecute when worker connection flips. + // Re-evaluate CanExecute when worker connection flips. _worker.PropertyChanged += (_, e) => { if (e.PropertyName == nameof(WorkerClient.IsConnected)) + { RunNowCommand.NotifyCanExecuteChanged(); + ContinueCommand.NotifyCanExecuteChanged(); + ResetCommand.NotifyCanExecuteChanged(); + } }; // If the task row's live status changes (e.g. TaskStarted/Finished), mirror it. @@ -215,6 +229,8 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase WorktreePath = null; BranchLine = null; AgentStatusLabel = "Idle"; + LatestRunSessionId = null; + ShowFailedActions = false; return; } @@ -243,6 +259,11 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase BranchLine = entity.Worktree is { } w ? $"{w.BranchName} \u2190 main" : null; AgentStatusLabel = entity.Status.ToString(); + var runRepo = new TaskRunRepository(ctx); + var latestRun = await runRepo.GetLatestByTaskIdAsync(row.Id, ct); + ct.ThrowIfCancellationRequested(); + LatestRunSessionId = latestRun?.SessionId; + // Subscribe only after DB load confirms the task exists _subscribedTaskId = row.Id; @@ -391,6 +412,32 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase private bool CanRunNow() => Task != null && _worker.IsConnected && !IsRunning; + + [RelayCommand(CanExecute = nameof(CanContinue))] + private async System.Threading.Tasks.Task ContinueAsync() + { + if (Task == null) return; + await _worker.ContinueTaskAsync(Task.Id, "Continue working on this task."); + } + + private bool CanContinue() => + Task != null && _worker.IsConnected && ShowFailedActions && !string.IsNullOrEmpty(LatestRunSessionId); + + [RelayCommand(CanExecute = nameof(CanReset))] + private async System.Threading.Tasks.Task ResetAsync() + { + if (Task == null) return; + if (ConfirmAsync != null) + { + var branchName = $"claudedo/{Task.Id.Replace("-", "")}"; + var ok = await ConfirmAsync($"Discard worktree and reset task?\nThis deletes branch {branchName} and all uncommitted changes."); + if (!ok) return; + } + await _worker.ResetTaskAsync(Task.Id); + } + + private bool CanReset() => + Task != null && _worker.IsConnected && ShowFailedActions; } public sealed partial class SubtaskRowViewModel : ViewModelBase