using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using ClaudeDo.Data.Models; using ClaudeDo.Ui.Helpers; using ClaudeDo.Ui.Localization; using ClaudeDo.Ui.Services; namespace ClaudeDo.Ui.ViewModels.Islands; public sealed partial class AgentSettingsSectionViewModel : ViewModelBase, IDisposable { private readonly IWorkerClient _worker; private readonly EventHandler _langChangedHandler; internal string? TaskId { get; set; } [ObservableProperty] [NotifyPropertyChangedFor(nameof(IsAgentSectionEnabled))] private bool _isRunning; public bool IsAgentSectionEnabled => !IsRunning; [ObservableProperty] private string? _taskModelSelection; [ObservableProperty] private string _taskSystemPrompt = ""; [ObservableProperty] private AgentInfo? _taskSelectedAgent; [ObservableProperty] private decimal? _taskMaxTurns; [ObservableProperty] private string _modelBadge = ""; [ObservableProperty] private string _modelInheritedHint = ""; [ObservableProperty] private string _turnsBadge = ""; [ObservableProperty] private string _turnsInheritedHint = ""; [ObservableProperty] private string _agentBadge = ""; [ObservableProperty] private string _effectiveSystemPromptHint = ""; private string _globalModel = ModelRegistry.DefaultAlias; private int _globalMaxTurns = 100; private string? _listModel; private int? _listMaxTurns; private string? _listAgentName; private bool _suppressAgentSave; private CancellationTokenSource? _agentSaveCts; public int EffectiveMaxTurns => TaskMaxTurns is decimal t ? (int)t : (_listMaxTurns ?? _globalMaxTurns); public ObservableCollection TaskModelOptions { get; } = new(ModelRegistry.Aliases); public ObservableCollection TaskAgentOptions { get; } = new(); public AgentSettingsSectionViewModel(IWorkerClient worker) { _worker = worker; _langChangedHandler = (_, _) => { RecomputeModelBadge(); RecomputeTurnsBadge(); RecomputeAgentBadge(); }; Loc.LanguageChanged += _langChangedHandler; } public void Dispose() => Loc.LanguageChanged -= _langChangedHandler; partial void OnTaskModelSelectionChanged(string? value) { RecomputeModelBadge(); QueueAgentSave(); } partial void OnTaskMaxTurnsChanged(decimal? value) { RecomputeTurnsBadge(); OnPropertyChanged(nameof(EffectiveMaxTurns)); QueueAgentSave(); } partial void OnTaskSystemPromptChanged(string value) => QueueAgentSave(); partial void OnTaskSelectedAgentChanged(AgentInfo? value) { RecomputeAgentBadge(); QueueAgentSave(); } private void RecomputeModelBadge() { var (value, source) = InheritanceResolver.Resolve(TaskModelSelection, _listModel, _globalModel); ModelInheritedHint = value; ModelBadge = BadgeFor(source, !string.IsNullOrWhiteSpace(TaskModelSelection)); } private void RecomputeTurnsBadge() { var (value, source) = InheritanceResolver.Resolve( TaskMaxTurns?.ToString(), _listMaxTurns?.ToString(), _globalMaxTurns.ToString()); TurnsInheritedHint = value; TurnsBadge = BadgeFor(source, TaskMaxTurns is not null); } private void RecomputeAgentBadge() { var taskSet = TaskSelectedAgent is not null && !string.IsNullOrWhiteSpace(TaskSelectedAgent.Path); var (_, source) = InheritanceResolver.Resolve( taskSet ? TaskSelectedAgent!.Path : null, _listAgentName, null); AgentBadge = BadgeFor(source, taskSet); } private static string BadgeFor(InheritSource source, bool taskSet) => taskSet ? Loc.T("settings.inherit.overrideBadge") : source == InheritSource.List ? Loc.T("settings.inherit.inheritedFromList") : Loc.T("settings.inherit.inheritedFromGlobal"); private void QueueAgentSave() { if (_suppressAgentSave || TaskId is null) return; _agentSaveCts?.Cancel(); _agentSaveCts = new CancellationTokenSource(); _ = SaveAgentSettingsAsync(_agentSaveCts.Token); } private async System.Threading.Tasks.Task SaveAgentSettingsAsync(CancellationToken ct) { try { await System.Threading.Tasks.Task.Delay(300, ct); if (TaskId is null) return; var model = string.IsNullOrWhiteSpace(TaskModelSelection) ? null : TaskModelSelection; var sp = string.IsNullOrWhiteSpace(TaskSystemPrompt) ? null : TaskSystemPrompt; var ap = TaskSelectedAgent is null || string.IsNullOrWhiteSpace(TaskSelectedAgent.Path) ? null : TaskSelectedAgent.Path; var turns = TaskMaxTurns is decimal d ? (int?)d : null; await _worker.UpdateTaskAgentSettingsAsync( new UpdateTaskAgentSettingsDto(TaskId, model, sp, ap, turns)); } catch (OperationCanceledException) { } catch { } } internal async System.Threading.Tasks.Task LoadAsync( ClaudeDo.Data.Models.TaskEntity entity, CancellationToken ct) { _suppressAgentSave = true; try { TaskAgentOptions.Clear(); TaskAgentOptions.Add(new AgentInfo("(inherited)", "", "")); var agents = await _worker.GetAgentsAsync(); foreach (var a in agents) TaskAgentOptions.Add(a); TaskModelSelection = string.IsNullOrWhiteSpace(entity.Model) ? null : entity.Model!; TaskMaxTurns = entity.MaxTurns is int tmt ? tmt : (decimal?)null; TaskSystemPrompt = entity.SystemPrompt ?? ""; TaskSelectedAgent = string.IsNullOrWhiteSpace(entity.AgentPath) ? TaskAgentOptions[0] : (TaskAgentOptions.FirstOrDefault(a => a.Path == entity.AgentPath) ?? TaskAgentOptions[0]); var listCfg = await _worker.GetListConfigAsync(entity.ListId); var app = await _worker.GetAppSettingsAsync(); _globalModel = app?.DefaultModel ?? ModelRegistry.DefaultAlias; _globalMaxTurns = app?.DefaultMaxTurns ?? 100; _listModel = listCfg?.Model; _listMaxTurns = listCfg?.MaxTurns; _listAgentName = string.IsNullOrWhiteSpace(listCfg?.AgentPath) ? null : System.IO.Path.GetFileName(listCfg!.AgentPath!); EffectiveSystemPromptHint = string.IsNullOrWhiteSpace(listCfg?.SystemPrompt) ? "" : listCfg!.SystemPrompt!; RecomputeModelBadge(); RecomputeTurnsBadge(); RecomputeAgentBadge(); OnPropertyChanged(nameof(EffectiveMaxTurns)); } finally { _suppressAgentSave = false; } } internal void Clear() { _suppressAgentSave = true; try { TaskModelSelection = null; TaskMaxTurns = null; TaskSystemPrompt = ""; TaskSelectedAgent = null; } finally { _suppressAgentSave = false; } EffectiveSystemPromptHint = ""; TaskId = null; } [RelayCommand] private void ResetTaskModel() => TaskModelSelection = null; [RelayCommand] private void ResetTaskTurns() => TaskMaxTurns = null; [RelayCommand] private void ResetTaskAgent() => TaskSelectedAgent = TaskAgentOptions.Count > 0 ? TaskAgentOptions[0] : null; }