TaskDetailView now edits Model / SystemPrompt / Agent inline (LostFocus save), matching the modal editor. Both TaskEditorView and TaskDetailView gain a Browse button that opens a .md file picker — external agent paths are preserved on reload via a synthetic AgentInfo entry. Both views also render the per-task subtask checklist (CheckBox + TextBox + remove), with diff-on-save in the editor and inline-save in the detail panel. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
86 lines
2.5 KiB
C#
86 lines
2.5 KiB
C#
using System.ComponentModel;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Input;
|
|
using Avalonia.Interactivity;
|
|
using Avalonia.Platform.Storage;
|
|
using ClaudeDo.Ui.ViewModels;
|
|
|
|
namespace ClaudeDo.Ui.Views;
|
|
|
|
public partial class TaskDetailView : UserControl
|
|
{
|
|
public TaskDetailView()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
private async void OnFieldLostFocus(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (DataContext is TaskDetailViewModel vm)
|
|
await vm.SaveAsync();
|
|
}
|
|
|
|
private void OnSubtaskTitleLostFocus(object? sender, RoutedEventArgs e)
|
|
{
|
|
// Title change is handled by SubtaskItemViewModel.PropertyChanged → OnSubtaskPropertyChanged in the VM
|
|
}
|
|
|
|
private async void OnBrowseAgent(object? sender, RoutedEventArgs e)
|
|
{
|
|
var topLevel = TopLevel.GetTopLevel(this);
|
|
if (topLevel is null) return;
|
|
var files = await topLevel.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
|
{
|
|
Title = "Select Agent File",
|
|
AllowMultiple = false,
|
|
FileTypeFilter = new[] { new FilePickerFileType("Agent Files") { Patterns = ["*.md"] } },
|
|
});
|
|
if (files.Count == 0) return;
|
|
var path = files[0].TryGetLocalPath();
|
|
if (path is null) return;
|
|
if (DataContext is TaskDetailViewModel vm)
|
|
{
|
|
vm.SetAgentFromPath(path);
|
|
await vm.SaveAsync();
|
|
}
|
|
}
|
|
|
|
private void OnTagInputKeyDown(object? sender, KeyEventArgs e)
|
|
{
|
|
if (e.Key == Key.Enter && DataContext is TaskDetailViewModel vm)
|
|
{
|
|
vm.AddTagCommand.Execute(null);
|
|
e.Handled = true;
|
|
}
|
|
}
|
|
|
|
public void FocusTitle()
|
|
{
|
|
this.FindControl<TextBox>("TitleBox")?.Focus();
|
|
}
|
|
|
|
private TaskDetailViewModel? _previousVm;
|
|
|
|
protected override void OnDataContextChanged(EventArgs e)
|
|
{
|
|
base.OnDataContextChanged(e);
|
|
if (_previousVm is not null)
|
|
_previousVm.PropertyChanged -= OnViewModelPropertyChanged;
|
|
_previousVm = DataContext as TaskDetailViewModel;
|
|
if (_previousVm is not null)
|
|
_previousVm.PropertyChanged += OnViewModelPropertyChanged;
|
|
}
|
|
|
|
private void OnViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
|
{
|
|
if (e.PropertyName == nameof(TaskDetailViewModel.LiveText))
|
|
{
|
|
var box = this.FindControl<TextBox>("LiveOutputBox");
|
|
if (box is not null)
|
|
{
|
|
box.CaretIndex = box.Text?.Length ?? 0;
|
|
}
|
|
}
|
|
}
|
|
}
|