HomeFeaturesPluginsDocsDownload

Plugin capabilities

Optional interfaces any plugin may add — provider or tool. The host detects each with a type check at load, so a plugin opts in simply by implementing the interface. Nothing changes in the manifest.

Persistent settings — declared

Plugin-wide values the user sets once (a path to an external binary, a default folder) that apply to every use of the plugin. Declare the fields; the host renders the form in Settings › Plugins and persists the values keyed by plugin id.

public sealed class MyTool : IToolPlugin, IPluginSettings { public IReadOnlyList<PluginSettingField> SettingsFields { get; } = [ new("binaryPath", "Executable path", PluginSettingFieldType.File, Group: "Paths"), new("logLevel", "Log level", PluginSettingFieldType.Choice, Default: "info", Choices: ["debug", "info", "warn", "error"], Group: "Behaviour"), ]; }

PluginSettingFieldType is Text | Bool | Choice | File | Folder; Group sections the pane under headers. At run time a tool reads a value with context.Host.GetPluginSetting("binaryPath").

Persistent settings — your own view

public interface ICustomPluginSettingsUi { Control CreateSettingsView(IPluginSettingsContext context); // read/write by key }

Values still flow through GetValue/SetValue, so the host persists them the same way either way. A plugin may implement both routes; the host prefers the custom view when present.

Keyboard shortcuts

Register global shortcuts that appear in Settings › Keyboard under your plugin's own section, where the user can rebind or clear them. They share the host's live conflict detection, persistence and rebinding with the built-in shortcuts.

public sealed record ShortcutContribution( string Id, // unique within the plugin; host namespaces it as pluginId:Id string Title, // label in the shortcut list string? DefaultGesture, // Avalonia gesture syntax; null = ships unbound Func<CancellationToken, Task> ExecuteAsync);
  • The Mod token maps to the platform's primary modifier — Cmd on macOS, Ctrl elsewhere — so "Mod+Shift+B" ships as ⌘⇧B on a Mac and Ctrl+Shift+B otherwise. Use plain Ctrl when you deliberately want the same key everywhere.
  • Ids are namespaced by the host, so two plugins can use the same local id. Keep yours stable — it is persisted.
  • The callback runs on the UI thread: capture what you need when you build the contribution, and offload heavy work yourself.

Referencing Avalonia for a custom view

Custom views return an Avalonia Control. Add Avalonia so the plugin compiles, but keep the host's copy authoritative across the ALC boundary:

<PackageReference Include="Avalonia" Version="12.0.5" ExcludeAssets="runtime" />

A plugin that only uses declared fields needs no Avalonia reference at all.

Writing the view as an .axaml UserControl works exactly like building it in C#: the XAML compiler turns it into a call that constructs the same control tree at compile time, resolved against that same ExcludeAssets="runtime" reference — no extra ALC risk.

Theming a custom view

The host applies its theme through Avalonia's ThemeVariant system, and your control is hosted inside the app's visual tree, so it inherits fonts and colours without doing anything. Standard controls already look right.

To match host chrome deliberately, reference the published theme brushes with DynamicResource — not StaticResource, or your control won't follow a live light/dark switch.

KeyUse
SEWindowBgBrush / SEPanelBgBrush / SESecondaryBgBrush / SEToolbarBgBrushSurface backgrounds
SEHairlineBrush / SEHoverBgBrush / SESelectionBgBrushBorders and interactive states
SETextPrimaryBrush / SETextSecondaryBrush / SETextFaintBrushText
SEAccentBrush / SEAccentHoverBrush / SEAccentPressedBrush / SEAccentFgBrushAccent colour and its states
SEStatusConnectedBrush / SEStatusErrorBrush / SEStatusBusyBrush / SEStatusWaitingBrushStatus indicators
SEControlRadius / SEPanelRadius / SEHairlineThickness / SEMonoFontCorner radius, border thickness, monospace font
These keys are a public, stable contract — safe to depend on across host versions. Anything not in this table is a host implementation detail and may change without notice; don't reference it from a plugin.