feat(installer): rewrite WelcomePage for download-mode + update heading

Removes SourceDirectory field (no longer in InstallContext), adds
dynamic Heading/Subheading/InstallDirEditable for FreshInstall vs Update
mode, and updates XAML to match sibling page style.
This commit is contained in:
Mika Kuns
2026-04-15 10:14:38 +02:00
parent 5e432a4a27
commit da1fe2109a
2 changed files with 38 additions and 73 deletions

View File

@@ -9,40 +9,29 @@
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel MaxWidth="520">
<TextBlock Text="Welcome to ClaudeDo Setup" FontSize="20" FontWeight="SemiBold"
Margin="0,0,0,6"/>
<TextBlock Text="This wizard will build, configure, and install ClaudeDo on your machine."
Foreground="{StaticResource TextSecondaryBrush}" Margin="0,0,0,24"
TextWrapping="Wrap"/>
<!-- Source Directory -->
<Label Content="Source Directory"/>
<Grid Margin="0,0,0,4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding SourceDirectory, UpdateSourceTrigger=PropertyChanged}"/>
<Button Grid.Column="1" Content="Browse..." Command="{Binding BrowseSourceCommand}"
Margin="8,0,0,0"/>
</Grid>
<TextBlock Text="{Binding SourceError}" Foreground="{StaticResource ErrorBrush}" FontSize="11"
Visibility="{Binding SourceError, Converter={StaticResource NullToCollapsedConverter}}"
Margin="0,0,0,16"/>
<TextBlock Text="{Binding Heading}" FontSize="20" FontWeight="SemiBold" Margin="0,0,0,6"/>
<TextBlock Text="{Binding Subheading}" TextWrapping="Wrap"
Foreground="{StaticResource TextSecondaryBrush}" Margin="0,0,0,24"/>
<!-- Install Directory -->
<Label Content="Install Directory"/>
<Grid Margin="0,0,0,4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding InstallDirectory, UpdateSourceTrigger=PropertyChanged}"/>
<Button Grid.Column="1" Content="Browse..." Command="{Binding BrowseInstallCommand}"
Margin="8,0,0,0"/>
<TextBox Grid.Column="0"
Text="{Binding InstallDirectory, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding InstallDirEditable}"/>
<Button Grid.Column="1"
Content="Browse..."
Margin="8,0,0,0"
Command="{Binding BrowseInstallCommand}"
IsEnabled="{Binding InstallDirEditable}"/>
</Grid>
<TextBlock Text="{Binding InstallError}" Foreground="{StaticResource ErrorBrush}" FontSize="11"
Visibility="{Binding InstallError, Converter={StaticResource NullToCollapsedConverter}}"/>
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@@ -19,88 +19,64 @@ public partial class WelcomePageViewModel : ObservableObject, IInstallerPage
public bool ShowInSettings => false;
public UserControl View => _view ??= new WelcomePageView { DataContext = this };
[ObservableProperty] private string _sourceDirectory = "";
[ObservableProperty] private string _installDirectory = @"C:\Program Files\ClaudeDo";
[ObservableProperty] private string? _sourceError;
[ObservableProperty] private string? _installError;
[ObservableProperty] private string _heading = "Install ClaudeDo";
[ObservableProperty] private string _subheading = "Set the installation directory and continue.";
[ObservableProperty] private bool _installDirEditable = true;
public WelcomePageViewModel(InstallContext context)
{
_context = context;
_sourceDirectory = DetectSourceDirectory();
}
public Task LoadAsync()
{
if (!string.IsNullOrEmpty(_context.SourceDirectory))
SourceDirectory = _context.SourceDirectory;
if (!string.IsNullOrEmpty(_context.InstallDirectory))
InstallDirectory = _context.InstallDirectory;
InstallDirectory = string.IsNullOrEmpty(_context.InstallDirectory)
? @"C:\Program Files\ClaudeDo"
: _context.InstallDirectory;
switch (_context.Mode)
{
case InstallerMode.FreshInstall:
Heading = "Install ClaudeDo";
Subheading = "Choose where to install ClaudeDo, then click Next.";
InstallDirEditable = true;
break;
case InstallerMode.Update:
Heading = $"Update ClaudeDo {_context.InstalledVersion} -> {_context.LatestVersion}";
Subheading = "Your tasks, config, and database will be preserved. Click Next to continue.";
InstallDirEditable = false;
break;
}
return Task.CompletedTask;
}
public Task ApplyAsync()
{
_context.SourceDirectory = SourceDirectory;
_context.InstallDirectory = InstallDirectory;
return Task.CompletedTask;
}
public bool Validate()
{
var valid = true;
if (string.IsNullOrWhiteSpace(SourceDirectory) ||
!File.Exists(Path.Combine(SourceDirectory, "ClaudeDo.slnx")))
{
SourceError = "Source directory must contain ClaudeDo.slnx";
valid = false;
}
else
{
SourceError = null;
}
if (string.IsNullOrWhiteSpace(InstallDirectory))
{
InstallError = "Install directory is required";
valid = false;
return false;
}
else
{
InstallError = null;
}
return valid;
}
[RelayCommand]
private void BrowseSource()
{
var dialog = new OpenFolderDialog { Title = "Select ClaudeDo source directory" };
if (dialog.ShowDialog() == true)
SourceDirectory = dialog.FolderName;
InstallError = null;
return true;
}
[RelayCommand]
private void BrowseInstall()
{
if (!InstallDirEditable) return;
var dialog = new OpenFolderDialog { Title = "Select installation directory" };
if (dialog.ShowDialog() == true)
InstallDirectory = dialog.FolderName;
}
private static string DetectSourceDirectory()
{
var dir = AppContext.BaseDirectory;
for (var i = 0; i < 8; i++)
{
if (File.Exists(Path.Combine(dir, "ClaudeDo.slnx")))
return dir;
var parent = Directory.GetParent(dir)?.FullName;
if (parent is null) break;
dir = parent;
}
return "";
}
}