feat(installer): Config view — Save/Repair/Uninstall commands + footer buttons

This commit is contained in:
Mika Kuns
2026-04-15 10:31:24 +02:00
parent ac38ea8c34
commit 2898bec314
2 changed files with 109 additions and 29 deletions

View File

@@ -1,5 +1,6 @@
using System.Windows; using System.Windows;
using ClaudeDo.Installer.Core; using ClaudeDo.Installer.Core;
using ClaudeDo.Installer.Steps;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
@@ -8,6 +9,11 @@ namespace ClaudeDo.Installer.Views;
public partial class SettingsViewModel : ObservableObject public partial class SettingsViewModel : ObservableObject
{ {
private readonly InstallContext _context; private readonly InstallContext _context;
private readonly IReleaseClient _releases;
private readonly StopServiceStep _stopService;
private readonly StartServiceStep _startService;
private readonly DownloadAndExtractStep _downloadStep;
private readonly UninstallRunner _uninstallRunner;
public IReadOnlyList<IInstallerPage> Pages { get; } public IReadOnlyList<IInstallerPage> Pages { get; }
@@ -20,12 +26,29 @@ public partial class SettingsViewModel : ObservableObject
[ObservableProperty] [ObservableProperty]
private bool _isStatusError; private bool _isStatusError;
public SettingsViewModel(PageResolver resolver, InstallContext context) [ObservableProperty]
private string _versionLabel = "";
public SettingsViewModel(
PageResolver resolver,
InstallContext context,
IReleaseClient releases,
StopServiceStep stopService,
StartServiceStep startService,
DownloadAndExtractStep downloadStep,
UninstallRunner uninstallRunner)
{ {
Pages = resolver.SettingsPages; Pages = resolver.SettingsPages;
_context = context; _context = context;
_releases = releases;
_stopService = stopService;
_startService = startService;
_downloadStep = downloadStep;
_uninstallRunner = uninstallRunner;
_selectedPage = Pages.FirstOrDefault(); _selectedPage = Pages.FirstOrDefault();
VersionLabel = $"Installed: {context.InstalledVersion ?? "?"} Installer: {context.InstallerVersion ?? "?"}";
_ = LoadAllAsync(); _ = LoadAllAsync();
} }
@@ -36,9 +59,8 @@ public partial class SettingsViewModel : ObservableObject
} }
[RelayCommand] [RelayCommand]
private async Task Apply() private async Task Save()
{ {
// Validate all pages
foreach (var page in Pages) foreach (var page in Pages)
{ {
if (!page.Validate()) if (!page.Validate())
@@ -50,11 +72,9 @@ public partial class SettingsViewModel : ObservableObject
} }
} }
// Apply all pages (writes to InstallContext)
foreach (var page in Pages) foreach (var page in Pages)
await page.ApplyAsync(); await page.ApplyAsync();
// Write config files directly
var workerCfg = new InstallerWorkerConfig var workerCfg = new InstallerWorkerConfig
{ {
DbPath = _context.DbPath, DbPath = _context.DbPath,
@@ -75,13 +95,67 @@ public partial class SettingsViewModel : ObservableObject
}; };
uiCfg.Save(); uiCfg.Save();
StatusMessage = "Settings saved successfully."; StatusMessage = "Settings saved.";
IsStatusError = false; IsStatusError = false;
} }
[RelayCommand] [RelayCommand]
private void Close() private async Task Repair()
{ {
var confirm = MessageBox.Show(
"Re-download and reinstall the ClaudeDo binaries? Your config and database are NOT affected.",
"Repair ClaudeDo",
MessageBoxButton.OKCancel,
MessageBoxImage.Question);
if (confirm != MessageBoxResult.OK) return;
StatusMessage = "Repairing...";
IsStatusError = false;
var progress = new Progress<string>(msg => StatusMessage = msg);
var steps = new IInstallStep[] { _stopService, _downloadStep, _startService };
foreach (var step in steps)
{
var r = await step.ExecuteAsync(_context, progress, CancellationToken.None);
if (!r.Success)
{
StatusMessage = $"{step.Name} failed: {r.ErrorMessage}";
IsStatusError = true;
return;
}
}
StatusMessage = "Repair complete.";
}
[RelayCommand]
private async Task Uninstall()
{
var confirm = MessageBox.Show(
"This will remove ClaudeDo AND delete all of your tasks, configuration, and database.\n\nContinue?",
"Uninstall ClaudeDo",
MessageBoxButton.YesNo,
MessageBoxImage.Warning);
if (confirm != MessageBoxResult.Yes) return;
var progress = new Progress<string>(msg => StatusMessage = msg);
var r = await _uninstallRunner.RunAsync(progress, CancellationToken.None);
if (!r.Success)
{
StatusMessage = $"Uninstall failed: {r.ErrorMessage}";
IsStatusError = true;
return;
}
MessageBox.Show("ClaudeDo has been removed.", "Uninstall complete",
MessageBoxButton.OK, MessageBoxImage.Information);
Application.Current.Shutdown(); Application.Current.Shutdown();
} }
[RelayCommand]
private void Close() => Application.Current.Shutdown();
} }

View File

@@ -61,31 +61,37 @@
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!-- Status message --> <!-- Status message / version label -->
<TextBlock Grid.Column="0" Text="{Binding StatusMessage}" <StackPanel Grid.Column="0" VerticalAlignment="Center">
VerticalAlignment="Center" FontSize="12"> <TextBlock Text="{Binding VersionLabel}" FontSize="11" Opacity="0.7"/>
<TextBlock.Style> <TextBlock Text="{Binding StatusMessage}" FontSize="12">
<Style TargetType="TextBlock"> <TextBlock.Style>
<Setter Property="Foreground" Value="{StaticResource AccentBrush}"/> <Style TargetType="TextBlock">
<Style.Triggers> <Setter Property="Foreground" Value="{StaticResource AccentBrush}"/>
<DataTrigger Binding="{Binding IsStatusError}" Value="True"> <Style.Triggers>
<Setter Property="Foreground" Value="{StaticResource ErrorBrush}"/> <DataTrigger Binding="{Binding IsStatusError}" Value="True">
</DataTrigger> <Setter Property="Foreground" Value="{StaticResource ErrorBrush}"/>
</Style.Triggers> </DataTrigger>
</Style> </Style.Triggers>
</TextBlock.Style> </Style>
</TextBlock> </TextBlock.Style>
</TextBlock>
</StackPanel>
<Button Grid.Column="1" Content="Close" <Button Grid.Column="1" Content="Uninstall" Margin="0,0,8,0"
Command="{Binding CloseCommand}" Command="{Binding UninstallCommand}"/>
Margin="0,0,8,0" MinWidth="80"/> <Button Grid.Column="2" Content="Repair" Margin="0,0,8,0"
Command="{Binding RepairCommand}"/>
<Button Grid.Column="2" Content="Save &amp; Apply" <Button Grid.Column="3" Content="Save" Margin="0,0,8,0"
Command="{Binding ApplyCommand}" Command="{Binding SaveCommand}"
Style="{StaticResource AccentButton}" Style="{StaticResource AccentButton}"/>
MinWidth="100"/> <Button Grid.Column="4" Content="Close"
Command="{Binding CloseCommand}"/>
</Grid> </Grid>
</Border> </Border>
</Grid> </Grid>