5.6 KiB
Plugin SDK v4 Migration Guide
This guide describes the breaking changes introduced by Plugin SDK 4.0.0.
Version Baseline
- Host plugin SDK baseline:
4.0.0 - Plugins targeting
3.xare rejected by default - Manifest file remains
plugin.json
Breaking Changes
AddPluginDesktopComponentnow uses options-first registration.PluginDesktopComponentOptionsis now the canonical component registration shape and must includeComponentId.- Appearance and radius access are provided through strongly typed APIs:
IPluginAppearanceContextPluginAppearanceSnapshotPluginCornerRadiusTokensPluginCornerRadiusPreset
PluginDesktopComponentContextnow exposesAppearanceas the primary appearance access point.
New Component Registration Pattern
services.AddPluginDesktopComponent<MyWidget>(new PluginDesktopComponentOptions
{
ComponentId = "YourPlugin.Widget",
DisplayName = "My Widget",
IconKey = "PuzzlePiece",
Category = "Plugins",
MinWidthCells = 4,
MinHeightCells = 3,
CornerRadiusPreset = PluginCornerRadiusPreset.Default
});
Appearance Usage Pattern
public MyWidget(PluginDesktopComponentContext context)
{
var mdRadius = context.Appearance.ResolveCornerRadius(PluginCornerRadiusPreset.Md);
var adaptiveRadius = context.Appearance.ResolveScaledCornerRadius(12, 8, 20);
}
Corner Radius System
Plugin widgets must follow the host's corner radius settings to maintain visual consistency with built-in components.
Why Plugins Cannot Use XAML Resources
Plugins run in a separate AssemblyLoadContext and cannot directly access the host's resource dictionary. Therefore, {DynamicResource DesignCornerRadiusComponent} is not available in plugin XAML. Instead, plugins must resolve corner radius values in code through PluginDesktopComponentContext.
Available Corner Radius Presets
| Preset | Default Value | Usage |
|---|---|---|
Micro |
6px | Tiny elements |
Xs |
12px | Small elements and icon containers |
Sm |
14px | Small colored blocks |
Md |
20px | Common buttons/cards |
Lg |
28px | Normal glass panels |
Xl |
32px | Emphasized containers |
Island |
36px | Large containers |
Component |
18px | Desktop widget standard radius |
Default |
(adaptive) | Adaptive based on cell size |
Corner Radius API Reference
public class MyWidget : Border
{
public MyWidget(PluginDesktopComponentContext context)
{
// Method 1: Use preset tokens (recommended for consistency)
CornerRadius = context.ResolveCornerRadius(PluginCornerRadiusPreset.Component);
// Method 2: Use preset with fallback (extension method)
CornerRadius = context.Appearance.Snapshot.ResolveCornerRadius(
PluginCornerRadiusPreset.Md,
fallback: new CornerRadius(8));
// Method 3: Custom radius with global scale applied
CornerRadius = context.ResolveScaledCornerRadius(baseRadius: 16, minimum: 8, maximum: 24);
// Method 4: Access tokens directly
var tokens = context.CornerRadiusTokens;
CornerRadius = tokens.ToCornerRadius(PluginCornerRadiusPreset.Md);
// Method 5: Get raw token value (double)
double componentRadius = context.CornerRadiusTokens.Component;
}
}
Best Practices
-
Always use
PluginCornerRadiusPreset.Componentfor the widget root container - This ensures consistency with built-in widgets. -
Apply corner radius in code, not XAML - Since plugins cannot access host resources, set
CornerRadiusin the constructor or code-behind. -
Re-apply radius on size changes - For adaptive layouts, subscribe to
SizeChangedand recalculate:
public MyWidget(PluginDesktopComponentContext context)
{
_context = context;
ApplyCornerRadius();
SizeChanged += (_, _) => ApplyCornerRadius();
}
private void ApplyCornerRadius()
{
var basis = Math.Min(Bounds.Width, Bounds.Height);
CornerRadius = _context.ResolveCornerRadius(
PluginCornerRadiusPreset.Component,
minimum: Math.Clamp(basis * 0.08, 8, 16),
maximum: Math.Clamp(basis * 0.15, 16, 28));
}
- Inner elements can use smaller presets - For cards or buttons inside your widget:
var innerCard = new Border
{
CornerRadius = _context.ResolveCornerRadius(PluginCornerRadiusPreset.Md)
};
Manifest Update
Update plugin manifests to API 4.x:
{
"apiVersion": "4.0.0"
}
Validation Checklist
plugin.jsondeclaresapiVersion4.0.0(or compatible4.x)- component registration migrated to options model
- runtime appearance access uses
IPluginAppearanceContext - plugin package rebuilt and republished as
.laapp
Process Isolation Additions
SDK 4.x now also reserves manifest and API surface for process isolation without breaking existing plugins.
Manifest
plugin.json can declare the desired runtime mode:
{
"runtime": {
"mode": "in-proc"
}
}
Supported values:
in-procisolated-backgroundisolated-window
If runtime is omitted, the host normalizes it to in-proc for backward compatibility.
Worker Entry
Plugins that opt into isolated execution can prepare a worker-side entry by implementing:
IPluginWorkerPluginWorkerBaseIPluginWorkerContext[PluginWorkerEntrance]
The first phase only targets isolated-background: background services, timers, network calls, and risky native integrations move into the worker process, while UI remains a host-side shell driven over IPC.