feat(ui): show inherited markers and max-turns override in task flyout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -138,28 +138,60 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
|
|||||||
OnPropertyChanged(nameof(ShowContinue));
|
OnPropertyChanged(nameof(ShowContinue));
|
||||||
OnPropertyChanged(nameof(ShowResetAndRetry));
|
OnPropertyChanged(nameof(ShowResetAndRetry));
|
||||||
OnPropertyChanged(nameof(IsAgentSectionEnabled));
|
OnPropertyChanged(nameof(IsAgentSectionEnabled));
|
||||||
OnPropertyChanged(nameof(EffectiveModelLabel));
|
|
||||||
OnPropertyChanged(nameof(EffectiveAgentLabel));
|
|
||||||
}
|
}
|
||||||
[ObservableProperty] private string? _model;
|
[ObservableProperty] private string? _model;
|
||||||
|
|
||||||
// Agent settings overrides
|
// Agent settings overrides
|
||||||
[ObservableProperty] private string _taskModelSelection = ModelRegistry.TaskInheritSentinel;
|
[ObservableProperty] private string? _taskModelSelection; // null = inherit
|
||||||
[ObservableProperty] private string _taskSystemPrompt = "";
|
[ObservableProperty] private string _taskSystemPrompt = "";
|
||||||
[ObservableProperty] private AgentInfo? _taskSelectedAgent;
|
[ObservableProperty] private AgentInfo? _taskSelectedAgent;
|
||||||
|
[ObservableProperty] private decimal? _taskMaxTurns; // null = inherit
|
||||||
[ObservableProperty] private string _effectiveModelHint = "";
|
[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 _effectiveSystemPromptHint = "";
|
||||||
[ObservableProperty] private string _effectiveAgentHint = "";
|
|
||||||
|
|
||||||
public string EffectiveModelLabel => Loc.T("vm.details.effectiveIfInherited", EffectiveModelHint);
|
private string _globalModel = ModelRegistry.DefaultAlias;
|
||||||
public string EffectiveAgentLabel => Loc.T("vm.details.effectiveIfInherited", EffectiveAgentHint);
|
private int _globalMaxTurns = 100;
|
||||||
|
private string? _listModel;
|
||||||
|
private int? _listMaxTurns;
|
||||||
|
private string? _listAgentName;
|
||||||
|
|
||||||
partial void OnEffectiveModelHintChanged(string value) => OnPropertyChanged(nameof(EffectiveModelLabel));
|
partial void OnTaskModelSelectionChanged(string? value) { RecomputeModelBadge(); QueueAgentSave(); }
|
||||||
partial void OnEffectiveAgentHintChanged(string value) => OnPropertyChanged(nameof(EffectiveAgentLabel));
|
partial void OnTaskMaxTurnsChanged(decimal? value) { RecomputeTurnsBadge(); QueueAgentSave(); }
|
||||||
|
|
||||||
public System.Collections.ObjectModel.ObservableCollection<string> TaskModelOptions { get; } = new(
|
private void RecomputeModelBadge()
|
||||||
new[] { ModelRegistry.TaskInheritSentinel }.Concat(ModelRegistry.Aliases));
|
{
|
||||||
|
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<string> TaskModelOptions { get; } = new(ModelRegistry.Aliases);
|
||||||
|
|
||||||
public System.Collections.ObjectModel.ObservableCollection<AgentInfo> TaskAgentOptions { get; } = new();
|
public System.Collections.ObjectModel.ObservableCollection<AgentInfo> TaskAgentOptions { get; } = new();
|
||||||
|
|
||||||
@@ -288,8 +320,9 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
|
|||||||
Loc.LanguageChanged += (_, _) =>
|
Loc.LanguageChanged += (_, _) =>
|
||||||
{
|
{
|
||||||
OnPropertyChanged(nameof(AgentStatusLabel));
|
OnPropertyChanged(nameof(AgentStatusLabel));
|
||||||
OnPropertyChanged(nameof(EffectiveModelLabel));
|
RecomputeModelBadge();
|
||||||
OnPropertyChanged(nameof(EffectiveAgentLabel));
|
RecomputeTurnsBadge();
|
||||||
|
RecomputeAgentBadge();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Subscribe once; filter by current task id inside the handler
|
// 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 });
|
Log.Add(new LogLineViewModel { Kind = LogKind.Claude, Text = piece });
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnTaskModelSelectionChanged(string value) => QueueAgentSave();
|
|
||||||
partial void OnTaskSystemPromptChanged(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)
|
partial void OnEditableDescriptionChanged(string value)
|
||||||
{
|
{
|
||||||
@@ -478,13 +510,14 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
|
|||||||
await System.Threading.Tasks.Task.Delay(300, ct);
|
await System.Threading.Tasks.Task.Delay(300, ct);
|
||||||
if (Task is null) return;
|
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 sp = string.IsNullOrWhiteSpace(TaskSystemPrompt) ? null : TaskSystemPrompt;
|
||||||
var ap = TaskSelectedAgent is null || string.IsNullOrWhiteSpace(TaskSelectedAgent.Path)
|
var ap = TaskSelectedAgent is null || string.IsNullOrWhiteSpace(TaskSelectedAgent.Path)
|
||||||
? null : TaskSelectedAgent.Path;
|
? null : TaskSelectedAgent.Path;
|
||||||
|
var turns = TaskMaxTurns is decimal d ? (int?)d : null;
|
||||||
|
|
||||||
await _worker.UpdateTaskAgentSettingsAsync(
|
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 (OperationCanceledException) { }
|
||||||
catch { }
|
catch { }
|
||||||
@@ -497,21 +530,31 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
TaskAgentOptions.Clear();
|
TaskAgentOptions.Clear();
|
||||||
TaskAgentOptions.Add(new AgentInfo(ModelRegistry.TaskInheritSentinel, "", ""));
|
TaskAgentOptions.Add(new AgentInfo("(inherited)", "", ""));
|
||||||
var agents = await _worker.GetAgentsAsync();
|
var agents = await _worker.GetAgentsAsync();
|
||||||
foreach (var a in agents) TaskAgentOptions.Add(a);
|
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 ?? "";
|
TaskSystemPrompt = entity.SystemPrompt ?? "";
|
||||||
TaskSelectedAgent = string.IsNullOrWhiteSpace(entity.AgentPath)
|
TaskSelectedAgent = string.IsNullOrWhiteSpace(entity.AgentPath)
|
||||||
? TaskAgentOptions[0]
|
? TaskAgentOptions[0]
|
||||||
: (TaskAgentOptions.FirstOrDefault(a => a.Path == entity.AgentPath) ?? TaskAgentOptions[0]);
|
: (TaskAgentOptions.FirstOrDefault(a => a.Path == entity.AgentPath) ?? TaskAgentOptions[0]);
|
||||||
|
|
||||||
var listCfg = await _worker.GetListConfigAsync(entity.ListId);
|
var listCfg = await _worker.GetListConfigAsync(entity.ListId);
|
||||||
EffectiveModelHint = string.IsNullOrWhiteSpace(listCfg?.Model) ? "(global default)" : listCfg!.Model!;
|
var app = await _worker.GetAppSettingsAsync();
|
||||||
EffectiveSystemPromptHint = string.IsNullOrWhiteSpace(listCfg?.SystemPrompt) ? "(none)" : listCfg!.SystemPrompt!;
|
_globalModel = app?.DefaultModel ?? ModelRegistry.DefaultAlias;
|
||||||
EffectiveAgentHint = string.IsNullOrWhiteSpace(listCfg?.AgentPath)
|
_globalMaxTurns = app?.DefaultMaxTurns ?? 100;
|
||||||
? "(none)" : System.IO.Path.GetFileName(listCfg!.AgentPath!);
|
_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
|
finally
|
||||||
{
|
{
|
||||||
@@ -583,7 +626,8 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
|
|||||||
_suppressAgentSave = true;
|
_suppressAgentSave = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
TaskModelSelection = ModelRegistry.TaskInheritSentinel;
|
TaskModelSelection = null;
|
||||||
|
TaskMaxTurns = null;
|
||||||
TaskSystemPrompt = "";
|
TaskSystemPrompt = "";
|
||||||
TaskSelectedAgent = null;
|
TaskSelectedAgent = null;
|
||||||
}
|
}
|
||||||
@@ -591,9 +635,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
_suppressAgentSave = false;
|
_suppressAgentSave = false;
|
||||||
}
|
}
|
||||||
EffectiveModelHint = "";
|
|
||||||
EffectiveSystemPromptHint = "";
|
EffectiveSystemPromptHint = "";
|
||||||
EffectiveAgentHint = "";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1085,6 +1127,10 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase
|
|||||||
|
|
||||||
private bool CanResetAndRetry() =>
|
private bool CanResetAndRetry() =>
|
||||||
Task != null && _worker.IsConnected && ShowResetAndRetry;
|
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
|
public sealed partial class SubtaskRowViewModel : ViewModelBase
|
||||||
|
|||||||
@@ -88,24 +88,50 @@
|
|||||||
<TextBlock Text="{loc:Tr details.agentSettingsHeading}" FontWeight="SemiBold"/>
|
<TextBlock Text="{loc:Tr details.agentSettingsHeading}" FontWeight="SemiBold"/>
|
||||||
|
|
||||||
<StackPanel Spacing="2">
|
<StackPanel Spacing="2">
|
||||||
<TextBlock Classes="field-label" Text="{loc:Tr details.modelLabel}"/>
|
<Grid ColumnDefinitions="Auto,Auto,*,Auto" ColumnSpacing="6">
|
||||||
|
<TextBlock Grid.Column="0" Classes="field-label" Text="{loc:Tr details.modelLabel}" VerticalAlignment="Center"/>
|
||||||
|
<ctl:InheritedBadge Grid.Column="1" BadgeText="{Binding ModelBadge}"/>
|
||||||
|
<Button Grid.Column="3" Classes="icon-btn" Content="↺" ToolTip.Tip="{loc:Tr settings.inherit.resetToInherited}"
|
||||||
|
Command="{Binding ResetTaskModelCommand}"/>
|
||||||
|
</Grid>
|
||||||
<ComboBox ItemsSource="{Binding TaskModelOptions}"
|
<ComboBox ItemsSource="{Binding TaskModelOptions}"
|
||||||
SelectedItem="{Binding TaskModelSelection, Mode=TwoWay}"
|
SelectedItem="{Binding TaskModelSelection, Mode=TwoWay}"
|
||||||
|
PlaceholderText="{Binding ModelInheritedHint}"
|
||||||
|
HorizontalAlignment="Stretch"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Spacing="2">
|
||||||
|
<Grid ColumnDefinitions="Auto,Auto,*,Auto" ColumnSpacing="6">
|
||||||
|
<TextBlock Grid.Column="0" Classes="field-label" Text="{loc:Tr details.maxTurnsLabel}" VerticalAlignment="Center"/>
|
||||||
|
<ctl:InheritedBadge Grid.Column="1" BadgeText="{Binding TurnsBadge}"/>
|
||||||
|
<Button Grid.Column="3" Classes="icon-btn" Content="↺" ToolTip.Tip="{loc:Tr settings.inherit.resetToInherited}"
|
||||||
|
Command="{Binding ResetTaskTurnsCommand}"/>
|
||||||
|
</Grid>
|
||||||
|
<NumericUpDown Value="{Binding TaskMaxTurns, Mode=TwoWay}"
|
||||||
|
PlaceholderText="{Binding TurnsInheritedHint}"
|
||||||
|
Minimum="1" Maximum="200" Increment="1" FormatString="0"
|
||||||
HorizontalAlignment="Stretch"/>
|
HorizontalAlignment="Stretch"/>
|
||||||
<TextBlock Classes="meta"
|
|
||||||
Text="{Binding EffectiveModelLabel}"
|
|
||||||
Opacity="0.6"/>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel Spacing="2">
|
<StackPanel Spacing="2">
|
||||||
<TextBlock Classes="field-label" Text="{loc:Tr details.systemPromptLabel}"/>
|
<TextBlock Classes="field-label" Text="{loc:Tr details.systemPromptLabel}"/>
|
||||||
<TextBox Text="{Binding TaskSystemPrompt, Mode=TwoWay}"
|
<TextBox Text="{Binding TaskSystemPrompt, Mode=TwoWay}"
|
||||||
AcceptsReturn="True" TextWrapping="Wrap" MinHeight="70"
|
AcceptsReturn="True" TextWrapping="Wrap" MinHeight="70"/>
|
||||||
PlaceholderText="{Binding EffectiveSystemPromptHint}"/>
|
<TextBlock Classes="meta" Opacity="0.6"
|
||||||
|
Text="{loc:Tr details.systemPromptPrepended}"
|
||||||
|
IsVisible="{Binding EffectiveSystemPromptHint, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
|
||||||
|
<TextBlock Classes="meta" Opacity="0.6" TextWrapping="Wrap"
|
||||||
|
Text="{Binding EffectiveSystemPromptHint}"
|
||||||
|
IsVisible="{Binding EffectiveSystemPromptHint, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel Spacing="2">
|
<StackPanel Spacing="2">
|
||||||
<TextBlock Classes="field-label" Text="{loc:Tr details.agentFileLabel}"/>
|
<Grid ColumnDefinitions="Auto,Auto,*,Auto" ColumnSpacing="6">
|
||||||
|
<TextBlock Grid.Column="0" Classes="field-label" Text="{loc:Tr details.agentFileLabel}" VerticalAlignment="Center"/>
|
||||||
|
<ctl:InheritedBadge Grid.Column="1" BadgeText="{Binding AgentBadge}"/>
|
||||||
|
<Button Grid.Column="3" Classes="icon-btn" Content="↺" ToolTip.Tip="{loc:Tr settings.inherit.resetToInherited}"
|
||||||
|
Command="{Binding ResetTaskAgentCommand}"/>
|
||||||
|
</Grid>
|
||||||
<ComboBox ItemsSource="{Binding TaskAgentOptions}"
|
<ComboBox ItemsSource="{Binding TaskAgentOptions}"
|
||||||
SelectedItem="{Binding TaskSelectedAgent, Mode=TwoWay}"
|
SelectedItem="{Binding TaskSelectedAgent, Mode=TwoWay}"
|
||||||
HorizontalAlignment="Stretch">
|
HorizontalAlignment="Stretch">
|
||||||
@@ -115,9 +141,6 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ComboBox.ItemTemplate>
|
</ComboBox.ItemTemplate>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
<TextBlock Classes="meta"
|
|
||||||
Text="{Binding EffectiveAgentLabel}"
|
|
||||||
Opacity="0.6"/>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Flyout>
|
</Flyout>
|
||||||
|
|||||||
Reference in New Issue
Block a user