feat(ui): surface agent roadblocks and run outcome in the detail pane
- Parse CLAUDEDO_BLOCKED roadblocks out of the run result and show them in a colored card between Details and Output (ApplyOutcome / ShowRoadblockCard). - Show the run outcome summary as an OUTCOME card in the Output tab, loaded from the task result (falls back to the run's ErrorMarkdown) and refreshed on finish. - Guard the Session tab so it only appears when there are child outcomes. - Make console resize per-task and proportional (description capped at 2/3, console floored at ~1/3) so a long description no longer spills over the footer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
using System.ComponentModel;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Reactive;
|
||||
using ClaudeDo.Ui.ViewModels.Islands;
|
||||
using ClaudeDo.Ui.Views.Modals;
|
||||
using ClaudeDo.Ui.Views.Planning;
|
||||
@@ -11,27 +12,42 @@ namespace ClaudeDo.Ui.Views.Islands;
|
||||
|
||||
public partial class DetailsIslandView : UserControl
|
||||
{
|
||||
private DetailsIslandViewModel? _subscribedVm;
|
||||
// Per-task description height (pixels) once the user drags the splitter.
|
||||
// Keyed by task id so each task keeps its own resize; tasks that were
|
||||
// never dragged stay dynamic (Auto-sized description).
|
||||
private readonly Dictionary<string, double> _descriptionHeights = new();
|
||||
private DetailsIslandViewModel? _vm;
|
||||
|
||||
public DetailsIslandView()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContextChanged += OnDataContextChanged;
|
||||
// Keep the row limits proportional to the island height: description
|
||||
// capped at 2/3, console floored at 1/3. The GridSplitter honours these
|
||||
// row Min/Max during a drag, so the console stops shrinking at 1/3.
|
||||
DetailBodyGrid.GetObservable(BoundsProperty)
|
||||
.Subscribe(new AnonymousObserver<Rect>(_ => UpdateRowLimits()));
|
||||
}
|
||||
|
||||
private void UpdateRowLimits()
|
||||
{
|
||||
var h = DetailBodyGrid.Bounds.Height;
|
||||
if (h <= 0) return;
|
||||
DetailBodyGrid.RowDefinitions[0].MaxHeight = h * 2.0 / 3.0;
|
||||
DetailBodyGrid.RowDefinitions[1].MinHeight = h / 3.0;
|
||||
}
|
||||
|
||||
private void OnDataContextChanged(object? sender, EventArgs e)
|
||||
{
|
||||
if (_subscribedVm is not null)
|
||||
_subscribedVm.PropertyChanged -= OnVmPropertyChanged;
|
||||
_subscribedVm = DataContext as DetailsIslandViewModel;
|
||||
if (_subscribedVm is not null)
|
||||
{
|
||||
_subscribedVm.PropertyChanged += OnVmPropertyChanged;
|
||||
ApplyConsoleMaximized(_subscribedVm.IsConsoleMaximized);
|
||||
}
|
||||
if (_vm != null)
|
||||
_vm.PropertyChanged -= OnViewModelPropertyChanged;
|
||||
|
||||
if (DataContext is DetailsIslandViewModel vm)
|
||||
{
|
||||
_vm = vm;
|
||||
vm.PropertyChanged += OnViewModelPropertyChanged;
|
||||
ApplyResizeStateForCurrentTask();
|
||||
|
||||
vm.ShowDiffModal = async (diffVm) =>
|
||||
{
|
||||
var owner = TopLevel.GetTopLevel(this) as Window;
|
||||
@@ -61,22 +77,39 @@ public partial class DetailsIslandView : UserControl
|
||||
}
|
||||
}
|
||||
|
||||
private void OnVmPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
// Restores the resize state for the currently-selected task: a task the
|
||||
// user has dragged before gets its pinned pixel height (cap lifted); a task
|
||||
// never dragged falls back to dynamic sizing (Auto row + the bound cap).
|
||||
private void OnViewModelPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(DetailsIslandViewModel.IsConsoleMaximized)
|
||||
&& sender is DetailsIslandViewModel vm)
|
||||
ApplyConsoleMaximized(vm.IsConsoleMaximized);
|
||||
if (e.PropertyName == nameof(DetailsIslandViewModel.Task))
|
||||
ApplyResizeStateForCurrentTask();
|
||||
}
|
||||
|
||||
// Maximized: shrink the description row to its MinHeight (the console fills
|
||||
// the rest). Restored: back to the 2:1 default. The GridSplitter keeps both
|
||||
// states draggable; MinHeight stops the console from ever covering it.
|
||||
private void ApplyConsoleMaximized(bool maximized)
|
||||
private void ApplyResizeStateForCurrentTask()
|
||||
{
|
||||
// A task dragged before keeps its pixel height (clamped by the row's
|
||||
// 2/3 MaxHeight); a task never dragged stays Auto-sized.
|
||||
DetailBodyGrid.RowDefinitions[0].Height = _vm?.Task?.Id is string id && _descriptionHeights.TryGetValue(id, out var h)
|
||||
? new GridLength(h, GridUnitType.Pixel)
|
||||
: GridLength.Auto;
|
||||
}
|
||||
|
||||
// Pin the (until now Auto-sized) description row to its current pixel
|
||||
// height so the splitter resizes smoothly from there.
|
||||
private void OnSplitterDragStarted(object? sender, VectorEventArgs e)
|
||||
{
|
||||
var descRow = DetailBodyGrid.RowDefinitions[0];
|
||||
descRow.Height = maximized
|
||||
? new GridLength(descRow.MinHeight, GridUnitType.Pixel)
|
||||
: new GridLength(2, GridUnitType.Star);
|
||||
if (descRow.Height.IsAuto)
|
||||
descRow.Height = new GridLength(DescriptionCard.Bounds.Height, GridUnitType.Pixel);
|
||||
}
|
||||
|
||||
// Remember the dragged height for this task so switching tasks keeps each
|
||||
// task's resize independent.
|
||||
private void OnSplitterDragCompleted(object? sender, VectorEventArgs e)
|
||||
{
|
||||
if (_vm?.Task?.Id is string id)
|
||||
_descriptionHeights[id] = DetailBodyGrid.RowDefinitions[0].Height.Value;
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task ShowErrorDialogAsync(string message)
|
||||
|
||||
Reference in New Issue
Block a user