refactor(launcher): reorganize into responsibility folders

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
lincube
2026-05-28 10:43:30 +08:00
parent 1ee6e68f33
commit b219f109ec
57 changed files with 92 additions and 197 deletions

View File

@@ -1,4 +1,4 @@
namespace LanMountainDesktop.Launcher.Services.AirApp;
namespace LanMountainDesktop.Launcher.AirApp;
internal sealed class AirAppHostLocator
{

View File

@@ -1,4 +1,4 @@
namespace LanMountainDesktop.Launcher.Services.AirApp;
namespace LanMountainDesktop.Launcher.AirApp;
internal static class AirAppInstanceKey
{

View File

@@ -1,7 +1,5 @@
using System.Diagnostics;
using LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Services.AirApp;
namespace LanMountainDesktop.Launcher.AirApp;
internal interface IAirAppProcessStarter
{
@@ -60,7 +58,7 @@ internal sealed class AirAppProcessStarter : IAirAppProcessStarter
AddArgument(startInfo, "--source-placement-id", sourcePlacementId.Trim());
}
LanMountainDesktop.Launcher.Services.Logger.Info(
Logger.Info(
$"Starting AirAppHost. AppId='{appId}'; InstanceKey='{instanceKey}'; HostPath='{hostPath}'; DataRoot='{dataRoot ?? string.Empty}'.");
var process = Process.Start(startInfo);
if (process is not null)
@@ -70,12 +68,12 @@ internal sealed class AirAppProcessStarter : IAirAppProcessStarter
{
try
{
LanMountainDesktop.Launcher.Services.Logger.Info(
Logger.Info(
$"AirAppHost exited. AppId='{appId}'; InstanceKey='{instanceKey}'; ProcessId={process.Id}; ExitCode={process.ExitCode}.");
}
catch (Exception ex)
{
LanMountainDesktop.Launcher.Services.Logger.Warn($"Failed to log AirAppHost exit: {ex.Message}");
Logger.Warn($"Failed to log AirAppHost exit: {ex.Message}");
}
};
}

View File

@@ -1,7 +1,7 @@
using LanMountainDesktop.Shared.IPC;
using LanMountainDesktop.Shared.IPC.Abstractions.Services;
namespace LanMountainDesktop.Launcher.Services.AirApp;
namespace LanMountainDesktop.Launcher.AirApp;
internal sealed class LauncherAirAppLifecycleIpcHost : IDisposable
{

View File

@@ -2,7 +2,7 @@ using System.Diagnostics;
using System.Runtime.InteropServices;
using LanMountainDesktop.Shared.IPC.Abstractions.Services;
namespace LanMountainDesktop.Launcher.Services.AirApp;
namespace LanMountainDesktop.Launcher.AirApp;
internal sealed class LauncherAirAppLifecycleService : IAirAppLifecycleService
{

View File

@@ -7,8 +7,6 @@ using Avalonia.Threading;
using LanMountainDesktop.Launcher.Models;
using LanMountainDesktop.Launcher.Resources;
using LanMountainDesktop.Launcher.Services;
using LanMountainDesktop.Launcher.Services.AirApp;
using LanMountainDesktop.Launcher.Services.Ipc;
using LanMountainDesktop.Launcher.Views;
using LanMountainDesktop.Shared.Contracts.Launcher;
using LanMountainDesktop.Shared.IPC;
@@ -70,12 +68,12 @@ public partial class App : Application
return;
}
// 调试模式:只显示 DevDebugWindow不走正常启动流程
// 避免启动主程序后 Launcher 自动退出,导致开发者无法预览 UI
// è°?è¯?模å¼<C3A5>ï¼?å<>ªæ?¾ç¤º DevDebugWindowï¼?ä¸<C3A4>走正常å<C2B8>¯å?¨æµ<C3A6>ç¨?
// é<EFBFBD>¿å?<3F>å<EFBFBD>¯å?¨ä¸»ç¨?åº<C3A5>å<EFBFBD>? Launcher è?ªå?¨é??å?ºï¼?导è?´å¼?å<>?è??æ? æ³?é¢?è§? UI
if (context.IsDebugMode && !context.IsPreviewCommand &&
!string.Equals(context.Command, "apply-update", StringComparison.OrdinalIgnoreCase))
{
Logger.Info("Debug mode active showing DevDebugWindow instead of normal launch flow.");
Logger.Info("Debug mode active â?? showing DevDebugWindow instead of normal launch flow.");
var devDebugWindow = new DevDebugWindow();
devDebugWindow.Show();
base.OnFrameworkInitializationCompleted();

View File

@@ -1,7 +1,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using LanMountainDesktop.Launcher.Models;
using LanMountainDesktop.Launcher.Services;
using LanMountainDesktop.Launcher.Plugins;
using LanMountainDesktop.Shared.Contracts.Launcher;
using LanMountainDesktop.Shared.IPC.Abstractions.Services;
@@ -30,15 +30,10 @@ namespace LanMountainDesktop.Launcher;
[JsonSerializable(typeof(PublicTaskbarStatus))]
[JsonSerializable(typeof(PublicShellActivationResult))]
[JsonSerializable(typeof(LauncherResult))]
[JsonSerializable(typeof(HostDiscoveryConfig))]
[JsonSerializable(typeof(PluginManifest))]
[JsonSerializable(typeof(PendingUpgrade))]
[JsonSerializable(typeof(List<PendingUpgrade>))]
[JsonSerializable(typeof(OobeStateFile))]
[JsonSerializable(typeof(DataLocationConfig))]
[JsonSerializable(typeof(GitHubRelease))]
[JsonSerializable(typeof(GitHubAsset))]
[JsonSerializable(typeof(List<GitHubRelease>))]
[JsonSerializable(typeof(StartupAttemptRecord))]
[JsonSerializable(typeof(PrivacyConfig))]
[JsonSerializable(typeof(PrivacyAgreementState))]

View File

@@ -3,7 +3,7 @@ using System.Text.Json;
using LanMountainDesktop.Launcher.Models;
using LanMountainDesktop.Shared.Contracts.Launcher;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Deployment;
internal sealed class DeploymentLocator
{

View File

@@ -1,4 +1,4 @@
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Deployment;
/// <summary>
/// 主程序发现选项

View File

@@ -1,6 +1,6 @@
using LanMountainDesktop.Shared.Contracts.Launcher;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Deployment;
internal sealed record HostLaunchPlan(
string HostPath,

View File

@@ -1,4 +1,4 @@
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Deployment;
internal sealed class HostResolutionResult
{

View File

@@ -1,7 +1,7 @@
using System.Diagnostics;
using Microsoft.Win32;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Deployment;
/// <summary>
/// 老版本检测器 - 检测 0.8.x 及更早的单应用模式安装

View File

@@ -0,0 +1,9 @@
global using LanMountainDesktop.Launcher.AirApp;
global using LanMountainDesktop.Launcher.Deployment;
global using LanMountainDesktop.Launcher.Infrastructure;
global using LanMountainDesktop.Launcher.Ipc;
global using LanMountainDesktop.Launcher.Oobe;
global using LanMountainDesktop.Launcher.Plugins;
global using LanMountainDesktop.Launcher.Startup;
global using LanMountainDesktop.Launcher.Update;
global using LanMountainDesktop.Launcher.Services;

View File

@@ -2,7 +2,7 @@ using System.Text;
using System.Text.Json;
using LanMountainDesktop.Launcher.Models;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Infrastructure;
internal static class Commands
{
@@ -171,16 +171,12 @@ internal static class Commands
? launcherDir
: AppContext.BaseDirectory);
// 发布版结构Launcher 和 app-* 目录在同一目录
// 检查当前目录是否有 app-* 子目录(发布版)
var appDirs = Directory.GetDirectories(baseDir, "app-*", SearchOption.TopDirectoryOnly);
if (appDirs.Length > 0)
{
// 找到 app-* 目录,说明是发布版结构
return baseDir;
}
// 开发环境:检查父目录是否有主程序
var parent = Path.GetFullPath(Path.Combine(baseDir, ".."));
var parentHost = OperatingSystem.IsWindows()
? Path.Combine(parent, "LanMountainDesktop.exe")
@@ -190,7 +186,6 @@ internal static class Commands
return parent;
}
// 默认返回 baseDir
return baseDir;
}
}

View File

@@ -1,7 +1,7 @@
using System.Text.Json;
using LanMountainDesktop.Launcher.Models;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Infrastructure;
/// <summary>
/// 解析应用数据目录位置。

View File

@@ -1,7 +1,7 @@
using Avalonia.Threading;
using LanMountainDesktop.Launcher.Views;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Infrastructure;
internal sealed class DeferredSplashStageReporter : ISplashStageReporter
{

View File

@@ -2,7 +2,7 @@ using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Infrastructure;
internal enum DotNetRuntimeArchitecture
{

View File

@@ -1,4 +1,4 @@
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Infrastructure;
internal interface ISplashStageReporter
{

View File

@@ -1,7 +1,7 @@
using System.Globalization;
using System.Text.Json.Nodes;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Infrastructure;
internal static class LanguagePreferenceService
{

View File

@@ -1,6 +1,6 @@
using Avalonia.Media.Imaging;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Infrastructure;
/// <summary>
/// 启动器背景图片服务

View File

@@ -1,4 +1,4 @@
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Infrastructure;
internal sealed record LauncherDebugSettings(bool DevModeEnabled, string? CustomHostPath);

View File

@@ -1,7 +1,7 @@
using System.Security.Principal;
using LanMountainDesktop.Launcher.Models;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Infrastructure;
internal static class LauncherExecutionContext
{

View File

@@ -1,6 +1,6 @@
using System.Text;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Infrastructure;
/// <summary>
/// 简单的日志记录器 - 同时输出到控制台和文件

View File

@@ -2,7 +2,7 @@ using Avalonia;
using Avalonia.Styling;
using FluentAvalonia.Styling;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Infrastructure;
/// <summary>
/// 主题服务,管理启动器的主题设置

View File

@@ -3,7 +3,7 @@ using System.Text;
using System.Text.Json;
using LanMountainDesktop.Launcher.Models;
namespace LanMountainDesktop.Launcher.Services.Ipc;
namespace LanMountainDesktop.Launcher.Ipc;
internal sealed class LauncherCoordinatorIpcClient
{

View File

@@ -4,7 +4,7 @@ using System.Text.Json;
using System.IO.Pipes;
using LanMountainDesktop.Launcher.Models;
namespace LanMountainDesktop.Launcher.Services.Ipc;
namespace LanMountainDesktop.Launcher.Ipc;
internal sealed class LauncherCoordinatorIpcServer : IDisposable
{

View File

@@ -2,10 +2,9 @@ using System.Buffers;
using System.IO.Pipes;
using System.Text;
using System.Text.Json;
using LanMountainDesktop.Launcher.Services;
using LanMountainDesktop.Shared.Contracts.Update;
namespace LanMountainDesktop.Launcher.Services.Ipc;
namespace LanMountainDesktop.Launcher.Ipc;
internal sealed class LauncherUpdateProgressIpcServer : IUpdateProgressReporter, IDisposable
{

View File

@@ -18,6 +18,7 @@
<ItemGroup>
<!-- 只引用 Shared.ContractsIPC 协议) -->
<ProjectReference Include="..\LanMountainDesktop.Shared.Contracts\LanMountainDesktop.Shared.Contracts.csproj" />
<ProjectReference Include="..\LanMountainDesktop.PluginPackaging\LanMountainDesktop.PluginPackaging.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Shared.IPC\LanMountainDesktop.Shared.IPC.csproj" />
</ItemGroup>

View File

@@ -2,7 +2,7 @@ using Avalonia.Threading;
using LanMountainDesktop.Launcher.Models;
using LanMountainDesktop.Launcher.Views;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Oobe;
internal sealed class DataLocationOobeStep : IOobeStep
{

View File

@@ -2,7 +2,7 @@ using System.Text.Json;
using System.Text.Json.Nodes;
using LanMountainDesktop.Shared.Contracts.Launcher;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Oobe;
/// <summary>
/// 在 OOBE 中向 Host 的 settings.json 写入启动与展示相关字段,属性名与 Host

View File

@@ -1,4 +1,4 @@
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Oobe;
internal interface IOobeStep
{

View File

@@ -1,7 +1,7 @@
using System;
using Microsoft.Win32;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Oobe;
/// <summary>
/// 将当前 Windows 用户登录时自启动项指向<strong>本 Launcher 进程</strong>(与正式入口一致)。

View File

@@ -1,7 +1,7 @@
using System.Text.Json;
using LanMountainDesktop.Launcher.Models;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Oobe;
internal sealed class OobeStateService
{

View File

@@ -3,7 +3,7 @@ using System.Text;
using System.Text.Json;
using LanMountainDesktop.Launcher.Models;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Oobe;
/// <summary>
/// 隐私协议同意状态管理服务(带防篡改保护)

View File

@@ -1,7 +1,7 @@
using Avalonia.Threading;
using LanMountainDesktop.Launcher.Views;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Oobe;
internal sealed class WelcomeOobeStep : IOobeStep
{

View File

@@ -2,7 +2,7 @@ using System.IO.Compression;
using System.Text.Json;
using LanMountainDesktop.Launcher.Models;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Plugins;
/// <summary>
/// 插件安装服务 - 简化版,不依赖 PluginSdk
@@ -290,7 +290,7 @@ internal sealed class PluginInstallerService
/// <summary>
/// 简化的插件清单模型
/// </summary>
public class PluginManifest
internal class PluginManifest
{
public string Id { get; set; } = "";
public string Name { get; set; } = "";

View File

@@ -1,7 +1,7 @@
using System.Text.Json;
using LanMountainDesktop.Launcher.Models;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Plugins;
internal sealed class PluginUpgradeQueueService
{
@@ -29,7 +29,7 @@ internal sealed class PluginUpgradeQueueService
}
var text = File.ReadAllText(pendingPath);
var pending = JsonSerializer.Deserialize(text, AppJsonContext.Default.ListPendingUpgrade) ?? [];
var pending = JsonSerializer.Deserialize<List<PendingUpgrade>>(text, AppJsonContext.Default.Options) ?? [];
var failures = new List<string>();
var succeeded = new List<PendingUpgrade>();
@@ -63,7 +63,7 @@ internal sealed class PluginUpgradeQueueService
}
else
{
File.WriteAllText(pendingPath, JsonSerializer.Serialize(remaining, AppJsonContext.Default.ListPendingUpgrade));
File.WriteAllText(pendingPath, JsonSerializer.Serialize(remaining, AppJsonContext.Default.Options));
}
return new LauncherResult

View File

@@ -1,7 +1,5 @@
using Avalonia;
using LanMountainDesktop.Launcher.Models;
using LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher;
public static class Program

View File

@@ -2,7 +2,6 @@ using System.Diagnostics;
using Avalonia.Threading;
using LanMountainDesktop.Launcher.Models;
using LanMountainDesktop.Launcher.Resources;
using LanMountainDesktop.Launcher.Services.Ipc;
using LanMountainDesktop.Launcher.Startup;
using LanMountainDesktop.Launcher.Views;
using LanMountainDesktop.Shared.Contracts.Launcher;
@@ -185,115 +184,18 @@ internal sealed partial class LauncherFlowCoordinator
}
}
private static async Task<PublicShellStatus?> TryGetPublicShellStatusAsync(
LanMountainDesktopIpcClient ipcClient) =>
await HostStartupMonitor.TryGetPublicShellStatusAsync(ipcClient).ConfigureAwait(false);
private static async Task<StartupSuccessState?> TryRecoverActivationThroughExistingHostAsync(
LanMountainDesktopIpcClient ipcClient,
StartupSuccessTracker startupSuccessTracker,
TimeSpan timeout)
{
var activation = await TryActivateExistingHostWithStatusAsync(ipcClient, timeout).ConfigureAwait(false);
if (activation is null)
{
return null;
}
if (startupSuccessTracker.TryResolve(activation.Status, out var shellSuccess))
{
return shellSuccess;
}
if (activation.Accepted)
{
return startupSuccessTracker.BuildRecoverySuccessState();
}
return HostActivationPolicy.IsRecoverableActivationFailure(activation)
? new StartupSuccessState(
StartupStage.Ready,
"startup_pending",
activation.Message)
: null;
}
private static async Task<PublicShellStatus?> TryGetPublicShellStatusAsync(
LanMountainDesktopIpcClient ipcClient)
{
try
{
var shellProxy = ipcClient.CreateProxy<IPublicShellControlService>();
return await shellProxy.GetShellStatusAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.Warn($"Failed to query public shell status: {ex.Message}");
return null;
}
}
private static async Task<StartupSuccessState?> TryRecoverWithPublicActivationAsync(
LanMountainDesktopIpcClient ipcClient,
Process hostProcess,
Task<StartupSuccessState> successTask,
StartupSuccessTracker startupSuccessTracker)
{
try
{
var shellProxy = ipcClient.CreateProxy<IPublicShellControlService>();
var activation = await shellProxy.ActivateMainWindowWithStatusAsync().ConfigureAwait(false);
StartupDiagnostics.TraceShellStatus("recovery_activation", activation.Status);
if (startupSuccessTracker.TryResolve(activation.Status, out var shellSuccess))
{
return shellSuccess;
}
var completedTask = await Task.WhenAny(successTask, Task.Delay(TimeSpan.FromSeconds(5))).ConfigureAwait(false);
if (completedTask == successTask)
{
return await successTask.ConfigureAwait(false);
}
if (!hostProcess.HasExited && (activation.Accepted || HostActivationPolicy.IsRecoverableActivationFailure(activation)))
{
return startupSuccessTracker.BuildRecoverySuccessState();
}
}
catch (Exception ex)
{
Logger.Warn($"Public activation recovery failed: {ex.Message}");
}
return null;
}
private static LoadingStateMessage BuildDelayedLoadingState(
LoadingStateMessage loadingState,
string summaryMessage,
string detailMessage,
DateTimeOffset startedAtUtc)
{
var delayedItems = loadingState.ActiveItems
.Where(item => !string.Equals(item.Id, "launcher-soft-timeout", StringComparison.OrdinalIgnoreCase))
.ToList();
delayedItems.Insert(0, new LoadingItem
{
Id = "launcher-soft-timeout",
Type = LoadingItemType.System,
Name = "Startup still in progress",
Description = detailMessage,
State = LoadingState.Delayed,
ProgressPercent = Math.Max(loadingState.OverallProgressPercent, 1),
Message = detailMessage,
StartTime = startedAtUtc
});
return loadingState with
{
ActiveItems = delayedItems,
Message = summaryMessage,
Timestamp = DateTimeOffset.UtcNow,
TotalCount = Math.Max(loadingState.TotalCount, delayedItems.Count)
};
}
TimeSpan timeout) =>
await HostStartupMonitor.TryRecoverActivationThroughExistingHostAsync(
ipcClient,
startupSuccessTracker,
timeout).ConfigureAwait(false);
private static Dictionary<string, string> BuildAttemptDetails(
StartupAttemptRecord? trackedAttempt,

View File

@@ -2,7 +2,6 @@ using System.Diagnostics;
using Avalonia.Threading;
using LanMountainDesktop.Launcher.Models;
using LanMountainDesktop.Launcher.Resources;
using LanMountainDesktop.Launcher.Services.Ipc;
using LanMountainDesktop.Launcher.Views;
using LanMountainDesktop.Shared.Contracts.Launcher;
using LanMountainDesktop.Shared.IPC;

View File

@@ -2,7 +2,6 @@ using System.Diagnostics;
using Avalonia.Threading;
using LanMountainDesktop.Launcher.Models;
using LanMountainDesktop.Launcher.Resources;
using LanMountainDesktop.Launcher.Services.Ipc;
using LanMountainDesktop.Launcher.Startup;
using LanMountainDesktop.Launcher.Views;
using LanMountainDesktop.Shared.Contracts.Launcher;
@@ -340,7 +339,7 @@ internal sealed partial class LauncherFlowCoordinator
{
softTimeoutShown = true;
reporter.Report("delayed", SoftTimeoutStatusMessage);
loadingState = BuildDelayedLoadingState(
loadingState = HostStartupMonitor.BuildDelayedLoadingState(
loadingState,
SoftTimeoutStatusMessage,
SoftTimeoutDetailsMessage,

View File

@@ -1,7 +1,6 @@
using System.Diagnostics;
using LanMountainDesktop.Launcher.Models;
using LanMountainDesktop.Launcher.Resources;
using LanMountainDesktop.Launcher.Services;
using LanMountainDesktop.Launcher.Views;
using LanMountainDesktop.Shared.Contracts.Launcher;
using LanMountainDesktop.Shared.IPC;

View File

@@ -1,4 +1,3 @@
using LanMountainDesktop.Launcher.Services;
using LanMountainDesktop.Shared.IPC;
namespace LanMountainDesktop.Launcher.Startup;

View File

@@ -5,7 +5,7 @@ using System.Text.Json;
using LanMountainDesktop.Launcher.Models;
using LanMountainDesktop.Shared.Contracts.Launcher;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Startup;
internal sealed class StartupAttemptRegistry
{

View File

@@ -1,5 +1,4 @@
using System.Text.Json;
using LanMountainDesktop.Launcher.Services;
using LanMountainDesktop.Shared.Contracts.Launcher;
using LanMountainDesktop.Shared.IPC.Abstractions.Services;

View File

@@ -1,4 +1,3 @@
using LanMountainDesktop.Launcher.Services;
using LanMountainDesktop.Shared.Contracts.Launcher;
using LanMountainDesktop.Shared.IPC.Abstractions.Services;

View File

@@ -1,6 +1,6 @@
using LanMountainDesktop.Shared.Contracts.Update;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Update;
public interface IUpdateProgressReporter
{

View File

@@ -1,6 +1,6 @@
using LanMountainDesktop.Shared.Contracts.Update;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Update;
internal sealed class NullUpdateProgressReporter : IUpdateProgressReporter
{

View File

@@ -4,7 +4,7 @@ using System.Text.Json;
using LanMountainDesktop.Launcher.Models;
using ContractsUpdate = LanMountainDesktop.Shared.Contracts.Update;
namespace LanMountainDesktop.Launcher.Services;
namespace LanMountainDesktop.Launcher.Update;
internal sealed class UpdateEngineService
{

View File

@@ -28,7 +28,7 @@ public partial class OobeWindow : Window
private bool _migrateExistingData;
// 主题选择
private Services.ThemeMode _selectedThemeMode = Services.ThemeMode.Light;
private ThemeMode _selectedThemeMode = ThemeMode.Light;
private string _selectedAccentColor = "#0078D4";
private MonetSource _selectedMonetSource = MonetSource.Wallpaper;
@@ -82,19 +82,19 @@ public partial class OobeWindow : Window
// 浅色/深色模式选择
if (this.FindControl<Border>("LightModeOption") is { } lightModeOption)
{
lightModeOption.PointerPressed += (s, e) => SelectThemeMode(Services.ThemeMode.Light);
lightModeOption.PointerPressed += (s, e) => SelectThemeMode(ThemeMode.Light);
}
if (this.FindControl<Border>("DarkModeOption") is { } darkModeOption)
{
darkModeOption.PointerPressed += (s, e) => SelectThemeMode(Services.ThemeMode.Dark);
darkModeOption.PointerPressed += (s, e) => SelectThemeMode(ThemeMode.Dark);
}
if (this.FindControl<RadioButton>("LightModeRadio") is { } lightModeRadio)
{
lightModeRadio.IsCheckedChanged += (s, e) =>
{
if (lightModeRadio.IsChecked == true) SelectThemeMode(Services.ThemeMode.Light);
if (lightModeRadio.IsChecked == true) SelectThemeMode(ThemeMode.Light);
};
}
@@ -102,7 +102,7 @@ public partial class OobeWindow : Window
{
darkModeRadio.IsCheckedChanged += (s, e) =>
{
if (darkModeRadio.IsChecked == true) SelectThemeMode(Services.ThemeMode.Dark);
if (darkModeRadio.IsChecked == true) SelectThemeMode(ThemeMode.Dark);
};
}
@@ -812,7 +812,7 @@ public partial class OobeWindow : Window
}
// 主题选择方法
private void SelectThemeMode(Services.ThemeMode mode)
private void SelectThemeMode(ThemeMode mode)
{
_selectedThemeMode = mode;
@@ -821,30 +821,30 @@ public partial class OobeWindow : Window
if (this.FindControl<RadioButton>("LightModeRadio") is { } lightModeRadio)
{
lightModeRadio.IsChecked = mode == Services.ThemeMode.Light;
lightModeRadio.IsChecked = mode == ThemeMode.Light;
}
if (this.FindControl<RadioButton>("DarkModeRadio") is { } darkModeRadio)
{
darkModeRadio.IsChecked = mode == Services.ThemeMode.Dark;
darkModeRadio.IsChecked = mode == ThemeMode.Dark;
}
if (this.FindControl<Border>("LightModeOption") is { } lightModeOption)
{
lightModeOption.BorderBrush = mode == Services.ThemeMode.Light
lightModeOption.BorderBrush = mode == ThemeMode.Light
? Application.Current?.Resources["AccentFillColorDefaultBrush"] as IBrush
: Application.Current?.Resources["CardStrokeColorDefaultBrush"] as IBrush;
lightModeOption.BorderThickness = mode == Services.ThemeMode.Light
lightModeOption.BorderThickness = mode == ThemeMode.Light
? new Thickness(2)
: new Thickness(1);
}
if (this.FindControl<Border>("DarkModeOption") is { } darkModeOption)
{
darkModeOption.BorderBrush = mode == Services.ThemeMode.Dark
darkModeOption.BorderBrush = mode == ThemeMode.Dark
? Application.Current?.Resources["AccentFillColorDefaultBrush"] as IBrush
: Application.Current?.Resources["CardStrokeColorDefaultBrush"] as IBrush;
darkModeOption.BorderThickness = mode == Services.ThemeMode.Dark
darkModeOption.BorderThickness = mode == ThemeMode.Dark
? new Thickness(2)
: new Thickness(1);
}