Zephyr Plugins
May 14, 2026 · View on GitHub
The curated registry of community plugins for Zephyr, plus the shared SDK and scaffolding plugin authors use to build them.
Zephyr fetches registry.json from this repository to populate its plugin browser. Each registry entry points to a folder under themes/, games/, mods/, or features/.
Repository layout
sdk/ @zephyr-plugin/sdk: Svelte components + host bridge
features/<plugin-id>/ manifest.json + Svelte app (built to dist/index.html)
themes/<plugin-id>/ manifest.json + theme.css
games/<plugin-id>/ manifest.json + game.json
mods/<plugin-id>/ manifest.json
registry.json generated by scripts/build-registry.mjs
scripts/ registry build + validate + scaffolding
Plugin types
| type | what it is | files it ships |
|---|---|---|
theme | A CSS file applied to Zephyr's UI. | theme.css |
feature | A full mini-app running in a sandboxed iframe with its own sidebar item. | dist/index.html (built from Svelte) |
game | A new game definition Zephyr can target. | game.json |
mod | A downloadable mod archive for a registered game. | manifest only |
Themes are static. Features are real apps: you write Svelte 5, build to a single HTML file, and Zephyr loads it in an iframe with a postMessage bridge for host capabilities (file storage, screen capture, notifications, etc.).
Quickstart: build a feature plugin
pnpm install # workspace install (gets the SDK)
pnpm new-plugin # interactive wizard, pick "Feature" or "Theme"
cd features/<your-slug>
pnpm dev # vite watch, rebuilds dist/index.html on save
The wizard prompts for:
- Type:
Theme(CSS only) orFeature(full Svelte app) - Display name, slug (folder name), author (GitHub username), description
- Sidebar label (features only, max 18 chars)
Then in Zephyr:
- Open Plugins → Dev Mode → Load plugin
- Pick the
features/<your-slug>/folder - The plugin appears in the sidebar; edits to your source rebuild and reload automatically
When you're ready, open a PR; the registry rebuild publishes it to all Zephyr users.
The SDK (@zephyr-plugin/sdk)
A workspace package every plugin imports. It gives you:
Pre-styled components matching Zephyr
<script lang="ts">
import { Toggle, Select, Button, Card, Row, StatusPill, StatCard } from '@zephyr-plugin/sdk';
let enabled = $state(false);
let quality = $state('1080p');
</script>
<Row title="Enable" description="...">
{#snippet control()}<Toggle bind:checked={enabled} />{/snippet}
</Row>
<Row title="Quality">
{#snippet control()}
<Select bind:value={quality} options={[
{ value: '720p', label: '720p' },
{ value: '1080p', label: '1080p' }
]} />
{/snippet}
</Row>
Available: Toggle, Select, Button, Card, Row, StatusPill, StatCard. Import @zephyr-plugin/sdk/tokens.css in your stylesheet to inherit Zephyr's color tokens.
Host bridge
import { zephyr } from '@zephyr-plugin/sdk';
// Per-plugin sandboxed key/value storage
await zephyr.storage.set({ enabled: true });
const state = await zephyr.storage.get<{ enabled: boolean }>();
// Per-plugin file storage (writes go to <app data>/plugin-storage/<id>/files/)
const filename = await zephyr.fs.writeBlob('clip.mp4', blob);
const files = await zephyr.fs.list('.mp4');
const url = await zephyr.fs.getUrl('clip.mp4'); // file:// URL safe to use in <video>
await zephyr.fs.delete('clip.mp4');
await zephyr.fs.openFolder(); // reveal in OS file manager
// Toasts and external links
await zephyr.notify('Done', { kind: 'info' });
await zephyr.openExternal('https://...');
// Plugin metadata
const info = await zephyr.plugin();
Everything is per-plugin. A plugin can't read another plugin's storage, can't write outside its own folder, can't spawn processes, and can't reach Zephyr's mod data.
What a feature plugin can do natively
The iframe has normal web platform access:
fetch()andWebSocketto any server (no CORS bypass from the host, same rules as a browser)getDisplayMediafor screen recording,getUserMediafor mic/cameraIndexedDB,localStorage,canvas,Web Audio,WebGL- Standard JS bundle size (single-file HTML up to ~10 MB inlined)
What it can't do without an SDK extension: read installed mods, hook into game-launch events, run native code, patch binaries.
Manifest reference
| field | required | applies to | notes |
|---|---|---|---|
id | yes | all | lowercase slug (a-z, 0-9, -). Must match the folder name. |
name | yes | all | display name in the plugin browser. |
version | yes | all | semver, e.g. 1.0.0. |
type | yes | all | one of theme, feature, game, mod. |
author.name | yes | all | shown on the plugin card. |
author.url | no | all | profile or project link. |
description | yes | all | one or two sentences. |
icon | no | all | filename in your folder, an absolute URL, or an Iconify id (mdi:palette). Shown on the plugin card. |
entry | yes | themes | filename of the CSS asset, usually theme.css. |
entry | no | features | path to the built HTML, defaults to dist/index.html. |
sidebarLabel | no | features | short label for the sidebar item. Falls back to name. |
sidebarIcon | no | features | Iconify id (mdi:record-rec, mdi:video, etc.). Defaults to mdi:puzzle-outline. |
package.url | yes | mods | URL of the mod archive. |
package.sha256 | yes | mods | sha256 of the archive. |
game | yes | mods | slug of the target game. |
defaultInstalled | no | features | bool, default false. |
removable | no | features | bool, default true. |
Example feature manifest
{
"id": "captures",
"name": "Captures",
"version": "1.0.0",
"type": "feature",
"author": { "name": "MathisBls" },
"description": "Auto-record gameplay when you launch a modded game.",
"icon": "icon.svg",
"sidebarLabel": "Captures",
"sidebarIcon": "mdi:record-rec",
"entry": "dist/index.html"
}
Build & ship
Feature plugins use vite-plugin-singlefile to inline JS and CSS into one dist/index.html. Zephyr downloads that single file at install time and serves it via file://, so there's no CDN or external fetch at runtime.
cd features/<plugin-id>
pnpm build # writes dist/index.html
Commit dist/index.html along with your source. The registry build does not run plugin builds. (The root .gitignore excludes dist/; each feature plugin re-allows it via its own .gitignore or you can git add -f.)
Local commands
pnpm new-plugin # interactive scaffolder (Theme or Feature)
pnpm build:registry # walk themes/features/games/mods and rewrite registry.json
pnpm validate # check every registered plugin
pnpm validate themes/neon-pulse # check a single folder
Contributing
- Scaffold or copy a folder under
features/,themes/,games/, ormods/. Folder name must equal theidfield. - For features:
pnpm installat the workspace root, then build withpnpm buildinside your feature folder. - Validate:
pnpm validate path/to/your/plugin. - Test it live in Zephyr via Dev Mode (no PR needed yet).
- When happy, commit (including
dist/index.htmlfor features) and open a PR. - CI regenerates
registry.jsonafter merge. Your plugin shows up in every Zephyr install within ~5 minutes (registry CDN refresh).