Merge claudedo/8c1c213004574c4fad6beb75b84b70d7

This commit is contained in:
mika kuns
2026-08-05 12:15:21 +02:00
8 changed files with 321 additions and 1 deletions
@@ -191,8 +191,24 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(ContinueCommand))]
[NotifyPropertyChangedFor(nameof(CanReplyToRoadblock))]
[NotifyCanExecuteChangedFor(nameof(SendRoadblockReplyCommand))]
private string? _latestRunSessionId;
// A resumable session to reply to a reported roadblock with (via ContinueAsync's
// transport). Distinct from ContinueCommand: that one is Failed/Cancelled-only and
// reruns with a fixed prompt, this one answers a roadblock reported on any terminal
// state (see ShowRoadblockCard) with the user's own text.
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(SendRoadblockReplyCommand))]
private string _roadblockReplyDraft = string.Empty;
public bool CanReplyToRoadblock => !string.IsNullOrEmpty(LatestRunSessionId);
// Surfaces a UI-originated failure (e.g. override slot busy) to the shell's footer
// error strip — mirrors TasksIslandViewModel.ErrorReported.
public event Action<string>? ErrorReported;
[ObservableProperty] private string? _model;
[ObservableProperty] private string? _worktreePath;
@@ -333,6 +349,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
DequeueCommand.NotifyCanExecuteChanged();
ResetAndRetryCommand.NotifyCanExecuteChanged();
ContinueCommand.NotifyCanExecuteChanged();
SendRoadblockReplyCommand.NotifyCanExecuteChanged();
}
};
_worker.PropertyChanged += _workerPropertyChangedHandler;
@@ -524,6 +541,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
Task = row;
OnPropertyChanged(nameof(TaskIdBadge));
Monitor.Reset();
RoadblockReplyDraft = string.Empty;
Subtasks.Clear();
ChildOutcomes.Clear();
Attachments.Clear();
@@ -1025,6 +1043,26 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
private bool CanContinue() =>
Task != null && _worker.IsConnected && ShowContinue && !string.IsNullOrEmpty(LatestRunSessionId);
[RelayCommand(CanExecute = nameof(CanSendRoadblockReply))]
private async System.Threading.Tasks.Task SendRoadblockReplyAsync()
{
if (Task == null) return;
var text = RoadblockReplyDraft;
if (string.IsNullOrWhiteSpace(text)) return;
try
{
await _worker.ContinueTaskAsync(Task.Id, text);
RoadblockReplyDraft = string.Empty;
}
catch (Exception ex)
{
ErrorReported?.Invoke(Loc.T("details.roadblockReply.failed", ex.Message));
}
}
private bool CanSendRoadblockReply() =>
Task != null && _worker.IsConnected && CanReplyToRoadblock && !string.IsNullOrWhiteSpace(RoadblockReplyDraft);
[RelayCommand(CanExecute = nameof(CanResetAndRetry))]
private async System.Threading.Tasks.Task ResetAndRetryAsync()
{
@@ -256,6 +256,7 @@ public sealed partial class IslandsShellViewModel : ViewModelBase, IDisposable
if (Lists.SelectedList is { } row)
Lists.LetClaudeHandleListCommand.Execute(row);
};
Details.ErrorReported += FlashFooterError;
Details.CloseDetail = () => Tasks.SelectedTask = null;
Details.DeleteFromList = row =>
{