124 lines
5.5 KiB
Markdown
124 lines
5.5 KiB
Markdown
# 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`
|
|
|
|
```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
|
|
|
|
```bash
|
|
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.
|