On this page
Plugin Development Guide
Decide whether you need file mode, standalone mode, or backend extensions before filling out the manifest. Start with the complete example, then add features incrementally.
Environment and workflow
- Use Bun and the Vite / TypeScript versions specified by your plugin project. Align development dependencies with Foxel's
web/package.jsonwhen using shared components. - The current Foxel
pyproject.tomlrequires Python>=3.14. Frontend-only plugins execute no Python at runtime; the example packer uses the Python standard library. - Prepare a Foxel development instance where you can sign in and install plugins, plus a test file you can read.
Write source → typecheck → build IIFE / CSS → package .foxpkg
→ install in Foxel → open a file or app → rebuild and reinstall
The host supplies FoxelRegister, shared dependencies, and file context. An ordinary vite dev page does not receive them automatically; a separate mock host is needed for standalone previews. Use a real Foxel installation for integration checks.
Plugin key
Use a reverse domain name such as com.example.jsonviewer or io.github.username.viewer. The host validates it with:
^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)+$
Use at least two segments, each starting with a lowercase letter and containing only lowercase letters and digits. my-plugin, com.example.my-plugin, and com.example.MyPlugin are invalid. Matching directory and archive names are recommended; the manifest key determines identity. Keep it stable after release.
Recommended manifest
This configuration includes the basic metadata needed for both local installation and App Center uploads. Replace the sample author and website.
{
"foxpkg": "1.0",
"hostApiVersion": ">=1.0,<2.0",
"key": "com.example.jsonviewer",
"name": "JSON 查看器",
"version": "1.0.0",
"description": "在 Foxel 中格式化并查看 JSON 文件。",
"i18n": {
"zh": {
"name": "JSON 查看器",
"description": "在 Foxel 中格式化并查看 JSON 文件。"
},
"en": {
"name": "JSON Viewer",
"description": "Formats and previews JSON files in Foxel."
}
},
"author": "Your Name",
"website": "https://example.com",
"license": "MIT",
"frontend": {
"entry": "frontend/index.js",
"styles": [
"frontend/player.css"
],
"icon": "frontend/icon.svg",
"publicDir": "frontend",
"capabilities": [],
"openApp": false,
"supportedExts": [
"json"
],
"defaultBounds": {
"width": 900,
"height": 700
},
"useSystemWindow": true
}
}
Top-level fields
| Field | Local installation | Meaning |
|---|---|---|
key, name | Required | Unique identity and default display name |
foxpkg | Defaults to "1.0" | Supported package format is 1.0; declare it explicitly |
hostApiVersion | Defaults to ">=1.0,<2.0" | Python packaging specifier; npm syntax such as ^1.0.0 is invalid |
version | Defaults to "1.0.0" | Prefer an explicit major.minor.patch |
description, author, website | Optional | Required and nonempty for App Center uploads |
i18n | Optional | name / description by zh and en; App Center requires i18n.en.name and i18n.en.description |
github, license | Optional | Source URL and license |
frontend, backend | Optional | Declare the extensions you implement |
dependencies | Optional | Python and package requirements; validated, never installed automatically |
App Center also requires explicit foxpkg: "1.0" and version, with strictly three numeric version segments. repository is extra metadata understood by App Center (a URL or an object containing url), rather than a current typed Foxel manifest field. Prefer github for source links. See build and package for publishing requirements.
frontend fields
Archive paths are relative to the plugin root. Use /, no leading slash, and no .. segments.
| Field | Default / behavior |
|---|---|
entry | Explicit script path for frontend plugins, such as frontend/index.js |
styles | CSS path array; the host inserts <link> elements before loading the entry. Emitted CSS is not loaded automatically |
publicDir | Defaults to frontend; a public subdirectory, never . or the plugin root |
assets | Extra public file paths, including resources outside the public directory |
icon | Prefer a bundled frontend/icon.svg; relative paths must exist |
capabilities | Defaults to []; only monaco, artplayer, react-markdown are supported |
supportedExts | Prefer lowercase extensions without dots, such as ["json", "txt"] |
openApp | true exposes a standalone app entry and requires mountApp |
defaultBounds | Default window size, such as { "width": 900, "height": 700 } |
defaultMaximized | Whether to maximize initially |
useSystemWindow | Usually true for the system window frame; with false, provide your own window UI |
Bundle every declared entry, stylesheet, asset, and local icon. Files in the public directory can be served by the resource API, so keep it limited to public resources. Python files, backend entries, hidden paths, and manifest.json are excluded. See frontend development.
backend and dependencies
{
"backend": {
"routes": [
{
"module": "backend/routes/api.py",
"prefix": "/api/com.example.jsonviewer",
"tags": ["JSON Viewer"]
}
],
"processors": [
{
"module": "backend/processors/format_json.py",
"type": "com.example.jsonviewer.format",
"name": "JSON Formatter"
}
]
},
"dependencies": {
"python": ">=3.14",
"packages": ["httpx>=0.28,<1"]
}
}
module is a Python file path inside the archive, not an import path such as backend.routes.api. Route files export router. Processor files export the fixed name PROCESSOR_FACTORY; a manifest factory field does not override it, and type is required.
Add dependencies only when needed. Foxel checks packages and versions already installed in its environment; it does not run pip. URL and extras dependencies are currently unsupported by automatic validation.
Adapting an existing plugin
Start with the official Markdown plugin, also available locally under plugins/cc.foxel.markdown. Update its manifest identity, extensions, capabilities, and metadata; update the package.json name and version; and update pack.py file lists and output name. Preserve the lockfile and inspect the actual build scripts. Remove capabilities your plugin does not use.
Use web/src/plugins/sdk/v1.ts and externals.ts as references for a local src/contract.ts containing the types you need. Those are source paths, not import paths for a published npm SDK.
Next steps
- Frontend development: registration, cleanup, shared dependencies, file APIs.
- Backend development: route prefixes, processor exports and results.
- Build and package: a working packer and troubleshooting table.