feat(ui): wire avalonia desktop ui to data and worker
App: build a ServiceProvider in Program.cs (AppSettings, SqliteConnectionFactory, all repositories, GitService, WorkerClient, all view-models), apply schema, then hand control to Avalonia. App.OnFrameworkInitializationCompleted resolves MainWindowViewModel from the container. Ui: - AppSettings POCO loaded from ~/.todo-app/ui.config.json (db path, hub url). - WorkerClient wraps HubConnection with auto-reconnect, exposes IsConnected and ActiveTasks plus C# events for TaskStarted/Finished/Message/Updated and WorktreeUpdated; all inbound events are marshalled to the UI thread. - ViewModels: MainWindow (lists CRUD via ListEditor dialog), TaskList (load by list, add/edit/delete, auto WakeQueue on agent+queued create), TaskItem (RunNow gated on connection + status), TaskDetail (description, result, live ndjson rolling buffer of 500 lines, worktree branch/diff with merge/keep/ discard via GitService), StatusBar, ListEditor, TaskEditor. - Views: 3-pane MainWindow (lists | tasks | detail) with GridSplitters, status bar, dialog windows for the editors. Status badges via StatusColorConverter. - Markdown rendering, folder picker, delete-confirmation, settings dialog and scroll-to-bottom on the live log are intentionally TODO -- functional scaffold only. Tests: also debounce the FIFO queue test (poll instead of Task.Delay(200)) so the assertion isn't racy when the suite runs alongside the slower git tests. 38 tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
61
src/ClaudeDo.Ui/ViewModels/TaskItemViewModel.cs
Normal file
61
src/ClaudeDo.Ui/ViewModels/TaskItemViewModel.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using ClaudeDo.Data.Models;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
||||
|
||||
namespace ClaudeDo.Ui.ViewModels;
|
||||
|
||||
public partial class TaskItemViewModel : ViewModelBase
|
||||
{
|
||||
[ObservableProperty] private string _title;
|
||||
[ObservableProperty] private string _statusText;
|
||||
[ObservableProperty] private string _tagsText;
|
||||
[ObservableProperty] private string _commitType;
|
||||
[ObservableProperty] private string? _description;
|
||||
[ObservableProperty] private TaskStatus _status;
|
||||
|
||||
public string Id { get; }
|
||||
public string ListId { get; }
|
||||
public TaskEntity Entity { get; private set; }
|
||||
|
||||
private readonly Func<string, Task>? _runNow;
|
||||
private readonly Func<bool> _canRunNow;
|
||||
|
||||
public TaskItemViewModel(TaskEntity entity, IReadOnlyList<TagEntity> tags,
|
||||
Func<string, Task>? runNow, Func<bool> canRunNow)
|
||||
{
|
||||
Entity = entity;
|
||||
Id = entity.Id;
|
||||
ListId = entity.ListId;
|
||||
_title = entity.Title;
|
||||
_status = entity.Status;
|
||||
_statusText = entity.Status.ToString().ToLowerInvariant();
|
||||
_tagsText = string.Join(", ", tags.Select(t => t.Name));
|
||||
_commitType = entity.CommitType;
|
||||
_description = entity.Description;
|
||||
_runNow = runNow;
|
||||
_canRunNow = canRunNow;
|
||||
}
|
||||
|
||||
public void Refresh(TaskEntity entity, IReadOnlyList<TagEntity> tags)
|
||||
{
|
||||
Entity = entity;
|
||||
Title = entity.Title;
|
||||
Status = entity.Status;
|
||||
StatusText = entity.Status.ToString().ToLowerInvariant();
|
||||
TagsText = string.Join(", ", tags.Select(t => t.Name));
|
||||
CommitType = entity.CommitType;
|
||||
Description = entity.Description;
|
||||
RunNowCommand.NotifyCanExecuteChanged();
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanRunNow))]
|
||||
private async Task RunNowAsync()
|
||||
{
|
||||
if (_runNow is not null)
|
||||
await _runNow(Id);
|
||||
}
|
||||
|
||||
private bool CanRunNow() =>
|
||||
_canRunNow() && Status == TaskStatus.Queued;
|
||||
}
|
||||
Reference in New Issue
Block a user