- Remove wrapping ScrollViewer from live output TextBox — Avalonia TextBox with AcceptsReturn handles its own scrolling; nested ScrollViewer caused layout collapse - Auto-scroll via CaretIndex instead of removed ScrollViewer - Add OnWindowClosed to both editor ViewModels, ensuring the TaskCompletionSource always resolves when the dialog closes (including via X button), preventing RelayCommand from staying permanently disabled Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
127 lines
4.2 KiB
C#
127 lines
4.2 KiB
C#
using ClaudeDo.Data.Models;
|
|
using ClaudeDo.Ui.Services;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using CommunityToolkit.Mvvm.Input;
|
|
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
|
|
|
namespace ClaudeDo.Ui.ViewModels;
|
|
|
|
public partial class TaskEditorViewModel : ViewModelBase
|
|
{
|
|
[ObservableProperty] private string _title = "";
|
|
[ObservableProperty] private string? _description;
|
|
[ObservableProperty] private string _commitType = "chore";
|
|
[ObservableProperty] private string _statusChoice = "manual";
|
|
[ObservableProperty] private string _tagsInput = "";
|
|
[ObservableProperty] private string _windowTitle = "New Task";
|
|
[ObservableProperty] private string _modelChoice = "(list default)";
|
|
[ObservableProperty] private string? _systemPromptOverride;
|
|
[ObservableProperty] private AgentInfo? _selectedAgent;
|
|
public List<AgentInfo> AvailableAgents { get; set; } = [];
|
|
|
|
private string? _editId;
|
|
private string _listId = "";
|
|
private DateTime _createdAt;
|
|
private TaskCompletionSource<TaskEntity?> _tcs = new();
|
|
|
|
public event Action? RequestClose;
|
|
|
|
public static string[] ModelChoices { get; } = ["(list default)", "Sonnet", "Opus", "Haiku"];
|
|
|
|
public static string[] CommitTypes { get; } =
|
|
["chore", "feat", "fix", "refactor", "docs", "test", "perf", "style", "build", "ci"];
|
|
|
|
public static string[] StatusChoices { get; } =
|
|
["manual", "queued"];
|
|
|
|
public async Task LoadAgentsAsync(WorkerClient worker)
|
|
{
|
|
AvailableAgents = await worker.GetAgentsAsync();
|
|
}
|
|
|
|
public IReadOnlyList<string> SelectedTagNames =>
|
|
TagsInput.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
|
.Distinct()
|
|
.ToList();
|
|
|
|
public void InitForCreate(string listId, string defaultCommitType = "chore")
|
|
{
|
|
_editId = null;
|
|
_listId = listId;
|
|
_createdAt = DateTime.UtcNow;
|
|
CommitType = defaultCommitType;
|
|
WindowTitle = "New Task";
|
|
}
|
|
|
|
public void InitForEdit(TaskEntity entity, IReadOnlyList<TagEntity> taskTags)
|
|
{
|
|
_editId = entity.Id;
|
|
_listId = entity.ListId;
|
|
_createdAt = entity.CreatedAt;
|
|
Title = entity.Title;
|
|
Description = entity.Description;
|
|
CommitType = entity.CommitType;
|
|
StatusChoice = entity.Status switch
|
|
{
|
|
TaskStatus.Manual => "manual",
|
|
TaskStatus.Queued => "queued",
|
|
_ => entity.Status.ToString().ToLowerInvariant(),
|
|
};
|
|
TagsInput = string.Join(", ", taskTags.Select(t => t.Name));
|
|
ModelChoice = entity.Model is not null
|
|
? ListEditorViewModel.ModelIdToDisplay(entity.Model)
|
|
: "(list default)";
|
|
SystemPromptOverride = entity.SystemPrompt;
|
|
SelectedAgent = entity.AgentPath is not null
|
|
? AvailableAgents.FirstOrDefault(a => a.Path == entity.AgentPath)
|
|
: null;
|
|
WindowTitle = $"Edit Task: {entity.Title}";
|
|
}
|
|
|
|
[RelayCommand]
|
|
private void Save()
|
|
{
|
|
if (string.IsNullOrWhiteSpace(Title)) return;
|
|
var status = StatusChoice switch
|
|
{
|
|
"queued" => TaskStatus.Queued,
|
|
_ => TaskStatus.Manual,
|
|
};
|
|
var entity = new TaskEntity
|
|
{
|
|
Id = _editId ?? Guid.NewGuid().ToString(),
|
|
ListId = _listId,
|
|
Title = Title.Trim(),
|
|
Description = string.IsNullOrWhiteSpace(Description) ? null : Description.Trim(),
|
|
Status = status,
|
|
CommitType = CommitType,
|
|
CreatedAt = _createdAt,
|
|
};
|
|
entity.Model = ModelChoice != "(list default)"
|
|
? ListEditorViewModel.ModelDisplayToId(ModelChoice)
|
|
: null;
|
|
entity.SystemPrompt = string.IsNullOrWhiteSpace(SystemPromptOverride) ? null : SystemPromptOverride.Trim();
|
|
entity.AgentPath = SelectedAgent?.Path;
|
|
_tcs.TrySetResult(entity);
|
|
RequestClose?.Invoke();
|
|
}
|
|
|
|
[RelayCommand]
|
|
private void Cancel()
|
|
{
|
|
_tcs.TrySetResult(null);
|
|
RequestClose?.Invoke();
|
|
}
|
|
|
|
public void OnWindowClosed()
|
|
{
|
|
_tcs.TrySetResult(null);
|
|
}
|
|
|
|
public Task<TaskEntity?> ShowAndWaitAsync()
|
|
{
|
|
_tcs = new TaskCompletionSource<TaskEntity?>();
|
|
return _tcs.Task;
|
|
}
|
|
}
|