Files
LiveWall/docs/plugins/packaging.md
T
2026-11-04 14:29:22 -06:00

5.5 KiB

Packaging, versioning and distribution

Folder layout

%APPDATA%\LiveWall\plugins\
└── com.example.clock\              ← folder name is free-form; the id comes from plugin.json
    ├── plugin.json                 ← required
    ├── ClockOverlayPlugin.dll      ← the entry assembly ("entry")
    ├── Newtonsoft.Json.dll         ← optional private dependencies
    └── assets\...                  ← optional data

%APPDATA%\LiveWall\plugin-data\<id>\ is yours to write to at run time (IPluginStorage).

The plugin folder is opened by Plugins ▸ Open plugin folder; a starter manifest is written by Create plugin template (into plugins\com.example.myplugin\).

plugin.json

{
  "id": "com.example.clock",
  "name": "Desktop Clock",
  "version": "1.0.0",
  "author": "You",
  "description": "A clock on the wallpaper.",
  "homepage": "https://example.com/clock",
  "apiVersion": "1.0",
  "entry": "ClockOverlayPlugin.dll",
  "type": "ClockOverlayPlugin.ClockPlugin",
  "minHostVersion": "1.8.0",
  "permissions": ["DesktopOverlay", "SettingsUi", "Commands"],
  "checksum": "",
  "loadPriority": 0
}
Field Required Notes
id yes Reverse-DNS-ish, unique, stable. Used for data folder, trust store and log prefix. No \ / : * ? " < > |.
name yes Shown in the UI. Falls back to the folder name.
version yes Informational (1.2.3).
author recommended Shown in the warning dialog.
description recommended One sentence.
homepage no Where the user can see the code.
apiVersion yes Must be "1.0" for this SDK. Major must match; 1, 1.0, 1.0.0 are accepted.
entry yes File name (or relative path) of the assembly inside the plugin folder.
type yes Full name of the public class implementing ILiveWallPlugin.
minHostVersion recommended Refuses to load on older LiveWall instead of failing at TypeLoadException.
permissions yes (may be empty) Array of PluginPermission names, or a single flag string. Unknown names are listed in the warning dialog as "asks for things this LiveWall does not know".
checksum optional SHA-256 (hex, case-insensitive) of the entry assembly. When set, a mismatch rejects the plugin.
loadPriority optional Higher loads earlier (default 0). Use it when plugin B expects plugin A's event to already be published.

Manifest mistakes are reported in the row (Invalid manifest: …) and in the log — nothing is guessed.

What can live in the folder

Everything in the plugin folder is added as a private assembly-probing path, so your own dependencies are found without touching LiveWall's:

  • Private libraries: copy them next to your DLL (normal dotnet build output).
  • Do not ship LiveWall.Sdk.dll, LiveWall.dll, WPF runtime assemblies, or the .NET runtime — they come from the host.
  • Native DLLs: AssemblyLoadContext probing covers managed assemblies only. Use NativeLibrary.Load(context.Storage.PathFor("your.dll")) with a copy in your data folder if you need one.
  • WPF controls with XAML resources: pack URIs of a referenced library work; XAML inside your own assembly works (/YourAssembly;component/...).

Versioning

Two independent numbers:

  • LiveWall plugin API — PluginApi.Version, currently 1.0. This is what plugin.json declares. It changes only when a contract changes incompatibly. New capabilities are added as new optional interfaces (e.g. a future IWallpaperSourceProvider), which keeps existing plugins compiling and loading unchanged.
  • LiveWall host version — 1.8.0 today. minHostVersion is compared numerically (1.6.10 is newer than 1.6.9), so you can safely require a bug fix without pinning a build.

Compatibility check on load, in order:

  1. apiVersion major ≠ host API major → Incompatible, with the two versions in the message.
  2. minHostVersion newer than the running host → Incompatible.
  3. checksum present and different → Rejected (ChecksumMismatch).
  4. Entry/type missing or not implementing ILiveWallPlugin → Failed.
  5. Exception in Initialize → Failed.

Distributing a plugin

There is no plugin store; distribution is a folder. The simplest safe pattern:

  1. Ship a zip with exactly the plugin folder inside (com.example.clock\...).
  2. Publish the SHA-256 of your DLL in the release notes.
  3. Tell users: unpack into %APPDATA%\LiveWall\plugins\, then Plugins ▸ Rescan ▸ Enable, and compare the hash the warning dialog shows against your published one.

Putting the same value in plugin.json's checksum makes the app verify it automatically and turns a corrupt download into a clear error instead of a mysterious load failure.

Updating a plugin

Replace the files, press Rescan. The old load context is unloaded first. Because the DLL has changed, the plugin becomes unverified and the user must approve the new hash — this is deliberate and cannot be bypassed by the plugin itself.

Uninstalling

Disable the plugin, delete its folder, and optionally delete %APPDATA%\LiveWall\plugin-data\<id>.

Building the sample

cd samples/ClockOverlayPlugin
dotnet build -c Release
# bin/Release/net8.0-windows/{ClockOverlayPlugin.dll, plugin.json}

The sample's csproj sets <Private>false</Private> on the SDK reference and CopyLocalLockFileAssemblies=false, so its output contains only the plugin assembly — exactly what you want to copy into the plugins folder.