Extension SDK
Shipping components and commands as loadable packages
Extensions let you add components to the editor without rebuilding it. An extension is a class library that implements
ISkiaExtension; the host discovers it, registers its components in the palette, and exposes its commands.
Table of Contents
The Contract
A minimal extension
public sealed class GaugeExtension : ISkiaExtension
{
public string Id => "acme.gauge";
public string Name => "Acme Gauge";
public string Version => "1.2.0";
public string Description => "Gauge and dial components";
public IEnumerable<Type> GetComponentTypes()
{
yield return typeof(GaugeNode); // any concrete SkiaComponent
}
public void Initialize(ISkiaExtensionContext context)
{
context.RegisterComponent(typeof(StatusLampNode), category: "Acme", displayName: "Status Lamp");
context.RegisterCommand("acme.reset", target => (target as GaugeNode)?.Reset());
context.Log("Acme gauge ready");
}
}
The host validates what an extension registers: types must be concrete SkiaComponent subclasses, and
anything invalid is skipped with a diagnostic instead of breaking loading.
Loading Extensions
SkiaExtensionHost loads extensions from assemblies or from a folder, and reports what it found:
var host = new SkiaExtensionHost();
host.LoadFromDirectory(Path.Combine(AppContext.BaseDirectory, "Extensions"));
foreach (var extension in host.Extensions)
Console.WriteLine($"{extension.Name} {extension.Version} ok={extension.Success}");
foreach (var component in host.Components)
Console.WriteLine($"palette entry: {component.DisplayName} ({component.Category})");
- Extensions are keyed by
Id; loading the same extension twice is ignored. - An extension whose constructor or
Initializethrows is recorded with its errors — other extensions still load. - Every action is written to
host.Logfor diagnostics.
Palette Integration
The WinForms host scans the extension folder at startup and adds each component to the palette under its declared category, so extension components behave exactly like built-in ones: they can be dragged onto the canvas, selected, configured in the property grid, and saved with the diagram.
Commands
Extensions can also contribute commands. The host invokes them with the current selection, which makes it easy to add context-menu style behaviour (reset, refresh, import) that only applies to the extension's own components.
host.InvokeCommand("acme.reset", selectedComponent);
Related
- Extension Marketplace — package, install and update extensions
- Extensibility Guide — other extension points
Extension Lifecycle
The host drives every extension through the same sequence:
- Discover — assemblies are scanned for types implementing
ISkiaExtension. - Instantiate — each extension type is created with its parameterless constructor.
- Initialize —
Initialize(context)runs once; register components, commands and diagnostics here. - Register —
RegisterComponentadds the type to the registry with an optional palette category and display name;RegisterCommandadds a named action. - Create — components are created on demand through the registry or
SkiaExtensionHost.CreateComponent. - Clear —
Clear()drops the loaded extensions and their registrations (host shutdown or tests).
| Host member | Description |
|---|---|
LoadFromAssemblies(assemblies) | Loads every extension found in the given assemblies; returns how many were loaded. |
LoadFromDirectory(directory, pattern) | Scans a folder for plugin assemblies (default *.dll). |
CreateComponent(assemblyQualifiedName) | Creates an instance of a contributed component type. |
InvokeCommand(name, target) | Runs a command registered by an extension against the current selection. |
Clear() | Unloads extensions and clears registrations. |
Packaging and Distribution
Extensions ship as .beepkg packages and are installed through the marketplace, which tracks versions and provenance:
var packages = new ExtensionPackageManager(extensionsRoot);
var result = packages.Install(@"C:\downloads\acme.pnid-1.0.0.beepkg");
if (!result.Success)
Console.WriteLine(result.Error);
var host = new SkiaExtensionHost();
foreach (var dir in packages.GetLoadDirectories())
host.LoadFromDirectory(dir);
// Later: upgrade or remove
packages.Update("com.acme.beep.pnid");
packages.Uninstall("com.acme.beep.pnid");
Installation validates the archive (path traversal and decompression-bomb protection), package version compatibility and declared dependencies before anything is copied. See the Extension Marketplace page for the package format.
Extension Checklist
- Stable reverse-DNS
Id, semanticVersion, meaningfulNameandDescription. - Every component type derives from
SkiaComponentand has a public parameterless constructor. GetComponentTypes()andRegisterComponentagree — the first is what hosts scan, the second is what the palette shows.- Component class names never change after release; saved diagrams resolve types by name.
- Commands are idempotent and safe to call with an empty selection.
- Use
context.Logfor diagnostics instead of writing to the console.