feat(ui): add About modal opened from Help menu
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ using ClaudeDo.Data;
|
|||||||
using ClaudeDo.Data.Models;
|
using ClaudeDo.Data.Models;
|
||||||
using ClaudeDo.Ui.Services;
|
using ClaudeDo.Ui.Services;
|
||||||
using ClaudeDo.Ui.ViewModels.Islands;
|
using ClaudeDo.Ui.ViewModels.Islands;
|
||||||
|
using ClaudeDo.Ui.ViewModels.Modals;
|
||||||
using ClaudeDo.Ui.ViewModels.Planning;
|
using ClaudeDo.Ui.ViewModels.Planning;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
@@ -35,6 +36,9 @@ public sealed partial class IslandsShellViewModel : ViewModelBase
|
|||||||
// Set by MainWindow to open the conflict resolution dialog.
|
// Set by MainWindow to open the conflict resolution dialog.
|
||||||
public Func<ConflictResolutionViewModel, Task>? ShowConflictDialog { get; set; }
|
public Func<ConflictResolutionViewModel, Task>? ShowConflictDialog { get; set; }
|
||||||
|
|
||||||
|
// Set by MainWindow to open the About dialog.
|
||||||
|
public Func<AboutModalViewModel, Task>? ShowAboutModal { get; set; }
|
||||||
|
|
||||||
[ObservableProperty] private bool _isUpdateBannerVisible;
|
[ObservableProperty] private bool _isUpdateBannerVisible;
|
||||||
[ObservableProperty] private string? _updateBannerLatestVersion;
|
[ObservableProperty] private string? _updateBannerLatestVersion;
|
||||||
[ObservableProperty] private string? _inlineUpdateStatus;
|
[ObservableProperty] private string? _inlineUpdateStatus;
|
||||||
@@ -222,6 +226,13 @@ public sealed partial class IslandsShellViewModel : ViewModelBase
|
|||||||
if (InlineUpdateStatus == text) InlineUpdateStatus = null;
|
if (InlineUpdateStatus == text) InlineUpdateStatus = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task OpenAbout()
|
||||||
|
{
|
||||||
|
var vm = new AboutModalViewModel();
|
||||||
|
if (ShowAboutModal is not null) await ShowAboutModal(vm);
|
||||||
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private async Task CheckForUpdatesAsync()
|
private async Task CheckForUpdatesAsync()
|
||||||
{
|
{
|
||||||
|
|||||||
34
src/ClaudeDo.Ui/ViewModels/Modals/AboutModalViewModel.cs
Normal file
34
src/ClaudeDo.Ui/ViewModels/Modals/AboutModalViewModel.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using ClaudeDo.Data;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
|
||||||
|
namespace ClaudeDo.Ui.ViewModels.Modals;
|
||||||
|
|
||||||
|
public sealed partial class AboutModalViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
public string AppVersion { get; } =
|
||||||
|
Assembly.GetExecutingAssembly().GetName().Version?.ToString(3) ?? "0.0.0";
|
||||||
|
public string DataFolderPath { get; } = Paths.AppDataRoot();
|
||||||
|
public string LogsFolderPath { get; } = Path.Combine(Paths.AppDataRoot(), "logs");
|
||||||
|
public string WorkerConfigPath { get; } = Path.Combine(Paths.AppDataRoot(), "worker.config.json");
|
||||||
|
|
||||||
|
public Action? CloseAction { get; set; }
|
||||||
|
|
||||||
|
[RelayCommand] private void Close() => CloseAction?.Invoke();
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void OpenPath(string? path)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(path)) return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var target = File.Exists(path) ? path : (Directory.Exists(path) ? path : null);
|
||||||
|
if (target is null) return;
|
||||||
|
Process.Start(new ProcessStartInfo("explorer.exe", $"\"{target}\"") { UseShellExecute = true });
|
||||||
|
}
|
||||||
|
catch { /* ignore */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,6 +67,7 @@
|
|||||||
Foreground="{DynamicResource TextDimBrush}">
|
Foreground="{DynamicResource TextDimBrush}">
|
||||||
<MenuItem Header="Check for updates"
|
<MenuItem Header="Check for updates"
|
||||||
Command="{Binding CheckForUpdatesCommand}"/>
|
Command="{Binding CheckForUpdatesCommand}"/>
|
||||||
|
<MenuItem Header="About…" Command="{Binding OpenAboutCommand}"/>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using ClaudeDo.Ui.ViewModels;
|
using ClaudeDo.Ui.ViewModels;
|
||||||
|
using ClaudeDo.Ui.Views.Modals;
|
||||||
using ClaudeDo.Ui.Views.Planning;
|
using ClaudeDo.Ui.Views.Planning;
|
||||||
|
|
||||||
namespace ClaudeDo.Ui.Views;
|
namespace ClaudeDo.Ui.Views;
|
||||||
@@ -23,6 +24,13 @@ public partial class MainWindow : Window
|
|||||||
var modal = new ConflictResolutionView { DataContext = conflictVm };
|
var modal = new ConflictResolutionView { DataContext = conflictVm };
|
||||||
await modal.ShowDialog(this);
|
await modal.ShowDialog(this);
|
||||||
};
|
};
|
||||||
|
vm.ShowAboutModal = async (aboutVm) =>
|
||||||
|
{
|
||||||
|
var dlg = new AboutModalView { DataContext = aboutVm };
|
||||||
|
var tcs = new TaskCompletionSource<bool>();
|
||||||
|
aboutVm.CloseAction = () => { dlg.Close(); tcs.TrySetResult(true); };
|
||||||
|
await dlg.ShowDialog(this);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
49
src/ClaudeDo.Ui/Views/Modals/AboutModalView.axaml
Normal file
49
src/ClaudeDo.Ui/Views/Modals/AboutModalView.axaml
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:vm="using:ClaudeDo.Ui.ViewModels.Modals"
|
||||||
|
x:Class="ClaudeDo.Ui.Views.Modals.AboutModalView"
|
||||||
|
x:DataType="vm:AboutModalViewModel"
|
||||||
|
Title="About ClaudeDo"
|
||||||
|
Width="480" Height="280"
|
||||||
|
SystemDecorations="None"
|
||||||
|
ExtendClientAreaToDecorationsHint="True"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
Background="{DynamicResource SurfaceBrush}">
|
||||||
|
<Window.KeyBindings>
|
||||||
|
<KeyBinding Gesture="Escape" Command="{Binding CloseCommand}"/>
|
||||||
|
</Window.KeyBindings>
|
||||||
|
<Border BorderBrush="{DynamicResource LineBrush}" BorderThickness="1">
|
||||||
|
<Grid RowDefinitions="36,*,52">
|
||||||
|
<Border Grid.Row="0" Background="{DynamicResource DeepBrush}"
|
||||||
|
BorderBrush="{DynamicResource LineBrush}" BorderThickness="0,0,0,1">
|
||||||
|
<Grid ColumnDefinitions="*,Auto" Margin="14,0">
|
||||||
|
<TextBlock Text="ABOUT" FontFamily="{DynamicResource MonoFont}" FontSize="11"
|
||||||
|
LetterSpacing="1.4" Foreground="{DynamicResource TextBrush}" VerticalAlignment="Center"/>
|
||||||
|
<Button Grid.Column="1" Classes="icon-btn" Content="✕" FontSize="12"
|
||||||
|
Command="{Binding CloseCommand}" VerticalAlignment="Center"/>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
<ScrollViewer Grid.Row="1" Padding="20,16">
|
||||||
|
<Grid RowDefinitions="Auto,Auto,Auto,Auto" ColumnDefinitions="90,*,Auto" RowSpacing="10">
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" Text="Version" Foreground="{DynamicResource TextDimBrush}" VerticalAlignment="Center"/>
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding AppVersion}" FontFamily="{DynamicResource MonoFont}" VerticalAlignment="Center"/>
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" Text="Data" Foreground="{DynamicResource TextDimBrush}" VerticalAlignment="Center"/>
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding DataFolderPath}" FontFamily="{DynamicResource MonoFont}" TextTrimming="CharacterEllipsis" VerticalAlignment="Center"/>
|
||||||
|
<Button Grid.Row="1" Grid.Column="2" Content="Open" Command="{Binding OpenPathCommand}" CommandParameter="{Binding DataFolderPath}"/>
|
||||||
|
<TextBlock Grid.Row="2" Grid.Column="0" Text="Logs" Foreground="{DynamicResource TextDimBrush}" VerticalAlignment="Center"/>
|
||||||
|
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding LogsFolderPath}" FontFamily="{DynamicResource MonoFont}" TextTrimming="CharacterEllipsis" VerticalAlignment="Center"/>
|
||||||
|
<Button Grid.Row="2" Grid.Column="2" Content="Open" Command="{Binding OpenPathCommand}" CommandParameter="{Binding LogsFolderPath}"/>
|
||||||
|
<TextBlock Grid.Row="3" Grid.Column="0" Text="Config" Foreground="{DynamicResource TextDimBrush}" VerticalAlignment="Center"/>
|
||||||
|
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding WorkerConfigPath}" FontFamily="{DynamicResource MonoFont}" TextTrimming="CharacterEllipsis" VerticalAlignment="Center"/>
|
||||||
|
<Button Grid.Row="3" Grid.Column="2" Content="Open" Command="{Binding OpenPathCommand}" CommandParameter="{Binding WorkerConfigPath}"/>
|
||||||
|
</Grid>
|
||||||
|
</ScrollViewer>
|
||||||
|
<Border Grid.Row="2" Background="{DynamicResource DeepBrush}"
|
||||||
|
BorderBrush="{DynamicResource LineBrush}" BorderThickness="0,1,0,0">
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="8" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="16,0">
|
||||||
|
<Button Content="Close" Command="{Binding CloseCommand}" MinWidth="90"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Window>
|
||||||
10
src/ClaudeDo.Ui/Views/Modals/AboutModalView.axaml.cs
Normal file
10
src/ClaudeDo.Ui/Views/Modals/AboutModalView.axaml.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace ClaudeDo.Ui.Views.Modals;
|
||||||
|
|
||||||
|
public partial class AboutModalView : Window
|
||||||
|
{
|
||||||
|
public AboutModalView() => InitializeComponent();
|
||||||
|
private void InitializeComponent() => AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user