mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-22 09:14:25 +08:00
0.7.1
This commit is contained in:
@@ -156,7 +156,7 @@ public sealed class PluginLoader
|
||||
var pluginType = ResolvePluginType(assembly);
|
||||
plugin = CreatePluginInstance(pluginType);
|
||||
AppLogger.Info("PluginLoader", $"Plugin instance created. PluginId='{manifest.Id}'; PluginType='{pluginType.FullName}'.");
|
||||
runtimeContext = CreateRuntimeContext(manifest, pluginDirectory, dataDirectory, properties);
|
||||
runtimeContext = CreateRuntimeContext(manifest, pluginDirectory, dataDirectory, properties, services);
|
||||
var serviceCollection = CreateServiceCollection(runtimeContext, services);
|
||||
var hostBuilderContext = CreateHostBuilderContext(runtimeContext);
|
||||
|
||||
@@ -297,13 +297,15 @@ public sealed class PluginLoader
|
||||
PluginManifest manifest,
|
||||
string pluginDirectory,
|
||||
string dataDirectory,
|
||||
IReadOnlyDictionary<string, object?>? properties)
|
||||
IReadOnlyDictionary<string, object?>? properties,
|
||||
IServiceProvider? hostServices)
|
||||
{
|
||||
return new PluginRuntimeContext(
|
||||
manifest,
|
||||
pluginDirectory,
|
||||
dataDirectory,
|
||||
CreateReadOnlyProperties(properties));
|
||||
CreateReadOnlyProperties(properties),
|
||||
BuildAppearanceSnapshot(hostServices));
|
||||
}
|
||||
|
||||
private ServiceCollection CreateServiceCollection(
|
||||
@@ -313,6 +315,7 @@ public sealed class PluginLoader
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton(runtimeContext);
|
||||
services.AddSingleton<IPluginRuntimeContext>(runtimeContext);
|
||||
services.AddSingleton<IPluginAppearanceContext>(runtimeContext.Appearance);
|
||||
services.AddSingleton(runtimeContext.Manifest);
|
||||
services.AddSingleton<IReadOnlyDictionary<string, object?>>(runtimeContext.Properties);
|
||||
services.AddSingleton<IPluginMessageBus, PluginMessageBus>();
|
||||
@@ -332,6 +335,33 @@ public sealed class PluginLoader
|
||||
return services;
|
||||
}
|
||||
|
||||
private static PluginAppearanceSnapshot BuildAppearanceSnapshot(IServiceProvider? hostServices)
|
||||
{
|
||||
var defaultSnapshot = new PluginAppearanceSnapshot(
|
||||
GlobalCornerRadiusScale: 1d,
|
||||
CornerRadiusTokens: new PluginCornerRadiusTokens(6, 10, 14, 18, 24, 30, 36),
|
||||
ThemeVariant: "Unknown");
|
||||
|
||||
if (hostServices?.GetService(typeof(IAppearanceThemeService)) is not IAppearanceThemeService appearanceThemeService)
|
||||
{
|
||||
return defaultSnapshot;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var hostSnapshot = appearanceThemeService.GetCurrent();
|
||||
return new PluginAppearanceSnapshot(
|
||||
GlobalCornerRadiusScale: Math.Max(0d, hostSnapshot.GlobalCornerRadiusScale),
|
||||
CornerRadiusTokens: PluginCornerRadiusTokens.FromShared(hostSnapshot.CornerRadiusTokens),
|
||||
ThemeVariant: hostSnapshot.IsNightMode ? "Dark" : "Light");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppLogger.Warn("PluginLoader", "Failed to resolve host appearance snapshot for plugin runtime context.", ex);
|
||||
return defaultSnapshot;
|
||||
}
|
||||
}
|
||||
|
||||
private static void RegisterHostService<TService>(IServiceCollection services, IServiceProvider? hostServices)
|
||||
where TService : class
|
||||
{
|
||||
@@ -730,12 +760,14 @@ public sealed class PluginLoader
|
||||
PluginManifest manifest,
|
||||
string pluginDirectory,
|
||||
string dataDirectory,
|
||||
IReadOnlyDictionary<string, object?> properties)
|
||||
IReadOnlyDictionary<string, object?> properties,
|
||||
PluginAppearanceSnapshot appearanceSnapshot)
|
||||
{
|
||||
Manifest = manifest;
|
||||
PluginDirectory = pluginDirectory;
|
||||
DataDirectory = dataDirectory;
|
||||
Properties = properties;
|
||||
Appearance = new PluginAppearanceContext(appearanceSnapshot);
|
||||
Services = NullServiceProvider.Instance;
|
||||
}
|
||||
|
||||
@@ -749,6 +781,8 @@ public sealed class PluginLoader
|
||||
|
||||
public IReadOnlyDictionary<string, object?> Properties { get; }
|
||||
|
||||
public IPluginAppearanceContext Appearance { get; }
|
||||
|
||||
public T? GetService<T>()
|
||||
{
|
||||
return (T?)Services.GetService(typeof(T));
|
||||
|
||||
@@ -90,16 +90,30 @@ internal static class AirAppMarketDefaults
|
||||
private static string? TryResolveWorkspacePath(string repositoryName, string relativePath)
|
||||
{
|
||||
var current = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
while (current is not null)
|
||||
while (current is not null && current.Exists)
|
||||
{
|
||||
var candidate = Path.Combine(current.FullName, repositoryName);
|
||||
if (Directory.Exists(candidate))
|
||||
var solutionPath = Path.Combine(current.FullName, "LanMountainDesktop.slnx");
|
||||
if (File.Exists(solutionPath))
|
||||
{
|
||||
var candidatePath = Path.GetFullPath(Path.Combine(candidate, relativePath));
|
||||
var workspaceRoot = current.Parent;
|
||||
if (workspaceRoot is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var candidateRepositoryPath = Path.Combine(workspaceRoot.FullName, repositoryName);
|
||||
if (!Directory.Exists(candidateRepositoryPath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var candidatePath = Path.GetFullPath(Path.Combine(candidateRepositoryPath, relativePath));
|
||||
if (File.Exists(candidatePath))
|
||||
{
|
||||
return candidatePath;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
current = current.Parent;
|
||||
|
||||
@@ -1,53 +1,26 @@
|
||||
# 宿主侧插件运行时 / Host Plugin Runtime
|
||||
|
||||
## 中文
|
||||
|
||||
本目录保存阑山桌面宿主侧插件运行时实现。
|
||||
|
||||
### 主要职责
|
||||
|
||||
- 发现、安装和替换 `.laapp` 插件包
|
||||
- 加载插件程序集和共享契约
|
||||
- 接入插件设置页、桌面组件与市场界面
|
||||
- 为 `3.0.0` API 基线插件构建插件作用域的 `IServiceCollection` / `ServiceProvider`
|
||||
- 在激活前解析共享契约缓存,并暴露显式插件导出
|
||||
|
||||
### 与 LanAirApp 的分工
|
||||
|
||||
- `LanAirApp` 负责官方市场索引、开发文档、校验工具和镜像样例
|
||||
- 本目录负责宿主运行时发现、安装、加载和界面接入
|
||||
- 权威示例插件是独立仓库 `LanMountainDesktop.SamplePlugin`,`LanAirApp` 中的样例目录只是镜像模板
|
||||
|
||||
### 市场安装顺序
|
||||
|
||||
1. 宿主读取官方 `LanAirApp/airappmarket/index.json`
|
||||
2. 若条目同时包含 `releaseTag` 与 `releaseAssetName`,优先解析 GitHub Release 资产
|
||||
3. 若 Release 解析失败,则回退到仓库根目录 `.laapp`
|
||||
4. 插件详情始终读取插件仓库根目录 `README.md`
|
||||
5. 市场安装为暂存安装,重启后生效
|
||||
|
||||
## English
|
||||
# Host Plugin Runtime
|
||||
|
||||
This directory contains the host-side plugin runtime for LanMountainDesktop.
|
||||
|
||||
### Responsibilities
|
||||
## Responsibilities
|
||||
|
||||
- discover, install, and replace `.laapp` packages
|
||||
- load plugin assemblies and shared contracts
|
||||
- integrate plugin settings pages, desktop components, and market UI
|
||||
- build a plugin-scoped `IServiceCollection` / `ServiceProvider` for API `3.0.0` plugins
|
||||
- resolve shared contract caches before activation and expose explicit plugin exports
|
||||
- Discover, install, replace, and stage `.laapp` plugin packages
|
||||
- Load plugin assemblies and shared contracts
|
||||
- Integrate plugin settings sections, desktop components, and market UI
|
||||
- Build plugin-scoped `IServiceCollection` / `ServiceProvider` for API `4.x` plugins
|
||||
- Resolve shared contracts before activation and expose explicit plugin exports
|
||||
|
||||
### Relationship with LanAirApp
|
||||
## Relationship with LanAirApp
|
||||
|
||||
- `LanAirApp` owns the official market index, developer docs, validation tools, and mirrored sample templates
|
||||
- this directory owns host-side discovery, installation, loading, and UI integration
|
||||
- the authoritative sample plugin lives in the standalone `LanMountainDesktop.SamplePlugin` repository; the `LanAirApp` sample directory is only a mirror/template copy
|
||||
- `LanAirApp` is a standalone repository and owns market metadata plus developer ecosystem materials
|
||||
- This host runtime only consumes market metadata and plugin packages
|
||||
- The host no longer maintains an embedded `LanAirApp/` mirror inside this repository
|
||||
- Workspace debugging resolves market files from sibling path `..\\LanAirApp\\...`
|
||||
|
||||
### Market install order
|
||||
## Market Install Flow
|
||||
|
||||
1. The host reads the official `LanAirApp/airappmarket/index.json`
|
||||
2. If an entry contains both `releaseTag` and `releaseAssetName`, the host first resolves the exact GitHub Release asset
|
||||
3. If Release resolution fails, the host falls back to the repository-root `.laapp`
|
||||
4. Plugin details always come from the plugin repository root `README.md`
|
||||
5. Market installs are staged and take effect after restart
|
||||
1. Host reads the official market index
|
||||
2. If both `releaseTag` and `releaseAssetName` are present, host resolves the exact GitHub Release asset first
|
||||
3. If release resolution fails, host falls back to repository-root `.laapp`
|
||||
4. Plugin detail text is read from plugin repository root `README.md`
|
||||
5. Installation is staged and becomes effective after restart
|
||||
|
||||
Reference in New Issue
Block a user