diff --git a/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs b/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs index e4335d1..f77d7f1 100644 --- a/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs +++ b/src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs @@ -138,28 +138,60 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase OnPropertyChanged(nameof(ShowContinue)); OnPropertyChanged(nameof(ShowResetAndRetry)); OnPropertyChanged(nameof(IsAgentSectionEnabled)); - OnPropertyChanged(nameof(EffectiveModelLabel)); - OnPropertyChanged(nameof(EffectiveAgentLabel)); } [ObservableProperty] private string? _model; // Agent settings overrides - [ObservableProperty] private string _taskModelSelection = ModelRegistry.TaskInheritSentinel; + [ObservableProperty] private string? _taskModelSelection; // null = inherit [ObservableProperty] private string _taskSystemPrompt = ""; [ObservableProperty] private AgentInfo? _taskSelectedAgent; - - [ObservableProperty] private string _effectiveModelHint = ""; + [ObservableProperty] private decimal? _taskMaxTurns; // null = inherit + [ObservableProperty] private string _modelBadge = ""; + [ObservableProperty] private string _modelInheritedHint = ""; + [ObservableProperty] private string _turnsBadge = ""; + [ObservableProperty] private string _turnsInheritedHint = ""; + [ObservableProperty] private string _agentBadge = ""; [ObservableProperty] private string _effectiveSystemPromptHint = ""; - [ObservableProperty] private string _effectiveAgentHint = ""; - public string EffectiveModelLabel => Loc.T("vm.details.effectiveIfInherited", EffectiveModelHint); - public string EffectiveAgentLabel => Loc.T("vm.details.effectiveIfInherited", EffectiveAgentHint); + private string _globalModel = ModelRegistry.DefaultAlias; + private int _globalMaxTurns = 100; + private string? _listModel; + private int? _listMaxTurns; + private string? _listAgentName; - partial void OnEffectiveModelHintChanged(string value) => OnPropertyChanged(nameof(EffectiveModelLabel)); - partial void OnEffectiveAgentHintChanged(string value) => OnPropertyChanged(nameof(EffectiveAgentLabel)); + partial void OnTaskModelSelectionChanged(string? value) { RecomputeModelBadge(); QueueAgentSave(); } + partial void OnTaskMaxTurnsChanged(decimal? value) { RecomputeTurnsBadge(); QueueAgentSave(); } - public System.Collections.ObjectModel.ObservableCollection TaskModelOptions { get; } = new( - new[] { ModelRegistry.TaskInheritSentinel }.Concat(ModelRegistry.Aliases)); + 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"); + + public System.Collections.ObjectModel.ObservableCollection TaskModelOptions { get; } = new(ModelRegistry.Aliases); public System.Collections.ObjectModel.ObservableCollection TaskAgentOptions { get; } = new(); @@ -288,8 +320,9 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase Loc.LanguageChanged += (_, _) => { OnPropertyChanged(nameof(AgentStatusLabel)); - OnPropertyChanged(nameof(EffectiveModelLabel)); - OnPropertyChanged(nameof(EffectiveAgentLabel)); + RecomputeModelBadge(); + RecomputeTurnsBadge(); + RecomputeAgentBadge(); }; // Subscribe once; filter by current task id inside the handler @@ -433,9 +466,8 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase Log.Add(new LogLineViewModel { Kind = LogKind.Claude, Text = piece }); } - partial void OnTaskModelSelectionChanged(string value) => QueueAgentSave(); partial void OnTaskSystemPromptChanged(string value) => QueueAgentSave(); - partial void OnTaskSelectedAgentChanged(AgentInfo? value) => QueueAgentSave(); + partial void OnTaskSelectedAgentChanged(AgentInfo? value) { RecomputeAgentBadge(); QueueAgentSave(); } partial void OnEditableDescriptionChanged(string value) { @@ -478,13 +510,14 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase await System.Threading.Tasks.Task.Delay(300, ct); if (Task is null) return; - var model = TaskModelSelection == ModelRegistry.TaskInheritSentinel ? null : TaskModelSelection; + 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 ClaudeDo.Ui.Services.UpdateTaskAgentSettingsDto(Task.Id, model, sp, ap)); + new ClaudeDo.Ui.Services.UpdateTaskAgentSettingsDto(Task.Id, model, sp, ap, turns)); } catch (OperationCanceledException) { } catch { } @@ -497,21 +530,31 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase try { TaskAgentOptions.Clear(); - TaskAgentOptions.Add(new AgentInfo(ModelRegistry.TaskInheritSentinel, "", "")); + TaskAgentOptions.Add(new AgentInfo("(inherited)", "", "")); var agents = await _worker.GetAgentsAsync(); foreach (var a in agents) TaskAgentOptions.Add(a); - TaskModelSelection = string.IsNullOrWhiteSpace(entity.Model) ? ModelRegistry.TaskInheritSentinel : entity.Model!; + 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); - EffectiveModelHint = string.IsNullOrWhiteSpace(listCfg?.Model) ? "(global default)" : listCfg!.Model!; - EffectiveSystemPromptHint = string.IsNullOrWhiteSpace(listCfg?.SystemPrompt) ? "(none)" : listCfg!.SystemPrompt!; - EffectiveAgentHint = string.IsNullOrWhiteSpace(listCfg?.AgentPath) - ? "(none)" : System.IO.Path.GetFileName(listCfg!.AgentPath!); + 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(); } finally { @@ -583,7 +626,8 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase _suppressAgentSave = true; try { - TaskModelSelection = ModelRegistry.TaskInheritSentinel; + TaskModelSelection = null; + TaskMaxTurns = null; TaskSystemPrompt = ""; TaskSelectedAgent = null; } @@ -591,9 +635,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase { _suppressAgentSave = false; } - EffectiveModelHint = ""; EffectiveSystemPromptHint = ""; - EffectiveAgentHint = ""; return; } @@ -1085,6 +1127,10 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase private bool CanResetAndRetry() => Task != null && _worker.IsConnected && ShowResetAndRetry; + + [RelayCommand] private void ResetTaskModel() => TaskModelSelection = null; + [RelayCommand] private void ResetTaskTurns() => TaskMaxTurns = null; + [RelayCommand] private void ResetTaskAgent() => TaskSelectedAgent = TaskAgentOptions.Count > 0 ? TaskAgentOptions[0] : null; } public sealed partial class SubtaskRowViewModel : ViewModelBase diff --git a/src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml b/src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml index 85ae705..a836fc0 100644 --- a/src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml +++ b/src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml @@ -88,24 +88,50 @@ - + + + +