From 699fe8a1484c46aaac59671db3d272d1f0f53b17 Mon Sep 17 00:00:00 2001 From: Mika Kuns Date: Tue, 14 Apr 2026 16:36:40 +0200 Subject: [PATCH] =?UTF-8?q?feat(ui):=20complete=20Batch=202=20=E2=80=94=20?= =?UTF-8?q?LiveText=20display,=20start=20feedback,=20modal=20theming,=20Li?= =?UTF-8?q?stEditor=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace LiveLines with formatted LiveText + StreamLineFormatter - Add log reload from disk for completed tasks - Add start feedback (starting.../running) on detail and list views - Apply dark theme (WindowBgBrush, AccentBrush) to editor modals - Add model/system-prompt/agent-path config to ListEditorView - Wire config loading/saving in MainWindowViewModel - Fix duplicate AgentInfo DTO (use canonical Data.Models version) Co-Authored-By: Claude Opus 4.6 (1M context) --- src/ClaudeDo.Ui/Services/WorkerClient.cs | 3 +- .../ViewModels/ListEditorViewModel.cs | 60 +++++++++++++++++-- .../ViewModels/MainWindowViewModel.cs | 11 +++- src/ClaudeDo.Ui/Views/ListEditorView.axaml | 44 ++++++++++++-- src/ClaudeDo.Ui/Views/TaskEditorView.axaml | 16 ++--- 5 files changed, 113 insertions(+), 21 deletions(-) diff --git a/src/ClaudeDo.Ui/Services/WorkerClient.cs b/src/ClaudeDo.Ui/Services/WorkerClient.cs index 107748e..ace7a7a 100644 --- a/src/ClaudeDo.Ui/Services/WorkerClient.cs +++ b/src/ClaudeDo.Ui/Services/WorkerClient.cs @@ -1,5 +1,6 @@ using System.Collections.ObjectModel; using Avalonia.Threading; +using ClaudeDo.Data.Models; using CommunityToolkit.Mvvm.ComponentModel; using Microsoft.AspNetCore.SignalR.Client; @@ -228,5 +229,3 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable public DateTime StartedAt { get; set; } } } - -public record AgentInfo(string Name, string Description, string Path); diff --git a/src/ClaudeDo.Ui/ViewModels/ListEditorViewModel.cs b/src/ClaudeDo.Ui/ViewModels/ListEditorViewModel.cs index 638f68a..a5ecc84 100644 --- a/src/ClaudeDo.Ui/ViewModels/ListEditorViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/ListEditorViewModel.cs @@ -1,6 +1,8 @@ using ClaudeDo.Data.Models; +using ClaudeDo.Ui.Services; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using AgentInfo = ClaudeDo.Data.Models.AgentInfo; namespace ClaudeDo.Ui.ViewModels; @@ -11,6 +13,11 @@ public partial class ListEditorViewModel : ViewModelBase [ObservableProperty] private string _defaultCommitType = "chore"; [ObservableProperty] private string _windowTitle = "New List"; + // Config fields + [ObservableProperty] private string _model = "Sonnet"; + [ObservableProperty] private string? _systemPrompt; + [ObservableProperty] private AgentInfo? _selectedAgent; + private string? _editId; private DateTime _createdAt; private TaskCompletionSource _tcs = new(); @@ -20,6 +27,31 @@ public partial class ListEditorViewModel : ViewModelBase public static string[] CommitTypes { get; } = ["chore", "feat", "fix", "refactor", "docs", "test", "perf", "style", "build", "ci"]; + public static string[] ModelDisplayNames { get; } = ["Sonnet", "Opus", "Haiku"]; + + private static readonly Dictionary ModelToId = new() + { + ["Sonnet"] = "claude-sonnet-4-6", + ["Opus"] = "claude-opus-4-6", + ["Haiku"] = "claude-haiku-4-5", + }; + + private static readonly Dictionary IdToModel = + ModelToId.ToDictionary(kv => kv.Value, kv => kv.Key); + + public static string ModelIdToDisplay(string? modelId) => + modelId is not null && IdToModel.TryGetValue(modelId, out var display) ? display : "Sonnet"; + + public static string? ModelDisplayToId(string display) => + ModelToId.TryGetValue(display, out var id) ? id : null; + + public List AvailableAgents { get; set; } = []; + + public async Task LoadAgentsAsync(WorkerClient worker) + { + AvailableAgents = await worker.GetAgentsAsync(); + } + public void InitForCreate() { _editId = null; @@ -27,7 +59,7 @@ public partial class ListEditorViewModel : ViewModelBase WindowTitle = "New List"; } - public void InitForEdit(ListEntity entity) + public void InitForEdit(ListEntity entity, ListConfigEntity? config) { _editId = entity.Id; _createdAt = entity.CreatedAt; @@ -35,6 +67,28 @@ public partial class ListEditorViewModel : ViewModelBase WorkingDir = entity.WorkingDir; DefaultCommitType = entity.DefaultCommitType; WindowTitle = $"Edit List: {entity.Name}"; + + if (config is not null) + { + Model = ModelIdToDisplay(config.Model); + SystemPrompt = config.SystemPrompt; + SelectedAgent = AvailableAgents.FirstOrDefault(a => a.Path == config.AgentPath); + } + } + + public ListConfigEntity? BuildConfig(string listId) + { + var modelId = ModelDisplayToId(Model); + if (modelId is null && SystemPrompt is null && SelectedAgent is null) + return null; + + return new ListConfigEntity + { + ListId = listId, + Model = modelId, + SystemPrompt = string.IsNullOrWhiteSpace(SystemPrompt) ? null : SystemPrompt.Trim(), + AgentPath = SelectedAgent?.Path, + }; } [RelayCommand] @@ -60,10 +114,6 @@ public partial class ListEditorViewModel : ViewModelBase RequestClose?.Invoke(); } - /// - /// Called by the view to await the editor result. - /// Returns the entity to persist or null if cancelled. - /// public Task ShowAndWaitAsync() { _tcs = new TaskCompletionSource(); diff --git a/src/ClaudeDo.Ui/ViewModels/MainWindowViewModel.cs b/src/ClaudeDo.Ui/ViewModels/MainWindowViewModel.cs index 4b3d573..9591001 100644 --- a/src/ClaudeDo.Ui/ViewModels/MainWindowViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/MainWindowViewModel.cs @@ -78,6 +78,7 @@ public partial class MainWindowViewModel : ViewModelBase private async Task AddList() { var editor = _listEditorFactory(); + await editor.LoadAgentsAsync(_worker); editor.InitForCreate(); var window = new ListEditorView { DataContext = editor }; @@ -90,6 +91,9 @@ public partial class MainWindowViewModel : ViewModelBase try { await _listRepo.AddAsync(entity); + var configEntity = editor.BuildConfig(entity.Id); + if (configEntity is not null) + await _listRepo.SetConfigAsync(configEntity); Lists.Add(new ListItemViewModel(entity)); } catch (Exception ex) @@ -105,8 +109,10 @@ public partial class MainWindowViewModel : ViewModelBase var existing = await _listRepo.GetByIdAsync(SelectedList.Id); if (existing is null) return; + var config = await _listRepo.GetConfigAsync(existing.Id); var editor = _listEditorFactory(); - editor.InitForEdit(existing); + await editor.LoadAgentsAsync(_worker); + editor.InitForEdit(existing, config); var window = new ListEditorView { DataContext = editor }; editor.RequestClose += () => window.Close(); @@ -118,6 +124,9 @@ public partial class MainWindowViewModel : ViewModelBase try { await _listRepo.UpdateAsync(entity); + var configEntity = editor.BuildConfig(entity.Id); + if (configEntity is not null) + await _listRepo.SetConfigAsync(configEntity); SelectedList.Name = entity.Name; SelectedList.WorkingDir = entity.WorkingDir; SelectedList.DefaultCommitType = entity.DefaultCommitType; diff --git a/src/ClaudeDo.Ui/Views/ListEditorView.axaml b/src/ClaudeDo.Ui/Views/ListEditorView.axaml index 3969ff3..d918674 100644 --- a/src/ClaudeDo.Ui/Views/ListEditorView.axaml +++ b/src/ClaudeDo.Ui/Views/ListEditorView.axaml @@ -1,29 +1,61 @@ + CanResize="False" + Background="{StaticResource WindowBgBrush}"> - + - +