feat(ui): add MonitorPaneView
This commit is contained in:
@@ -233,6 +233,10 @@
|
|||||||
"reviewContinueTip": "Dieses Feedback senden und die Aufgabe erneut ausführen",
|
"reviewContinueTip": "Dieses Feedback senden und die Aufgabe erneut ausführen",
|
||||||
"reviewResetTip": "Alle Änderungen verwerfen und die Aufgabe auf Leerlauf zurücksetzen"
|
"reviewResetTip": "Alle Änderungen verwerfen und die Aufgabe auf Leerlauf zurücksetzen"
|
||||||
},
|
},
|
||||||
|
"missionControl": {
|
||||||
|
"openInApp": "In App öffnen",
|
||||||
|
"cancel": "Abbrechen"
|
||||||
|
},
|
||||||
"modals": {
|
"modals": {
|
||||||
"logVisualizer": {
|
"logVisualizer": {
|
||||||
"title": "WORKER-LOGS — LETZTE 30 MIN",
|
"title": "WORKER-LOGS — LETZTE 30 MIN",
|
||||||
|
|||||||
@@ -233,6 +233,10 @@
|
|||||||
"reviewContinueTip": "Send this feedback and re-run the task",
|
"reviewContinueTip": "Send this feedback and re-run the task",
|
||||||
"reviewResetTip": "Discard all changes and reset the task to Idle"
|
"reviewResetTip": "Discard all changes and reset the task to Idle"
|
||||||
},
|
},
|
||||||
|
"missionControl": {
|
||||||
|
"openInApp": "Open in app",
|
||||||
|
"cancel": "Cancel"
|
||||||
|
},
|
||||||
"modals": {
|
"modals": {
|
||||||
"logVisualizer": {
|
"logVisualizer": {
|
||||||
"title": "WORKER LOGS — LAST 30 MIN",
|
"title": "WORKER LOGS — LAST 30 MIN",
|
||||||
|
|||||||
@@ -26,6 +26,13 @@ public sealed partial class TaskMonitorViewModel : ViewModelBase, IDisposable
|
|||||||
|
|
||||||
[ObservableProperty] private string _agentState = "idle";
|
[ObservableProperty] private string _agentState = "idle";
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
[NotifyPropertyChangedFor(nameof(DisplayTitle))]
|
||||||
|
private string? _title;
|
||||||
|
|
||||||
|
public string DisplayTitle =>
|
||||||
|
string.IsNullOrWhiteSpace(Title) ? (SubscribedTaskId ?? "task") : Title!;
|
||||||
|
|
||||||
public string AgentStatusLabel => Loc.T($"vm.agentStatus.{AgentState}");
|
public string AgentStatusLabel => Loc.T($"vm.agentStatus.{AgentState}");
|
||||||
public bool IsIdle => AgentState == "idle";
|
public bool IsIdle => AgentState == "idle";
|
||||||
public bool IsQueued => AgentState == "queued";
|
public bool IsQueued => AgentState == "queued";
|
||||||
@@ -142,6 +149,13 @@ public sealed partial class TaskMonitorViewModel : ViewModelBase, IDisposable
|
|||||||
OpenInAppRequested?.Invoke(_subscribedTaskId);
|
OpenInAppRequested?.Invoke(_subscribedTaskId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async System.Threading.Tasks.Task CancelTask()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(_subscribedTaskId) && (IsRunning || IsQueued))
|
||||||
|
await _worker.CancelTaskAsync(_subscribedTaskId);
|
||||||
|
}
|
||||||
|
|
||||||
public void SetTaskId(string id) => _subscribedTaskId = id;
|
public void SetTaskId(string id) => _subscribedTaskId = id;
|
||||||
|
|
||||||
public void ApplyState(ClaudeDo.Data.Models.TaskStatus status) =>
|
public void ApplyState(ClaudeDo.Data.Models.TaskStatus status) =>
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
|
|||||||
var entity = await ctx.Tasks.AsNoTracking().FirstOrDefaultAsync(t => t.Id == taskId);
|
var entity = await ctx.Tasks.AsNoTracking().FirstOrDefaultAsync(t => t.Id == taskId);
|
||||||
if (entity is null || monitor.SubscribedTaskId != taskId) return;
|
if (entity is null || monitor.SubscribedTaskId != taskId) return;
|
||||||
monitor.ApplyState(entity.Status);
|
monitor.ApplyState(entity.Status);
|
||||||
|
monitor.Title = entity.Title;
|
||||||
var latestRun = await new TaskRunRepository(ctx).GetLatestByTaskIdAsync(taskId);
|
var latestRun = await new TaskRunRepository(ctx).GetLatestByTaskIdAsync(taskId);
|
||||||
monitor.ApplyOutcome(entity.Result, latestRun?.ErrorMarkdown);
|
monitor.ApplyOutcome(entity.Result, latestRun?.ErrorMarkdown);
|
||||||
await monitor.ReplayLogFileAsync(entity.LogPath, CancellationToken.None);
|
await monitor.ReplayLogFileAsync(entity.LogPath, CancellationToken.None);
|
||||||
|
|||||||
53
src/ClaudeDo.Ui/Views/MissionControl/MonitorPaneView.axaml
Normal file
53
src/ClaudeDo.Ui/Views/MissionControl/MonitorPaneView.axaml
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:vm="using:ClaudeDo.Ui.ViewModels.Islands"
|
||||||
|
xmlns:islands="using:ClaudeDo.Ui.Views.Islands"
|
||||||
|
xmlns:loc="using:ClaudeDo.Ui.Localization"
|
||||||
|
x:DataType="vm:TaskMonitorViewModel"
|
||||||
|
x:Class="ClaudeDo.Ui.Views.MissionControl.MonitorPaneView">
|
||||||
|
<Border Background="{DynamicResource Surface1Brush}"
|
||||||
|
BorderBrush="{DynamicResource LineBrush}"
|
||||||
|
BorderThickness="1" CornerRadius="10" ClipToBounds="True">
|
||||||
|
<DockPanel LastChildFill="True">
|
||||||
|
|
||||||
|
<!-- Footer: pane actions -->
|
||||||
|
<Border DockPanel.Dock="Bottom"
|
||||||
|
Background="{DynamicResource Surface2Brush}"
|
||||||
|
BorderBrush="{DynamicResource LineBrush}"
|
||||||
|
BorderThickness="0,1,0,0" Padding="8,6">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
|
<Button Classes="btn" Content="{loc:Tr missionControl.openInApp}"
|
||||||
|
Command="{Binding OpenInAppCommand}" />
|
||||||
|
<Button Classes="btn" Content="{loc:Tr missionControl.cancel}"
|
||||||
|
Command="{Binding CancelTaskCommand}"
|
||||||
|
IsVisible="{Binding IsRunning}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Blocking / roadblock banner -->
|
||||||
|
<Border DockPanel.Dock="Top"
|
||||||
|
IsVisible="{Binding ShowRoadblock}"
|
||||||
|
Background="{DynamicResource ErrorTintBrush}"
|
||||||
|
BorderBrush="{DynamicResource BloodBrush}"
|
||||||
|
BorderThickness="0,0,0,1" Padding="12,6">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
|
<PathIcon Data="{StaticResource Icon.Warning}"
|
||||||
|
Foreground="{DynamicResource BloodBrush}"
|
||||||
|
Width="13" Height="13" VerticalAlignment="Center" />
|
||||||
|
<TextBlock Classes="meta" Text="{Binding RoadblockMessage}"
|
||||||
|
Foreground="{DynamicResource BloodBrush}"
|
||||||
|
TextWrapping="Wrap" VerticalAlignment="Center" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Console body: reuse SessionTerminalView -->
|
||||||
|
<islands:SessionTerminalView
|
||||||
|
Entries="{Binding Log}"
|
||||||
|
Label="{Binding DisplayTitle}"
|
||||||
|
IsRunning="{Binding IsRunning}"
|
||||||
|
IsDone="{Binding IsDone}"
|
||||||
|
IsFailed="{Binding IsFailed}" />
|
||||||
|
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace ClaudeDo.Ui.Views.MissionControl;
|
||||||
|
|
||||||
|
public partial class MonitorPaneView : UserControl
|
||||||
|
{
|
||||||
|
public MonitorPaneView() => InitializeComponent();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user