feat(ui): add WorkConsole detail component

Standalone terminal-styled card with traffic-light title bar, roadblock
band, and three tabs (Output / Actions / Session). Renders fully via
design-time sample data; does not touch DetailsIslandView.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-06-04 19:28:08 +02:00
parent c323953f8c
commit ce50f9fcce
3 changed files with 402 additions and 0 deletions

View File

@@ -0,0 +1,145 @@
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using ClaudeDo.Ui.ViewModels.Islands;
namespace ClaudeDo.Ui.ViewModels.Islands.Detail;
public sealed class WorkConsoleChildOutcomeRowViewModel
{
public required string Title { get; init; }
public bool HasRoadblock { get; init; }
public string RoadblockText { get; init; } = "";
public required string StatusLabel { get; init; }
}
public sealed partial class WorkConsoleViewModel : ViewModelBase
{
// ── Tab selection ──────────────────────────────────────────────
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsOutputTab))]
[NotifyPropertyChangedFor(nameof(IsActionsTab))]
[NotifyPropertyChangedFor(nameof(IsSessionTab))]
private string _selectedTab = "output";
public bool IsOutputTab => SelectedTab == "output";
public bool IsActionsTab => SelectedTab == "actions";
public bool IsSessionTab => SelectedTab == "session";
[RelayCommand]
private void SelectTab(string tab) => SelectedTab = tab;
// ── Info header ────────────────────────────────────────────────
[ObservableProperty] private string _model = "";
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(TurnsText))]
private int _turns;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(DiffAddText))]
private int _diffAdditions;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(DiffDelText))]
private int _diffDeletions;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ShowRoadblock))]
private bool _isRunning;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ShowRoadblock))]
private bool _isDone;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ShowRoadblock))]
private bool _isFailed;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ShowRoadblock))]
private bool _isCancelled;
[ObservableProperty] private string _sessionLabel = "";
public string TurnsText => $"{Turns} turns";
public string DiffAddText => $"+{DiffAdditions}";
public string DiffDelText => $"-{DiffDeletions}";
// ── Roadblock ──────────────────────────────────────────────────
public bool ShowRoadblock => IsFailed || IsCancelled;
[ObservableProperty] private string _roadblockMessage = "";
[ObservableProperty] private bool _showContinue;
[ObservableProperty] private bool _showResetAndRetry;
[RelayCommand] private void Continue() { }
[RelayCommand] private void ResetAndRetry() { }
// ── Actions tab ────────────────────────────────────────────────
public ObservableCollection<string> MergeTargetBranches { get; } = new();
[ObservableProperty] private string? _selectedMergeTarget;
[ObservableProperty] private bool _canMergeAll;
[ObservableProperty] private string _mergeAllDisabledReason = "";
[ObservableProperty] private string? _mergeAllError;
[RelayCommand] private void OpenDiff() { }
[RelayCommand] private void OpenWorktree() { }
[RelayCommand] private void ReviewCombinedDiff() { }
[RelayCommand] private void MergeAll() { }
// ── Session tab ────────────────────────────────────────────────
[ObservableProperty] private bool _isWaitingForReview;
[ObservableProperty] private string _reviewFeedback = "";
public ObservableCollection<WorkConsoleChildOutcomeRowViewModel> ChildOutcomes { get; } = new();
public bool HasChildOutcomes => ChildOutcomes.Count > 0;
[RelayCommand] private void ApproveReview() { }
[RelayCommand] private void RejectReview() { }
[RelayCommand] private void ParkReview() { }
[RelayCommand] private void CancelReview() { }
public ObservableCollection<LogLineViewModel> Log { get; } = new();
public WorkConsoleViewModel()
{
ChildOutcomes.CollectionChanged += (_, _) => OnPropertyChanged(nameof(HasChildOutcomes));
// ── Design-time sample data ────────────────────────────────
_model = "sonnet";
_turns = 40;
_diffAdditions = 84;
_diffDeletions = 31;
_isRunning = true;
_sessionLabel = "feat/work-console";
MergeTargetBranches.Add("main");
MergeTargetBranches.Add("develop");
_selectedMergeTarget = "main";
_canMergeAll = true;
Log.Add(new LogLineViewModel { Kind = LogKind.Sys, Text = "Starting claude session…" });
Log.Add(new LogLineViewModel { Kind = LogKind.Claude, Text = "Reading DetailsIslandView.axaml to understand existing layout." });
Log.Add(new LogLineViewModel { Kind = LogKind.Tool, Text = "Read(src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml)" });
Log.Add(new LogLineViewModel { Kind = LogKind.Claude, Text = "Building WorkConsole component with three tabs." });
Log.Add(new LogLineViewModel { Kind = LogKind.Stdout, Text = "dotnet build succeeded — 0 error(s)" });
ChildOutcomes.Add(new WorkConsoleChildOutcomeRowViewModel
{
Title = "Add WorkConsole XAML",
StatusLabel = "Done"
});
ChildOutcomes.Add(new WorkConsoleChildOutcomeRowViewModel
{
Title = "Wire ViewModel bindings",
HasRoadblock = true,
RoadblockText = "Missing token",
StatusLabel = "Failed"
});
// To preview roadblock state: _isFailed = true; _roadblockMessage = "Session ended unexpectedly"; _showResetAndRetry = true; _isRunning = false;
// To preview review state: _isWaitingForReview = true; _isDone = false; _isRunning = false;
}
}