diff --git a/src/ClaudeDo.Localization/locales/de.json b/src/ClaudeDo.Localization/locales/de.json index 3381a55..76d879b 100644 --- a/src/ClaudeDo.Localization/locales/de.json +++ b/src/ClaudeDo.Localization/locales/de.json @@ -236,7 +236,8 @@ "placeholder": "Nachricht an die Sitzung…", "send": "Senden", "stop": "Sitzung beenden", - "interrupt": "Aktuellen Zug unterbrechen" + "interrupt": "Aktuellen Zug unterbrechen", + "queued": "Wartet — wird nach dem aktuellen Zug gesendet" } }, "missionControl": { diff --git a/src/ClaudeDo.Localization/locales/en.json b/src/ClaudeDo.Localization/locales/en.json index 82afbc4..05156e6 100644 --- a/src/ClaudeDo.Localization/locales/en.json +++ b/src/ClaudeDo.Localization/locales/en.json @@ -236,7 +236,8 @@ "placeholder": "Message the session…", "send": "Send", "stop": "Stop session", - "interrupt": "Interrupt current turn" + "interrupt": "Interrupt current turn", + "queued": "Queued — sends after the current turn" } }, "missionControl": { diff --git a/src/ClaudeDo.Ui/ViewModels/Islands/TaskMonitorViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Islands/TaskMonitorViewModel.cs index 872b4d7..1762275 100644 --- a/src/ClaudeDo.Ui/ViewModels/Islands/TaskMonitorViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/Islands/TaskMonitorViewModel.cs @@ -70,6 +70,9 @@ public sealed partial class TaskMonitorViewModel : ViewModelBase, IDisposable private const string RoadblockMarker = "Roadblocks reported during the run:"; + public ObservableCollection QueuedMessages { get; } = new(); + public bool HasQueuedMessages => QueuedMessages.Count > 0; + // Captured handler delegates for disposal private readonly Action _onTaskMessage; private readonly Action _onTaskStarted; @@ -79,6 +82,8 @@ public sealed partial class TaskMonitorViewModel : ViewModelBase, IDisposable private readonly Action _onTaskQuestionResolved; private readonly Action _onInteractiveStarted; private readonly Action _onInteractiveEnded; + private readonly Action> _onInteractiveQueueChanged; + private readonly Action _onInteractiveMessageSent; // Interactive composer — active while the worker is in an interactive session. [ObservableProperty] @@ -164,9 +169,29 @@ public sealed partial class TaskMonitorViewModel : ViewModelBase, IDisposable _onInteractiveEnded = taskId => { - if (taskId == _subscribedTaskId) { IsInteractiveLive = false; AgentState = "done"; } + if (taskId != _subscribedTaskId) return; + IsInteractiveLive = false; + AgentState = "done"; + QueuedMessages.Clear(); + OnPropertyChanged(nameof(HasQueuedMessages)); }; _worker.InteractiveSessionEndedEvent += _onInteractiveEnded; + + _onInteractiveQueueChanged = (taskId, pending) => + { + if (taskId != _subscribedTaskId) return; + QueuedMessages.Clear(); + foreach (var m in pending) QueuedMessages.Add(m); + OnPropertyChanged(nameof(HasQueuedMessages)); + }; + _worker.InteractiveQueueChangedEvent += _onInteractiveQueueChanged; + + _onInteractiveMessageSent = (taskId, text) => + { + if (taskId == _subscribedTaskId) + Log.Add(new LogLineViewModel { Kind = LogKind.User, Text = text }); + }; + _worker.InteractiveMessageSentEvent += _onInteractiveMessageSent; } // Surface a pending question (used by live event + re-attach hydration). @@ -189,7 +214,6 @@ public sealed partial class TaskMonitorViewModel : ViewModelBase, IDisposable if (string.IsNullOrEmpty(_subscribedTaskId)) return; var text = ComposerDraft; if (string.IsNullOrWhiteSpace(text)) return; - Log.Add(new LogLineViewModel { Kind = LogKind.User, Text = text }); ComposerDraft = string.Empty; await _worker.SendInteractiveMessageAsync(_subscribedTaskId, text); } @@ -260,6 +284,8 @@ public sealed partial class TaskMonitorViewModel : ViewModelBase, IDisposable ClearPendingQuestion(); IsInteractiveLive = false; ComposerDraft = string.Empty; + QueuedMessages.Clear(); + OnPropertyChanged(nameof(HasQueuedMessages)); } [ObservableProperty] @@ -485,5 +511,7 @@ public sealed partial class TaskMonitorViewModel : ViewModelBase, IDisposable _worker.TaskQuestionResolvedEvent -= _onTaskQuestionResolved; _worker.InteractiveSessionStartedEvent -= _onInteractiveStarted; _worker.InteractiveSessionEndedEvent -= _onInteractiveEnded; + _worker.InteractiveQueueChangedEvent -= _onInteractiveQueueChanged; + _worker.InteractiveMessageSentEvent -= _onInteractiveMessageSent; } } diff --git a/src/ClaudeDo.Ui/Views/Islands/Detail/WorkConsole.axaml b/src/ClaudeDo.Ui/Views/Islands/Detail/WorkConsole.axaml index e89c096..59cd30e 100644 --- a/src/ClaudeDo.Ui/Views/Islands/Detail/WorkConsole.axaml +++ b/src/ClaudeDo.Ui/Views/Islands/Detail/WorkConsole.axaml @@ -263,41 +263,71 @@ Command="{Binding RejectReviewCommand}" /> - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - + - +