Files
ClaudeDo/src/ClaudeDo.Ui/Views/TaskDetailView.axaml.cs
Mika Kuns 9a407bde83 feat(ui): agent config inline in detail panel, file picker, subtask UI
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>
2026-04-15 11:20:17 +02:00

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;
}
}
}
}