mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
Migrate to Avalonia 12 and Plugin SDK v5
Upgrade project to the Avalonia 12 baseline and Plugin SDK v5: centralize Avalonia packages, remove legacy WebView.Avalonia usage (use NativeWebView/WebView2 EnvironmentRequested), and update Fluent/Material icon/package usages. Bump multiple package/project versions to 5.0.0 and Avalonia 12.0.1, update plugin template and README/docs to SDK v5, and add PLUGIN_SDK_V5_MIGRATION.md. Also fix runtime/behavior bugs: make DataLocationResolver use a fixed bootstrap launcher data path and avoid recursive ResolveDataRoot; add legacy-state handling and extraction in OobeStateService; and update component settings tests to reflect migrated storage (DB/backup) and reset cache for test reloads. Various csproj, tests, and docs updated to reflect the migration and ensure build/test compatibility.
This commit is contained in:
@@ -1,14 +1,21 @@
|
|||||||
# Checklist
|
# Checklist
|
||||||
|
|
||||||
- [ ] `SettingsWindow.ApplyChromeMode()` 不再使用 `ExtendClientAreaChromeHints` 和 `SystemDecorations`
|
- [x] `Directory.Packages.props` contains the Avalonia 12 dependency baseline.
|
||||||
- [ ] `ComponentEditorWindow.ApplyChromeMode()` 不再使用 `ExtendClientAreaChromeHints` 和 `SystemDecorations`
|
- [x] Main host references `Avalonia.Controls.WebView`.
|
||||||
- [ ] 所有 `.axaml` 文件中的 `SystemDecorations` 已替换为 `WindowDecorations`
|
- [x] Source no longer references `WebView.Avalonia`, `AvaloniaWebView`, or `.UseDesktopWebView()`.
|
||||||
- [ ] `MainWindow.ComponentSystem.cs` 中 `centerLeft` 和 `positions` 变量已正确定义
|
- [x] `BrowserWidget` uses `NativeWebView.Source`, `Navigate`, `Refresh()`, `NavigationStarted`, and `EnvironmentRequested`.
|
||||||
- [ ] `MainWindow.DesktopPaging.cs` 中 `child` 和 `_isThreeFingerOrRightDragSwipeActive` 变量已正确定义
|
- [x] WebView blanking navigates to `about:blank`.
|
||||||
- [ ] `App.axaml.cs` 中 `BindingPlugins.DataValidators` 代码已移除
|
- [x] Plugin SDK package version is `5.0.0`.
|
||||||
- [ ] `DesktopComponentFailureView.cs` 使用 `ClipboardExtensions.SetTextAsync`
|
- [x] `PluginSdkInfo.ApiVersion` is `5.0.0`.
|
||||||
- [ ] `MonetColorService.cs` 使用正确的 `Bitmap.CopyPixels` 签名
|
- [x] Plugin template package version default is `5.0.0`.
|
||||||
- [ ] `SettingsWindow.axaml.cs` 使用 `FluentIcons.Avalonia.FluentIcon` 替代 `SymbolIconSource`
|
- [x] Plugin template manifest `apiVersion` is `5.0.0`.
|
||||||
- [ ] 所有 `TextBox.Watermark` 已替换为 `PlaceholderText`
|
- [x] Launcher data location config resolution cannot recurse through `ResolveDataRoot()`.
|
||||||
- [ ] `dotnet build LanMountainDesktop.slnx -c Debug` 0 errors, 0 warnings(过时 API 警告)
|
- [x] `OobeStateServiceTests` pass.
|
||||||
- [ ] `dotnet test LanMountainDesktop.slnx -c Debug` 全部通过
|
- [x] `dotnet build LanMountainDesktop.slnx -c Debug` has 0 errors.
|
||||||
|
- [x] `dotnet test LanMountainDesktop.slnx -c Debug` completes without a test host stack overflow.
|
||||||
|
- [ ] Windows host smoke test completed.
|
||||||
|
- [ ] Windows Launcher smoke test completed.
|
||||||
|
- [ ] Settings window FluentAvalonia 3 smoke test completed.
|
||||||
|
- [ ] Component editor Material smoke test completed.
|
||||||
|
- [ ] BrowserWidget navigation/refresh/page activation smoke test completed.
|
||||||
|
- [ ] WebView2 missing-runtime diagnostic smoke test completed.
|
||||||
|
|||||||
@@ -1,63 +1,49 @@
|
|||||||
# Avalonia 12 迁移规格
|
# Avalonia 12 Full Stack Migration
|
||||||
|
|
||||||
## Why
|
## Summary
|
||||||
|
|
||||||
Avalonia 12 带来性能改进(SkiaSharp 3.0、编译绑定默认开启)、新的窗口装饰体系(WindowDrawnDecorations)和更简洁的 API 设计。项目当前已升级包引用,但存在 18 个编译错误和若干过时 API 警告,需要系统性修复以确保构建通过。
|
LanMountainDesktop has moved its desktop stack to the Avalonia 12 baseline. The migration covers the main host, Launcher, Plugin SDK, plugin runtime loading policy, official WebView usage, ClassIsland Markdown, FluentAvalonia, FluentIcons, and Material-related dependencies.
|
||||||
|
|
||||||
## What Changes
|
## Requirements
|
||||||
|
|
||||||
- **BREAKING**: 移除 `ExtendClientAreaChromeHints` 和 `SystemDecorations` 的使用,迁移到 `WindowDecorations`
|
### Requirement: Centralized Avalonia 12 dependency baseline
|
||||||
- **BREAKING**: 移除 `BindingPlugins.DataValidators` 的使用(v12 已移除绑定插件体系)
|
|
||||||
- **BREAKING**: 替换 `IClipboard.SetTextAsync` 为 `ClipboardExtensions.SetTextAsync`
|
|
||||||
- **BREAKING**: 更新 `Bitmap.CopyPixels` 调用签名(移除 `AlphaFormat` 参数)
|
|
||||||
- **BREAKING**: 替换 `FluentIcons.Avalonia.SymbolIconSource` 为 v3 等效 API
|
|
||||||
- 修复 `MainWindow.ComponentSystem.cs` 和 `MainWindow.DesktopPaging.cs` 中缺失的字段/变量
|
|
||||||
- 批量替换 `TextBox.Watermark` → `PlaceholderText`
|
|
||||||
|
|
||||||
## Impact
|
The solution SHALL use central package management for direct Avalonia-facing projects and keep the core UI dependency baseline on Avalonia `12.0.1`.
|
||||||
|
|
||||||
- 受影响代码:
|
Required package baseline:
|
||||||
- `LanMountainDesktop/Views/SettingsWindow.axaml.cs`
|
|
||||||
- `LanMountainDesktop/Views/ComponentEditorWindow.axaml.cs`
|
|
||||||
- `LanMountainDesktop/Views/MainWindow.ComponentSystem.cs`
|
|
||||||
- `LanMountainDesktop/Views/MainWindow.DesktopPaging.cs`
|
|
||||||
- `LanMountainDesktop/App.axaml.cs`
|
|
||||||
- `LanMountainDesktop/Views/Components/DesktopComponentFailureView.cs`
|
|
||||||
- `LanMountainDesktop/Services/MonetColorService.cs`
|
|
||||||
- 13 个 `.axaml` 文件(`SystemDecorations` → `WindowDecorations`)
|
|
||||||
- 7 个 `.cs` 文件 + 7 个 `.axaml` 文件(`Watermark` → `PlaceholderText`)
|
|
||||||
- 受影响规格:无现有规格直接关联
|
|
||||||
|
|
||||||
## ADDED Requirements
|
- `Avalonia*` `12.0.1`
|
||||||
|
- `Avalonia.Controls.WebView` `12.0.0`
|
||||||
|
- `ClassIsland.Markdown.Avalonia` `12.0.0`
|
||||||
|
- `FluentAvaloniaUI` `3.0.0-preview1`
|
||||||
|
- `FluentIcons.Avalonia` `2.1.325`
|
||||||
|
- `Material.Avalonia` `3.16.0`
|
||||||
|
- `Material.Icons.Avalonia` `3.0.2`
|
||||||
|
|
||||||
### Requirement: 窗口装饰 API 迁移
|
### Requirement: Official WebView
|
||||||
系统 SHALL 使用 Avalonia 12 的 `WindowDecorations` 属性替代已移除的 `SystemDecorations` 和 `ExtendClientAreaChromeHints`。
|
|
||||||
|
|
||||||
#### Scenario: SettingsWindow 无边框模式
|
The host SHALL use `Avalonia.Controls.NativeWebView` for the browser widget and SHALL NOT reference `WebView.Avalonia`, `AvaloniaWebView`, or `.UseDesktopWebView()`.
|
||||||
- **WHEN** `ApplyChromeMode(false)` 被调用
|
|
||||||
- **THEN** `WindowDecorations = WindowDecorations.BorderOnly` 且 `ExtendClientAreaToDecorationsHint = true`
|
|
||||||
|
|
||||||
#### Scenario: SettingsWindow 系统 Chrome 模式
|
Windows WebView2 user data configuration SHALL be supplied through `EnvironmentRequested` using `WindowsWebView2EnvironmentRequestedEventArgs.UserDataFolder`.
|
||||||
- **WHEN** `ApplyChromeMode(true)` 被调用
|
|
||||||
- **THEN** `WindowDecorations = WindowDecorations.Full` 且 `ExtendClientAreaToDecorationsHint = true`
|
|
||||||
|
|
||||||
### Requirement: 剪贴板 API 迁移
|
### Requirement: Plugin SDK v5
|
||||||
系统 SHALL 使用 Avalonia 12 的 `ClipboardExtensions.SetTextAsync` 替代已移除的 `IClipboard.SetTextAsync`。
|
|
||||||
|
|
||||||
### Requirement: Bitmap.CopyPixels 签名更新
|
The Plugin SDK API baseline SHALL be `5.0.0`. SDK v4 plugins are treated as incompatible until rebuilt.
|
||||||
系统 SHALL 使用新的 `CopyPixels` 签名,不再传入 `AlphaFormat` 参数。
|
|
||||||
|
|
||||||
### Requirement: FluentIcons v3 API 适配
|
The SDK SHALL keep the existing public UI extension shape, including `SettingsPageBase` and Avalonia `Control` based desktop components.
|
||||||
系统 SHALL 使用 `FluentIcons.Avalonia.FluentIcon` 替代已移除的 `SymbolIconSource`。
|
|
||||||
|
|
||||||
## MODIFIED Requirements
|
### Requirement: Launcher data location stability
|
||||||
|
|
||||||
### Requirement: 编译绑定验证
|
Launcher data location configuration SHALL be read from a fixed bootstrap Launcher data directory so resolving the selected data root cannot recursively require resolving itself.
|
||||||
- **修改前**:`BindingPlugins.DataValidators.RemoveAt(0)` 移除默认数据注解验证插件
|
|
||||||
- **修改后**:v12 默认禁用数据注解验证插件,无需手动移除
|
|
||||||
|
|
||||||
## REMOVED Requirements
|
### Requirement: OOBE state compatibility
|
||||||
|
|
||||||
### Requirement: ExtendClientAreaChromeHints 配置
|
The Launcher SHALL read current OOBE state from the resolved `Launcher/state` directory and SHALL continue to migrate the legacy `.launcher/state/first_run_completed` marker.
|
||||||
**Reason**: Avalonia 12 移除此属性,由 `WindowDecorations` 统一管理
|
|
||||||
**Migration**: 删除所有 `ExtendClientAreaChromeHints` 赋值代码
|
## Acceptance
|
||||||
|
|
||||||
|
- `dotnet build LanMountainDesktop.slnx -c Debug` completes with 0 errors.
|
||||||
|
- `OobeStateServiceTests` pass.
|
||||||
|
- Full `dotnet test LanMountainDesktop.slnx -c Debug` no longer aborts from `DataLocationResolver` recursion.
|
||||||
|
- Plugin template defaults to SDK package version `5.0.0` and manifest `apiVersion` `5.0.0`.
|
||||||
|
- Current developer documentation points to SDK v5 and Avalonia 12.
|
||||||
|
|||||||
@@ -1,39 +1,18 @@
|
|||||||
# Tasks
|
# Tasks
|
||||||
|
|
||||||
- [ ] Task 1: 修复窗口装饰 API(Phase 1)
|
- [x] Centralize Avalonia 12 package versions in `Directory.Packages.props`.
|
||||||
- [x] SubTask 1.1: 重写 `SettingsWindow.ApplyChromeMode()` 移除 `ExtendClientAreaChromeHints`
|
- [x] Move the host, Launcher, Plugin SDK, DesktopHost, Shared.Contracts, and Avalonia-facing projects onto central package versions.
|
||||||
- [x] SubTask 1.2: 重写 `ComponentEditorWindow.ApplyChromeMode()` 移除 `ExtendClientAreaChromeHints`
|
- [x] Replace third-party `WebView.Avalonia` usage with official `NativeWebView`.
|
||||||
- [x] SubTask 1.3: 批量替换所有 `.axaml` 中的 `SystemDecorations` → `WindowDecorations`
|
- [x] Configure WebView2 user data through `EnvironmentRequested`.
|
||||||
- [ ] SubTask 1.4: 验证构建错误减少
|
- [x] Move FluentAvalonia usages to the FA3 control names and package baseline.
|
||||||
|
- [x] Move FluentIcons usage to `FluentIcons.Avalonia` and remove the old `.Fluent` package.
|
||||||
- [ ] Task 2: 修复 MainWindow 编译错误(Phase 2)
|
- [x] Update Plugin SDK package version and API baseline to `5.0.0`.
|
||||||
- [ ] SubTask 2.1: 修复 `MainWindow.ComponentSystem.cs` 中 `centerLeft` 和 `positions` 未定义错误
|
- [x] Update plugin runtime shared assembly policy for Avalonia 12 / FluentAvalonia / FluentIcons / Material.
|
||||||
- [ ] SubTask 2.2: 修复 `MainWindow.DesktopPaging.cs` 中 `child` 和 `_isThreeFingerOrRightDragSwipeActive` 未定义错误
|
- [x] Fix Avalonia 12 compile breaks in window chrome, binding plugin access, clipboard, bitmap copy, and icon source usage.
|
||||||
- [ ] SubTask 2.3: 验证构建错误减少
|
- [x] Fix Launcher data location recursion by using a fixed bootstrap config path.
|
||||||
|
- [x] Fix OOBE state tests and legacy marker compatibility.
|
||||||
- [ ] Task 3: 修复 Avalonia 12 API 变更(Phase 3)
|
- [x] Update PluginTemplate defaults to SDK v5.
|
||||||
- [ ] SubTask 3.1: 移除 `App.axaml.cs` 中 `BindingPlugins.DataValidators` 代码
|
- [x] Add SDK v5 migration documentation.
|
||||||
- [ ] SubTask 3.2: 替换 `DesktopComponentFailureView.cs` 中 `IClipboard.SetTextAsync` 为 `ClipboardExtensions.SetTextAsync`
|
- [x] Update current docs from SDK v4 / Avalonia 11 examples to SDK v5 / Avalonia 12.
|
||||||
- [ ] SubTask 3.3: 更新 `MonetColorService.cs` 中 `Bitmap.CopyPixels` 调用签名
|
- [x] Run full solution tests and record any remaining non-upgrade failures.
|
||||||
- [ ] SubTask 3.4: 验证构建错误减少
|
- [ ] Perform Windows manual smoke test for host, Launcher, settings, component editor, BrowserWidget, and WebView2 missing-runtime handling.
|
||||||
|
|
||||||
- [ ] Task 4: 修复第三方库变更(Phase 4)
|
|
||||||
- [ ] SubTask 4.1: 替换 `SettingsWindow.axaml.cs` 中 `FluentIcons.Avalonia.SymbolIconSource` 为 `FluentIcon`
|
|
||||||
- [ ] SubTask 4.2: 验证构建错误减少
|
|
||||||
|
|
||||||
- [ ] Task 5: 清理过时属性(Phase 5)
|
|
||||||
- [ ] SubTask 5.1: 批量替换 `.cs` 文件中 `Watermark` → `PlaceholderText`
|
|
||||||
- [ ] SubTask 5.2: 批量替换 `.axaml` 文件中 `Watermark` → `PlaceholderText`
|
|
||||||
- [ ] SubTask 5.3: 验证无过时警告
|
|
||||||
|
|
||||||
- [ ] Task 6: 最终验证
|
|
||||||
- [ ] SubTask 6.1: `dotnet build LanMountainDesktop.slnx -c Debug` 0 errors
|
|
||||||
- [ ] SubTask 6.2: `dotnet test LanMountainDesktop.slnx -c Debug` 通过
|
|
||||||
|
|
||||||
# Task Dependencies
|
|
||||||
|
|
||||||
- Task 2 不依赖 Task 1(可并行)
|
|
||||||
- Task 3 不依赖 Task 1/2(可并行)
|
|
||||||
- Task 4 不依赖 Task 1/2/3(可并行)
|
|
||||||
- Task 5 依赖 Task 1/2/3/4(低优先级,最后执行)
|
|
||||||
- Task 6 依赖所有前置任务
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ dotnet test LanMountainDesktop.slnx -c Debug
|
|||||||
- SDK 公共 API 以 `LanMountainDesktop.PluginSdk/` 为准
|
- SDK 公共 API 以 `LanMountainDesktop.PluginSdk/` 为准
|
||||||
- 共享契约以 `LanMountainDesktop.Shared.Contracts/` 为准
|
- 共享契约以 `LanMountainDesktop.Shared.Contracts/` 为准
|
||||||
- market 数据来源默认是兄弟仓库 `..\\LanAirApp`
|
- market 数据来源默认是兄弟仓库 `..\\LanAirApp`
|
||||||
- 迁移或 breaking change 优先同步 `docs/PLUGIN_SDK_V4_MIGRATION.md`
|
- 迁移或 breaking change 优先同步 `docs/PLUGIN_SDK_V5_MIGRATION.md`
|
||||||
|
|
||||||
### 设置与主题
|
### 设置与主题
|
||||||
|
|
||||||
@@ -91,6 +91,6 @@ dotnet test LanMountainDesktop.slnx -c Debug
|
|||||||
- 视觉规范:`docs/VISUAL_SPEC.md`
|
- 视觉规范:`docs/VISUAL_SPEC.md`
|
||||||
- 圆角规范:`docs/CORNER_RADIUS_SPEC.md`
|
- 圆角规范:`docs/CORNER_RADIUS_SPEC.md`
|
||||||
- 生态边界:`docs/ECOSYSTEM_BOUNDARIES.md`
|
- 生态边界:`docs/ECOSYSTEM_BOUNDARIES.md`
|
||||||
- SDK v4 迁移:`docs/PLUGIN_SDK_V4_MIGRATION.md`
|
- SDK v5 迁移:`docs/PLUGIN_SDK_V5_MIGRATION.md`
|
||||||
|
|
||||||
如果多个文档都提到同一件事,以 `docs/ai/DOC_SOURCES.md` 列出的权威来源为准。
|
如果多个文档都提到同一件事,以 `docs/ai/DOC_SOURCES.md` 列出的权威来源为准。
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ internal sealed class DataLocationResolver
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string DefaultPortableDataPath => Path.Combine(_appRoot, "AppData");
|
public string DefaultPortableDataPath => Path.Combine(_appRoot, "AppData");
|
||||||
|
|
||||||
|
private string ResolveBootstrapLauncherDataPath()
|
||||||
|
{
|
||||||
|
return Path.Combine(_defaultSystemDataPath, LauncherFolderName);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 检查是否允许便携模式(应用目录是否可写)
|
/// 检查是否允许便携模式(应用目录是否可写)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -56,6 +61,11 @@ internal sealed class DataLocationResolver
|
|||||||
public string ResolveDataRoot()
|
public string ResolveDataRoot()
|
||||||
{
|
{
|
||||||
var config = LoadConfig();
|
var config = LoadConfig();
|
||||||
|
return ResolveDataRoot(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ResolveDataRoot(DataLocationConfig? config)
|
||||||
|
{
|
||||||
if (config is null)
|
if (config is null)
|
||||||
{
|
{
|
||||||
return _defaultSystemDataPath;
|
return _defaultSystemDataPath;
|
||||||
@@ -65,7 +75,7 @@ internal sealed class DataLocationResolver
|
|||||||
{
|
{
|
||||||
var portablePath = !string.IsNullOrWhiteSpace(config.PortableDataPath)
|
var portablePath = !string.IsNullOrWhiteSpace(config.PortableDataPath)
|
||||||
? config.PortableDataPath
|
? config.PortableDataPath
|
||||||
: _defaultSystemDataPath;
|
: DefaultPortableDataPath;
|
||||||
return Path.GetFullPath(portablePath);
|
return Path.GetFullPath(portablePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +105,7 @@ internal sealed class DataLocationResolver
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string ResolveConfigPath()
|
public string ResolveConfigPath()
|
||||||
{
|
{
|
||||||
return Path.Combine(ResolveLauncherDataPath(), ConfigFileName);
|
return Path.Combine(ResolveBootstrapLauncherDataPath(), ConfigFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -151,7 +161,7 @@ internal sealed class DataLocationResolver
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var launcherPath = ResolveLauncherDataPath();
|
var launcherPath = ResolveBootstrapLauncherDataPath();
|
||||||
Directory.CreateDirectory(launcherPath);
|
Directory.CreateDirectory(launcherPath);
|
||||||
|
|
||||||
var configPath = ResolveConfigPath();
|
var configPath = ResolveConfigPath();
|
||||||
@@ -182,8 +192,9 @@ internal sealed class DataLocationResolver
|
|||||||
// 先创建目录结构
|
// 先创建目录结构
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(ResolveLauncherDataPath());
|
var resolvedDataRoot = ResolveDataRoot(config);
|
||||||
Directory.CreateDirectory(ResolveDesktopDataPath());
|
Directory.CreateDirectory(Path.Combine(resolvedDataRoot, LauncherFolderName));
|
||||||
|
Directory.CreateDirectory(Path.Combine(resolvedDataRoot, DesktopFolderName));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ internal sealed class OobeStateService
|
|||||||
|
|
||||||
private readonly string _stateDirectory;
|
private readonly string _stateDirectory;
|
||||||
private readonly string _statePath;
|
private readonly string _statePath;
|
||||||
|
private readonly string _legacyStatePath;
|
||||||
private readonly string _legacyMarkerPath;
|
private readonly string _legacyMarkerPath;
|
||||||
private readonly LauncherExecutionSnapshot _executionSnapshot;
|
private readonly LauncherExecutionSnapshot _executionSnapshot;
|
||||||
|
|
||||||
@@ -25,7 +26,13 @@ internal sealed class OobeStateService
|
|||||||
: Path.GetFullPath(stateRootOverride);
|
: Path.GetFullPath(stateRootOverride);
|
||||||
_stateDirectory = Path.Combine(stateRoot, "Launcher", "state");
|
_stateDirectory = Path.Combine(stateRoot, "Launcher", "state");
|
||||||
_statePath = Path.Combine(_stateDirectory, "oobe-state.json");
|
_statePath = Path.Combine(_stateDirectory, "oobe-state.json");
|
||||||
_legacyMarkerPath = Path.Combine(_stateDirectory, "first_run_completed");
|
|
||||||
|
var legacyRoot = string.IsNullOrWhiteSpace(stateRootOverride)
|
||||||
|
? Path.GetFullPath(appRoot)
|
||||||
|
: Path.GetFullPath(stateRootOverride);
|
||||||
|
var legacyStateDirectory = Path.Combine(legacyRoot, ".launcher", "state");
|
||||||
|
_legacyStatePath = Path.Combine(legacyStateDirectory, "oobe-state.json");
|
||||||
|
_legacyMarkerPath = Path.Combine(legacyStateDirectory, "first_run_completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
public OobeLaunchDecision Evaluate(CommandContext context)
|
public OobeLaunchDecision Evaluate(CommandContext context)
|
||||||
@@ -100,14 +107,12 @@ internal sealed class OobeStateService
|
|||||||
var migratedLegacyMarker = false;
|
var migratedLegacyMarker = false;
|
||||||
if (File.Exists(_statePath))
|
if (File.Exists(_statePath))
|
||||||
{
|
{
|
||||||
using var stream = File.OpenRead(_statePath);
|
return EvaluateStateFile(context, _statePath, migratedLegacyState: false);
|
||||||
var state = JsonSerializer.Deserialize(stream, AppJsonContext.Default.OobeStateFile);
|
}
|
||||||
if (state is null || state.SchemaVersion <= 0 || string.IsNullOrWhiteSpace(state.CompletedAtUtc))
|
|
||||||
{
|
|
||||||
return BuildUnavailableDecision(context, "OOBE state file is invalid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return BuildDecision(context, OobeStateStatus.Completed, shouldShowOobe: false, migratedLegacyMarker: false);
|
if (File.Exists(_legacyStatePath))
|
||||||
|
{
|
||||||
|
return EvaluateStateFile(context, _legacyStatePath, migratedLegacyState: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (File.Exists(_legacyMarkerPath))
|
if (File.Exists(_legacyMarkerPath))
|
||||||
@@ -140,6 +145,18 @@ internal sealed class OobeStateService
|
|||||||
return result.Success;
|
return result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private OobeLaunchDecision EvaluateStateFile(CommandContext context, string statePath, bool migratedLegacyState)
|
||||||
|
{
|
||||||
|
using var stream = File.OpenRead(statePath);
|
||||||
|
var state = JsonSerializer.Deserialize(stream, AppJsonContext.Default.OobeStateFile);
|
||||||
|
if (state is null || state.SchemaVersion <= 0 || string.IsNullOrWhiteSpace(state.CompletedAtUtc))
|
||||||
|
{
|
||||||
|
return BuildUnavailableDecision(context, "OOBE state file is invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return BuildDecision(context, OobeStateStatus.Completed, shouldShowOobe: false, migratedLegacyMarker: migratedLegacyState);
|
||||||
|
}
|
||||||
|
|
||||||
private void TryDeleteLegacyMarker()
|
private void TryDeleteLegacyMarker()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Version>1.0.0</Version>
|
<Version>5.0.0</Version>
|
||||||
<PackageId>LanMountainDesktop.PluginIsolation.Contracts</PackageId>
|
<PackageId>LanMountainDesktop.PluginIsolation.Contracts</PackageId>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<Authors>LanMountainDesktop</Authors>
|
<Authors>LanMountainDesktop</Authors>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Version>1.0.0</Version>
|
<Version>5.0.0</Version>
|
||||||
<PackageId>LanMountainDesktop.PluginIsolation.Ipc</PackageId>
|
<PackageId>LanMountainDesktop.PluginIsolation.Ipc</PackageId>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<Authors>LanMountainDesktop</Authors>
|
<Authors>LanMountainDesktop</Authors>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Version>5.0.0-preview1</Version>
|
<Version>5.0.0</Version>
|
||||||
<PackageId>LanMountainDesktop.PluginSdk</PackageId>
|
<PackageId>LanMountainDesktop.PluginSdk</PackageId>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<Authors>LanMountainDesktop</Authors>
|
<Authors>LanMountainDesktop</Authors>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Official SDK package for LanMountainDesktop plugins.
|
|||||||
|
|
||||||
```xml
|
```xml
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="LanMountainDesktop.PluginSdk" Version="4.0.1" />
|
<PackageReference Include="LanMountainDesktop.PluginSdk" Version="5.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
"pluginSdkVersion": {
|
"pluginSdkVersion": {
|
||||||
"type": "parameter",
|
"type": "parameter",
|
||||||
"datatype": "text",
|
"datatype": "text",
|
||||||
"defaultValue": "4.0.2",
|
"defaultValue": "5.0.0",
|
||||||
"description": "LanMountainDesktop.PluginSdk package version.",
|
"description": "LanMountainDesktop.PluginSdk package version.",
|
||||||
"replaces": "__PLUGIN_SDK_VERSION__"
|
"replaces": "__PLUGIN_SDK_VERSION__"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"description": "__PLUGIN_DESCRIPTION__",
|
"description": "__PLUGIN_DESCRIPTION__",
|
||||||
"author": "__PLUGIN_AUTHOR__",
|
"author": "__PLUGIN_AUTHOR__",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"apiVersion": "4.0.2",
|
"apiVersion": "5.0.0",
|
||||||
"entranceAssembly": "LanMountainDesktop.PluginTemplate.dll",
|
"entranceAssembly": "LanMountainDesktop.PluginTemplate.dll",
|
||||||
"sharedContracts": [],
|
"sharedContracts": [],
|
||||||
"runtime": {
|
"runtime": {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Version>0.0.0-dev</Version>
|
<Version>5.0.0</Version>
|
||||||
<PackageId>LanMountainDesktop.Shared.Contracts</PackageId>
|
<PackageId>LanMountainDesktop.Shared.Contracts</PackageId>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<Authors>LanMountainDesktop</Authors>
|
<Authors>LanMountainDesktop</Authors>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Version>1.0.0</Version>
|
<Version>5.0.0</Version>
|
||||||
<PackageId>LanMountainDesktop.Shared.IPC</PackageId>
|
<PackageId>LanMountainDesktop.Shared.IPC</PackageId>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<Authors>LanMountainDesktop</Authors>
|
<Authors>LanMountainDesktop</Authors>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Text.Json;
|
|
||||||
using LanMountainDesktop.Models;
|
using LanMountainDesktop.Models;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@@ -34,10 +33,14 @@ public sealed class ComponentSettingsServiceTests
|
|||||||
Assert.Equal("Sweep", snapshot.DesktopClockSecondHandMode);
|
Assert.Equal("Sweep", snapshot.DesktopClockSecondHandMode);
|
||||||
Assert.Single(snapshot.ImportedClassSchedules);
|
Assert.Single(snapshot.ImportedClassSchedules);
|
||||||
|
|
||||||
using var document = JsonDocument.Parse(File.ReadAllText(sandbox.SettingsPath));
|
Assert.True(File.Exists(sandbox.DatabasePath));
|
||||||
Assert.True(document.RootElement.TryGetProperty("defaultSettings", out var defaultSettings));
|
Assert.False(File.Exists(sandbox.SettingsPath));
|
||||||
Assert.Equal("Sweep", defaultSettings.GetProperty("desktopClockSecondHandMode").GetString());
|
Assert.True(File.Exists(sandbox.SettingsBackupPath));
|
||||||
Assert.False(document.RootElement.TryGetProperty("DesktopClockSecondHandMode", out _));
|
|
||||||
|
ComponentSettingsService.ResetCacheForTests();
|
||||||
|
var reloadedService = sandbox.CreateService();
|
||||||
|
var reloaded = reloadedService.Load();
|
||||||
|
Assert.Equal("Sweep", reloaded.DesktopClockSecondHandMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -72,11 +75,16 @@ public sealed class ComponentSettingsServiceTests
|
|||||||
Assert.Equal("Sweep", snapshot.DesktopClockSecondHandMode);
|
Assert.Equal("Sweep", snapshot.DesktopClockSecondHandMode);
|
||||||
Assert.True(pluginSettings.SampleFlag);
|
Assert.True(pluginSettings.SampleFlag);
|
||||||
|
|
||||||
using var document = JsonDocument.Parse(File.ReadAllText(sandbox.SettingsPath));
|
Assert.True(File.Exists(sandbox.DatabasePath));
|
||||||
Assert.True(document.RootElement.TryGetProperty("instanceSettings", out var instanceSettings));
|
Assert.False(File.Exists(sandbox.SettingsPath));
|
||||||
Assert.True(instanceSettings.TryGetProperty("DesktopClock::clock-2x2", out var clockSettings));
|
Assert.True(File.Exists(sandbox.SettingsBackupPath));
|
||||||
Assert.Equal("Sweep", clockSettings.GetProperty("desktopClockSecondHandMode").GetString());
|
|
||||||
Assert.False(document.RootElement.TryGetProperty("InstanceSettings", out _));
|
ComponentSettingsService.ResetCacheForTests();
|
||||||
|
var reloadedService = sandbox.CreateService();
|
||||||
|
var reloadedSnapshot = reloadedService.LoadForComponent("DesktopClock", "clock-2x2");
|
||||||
|
var reloadedPluginSettings = reloadedService.LoadPluginSettings<SamplePluginSettings>("DesktopClock", "clock-2x2");
|
||||||
|
Assert.Equal("Sweep", reloadedSnapshot.DesktopClockSecondHandMode);
|
||||||
|
Assert.True(reloadedPluginSettings.SampleFlag);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -132,12 +140,7 @@ public sealed class ComponentSettingsServiceTests
|
|||||||
Assert.True(pluginSettings.SampleFlag);
|
Assert.True(pluginSettings.SampleFlag);
|
||||||
Assert.Equal("schedule-settings", pluginSettings.Title);
|
Assert.Equal("schedule-settings", pluginSettings.Title);
|
||||||
|
|
||||||
using var document = JsonDocument.Parse(File.ReadAllText(sandbox.SettingsPath));
|
Assert.True(File.Exists(sandbox.DatabasePath));
|
||||||
Assert.True(document.RootElement.TryGetProperty("instanceSettings", out var instanceSettings));
|
|
||||||
Assert.True(instanceSettings.TryGetProperty("DesktopClock::clock-2x2", out _));
|
|
||||||
Assert.True(instanceSettings.TryGetProperty("DesktopClassSchedule::class-schedule-2x2", out _));
|
|
||||||
Assert.True(document.RootElement.TryGetProperty("pluginSettings", out var pluginSettingsNode));
|
|
||||||
Assert.True(pluginSettingsNode.TryGetProperty("DesktopClassSchedule::class-schedule-2x2", out _));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class ComponentSettingsSandbox : IDisposable
|
private sealed class ComponentSettingsSandbox : IDisposable
|
||||||
@@ -155,6 +158,10 @@ public sealed class ComponentSettingsServiceTests
|
|||||||
|
|
||||||
public string SettingsPath => Path.Combine(_directoryPath, "component-settings.json");
|
public string SettingsPath => Path.Combine(_directoryPath, "component-settings.json");
|
||||||
|
|
||||||
|
public string SettingsBackupPath => $"{SettingsPath}.migrated.bak";
|
||||||
|
|
||||||
|
public string DatabasePath => Path.Combine(_directoryPath, "component-state.db");
|
||||||
|
|
||||||
public ComponentSettingsService CreateService()
|
public ComponentSettingsService CreateService()
|
||||||
{
|
{
|
||||||
return new ComponentSettingsService(_directoryPath);
|
return new ComponentSettingsService(_directoryPath);
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ public sealed class OobeStateServiceTests : IDisposable
|
|||||||
executionSnapshot: executionSnapshot ?? new LauncherExecutionSnapshot(false, "tester", "S-1-5-test"));
|
executionSnapshot: executionSnapshot ?? new LauncherExecutionSnapshot(false, "tester", "S-1-5-test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetStatePath() => Path.Combine(_tempRoot, ".launcher", "state", "oobe-state.json");
|
private string GetStatePath() => Path.Combine(_tempRoot, "Launcher", "state", "oobe-state.json");
|
||||||
|
|
||||||
private string GetLegacyMarkerPath() => Path.Combine(_tempRoot, ".launcher", "state", "first_run_completed");
|
private string GetLegacyMarkerPath() => Path.Combine(_tempRoot, ".launcher", "state", "first_run_completed");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,9 +96,9 @@ dotnet new install LanMountainDesktop.PluginTemplate
|
|||||||
dotnet new lmd-plugin -n MyPlugin
|
dotnet new lmd-plugin -n MyPlugin
|
||||||
```
|
```
|
||||||
|
|
||||||
- **Plugin SDK**: `LanMountainDesktop.PluginSdk` (API 4.0.1)
|
- **Plugin SDK**: `LanMountainDesktop.PluginSdk` (API 5.0.0)
|
||||||
- **共享契约**: `LanMountainDesktop.Shared.Contracts`
|
- **共享契约**: `LanMountainDesktop.Shared.Contracts`
|
||||||
- **迁移指南**: [PLUGIN_SDK_V4_MIGRATION.md](docs/PLUGIN_SDK_V4_MIGRATION.md)
|
- **迁移指南**: [PLUGIN_SDK_V5_MIGRATION.md](docs/PLUGIN_SDK_V5_MIGRATION.md)
|
||||||
|
|
||||||
## 项目结构
|
## 项目结构
|
||||||
|
|
||||||
|
|||||||
@@ -63,11 +63,11 @@ MyAwesomePlugin/
|
|||||||
|
|
||||||
### 插件 SDK 版本
|
### 插件 SDK 版本
|
||||||
|
|
||||||
当前 SDK 版本: **4.0.1**
|
当前 SDK 版本: **5.0.0**
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<PackageReference Include="LanMountainDesktop.PluginSdk" Version="4.0.1" />
|
<PackageReference Include="LanMountainDesktop.PluginSdk" Version="5.0.0" />
|
||||||
<PackageReference Include="LanMountainDesktop.Shared.Contracts" Version="4.0.1" />
|
<PackageReference Include="LanMountainDesktop.Shared.Contracts" Version="5.0.0" />
|
||||||
```
|
```
|
||||||
|
|
||||||
### 插件清单 (plugin.json)
|
### 插件清单 (plugin.json)
|
||||||
@@ -175,9 +175,9 @@ public class Plugin : IPlugin
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="LanMountainDesktop.PluginSdk" Version="4.0.1" />
|
<PackageReference Include="LanMountainDesktop.PluginSdk" Version="5.0.0" />
|
||||||
<PackageReference Include="LanMountainDesktop.Shared.Contracts" Version="4.0.1" />
|
<PackageReference Include="LanMountainDesktop.Shared.Contracts" Version="5.0.0" />
|
||||||
<PackageReference Include="Avalonia" Version="11.3.12" />
|
<PackageReference Include="Avalonia" Version="12.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- 复制 plugin.json 到输出目录 -->
|
<!-- 复制 plugin.json 到输出目录 -->
|
||||||
@@ -680,7 +680,7 @@ if (!url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
|
|||||||
|
|
||||||
## 相关文档
|
## 相关文档
|
||||||
|
|
||||||
- [Plugin SDK v4 迁移指南](PLUGIN_SDK_V4_MIGRATION.md)
|
- [Plugin SDK v5 迁移指南](PLUGIN_SDK_V5_MIGRATION.md)
|
||||||
- [组件开发指南](COMPONENT_DEVELOPMENT.md)
|
- [组件开发指南](COMPONENT_DEVELOPMENT.md)
|
||||||
- [API 参考](API_REFERENCE.md)
|
- [API 参考](API_REFERENCE.md)
|
||||||
- [架构文档](ARCHITECTURE.md)
|
- [架构文档](ARCHITECTURE.md)
|
||||||
|
|||||||
34
docs/PLUGIN_SDK_V5_MIGRATION.md
Normal file
34
docs/PLUGIN_SDK_V5_MIGRATION.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Plugin SDK v5 Migration Guide
|
||||||
|
|
||||||
|
Plugin SDK v5 is the Avalonia 12 compatibility baseline for LanMountainDesktop plugins.
|
||||||
|
|
||||||
|
## What Changed
|
||||||
|
|
||||||
|
- Rebuild plugins against `LanMountainDesktop.PluginSdk` `5.0.0`.
|
||||||
|
- Set `plugin.json` `apiVersion` to `5.0.0`.
|
||||||
|
- Target `net10.0` and use Avalonia `12.0.1` compatible UI dependencies.
|
||||||
|
- Use `FluentAvaloniaUI` `3.0.0-preview1` and `FluentIcons.Avalonia` `2.1.325` when a plugin directly references those packages.
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
SDK v5 is a binary breaking change because the SDK exposes Avalonia UI types such as `Control`, `UserControl`, and `SettingsPageBase`. Plugins built for SDK v4 must be rebuilt and republished for SDK v5.
|
||||||
|
|
||||||
|
The host does not provide an Avalonia 11 / Avalonia 12 dual UI stack. The public extension entry points remain the same: custom settings pages still derive from `SettingsPageBase`, and desktop components still provide Avalonia controls through the existing registration APIs.
|
||||||
|
|
||||||
|
## Minimal Package Update
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="LanMountainDesktop.PluginSdk" Version="5.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"apiVersion": "5.0.0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
After updating package versions and rebuilding the plugin, verify that the generated `.laapp` contains the rebuilt assembly, `plugin.json`, and `.deps.json` next to the plugin entry assembly.
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
### 当前阶段
|
### 当前阶段
|
||||||
|
|
||||||
- 产品版本:`1.0.0`
|
- 产品版本:`1.0.0`
|
||||||
- Plugin SDK API 基线:`4.0.1`
|
- Plugin SDK API 基线:`5.0.0`
|
||||||
- 当前重点:持续完善宿主体验、设置页体验、组件能力与插件生态
|
- 当前重点:持续完善宿主体验、设置页体验、组件能力与插件生态
|
||||||
- 近期需求入口:以 `.trae/specs/` 中的 feature spec 为准
|
- 近期需求入口:以 `.trae/specs/` 中的 feature spec 为准
|
||||||
|
|
||||||
@@ -59,4 +59,4 @@
|
|||||||
|
|
||||||
LanMountainDesktop is a cross-platform desktop enhancement product built with Avalonia UI and .NET 10. It targets students, office users, and customization-focused users who want a programmable desktop surface for information, tools, and plugin-driven extensions.
|
LanMountainDesktop is a cross-platform desktop enhancement product built with Avalonia UI and .NET 10. It targets students, office users, and customization-focused users who want a programmable desktop surface for information, tools, and plugin-driven extensions.
|
||||||
|
|
||||||
This repository is the source of truth for the desktop host, plugin runtime, Plugin SDK, shared contracts, and core appearance/settings infrastructure. The current product version is `1.0.0`, and the active Plugin SDK baseline in this repository is `4.0.1`.
|
This repository is the source of truth for the desktop host, plugin runtime, Plugin SDK, shared contracts, and core appearance/settings infrastructure. The current product version is `1.0.0`, and the active Plugin SDK baseline in this repository is `5.0.0`.
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ cd MyFirstPlugin
|
|||||||
"description": "这是一个测试插件",
|
"description": "这是一个测试插件",
|
||||||
"author": "你的名字",
|
"author": "你的名字",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"apiVersion": "4.0.1",
|
"apiVersion": "5.0.0",
|
||||||
"entranceAssembly": "MyFirstPlugin.dll",
|
"entranceAssembly": "MyFirstPlugin.dll",
|
||||||
"sharedContracts": []
|
"sharedContracts": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ MyPlugin/
|
|||||||
"description": "这是一个示例插件",
|
"description": "这是一个示例插件",
|
||||||
"author": "作者名称",
|
"author": "作者名称",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"apiVersion": "4.0.1",
|
"apiVersion": "5.0.0",
|
||||||
"entranceAssembly": "MyPlugin.dll",
|
"entranceAssembly": "MyPlugin.dll",
|
||||||
"sharedContracts": [],
|
"sharedContracts": [],
|
||||||
"website": "https://example.com",
|
"website": "https://example.com",
|
||||||
@@ -53,7 +53,7 @@ MyPlugin/
|
|||||||
| `description` | ✅ | 简短描述 | `显示实时天气信息` |
|
| `description` | ✅ | 简短描述 | `显示实时天气信息` |
|
||||||
| `author` | ✅ | 作者名称 | `张三` |
|
| `author` | ✅ | 作者名称 | `张三` |
|
||||||
| `version` | ✅ | 版本号(语义化版本) | `1.0.0` |
|
| `version` | ✅ | 版本号(语义化版本) | `1.0.0` |
|
||||||
| `apiVersion` | ✅ | SDK API 版本 | `4.0.1` |
|
| `apiVersion` | ✅ | SDK API 版本 | `5.0.0` |
|
||||||
| `entranceAssembly` | ✅ | 入口程序集文件名 | `MyPlugin.dll` |
|
| `entranceAssembly` | ✅ | 入口程序集文件名 | `MyPlugin.dll` |
|
||||||
| `sharedContracts` | ✅ | 共享契约类型列表 | `[]` |
|
| `sharedContracts` | ✅ | 共享契约类型列表 | `[]` |
|
||||||
| `website` | ❌ | 项目网站 | `https://github.com/...` |
|
| `website` | ❌ | 项目网站 | `https://github.com/...` |
|
||||||
@@ -74,7 +74,7 @@ MyPlugin/
|
|||||||
|
|
||||||
⚠️ **apiVersion 字段规则:**
|
⚠️ **apiVersion 字段规则:**
|
||||||
- 必须与引用的 SDK 版本兼容
|
- 必须与引用的 SDK 版本兼容
|
||||||
- 当前最新版本:`4.0.1`
|
- 当前最新版本:`5.0.0`
|
||||||
- 不兼容时宿主将拒绝加载插件
|
- 不兼容时宿主将拒绝加载插件
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -96,7 +96,7 @@ MyPlugin/
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="LanMountainDesktop.PluginSdk" Version="4.0.1" />
|
<PackageReference Include="LanMountainDesktop.PluginSdk" Version="5.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -122,7 +122,7 @@ MyPlugin/
|
|||||||
### SDK 引用
|
### SDK 引用
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<PackageReference Include="LanMountainDesktop.PluginSdk" Version="4.0.1" />
|
<PackageReference Include="LanMountainDesktop.PluginSdk" Version="5.0.0" />
|
||||||
```
|
```
|
||||||
|
|
||||||
⚠️ **版本必须匹配:**
|
⚠️ **版本必须匹配:**
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ public override void Initialize(HostBuilderContext context, IServiceCollection s
|
|||||||
- 关闭阑山桌面
|
- 关闭阑山桌面
|
||||||
|
|
||||||
**当前限制:**
|
**当前限制:**
|
||||||
- SDK v4 暂无显式的卸载回调方法
|
- SDK v5 暂无显式的卸载回调方法
|
||||||
- 资源释放依赖 .NET 垃圾回收
|
- 资源释放依赖 .NET 垃圾回收
|
||||||
- 建议:
|
- 建议:
|
||||||
- 使用 `IDisposable` 模式管理资源
|
- 使用 `IDisposable` 模式管理资源
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ MyPlugin.laapp
|
|||||||
"description": "插件描述",
|
"description": "插件描述",
|
||||||
"author": "作者名",
|
"author": "作者名",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"apiVersion": "4.0.1",
|
"apiVersion": "5.0.0",
|
||||||
"entranceAssembly": "MyPlugin.dll",
|
"entranceAssembly": "MyPlugin.dll",
|
||||||
"sharedContracts": []
|
"sharedContracts": []
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ MyPlugin.laapp
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="LanMountainDesktop.PluginSdk" Version="4.0.1" />
|
<PackageReference Include="LanMountainDesktop.PluginSdk" Version="5.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- 确保资源文件复制到输出目录 -->
|
<!-- 确保资源文件复制到输出目录 -->
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
| 视觉规范 | `docs/VISUAL_SPEC.md` | 颜色、语义资源、玻璃层级 |
|
| 视觉规范 | `docs/VISUAL_SPEC.md` | 颜色、语义资源、玻璃层级 |
|
||||||
| 圆角规范 | `docs/CORNER_RADIUS_SPEC.md` | 圆角层级与动态规则 |
|
| 圆角规范 | `docs/CORNER_RADIUS_SPEC.md` | 圆角层级与动态规则 |
|
||||||
| 插件生态边界 | `docs/ECOSYSTEM_BOUNDARIES.md` | 仓库边界和 market 所属 |
|
| 插件生态边界 | `docs/ECOSYSTEM_BOUNDARIES.md` | 仓库边界和 market 所属 |
|
||||||
| SDK v4 迁移 | `docs/PLUGIN_SDK_V4_MIGRATION.md` | Plugin SDK breaking changes |
|
| SDK v5 迁移 | `docs/PLUGIN_SDK_V5_MIGRATION.md` | Plugin SDK breaking changes |
|
||||||
|
|
||||||
## 已废弃来源
|
## 已废弃来源
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user