diff --git a/src/ClaudeDo.Ui/ViewModels/Islands/TaskRowViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Islands/TaskRowViewModel.cs index b5c16431..57b52327 100644 --- a/src/ClaudeDo.Ui/ViewModels/Islands/TaskRowViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/Islands/TaskRowViewModel.cs @@ -58,6 +58,12 @@ public sealed partial class TaskRowViewModel : ViewModelBase // Set by the custom drag while this row is being dragged — drives the "grabbed" row style. [ObservableProperty] private bool _isDragging; + // Transient: set from HubBroadcaster's OperationProgress while the worker is still creating + // this task's worktree (the silent gap between Queued and the first agent output). Cleared + // by the next entity refresh — UpdateFromEntity always reflects a settled state, so there's + // nothing left to show past that point. + [ObservableProperty] private string? _creationPhase; + // True while a drag is hovering this row (i.e. it would show a drop-hint gap). Used to // suppress the ordinary hover highlight/transitions so they don't fight the hint. public bool IsDropTarget => DropHintAbove || DropHintBelow; @@ -145,6 +151,16 @@ public sealed partial class TaskRowViewModel : ViewModelBase ? "1 roadblock reported during the run — see details" : $"{RoadblockCount} roadblocks reported during the run — see details"; + /// Mirrors WorktreeManager.PhaseCreatingWorktree — a hub payload token, not a display string. + private const string PhaseCreatingWorktree = "creating_worktree"; + + public bool HasCreationPhase => CreationPhase is not null; + public string? CreationPhaseLabel => CreationPhase switch + { + PhaseCreatingWorktree => Loc.T("ops.worker.creatingWorktree"), + _ => null, + }; + // True for every Failed task, even one that predates this field — FailureReasonLabel then // falls back to "unknown" instead of leaving the tooltip blank. public bool HasFailureReason => Status == TaskStatus.Failed; @@ -345,6 +361,11 @@ public sealed partial class TaskRowViewModel : ViewModelBase partial void OnRoadblockCountChanged(int value) { OnPropertyChanged(nameof(HasRoadblock)); OnPropertyChanged(nameof(RoadblockTooltip)); } partial void OnDropHintAboveChanged(bool value) => OnPropertyChanged(nameof(IsDropTarget)); partial void OnDropHintBelowChanged(bool value) => OnPropertyChanged(nameof(IsDropTarget)); + partial void OnCreationPhaseChanged(string? value) + { + OnPropertyChanged(nameof(HasCreationPhase)); + OnPropertyChanged(nameof(CreationPhaseLabel)); + } public void RefreshLocalized() { @@ -363,6 +384,9 @@ public sealed partial class TaskRowViewModel : ViewModelBase public void UpdateFromEntity(TaskEntity t) { + // Any entity-backed refresh reflects a settled state, so the transient creation-phase + // banner (set from the OperationProgress broadcast) has nothing left to announce. + CreationPhase = null; var (add, del) = ParseDiffStat(t.Worktree?.DiffStat); Number = t.Number; Title = t.Title; diff --git a/src/ClaudeDo.Ui/ViewModels/Islands/TasksIslandViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Islands/TasksIslandViewModel.cs index 0b5fb0a2..17d1920f 100644 --- a/src/ClaudeDo.Ui/ViewModels/Islands/TasksIslandViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/Islands/TasksIslandViewModel.cs @@ -188,6 +188,7 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable _worker.ConnectionRestoredEvent += () => LoadForList(_currentList); _worker.RefineStartedEvent += OnRefineStarted; _worker.RefineFinishedEvent += OnRefineFinished; + _worker.OperationProgressEvent += OnWorkerOperationProgress; } _langChangedHandler = (_, _) => RefreshLocalizedText(); Loc.LanguageChanged += _langChangedHandler; @@ -1563,6 +1564,18 @@ public sealed partial class TasksIslandViewModel : ViewModelBase, IDisposable if (row is not null) row.IsRefining = false; } + // OperationProgress is a generic channel shared with merge phases (TaskMergeService), whose + // opKey is also the task id — filter on the phase token so a merge in flight can't clobber + // this row's creation-phase banner. + private const string CreationPhaseCreatingWorktree = "creating_worktree"; + + private void OnWorkerOperationProgress(string opKey, string phase, int current, int total) + { + if (phase != CreationPhaseCreatingWorktree) return; + var row = Items.FirstOrDefault(r => r.Id == opKey); + if (row is not null) row.CreationPhase = phase; + } + partial void OnSelectedTaskChanged(TaskRowViewModel? value) { foreach (var i in Items) i.IsSelected = ReferenceEquals(i, value); diff --git a/src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml b/src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml index 07af2efa..928b059e 100644 --- a/src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml +++ b/src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml @@ -143,6 +143,12 @@ + + + + +