using System.Collections.ObjectModel; using ClaudeDo.Data; using ClaudeDo.Data.Models; using ClaudeDo.Data.Repositories; using ClaudeDo.Ui.Localization; using ClaudeDo.Ui.Services; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.EntityFrameworkCore; namespace ClaudeDo.Ui.ViewModels.Modals; public sealed partial class ListSettingsModalViewModel : ViewModelBase { private readonly WorkerClient _worker; private readonly IDbContextFactory _dbFactory; public string ListId { get; set; } = ""; // True after the list was deleted, so the caller reloads the list nav instead of refreshing the row. public bool Deleted { get; private set; } // Wired by the view to prompt yes/no before deleting and to surface a blocking-FK error. public Func>? ConfirmAsync { get; set; } public Func? ShowErrorAsync { get; set; } [ObservableProperty] private string _name = ""; [ObservableProperty] private string _workingDir = ""; [ObservableProperty] private string _defaultCommitType = CommitTypeRegistry.DefaultType; [ObservableProperty] private string? _selectedModel; // null = inherit from global [ObservableProperty] private decimal? _maxTurns; // null = inherit from global [ObservableProperty] private string _modelInheritedHint = ""; // resolved value placeholder, e.g. "sonnet" [ObservableProperty] private string _modelBadge = ""; [ObservableProperty] private string _turnsInheritedHint = ""; [ObservableProperty] private string _turnsBadge = ""; [ObservableProperty] private string _agentBadge = ""; [ObservableProperty] private string _systemPrompt = ""; [ObservableProperty] private AgentInfo? _selectedAgent; private string _globalModel = ModelRegistry.DefaultAlias; private int _globalMaxTurns = 100; public ObservableCollection ModelOptions { get; } = new(ModelRegistry.Aliases); public ObservableCollection CommitTypeOptions { get; } = new(CommitTypeRegistry.Types); public ObservableCollection Agents { get; } = new(); public Action? CloseAction { get; set; } public ListSettingsModalViewModel(WorkerClient worker, IDbContextFactory dbFactory) { _worker = worker; _dbFactory = dbFactory; } partial void OnSelectedModelChanged(string? value) => RecomputeModelBadge(); partial void OnMaxTurnsChanged(decimal? value) => RecomputeTurnsBadge(); partial void OnSelectedAgentChanged(AgentInfo? value) => RecomputeAgentBadge(); private void RecomputeModelBadge() { ModelInheritedHint = _globalModel; ModelBadge = !string.IsNullOrWhiteSpace(SelectedModel) ? Loc.T("settings.inherit.overrideBadge") : Loc.T("settings.inherit.inheritedFromGlobal"); } private void RecomputeTurnsBadge() { TurnsInheritedHint = _globalMaxTurns.ToString(); TurnsBadge = MaxTurns is not null ? Loc.T("settings.inherit.overrideBadge") : Loc.T("settings.inherit.inheritedFromGlobal"); } private void RecomputeAgentBadge() { var overridden = SelectedAgent is not null && !string.IsNullOrWhiteSpace(SelectedAgent.Path); AgentBadge = overridden ? Loc.T("settings.inherit.overrideBadge") : Loc.T("settings.inherit.inheritedFromGlobal"); } public async Task LoadAsync( string listId, string name, string? workingDir, string defaultCommitType, CancellationToken ct = default) { ListId = listId; Name = name; WorkingDir = workingDir ?? ""; DefaultCommitType = string.IsNullOrWhiteSpace(defaultCommitType) ? CommitTypeRegistry.DefaultType : defaultCommitType; Agents.Clear(); Agents.Add(new AgentInfo("(none)", "", "")); var agents = await _worker.GetAgentsAsync(); foreach (var a in agents) Agents.Add(a); var config = await _worker.GetListConfigAsync(listId); var app = await _worker.GetAppSettingsAsync(); _globalModel = app?.DefaultModel ?? ModelRegistry.DefaultAlias; _globalMaxTurns = app?.DefaultMaxTurns ?? 100; SelectedModel = string.IsNullOrWhiteSpace(config?.Model) ? null : config!.Model!; MaxTurns = config?.MaxTurns is int mt ? mt : (decimal?)null; SystemPrompt = config?.SystemPrompt ?? ""; SelectedAgent = string.IsNullOrWhiteSpace(config?.AgentPath) ? Agents[0] : (Agents.FirstOrDefault(a => a.Path == config!.AgentPath) ?? Agents[0]); RecomputeModelBadge(); RecomputeTurnsBadge(); RecomputeAgentBadge(); } [RelayCommand] private async Task SaveAsync() { var model = string.IsNullOrWhiteSpace(SelectedModel) ? null : SelectedModel; var sp = string.IsNullOrWhiteSpace(SystemPrompt) ? null : SystemPrompt; var ap = SelectedAgent is null || string.IsNullOrWhiteSpace(SelectedAgent.Path) ? null : SelectedAgent.Path; var turns = MaxTurns is decimal d ? (int?)d : null; await _worker.UpdateListAsync(new UpdateListDto( ListId, string.IsNullOrWhiteSpace(Name) ? Loc.T("vm.listSettings.untitled") : Name, string.IsNullOrWhiteSpace(WorkingDir) ? null : WorkingDir, DefaultCommitType)); await _worker.UpdateListConfigAsync(new UpdateListConfigDto(ListId, model, sp, ap, turns)); CloseAction?.Invoke(); } [RelayCommand] private async Task DeleteAsync() { var displayName = string.IsNullOrWhiteSpace(Name) ? Loc.T("vm.listSettings.untitled") : Name; if (ConfirmAsync is not null) { var ok = await ConfirmAsync($"Delete list \"{displayName}\" and all its tasks? This cannot be undone."); if (!ok) return; } try { await using var ctx = await _dbFactory.CreateDbContextAsync(); var lists = new ListRepository(ctx); await lists.DeleteAsync(ListId); } catch (Exception ex) when ( (ex is Microsoft.Data.Sqlite.SqliteException || ex.InnerException is Microsoft.Data.Sqlite.SqliteException) && (ex.Message.Contains("FOREIGN KEY", StringComparison.OrdinalIgnoreCase) || ex.InnerException?.Message.Contains("FOREIGN KEY", StringComparison.OrdinalIgnoreCase) == true)) { if (ShowErrorAsync is not null) await ShowErrorAsync("This list has planning sessions with child tasks. Discard those first, then delete the list."); return; } Deleted = true; CloseAction?.Invoke(); } [RelayCommand] private void Cancel() => CloseAction?.Invoke(); [RelayCommand] private void ResetModel() => SelectedModel = null; [RelayCommand] private void ResetTurns() => MaxTurns = null; [RelayCommand] private void ResetAgent() => SelectedAgent = Agents.Count > 0 ? Agents[0] : null; [RelayCommand] private void ResetAgentSettings() { SelectedModel = null; MaxTurns = null; SystemPrompt = ""; SelectedAgent = Agents.Count > 0 ? Agents[0] : null; } }