-
Notifications
You must be signed in to change notification settings - Fork 1
Packaging and Distribution
A LoupixDeck plugin is a regular .NET class library shipped as a folder
containing the plugin DLL, a plugin.json manifest, and any runtime
dependencies. The host reads plugin.json before loading the assembly —
it determines the plugin's identity, target OS, SDK compatibility and which
DLL to load. PluginMetadata (returned by your LoupixPlugin subclass) is
then read at runtime and must agree with the manifest.
One plugin.json per plugin folder, sitting next to the entry assembly:
{
"id": "myplugin",
"name": "My Plugin",
"version": "1.0.0",
"sdkVersion": "1.6",
"entryAssembly": "MyPlugin.dll",
"platform": "All"
}| Field | Notes |
|---|---|
id |
Stable, filesystem-safe identifier. Must match the folder name under the plugin root and PluginMetadata.Id. Required. |
name |
Display name shown in the host UI. |
version |
Plugin version (SemVer). Keep in sync with PluginMetadata.Version. |
sdkVersion |
SDK contract version you compiled against (SemVer). The host enforces a major-version match — a host on SDK 2.x refuses a plugin built against 1.x. |
entryAssembly |
File name of the DLL containing your LoupixPlugin subclass. Required. |
platform |
One of All, Windows, Linux. Plugins targeting a platform the current host is not running on are skipped silently. Defaults to All if omitted. |
The host fails the plugin if plugin.json is missing, malformed, or lacks
id / entryAssembly.
Make sure the manifest reaches the build output by adding to your .csproj:
<ItemGroup>
<None Update="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>Two versions matter, and they are different things:
| Field | What it is | Bump when |
|---|---|---|
PluginMetadata.Version (and plugin.json version) |
Your plugin's own version | You ship new behavior or bug fixes. |
PluginMetadata.SdkVersion (and plugin.json sdkVersion) |
The SDK contract version you compiled against | Always SdkInfo.Version — the SDK package handles it for you. |
The host enforces a major-version match on SdkVersion. Building against
SDK 1.x runs on any host shipped with SDK 1.x — across the entire 1.x line the
contracts are source- and binary-compatible. A host on SDK 2.x will refuse to
load a plugin built against 1.x (and vice versa).
SdkVersion = SdkInfo.Version, // always this — never hard-codeThe SDK's AssemblyVersion is intentionally pinned at 1.0.0.0 across the
entire 1.x package range so the plugin load context resolves one shared SDK
assembly regardless of which 1.x package the plugin was built against.
dotnet build -c ReleaseShip the contents of bin/Release/net9.0/ minus LoupixDeck.PluginSdk.dll:
-
MyPlugin.dll— your plugin -
plugin.json— the manifest the host reads before loading the assembly - Any third-party runtime dependencies (HttpClient libs, JSON serializers your plugin uses beyond the BCL, etc.)
- Optional
MyPlugin.pdbif you want symbols available for end-user crash reports (the SDK ships embedded PDBs already)
Do not redistribute
LoupixDeck.PluginSdk.dll. The host provides it. Bundling it causes assembly-load conflicts. The NuGet package reference already does the right thing — don't manually set<Private>true</Private>or copy the file by hand.
One folder per plugin, named after Metadata.Id, placed inside the host's
plugin root:
<plugin root>/
└── myplugin/ ← matches PluginMetadata.Id and plugin.json "id"
├── plugin.json
├── MyPlugin.dll
├── ThirdParty.Dep.dll
└── (your plugin's other runtime deps)
Per-plugin data the host creates at runtime sits next to your DLLs:
└── myplugin/
└── settings.json ← managed by the host (IPluginSettings)
See Debugging for the exact plugin-root path on each OS.
PluginMetadata.Icon is optional raw bytes (PNG recommended, SVG accepted).
Read the bytes from an embedded resource so you don't depend on the install
layout:
public override PluginMetadata Metadata { get; } = new()
{
Id = "myplugin", Name = "My Plugin",
Version = new Version(1, 0, 0), SdkVersion = SdkInfo.Version,
Icon = LoadEmbeddedIcon("MyPlugin.icon.png")
};
private static byte[] LoadEmbeddedIcon(string name)
{
using var s = typeof(MyPlugin).Assembly.GetManifestResourceStream(name)
?? throw new InvalidOperationException($"Missing resource: {name}");
using var ms = new MemoryStream();
s.CopyTo(ms);
return ms.ToArray();
}In the .csproj:
<ItemGroup>
<EmbeddedResource Include="icon.png" LogicalName="MyPlugin.icon.png" />
</ItemGroup>There is no central plugin registry yet. Typical release flow:
- Bump
PluginMetadata.Versionand the<Version>in the.csproj. -
dotnet build -c Release. - Zip the
bin/Release/net9.0/folder (without the SDK DLL). - Publish the zip as a GitHub Release on the plugin's own repo and tell users
to extract it into
<plugin root>/<id>/.
Getting started
API reference
Advanced
Operations
Release notes