diff --git a/src/ClaudeDo.Localization/locales/de.json b/src/ClaudeDo.Localization/locales/de.json index 2dd53a4..043c575 100644 --- a/src/ClaudeDo.Localization/locales/de.json +++ b/src/ClaudeDo.Localization/locales/de.json @@ -384,7 +384,7 @@ "vm": { "connection": { "online": "Online", "connecting": "Verbinden…", "offline": "Offline" }, "shell": { "restartingWorker": "Worker wird neu gestartet…" }, - "agentStatus": { "idle": "Leerlauf", "queued": "In Warteschlange", "running": "Läuft", "done": "Fertig", "failed": "Fehlgeschlagen", "cancelled": "Abgebrochen" }, + "agentStatus": { "idle": "Leerlauf", "queued": "In Warteschlange", "running": "Läuft", "review": "Prüfung", "done": "Fertig", "failed": "Fehlgeschlagen", "cancelled": "Abgebrochen" }, "taskStatus": { "idle": "Leerlauf", "queued": "In Warteschlange", "running": "Läuft", "waitingForReview": "Wartet auf Prüfung", "done": "Fertig", "failed": "Fehlgeschlagen", "cancelled": "Abgebrochen" }, "planningBadge": { "active": "PLANUNG", "finalized": "GEPLANT" }, "taskRow": { "createdPrefix": "Erstellt {0}", "stepsText": "{0}/{1} Schritte" }, diff --git a/src/ClaudeDo.Localization/locales/en.json b/src/ClaudeDo.Localization/locales/en.json index c9f228a..9046651 100644 --- a/src/ClaudeDo.Localization/locales/en.json +++ b/src/ClaudeDo.Localization/locales/en.json @@ -384,7 +384,7 @@ "vm": { "connection": { "online": "Online", "connecting": "Connecting…", "offline": "Offline" }, "shell": { "restartingWorker": "Restarting worker…" }, - "agentStatus": { "idle": "Idle", "queued": "Queued", "running": "Running", "done": "Done", "failed": "Failed", "cancelled": "Cancelled" }, + "agentStatus": { "idle": "Idle", "queued": "Queued", "running": "Running", "review": "Review", "done": "Done", "failed": "Failed", "cancelled": "Cancelled" }, "taskStatus": { "idle": "Idle", "queued": "Queued", "running": "Running", "waitingForReview": "Waiting for Review", "done": "Done", "failed": "Failed", "cancelled": "Cancelled" }, "planningBadge": { "active": "PLANNING", "finalized": "PLANNED" }, "taskRow": { "createdPrefix": "Created {0}", "stepsText": "{0}/{1} steps" }, diff --git a/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs index f77d7f1..b57d239 100644 --- a/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs @@ -110,12 +110,13 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase [NotifyCanExecuteChangedFor(nameof(ContinueCommand))] private string _agentState = "idle"; public string AgentStatusLabel => Loc.T($"vm.agentStatus.{AgentState}"); - public bool IsIdle => AgentState == "idle"; - public bool IsQueued => AgentState == "queued"; - public bool IsRunning => AgentState == "running"; - public bool IsDone => AgentState == "done"; - public bool IsFailed => AgentState == "failed"; - public bool IsCancelled => AgentState == "cancelled"; + public bool IsIdle => AgentState == "idle"; + public bool IsQueued => AgentState == "queued"; + public bool IsRunning => AgentState == "running"; + public bool IsWaitingForReview => AgentState == "review"; + public bool IsDone => AgentState == "done"; + public bool IsFailed => AgentState == "failed"; + public bool IsCancelled => AgentState == "cancelled"; // Recovery actions: Continue (resume session) for Failed/Cancelled. public bool ShowContinue => IsFailed || IsCancelled; @@ -132,6 +133,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase OnPropertyChanged(nameof(IsIdle)); OnPropertyChanged(nameof(IsQueued)); OnPropertyChanged(nameof(IsRunning)); + OnPropertyChanged(nameof(IsWaitingForReview)); OnPropertyChanged(nameof(IsDone)); OnPropertyChanged(nameof(IsFailed)); OnPropertyChanged(nameof(IsCancelled)); @@ -279,7 +281,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase { ClaudeDo.Data.Models.TaskStatus.Queued => "queued", ClaudeDo.Data.Models.TaskStatus.Running => "running", - ClaudeDo.Data.Models.TaskStatus.WaitingForReview => "running", + ClaudeDo.Data.Models.TaskStatus.WaitingForReview => "review", ClaudeDo.Data.Models.TaskStatus.Done => "done", ClaudeDo.Data.Models.TaskStatus.Failed => "failed", ClaudeDo.Data.Models.TaskStatus.Cancelled => "cancelled", @@ -291,7 +293,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase "done" => "done", "failed" => "failed", "cancelled" => "cancelled", - "waiting_for_review" => "running", + "waiting_for_review" => "review", _ => status.ToLowerInvariant(), }; @@ -880,6 +882,9 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase AgentState = StatusToStateKey(entity.Status); if (Task is { } row && entity.Worktree?.DiffStat is { } stat) row.DiffStat = stat; + var (add, del) = ParseDiffStat(entity.Worktree?.DiffStat); + DiffAdditions = add; + DiffDeletions = del; } catch { /* best-effort refresh */ } } @@ -1131,6 +1136,52 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase [RelayCommand] private void ResetTaskModel() => TaskModelSelection = null; [RelayCommand] private void ResetTaskTurns() => TaskMaxTurns = null; [RelayCommand] private void ResetTaskAgent() => TaskSelectedAgent = TaskAgentOptions.Count > 0 ? TaskAgentOptions[0] : null; + + // ── Review actions ────────────────────────────────────────────────────────── + [ObservableProperty] private string _reviewFeedback = ""; + + [RelayCommand] + private async System.Threading.Tasks.Task ApproveReviewAsync() + { + if (Task is null || !_worker.IsConnected) return; + await _worker.ApproveReviewAsync(Task.Id); + } + + [RelayCommand] + private async System.Threading.Tasks.Task RejectReviewAsync() + { + if (Task is null || !_worker.IsConnected) return; + var feedback = ReviewFeedback; + if (string.IsNullOrWhiteSpace(feedback)) return; + await _worker.RejectReviewToQueueAsync(Task.Id, feedback); + ReviewFeedback = ""; + } + + [RelayCommand] + private async System.Threading.Tasks.Task ParkReviewAsync() + { + if (Task is null || !_worker.IsConnected) return; + await _worker.RejectReviewToIdleAsync(Task.Id); + } + + [RelayCommand] + private async System.Threading.Tasks.Task CancelReviewAsync() + { + if (Task is null || !_worker.IsConnected) return; + await _worker.CancelReviewAsync(Task.Id); + } + + // ── Diff meter parser ─────────────────────────────────────────────────────── + internal static (int Additions, int Deletions) ParseDiffStat(string? stat) + { + if (string.IsNullOrEmpty(stat)) return (0, 0); + int add = 0, del = 0; + var m1 = System.Text.RegularExpressions.Regex.Match(stat, @"(\d+)\s+insertion"); + var m2 = System.Text.RegularExpressions.Regex.Match(stat, @"(\d+)\s+deletion"); + if (m1.Success) int.TryParse(m1.Groups[1].Value, out add); + if (m2.Success) int.TryParse(m2.Groups[1].Value, out del); + return (add, del); + } } public sealed partial class SubtaskRowViewModel : ViewModelBase diff --git a/src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml b/src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml index a836fc0..e8c93d5 100644 --- a/src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml +++ b/src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml @@ -185,6 +185,44 @@ + + + + + + + +