feat(ui): Settings-Modal auf Sidebar-Kategorien umbauen
TabControl bleibt Content-Host, TabStrip wird retempliert (nur PART_SelectedContentHost) und durch eine zweigruppige Sidebar (BASIS/ERWEITERT) ersetzt. General wird in Allgemein/Ausfuehrung/Berichte gesplittet, Session Skills (Checkbox-Liste aus General + Skills-Tab) zu einer Liste zusammengelegt, und ein Repo-Hinweisstreifen erscheint auf Worktrees/Prime Claude/Session Skills/Berichte solange keine Liste ein WorkingDir hat. Fensterbreite 580 -> 700.
This commit is contained in:
@@ -6,12 +6,19 @@ using ClaudeDo.Ui.Services;
|
||||
using ClaudeDo.Ui.ViewModels.Modals.Settings;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ClaudeDo.Ui.ViewModels.Modals;
|
||||
|
||||
/// <summary>One sidebar entry. <c>Group</c> is an internal split key ("Basis"/"Erweitert"), not
|
||||
/// user-facing text — the sidebar renders the two groups as separate lists with their own
|
||||
/// (localized) heading. <c>RequiresRepo</c> marks the categories that show the repo-hint strip.</summary>
|
||||
public sealed record SettingsCategory(string Key, LocalizedString Label, string Group, bool IsVisible, bool RequiresRepo);
|
||||
|
||||
public sealed partial class SettingsModalViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IWorkerClient _worker;
|
||||
private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
|
||||
|
||||
public GeneralSettingsTabViewModel General { get; }
|
||||
public WorktreesSettingsTabViewModel Worktrees { get; }
|
||||
@@ -19,10 +26,44 @@ public sealed partial class SettingsModalViewModel : ViewModelBase
|
||||
public PrimeClaudeTabViewModel Prime { get; }
|
||||
public OnlineInboxSettingsViewModel OnlineInbox { get; }
|
||||
public SessionSkillsSettingsTabViewModel SessionSkills { get; }
|
||||
|
||||
|
||||
// Online Inbox ist unfertig und vor Usern verborgen — hier auf true für Reaktivierung.
|
||||
public bool ShowOnlineInbox => false;
|
||||
|
||||
// Fixed order — mirrors the TabItem order in SettingsModalView.axaml 1:1, so
|
||||
// _categories.IndexOf(category) is a valid TabControl.SelectedIndex.
|
||||
private readonly List<SettingsCategory> _categories;
|
||||
public IReadOnlyList<SettingsCategory> Categories => _categories;
|
||||
public IReadOnlyList<SettingsCategory> BasisCategories { get; }
|
||||
public IReadOnlyList<SettingsCategory> ErweitertCategories { get; }
|
||||
|
||||
private SettingsCategory? _selectedCategory;
|
||||
public SettingsCategory? SelectedCategory
|
||||
{
|
||||
get => _selectedCategory;
|
||||
set
|
||||
{
|
||||
// The two sidebar ListBoxes share this property; selecting an item in one pushes null
|
||||
// into the other's binding (item not found in its own ItemsSource) — ignore that noise
|
||||
// instead of letting it clobber the real selection.
|
||||
if (value is null) return;
|
||||
if (SetProperty(ref _selectedCategory, value))
|
||||
SelectedIndex = _categories.IndexOf(value);
|
||||
}
|
||||
}
|
||||
|
||||
[ObservableProperty] private int _selectedIndex;
|
||||
|
||||
// True once at least one list has a linked repo (WorkingDir). Drives the repo-hint strip on
|
||||
// Worktrees/Prime Claude/Session Skills/Berichte — nothing is ever hidden, just annotated.
|
||||
[ObservableProperty] private bool _hasLinkedRepo;
|
||||
|
||||
// Wired by WindowDialogService to close this modal and open the existing repo-import flow.
|
||||
public Action? RequestRepoImport { get; set; }
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenRepoImport() => RequestRepoImport?.Invoke();
|
||||
|
||||
[ObservableProperty] private string _validationError = "";
|
||||
[ObservableProperty] private bool _isBusy;
|
||||
[ObservableProperty] private string _statusMessage = "";
|
||||
@@ -31,9 +72,11 @@ public sealed partial class SettingsModalViewModel : ViewModelBase
|
||||
|
||||
public SettingsModalViewModel(IWorkerClient worker, PrimeClaudeTabViewModel prime,
|
||||
IOnlineLoginService onlineLoginService,
|
||||
ILocalizer localizer, AppSettings appSettings)
|
||||
ILocalizer localizer, AppSettings appSettings,
|
||||
IDbContextFactory<ClaudeDoDbContext> dbFactory)
|
||||
{
|
||||
_worker = worker;
|
||||
_dbFactory = dbFactory;
|
||||
General = new GeneralSettingsTabViewModel(localizer, code =>
|
||||
{
|
||||
appSettings.Language = code;
|
||||
@@ -49,6 +92,21 @@ public sealed partial class SettingsModalViewModel : ViewModelBase
|
||||
Prime = prime;
|
||||
OnlineInbox = new OnlineInboxSettingsViewModel(worker, onlineLoginService);
|
||||
SessionSkills = new SessionSkillsSettingsTabViewModel(worker);
|
||||
|
||||
_categories = new List<SettingsCategory>
|
||||
{
|
||||
new("General", new LocalizedString(localizer, "settings.sidebar.categoryGeneral"), "Basis", true, false),
|
||||
new("Execution", new LocalizedString(localizer, "settings.sidebar.categoryExecution"), "Basis", true, false),
|
||||
new("Prime", new LocalizedString(localizer, "settings.sidebar.categoryPrime"), "Basis", true, true),
|
||||
new("Worktrees", new LocalizedString(localizer, "settings.sidebar.categoryWorktrees"), "Erweitert", true, true),
|
||||
new("Files", new LocalizedString(localizer, "settings.sidebar.categoryFiles"), "Erweitert", true, false),
|
||||
new("SessionSkills", new LocalizedString(localizer, "settings.sidebar.categorySkills"), "Erweitert", true, true),
|
||||
new("Reports", new LocalizedString(localizer, "settings.sidebar.categoryReports"), "Erweitert", true, true),
|
||||
new("OnlineInbox", new LocalizedString(localizer, "settings.onlineInbox.tabHeader"), "Erweitert", ShowOnlineInbox, false),
|
||||
};
|
||||
BasisCategories = _categories.Where(c => c.Group == "Basis").ToList();
|
||||
ErweitertCategories = _categories.Where(c => c.Group == "Erweitert").ToList();
|
||||
_selectedCategory = _categories[0];
|
||||
}
|
||||
|
||||
// Throttle stages are edited by dragging the usage-monitor gauges, not on any Settings tab —
|
||||
@@ -89,9 +147,11 @@ public sealed partial class SettingsModalViewModel : ViewModelBase
|
||||
|
||||
await Prime.LoadAsync();
|
||||
await OnlineInbox.LoadAsync();
|
||||
await SessionSkills.LoadAsync();
|
||||
await General.LoadSessionSkillsAsync(_worker, dto?.SessionSkills);
|
||||
await SessionSkills.LoadAsync(dto?.SessionSkills);
|
||||
General.LoadModelPresets(dto?.ModelPresets, General.MaxTurnsCeiling);
|
||||
|
||||
await using var ctx = await _dbFactory.CreateDbContextAsync();
|
||||
HasLinkedRepo = await ctx.Lists.AnyAsync(l => l.WorkingDir != null && l.WorkingDir != "");
|
||||
}
|
||||
finally { IsBusy = false; }
|
||||
}
|
||||
@@ -121,7 +181,7 @@ public sealed partial class SettingsModalViewModel : ViewModelBase
|
||||
.Split('\n').Select(l => l.Trim().TrimEnd('\r')).Where(l => l.Length > 0).ToList()),
|
||||
General.StandupWeekday,
|
||||
Prime.DailyPrepMaxTasks,
|
||||
General.SelectedSessionSkillNames(),
|
||||
SessionSkills.SelectedSkillNames(),
|
||||
General.ModelPresetDtos(),
|
||||
General.UsageGateFiveHourPct,
|
||||
General.UsageGateSevenDayPct,
|
||||
|
||||
Reference in New Issue
Block a user