diff --git a/LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.csproj b/LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.csproj
index de158af..7b5f45f 100644
--- a/LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.csproj
+++ b/LanMountainDesktop.Launcher/LanMountainDesktop.Launcher.csproj
@@ -25,6 +25,7 @@
+
diff --git a/LanMountainDesktop.Launcher/Services/ThemeService.cs b/LanMountainDesktop.Launcher/Services/ThemeService.cs
new file mode 100644
index 0000000..d5818e3
--- /dev/null
+++ b/LanMountainDesktop.Launcher/Services/ThemeService.cs
@@ -0,0 +1,68 @@
+using Avalonia;
+using Avalonia.Styling;
+using FluentAvalonia.Styling;
+
+namespace LanMountainDesktop.Launcher.Services;
+
+///
+/// 主题服务,管理启动器的主题设置
+///
+public static class ThemeService
+{
+ private static ThemeVariant _currentTheme = ThemeVariant.Light;
+ private static string _accentColor = "#0078D4";
+
+ ///
+ /// 获取当前主题
+ ///
+ public static ThemeVariant CurrentTheme => _currentTheme;
+
+ ///
+ /// 获取当前主题色
+ ///
+ public static string AccentColor => _accentColor;
+
+ ///
+ /// 应用主题设置
+ ///
+ public static void ApplyTheme(ThemeMode mode, string accentColor)
+ {
+ _currentTheme = mode switch
+ {
+ ThemeMode.Dark => ThemeVariant.Dark,
+ _ => ThemeVariant.Light
+ };
+ _accentColor = accentColor;
+
+ // 应用到当前应用程序
+ if (Application.Current is { } app)
+ {
+ app.RequestedThemeVariant = _currentTheme;
+ }
+ }
+
+ ///
+ /// 应用浅色主题
+ ///
+ public static void ApplyLightTheme(string accentColor)
+ {
+ ApplyTheme(ThemeMode.Light, accentColor);
+ }
+
+ ///
+ /// 应用深色主题
+ ///
+ public static void ApplyDarkTheme(string accentColor)
+ {
+ ApplyTheme(ThemeMode.Dark, accentColor);
+ }
+}
+
+///
+/// 主题模式
+///
+public enum ThemeMode
+{
+ Light,
+ Dark
+}
diff --git a/LanMountainDesktop.Launcher/Services/UpdateEngineService.cs b/LanMountainDesktop.Launcher/Services/UpdateEngineService.cs
index 8295942..7c008d7 100644
--- a/LanMountainDesktop.Launcher/Services/UpdateEngineService.cs
+++ b/LanMountainDesktop.Launcher/Services/UpdateEngineService.cs
@@ -22,6 +22,7 @@ internal sealed class UpdateEngineService
private readonly DeploymentLocator _deploymentLocator;
private readonly string _appRoot;
+ private readonly string _dataRoot;
private readonly string _launcherRoot;
private readonly string _incomingRoot;
private readonly string _snapshotsRoot;
@@ -30,7 +31,8 @@ internal sealed class UpdateEngineService
{
_deploymentLocator = deploymentLocator;
_appRoot = deploymentLocator.GetAppRoot();
- _launcherRoot = Path.Combine(_appRoot, LauncherDirectoryName);
+ _dataRoot = new DataLocationResolver(_appRoot).ResolveDataRoot();
+ _launcherRoot = Path.Combine(_dataRoot, LauncherDirectoryName);
_incomingRoot = Path.Combine(_launcherRoot, UpdateDirectoryName, IncomingDirectoryName);
_snapshotsRoot = Path.Combine(_launcherRoot, SnapshotsDirectoryName);
}
diff --git a/LanMountainDesktop.Launcher/ViewModels/DevDebugWindowViewModel.cs b/LanMountainDesktop.Launcher/ViewModels/DevDebugWindowViewModel.cs
index e7a0239..b149e5a 100644
--- a/LanMountainDesktop.Launcher/ViewModels/DevDebugWindowViewModel.cs
+++ b/LanMountainDesktop.Launcher/ViewModels/DevDebugWindowViewModel.cs
@@ -13,6 +13,7 @@ public sealed class DevDebugWindowViewModel : INotifyPropertyChanged
private bool _isErrorEnabled = true;
private bool _isUpdateEnabled = true;
private bool _isOobeEnabled = true;
+ private bool _isDataLocationEnabled = true;
private string _statusMessage = "就绪";
public event PropertyChangedEventHandler? PropertyChanged;
@@ -87,6 +88,23 @@ public sealed class DevDebugWindowViewModel : INotifyPropertyChanged
}
}
+ ///
+ /// 数据位置选择页面是否启用实际功能
+ ///
+ public bool IsDataLocationEnabled
+ {
+ get => _isDataLocationEnabled;
+ set
+ {
+ if (_isDataLocationEnabled != value)
+ {
+ _isDataLocationEnabled = value;
+ OnPropertyChanged();
+ UpdateStatus($"数据位置选择: {(value ? "功能模式" : "仅查看")}");
+ }
+ }
+ }
+
#endregion
#region 状态信息
@@ -131,6 +149,11 @@ public sealed class DevDebugWindowViewModel : INotifyPropertyChanged
///
public ICommand OpenOobeCommand { get; }
+ ///
+ /// 打开数据位置选择页面命令
+ ///
+ public ICommand OpenDataLocationCommand { get; }
+
///
/// 全部切换到查看模式命令
///
@@ -170,6 +193,11 @@ public sealed class DevDebugWindowViewModel : INotifyPropertyChanged
///
public event EventHandler? OpenOobeRequested;
+ ///
+ /// 请求打开数据位置选择页面
+ ///
+ public event EventHandler? OpenDataLocationRequested;
+
///
/// 请求关闭窗口
///
@@ -199,12 +227,18 @@ public sealed class DevDebugWindowViewModel : INotifyPropertyChanged
OpenOobeRequested?.Invoke(this, new OobeOpenEventArgs(IsOobeEnabled));
});
+ OpenDataLocationCommand = new RelayCommand(() =>
+ {
+ OpenDataLocationRequested?.Invoke(this, new DataLocationOpenEventArgs(IsDataLocationEnabled));
+ });
+
SetAllViewOnlyCommand = new RelayCommand(() =>
{
IsSplashEnabled = false;
IsErrorEnabled = false;
IsUpdateEnabled = false;
IsOobeEnabled = false;
+ IsDataLocationEnabled = false;
UpdateStatus("全部页面已切换到查看模式");
});
@@ -214,6 +248,7 @@ public sealed class DevDebugWindowViewModel : INotifyPropertyChanged
IsErrorEnabled = true;
IsUpdateEnabled = true;
IsOobeEnabled = true;
+ IsDataLocationEnabled = true;
UpdateStatus("全部页面已切换到功能模式");
});
@@ -260,4 +295,10 @@ public class OobeOpenEventArgs : EventArgs
public OobeOpenEventArgs(bool isFunctional) => IsFunctional = isFunctional;
}
+public class DataLocationOpenEventArgs : EventArgs
+{
+ public bool IsFunctional { get; }
+ public DataLocationOpenEventArgs(bool isFunctional) => IsFunctional = isFunctional;
+}
+
#endregion
diff --git a/LanMountainDesktop.Launcher/Views/DataLocationPromptWindow.axaml.cs b/LanMountainDesktop.Launcher/Views/DataLocationPromptWindow.axaml.cs
index d2cdf2a..5a2e09d 100644
--- a/LanMountainDesktop.Launcher/Views/DataLocationPromptWindow.axaml.cs
+++ b/LanMountainDesktop.Launcher/Views/DataLocationPromptWindow.axaml.cs
@@ -221,6 +221,7 @@ internal partial class DataLocationPromptWindow : Window
{
Duration = TimeSpan.FromMilliseconds(500),
Easing = new CubicEaseOut(),
+ FillMode = FillMode.Forward,
Children =
{
new KeyFrame
@@ -240,6 +241,7 @@ internal partial class DataLocationPromptWindow : Window
{
Duration = TimeSpan.FromMilliseconds(500),
Easing = new CubicEaseOut(),
+ FillMode = FillMode.Forward,
Children =
{
new KeyFrame
@@ -280,6 +282,7 @@ internal partial class DataLocationPromptWindow : Window
{
Duration = TimeSpan.FromMilliseconds(200),
Easing = new CubicEaseIn(),
+ FillMode = FillMode.Forward,
Children =
{
new KeyFrame
diff --git a/LanMountainDesktop.Launcher/Views/DevDebugWindow.axaml b/LanMountainDesktop.Launcher/Views/DevDebugWindow.axaml
index 7bae0ad..e04ae05 100644
--- a/LanMountainDesktop.Launcher/Views/DevDebugWindow.axaml
+++ b/LanMountainDesktop.Launcher/Views/DevDebugWindow.axaml
@@ -141,6 +141,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LanMountainDesktop.Launcher/Views/DevDebugWindow.axaml.cs b/LanMountainDesktop.Launcher/Views/DevDebugWindow.axaml.cs
index 0c2554b..01bb03a 100644
--- a/LanMountainDesktop.Launcher/Views/DevDebugWindow.axaml.cs
+++ b/LanMountainDesktop.Launcher/Views/DevDebugWindow.axaml.cs
@@ -25,6 +25,7 @@ public partial class DevDebugWindow : Window
_viewModel.OpenErrorRequested += OnOpenErrorRequested;
_viewModel.OpenUpdateRequested += OnOpenUpdateRequested;
_viewModel.OpenOobeRequested += OnOpenOobeRequested;
+ _viewModel.OpenDataLocationRequested += OnOpenDataLocationRequested;
_viewModel.CloseRequested += OnCloseRequested;
}
@@ -135,6 +136,17 @@ public partial class DevDebugWindow : Window
}
}
+ ///
+ /// 打开数据位置选择页面
+ ///
+ private void OnOpenDataLocationRequested(object? sender, DataLocationOpenEventArgs e)
+ {
+ var appRoot = AppDomain.CurrentDomain.BaseDirectory;
+ var resolver = new DataLocationResolver(appRoot);
+ var window = new DataLocationPromptWindow(resolver);
+ window.Show();
+ }
+
///
/// 关闭窗口
///
diff --git a/LanMountainDesktop.Launcher/Views/OobeWindow.axaml b/LanMountainDesktop.Launcher/Views/OobeWindow.axaml
index 0138473..313e442 100644
--- a/LanMountainDesktop.Launcher/Views/OobeWindow.axaml
+++ b/LanMountainDesktop.Launcher/Views/OobeWindow.axaml
@@ -4,13 +4,14 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:LanMountainDesktop.Launcher.Views"
xmlns:ui="using:FluentAvalonia.UI.Controls"
+ xmlns:fi="using:FluentIcons.Avalonia"
mc:Ignorable="d"
- d:DesignWidth="600"
+ d:DesignWidth="700"
d:DesignHeight="500"
x:Class="LanMountainDesktop.Launcher.Views.OobeWindow"
x:DataType="views:OobeWindow"
Title="欢迎使用阑山桌面"
- Width="600"
+ Width="700"
Height="500"
CanResize="False"
WindowStartupLocation="CenterScreen"
@@ -21,59 +22,600 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
diff --git a/LanMountainDesktop.Launcher/Views/OobeWindow.axaml.cs b/LanMountainDesktop.Launcher/Views/OobeWindow.axaml.cs
index 0c1e17a..ceff7e7 100644
--- a/LanMountainDesktop.Launcher/Views/OobeWindow.axaml.cs
+++ b/LanMountainDesktop.Launcher/Views/OobeWindow.axaml.cs
@@ -1,182 +1,709 @@
using Avalonia;
using Avalonia.Animation;
-using Avalonia.Animation.Easings;
using Avalonia.Controls;
+using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
-using Avalonia.Styling;
+using Avalonia.Threading;
+using LanMountainDesktop.Launcher.Models;
+using LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Views;
public partial class OobeWindow : Window
{
+ private const int AnimationDurationMs = 300;
+ private const int TypingDelayMs = 100;
+
private readonly TaskCompletionSource _completionSource = new();
+ private readonly DataLocationResolver _resolver;
private bool _isTransitioning;
+ private bool _isDebugMode;
+ private int _currentStep = 1;
+
+ // 数据位置选择
+ private DataLocationMode _selectedDataLocationMode = DataLocationMode.System;
+ private bool _migrateExistingData;
+
+ // 主题选择
+ private Services.ThemeMode _selectedThemeMode = Services.ThemeMode.Light;
+ private string _selectedAccentColor = "#0078D4";
+ private MonetSource _selectedMonetSource = MonetSource.Wallpaper;
public OobeWindow()
{
AvaloniaXamlLoader.Load(this);
Loaded += OnWindowLoaded;
Opened += OnWindowOpened;
+
+ var appRoot = AppDomain.CurrentDomain.BaseDirectory;
+ _resolver = new DataLocationResolver(appRoot);
}
+ public void SetDebugMode(bool isDebugMode)
+ {
+ _isDebugMode = isDebugMode;
+ }
+
+ public Task WaitForEnterAsync() => _completionSource.Task;
+
private void OnWindowLoaded(object? sender, RoutedEventArgs e)
{
- Console.WriteLine("[OobeWindow] Window loaded, initializing components...");
+ InitializeDataLocationStep();
+ SetupEventHandlers();
+ }
- var enterButton = this.FindControl