# 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\\` 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` ```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\`. ## Building the sample ```bash cd samples/ClockOverlayPlugin dotnet build -c Release # bin/Release/net8.0-windows/{ClockOverlayPlugin.dll, plugin.json} ``` The sample's csproj sets `false` 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.