initial commit
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user