Files
ClaudeDo/src/ClaudeDo.Ui/ViewModels/Modals/SettingsModalViewModel.cs
2026-06-03 12:43:30 +02:00

105 lines
4.2 KiB
C#

using System.Linq;
using ClaudeDo.Data;
using ClaudeDo.Localization;
using ClaudeDo.Ui.Localization;
using ClaudeDo.Ui.Services;
using ClaudeDo.Ui.ViewModels.Modals.Settings;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace ClaudeDo.Ui.ViewModels.Modals;
public sealed partial class SettingsModalViewModel : ViewModelBase
{
private readonly WorkerClient _worker;
public GeneralSettingsTabViewModel General { get; }
public WorktreesSettingsTabViewModel Worktrees { get; }
public FilesSettingsTabViewModel Files { get; }
public PrimeClaudeTabViewModel Prime { get; }
[ObservableProperty] private string _validationError = "";
[ObservableProperty] private bool _isBusy;
[ObservableProperty] private string _statusMessage = "";
public Action? CloseAction { get; set; }
public SettingsModalViewModel(WorkerClient worker, PrimeClaudeTabViewModel prime,
ILocalizer localizer, AppSettings appSettings)
{
_worker = worker;
General = new GeneralSettingsTabViewModel(localizer, code =>
{
appSettings.Language = code;
appSettings.Save();
});
Worktrees = new WorktreesSettingsTabViewModel(worker);
Files = new FilesSettingsTabViewModel(worker);
Prime = prime;
}
public async Task LoadAsync()
{
IsBusy = true;
try
{
var dto = await _worker.GetAppSettingsAsync();
if (dto is not null)
{
General.DefaultClaudeInstructions = dto.DefaultClaudeInstructions ?? "";
General.DefaultModel = dto.DefaultModel ?? "sonnet";
General.DefaultMaxTurns = dto.DefaultMaxTurns;
General.DefaultPermissionMode = dto.DefaultPermissionMode ?? "auto";
General.MaxParallelExecutions = dto.MaxParallelExecutions;
Worktrees.WorktreeStrategy = dto.WorktreeStrategy ?? "sibling";
Worktrees.CentralWorktreeRoot = dto.CentralWorktreeRoot;
Worktrees.WorktreeAutoCleanupEnabled = dto.WorktreeAutoCleanupEnabled;
Worktrees.WorktreeAutoCleanupDays = dto.WorktreeAutoCleanupDays;
General.ReportExcludedPaths = string.IsNullOrWhiteSpace(dto.ReportExcludedPaths)
? @"C:\Private"
: string.Join(Environment.NewLine,
System.Text.Json.JsonSerializer.Deserialize<List<string>>(dto.ReportExcludedPaths) ?? new());
General.StandupWeekday = dto.StandupWeekday is >= 0 and <= 6 ? dto.StandupWeekday : (int)DayOfWeek.Wednesday;
}
else StatusMessage = Loc.T("vm.settingsModal.workerOffline");
await Prime.LoadAsync();
}
finally { IsBusy = false; }
}
[RelayCommand]
private async Task Save()
{
var err = General.Validate() ?? Worktrees.Validate() ?? Prime.Validate();
if (err is not null) { ValidationError = err; return; }
ValidationError = "";
IsBusy = true;
try
{
var dto = new AppSettingsDto(
General.DefaultClaudeInstructions ?? "",
General.DefaultModel ?? "sonnet",
General.DefaultMaxTurns,
General.DefaultPermissionMode ?? "auto",
General.MaxParallelExecutions,
Worktrees.WorktreeStrategy ?? "sibling",
string.IsNullOrWhiteSpace(Worktrees.CentralWorktreeRoot) ? null : Worktrees.CentralWorktreeRoot,
Worktrees.WorktreeAutoCleanupEnabled,
Worktrees.WorktreeAutoCleanupDays,
System.Text.Json.JsonSerializer.Serialize(
General.ReportExcludedPaths
.Split('\n').Select(l => l.Trim().TrimEnd('\r')).Where(l => l.Length > 0).ToList()),
General.StandupWeekday);
await _worker.UpdateAppSettingsAsync(dto);
await Prime.SaveAsync();
CloseAction?.Invoke();
}
catch (Exception ex) { StatusMessage = Loc.T("vm.settingsModal.saveFailed", ex.Message); }
finally { IsBusy = false; }
}
[RelayCommand] private void Cancel() => CloseAction?.Invoke();
}