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

typewhat it isfiles it ships
themeA CSS file applied to Zephyr's UI.theme.css
featureA full mini-app running in a sandboxed iframe with its own sidebar item.dist/index.html (built from Svelte)
gameA new game definition Zephyr can target.game.json
modA 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) or Feature (full Svelte app)
  • Display name, slug (folder name), author (GitHub username), description
  • Sidebar label (features only, max 18 chars)

Then in Zephyr:

  1. Open Plugins → Dev Mode → Load plugin
  2. Pick the features/<your-slug>/ folder
  3. 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() and WebSocket to any server (no CORS bypass from the host, same rules as a browser)
  • getDisplayMedia for screen recording, getUserMedia for mic/camera
  • IndexedDB, 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

fieldrequiredapplies tonotes
idyesalllowercase slug (a-z, 0-9, -). Must match the folder name.
nameyesalldisplay name in the plugin browser.
versionyesallsemver, e.g. 1.0.0.
typeyesallone of theme, feature, game, mod.
author.nameyesallshown on the plugin card.
author.urlnoallprofile or project link.
descriptionyesallone or two sentences.
iconnoallfilename in your folder, an absolute URL, or an Iconify id (mdi:palette). Shown on the plugin card.
entryyesthemesfilename of the CSS asset, usually theme.css.
entrynofeaturespath to the built HTML, defaults to dist/index.html.
sidebarLabelnofeaturesshort label for the sidebar item. Falls back to name.
sidebarIconnofeaturesIconify id (mdi:record-rec, mdi:video, etc.). Defaults to mdi:puzzle-outline.
package.urlyesmodsURL of the mod archive.
package.sha256yesmodssha256 of the archive.
gameyesmodsslug of the target game.
defaultInstallednofeaturesbool, default false.
removablenofeaturesbool, 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

  1. Scaffold or copy a folder under features/, themes/, games/, or mods/. Folder name must equal the id field.
  2. For features: pnpm install at the workspace root, then build with pnpm build inside your feature folder.
  3. Validate: pnpm validate path/to/your/plugin.
  4. Test it live in Zephyr via Dev Mode (no PR needed yet).
  5. When happy, commit (including dist/index.html for features) and open a PR.
  6. CI regenerates registry.json after merge. Your plugin shows up in every Zephyr install within ~5 minutes (registry CDN refresh).