jarvis:// custom protocol, have access to the IPC bridge (window.jarvis.ipc), and can communicate bidirectionally with the Rust backend.
Folder Structure
Local plugins live in the platform-specific plugins directory:| OS | Plugins directory |
|---|---|
| macOS | ~/Library/Application Support/jarvis/plugins/ or ~/.config/jarvis/plugins/ |
| Linux | ~/.config/jarvis/plugins/ |
| Windows | %APPDATA%\jarvis\plugins\ |
The Manifest: plugin.toml
Every local plugin requires aplugin.toml file in its root folder. The manifest supports three fields:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | No | Folder name | Display name in the command palette |
category | string | No | "Plugins" | Palette category for grouping |
entry | string | No | "index.html" | Entry point HTML file |
Minimal Valid Manifest
Full Manifest
Discovery
Plugin discovery is performed by thediscover_local_plugins() function. This runs at startup and whenever “Reload Config” is dispatched.
The discovery algorithm:
Construct LocalPlugin
Constructs a
LocalPlugin struct with the folder name as id, the parsed name (or folder name as fallback), category, and entry.Creating Your First Plugin
Styling and Theming
Plugins run in a transparent webview with the Jarvis theme’s CSS variables injected into every page. Using these variables ensures your plugin matches the user’s chosen theme automatically.Available CSS Variables
Starter Template
Asset Loading and MIME Types
Referencing Assets
Your plugin can include any static assets. Reference them with relative paths in your HTML:jarvis://localhost/plugins/my-plugin/index.html).
Supported MIME Types
| Extension | MIME Type | |-----------|-----------|| |.html, .htm | text/html |
| .css | text/css |
| .js, .mjs | application/javascript |
| .json | application/json |
| .png | image/png |
| .jpg, .jpeg | image/jpeg |
| .gif | image/gif |
| .svg | image/svg+xml |
| .webp | image/webp |
| .wasm | application/wasm |
| .ico | image/x-icon |
| .woff, .woff2 | font/woff, font/woff2 |
| .ttf, .otf | font/ttf, font/otf |
| .mp3, .ogg, .wav | audio/mpeg, audio/ogg, audio/wav |
| .mp4, .webm | video/mp4, video/webm |
| .txt | text/plain |
| .xml | application/xml |
| (other) | application/octet-stream |
External Resources
Plugins can load resources from the web. Allhttps:// and http:// URLs are allowed:
fetch() and XMLHttpRequest calls.
WebAssembly
.wasm files are served with the correct application/wasm MIME type, enabling you to load WebAssembly modules:
Security Model
Directory Traversal Protection
Plugin assets are sandboxed to their own folder. TheContentProvider enforces this by canonicalizing both the plugin’s base path and the requested file path, then verifying the file path starts with the base path.
Attempting to access files outside the plugin directory returns a 404:
std::fs::canonicalize resolves symlinks to their real paths before the containment check.
What Plugins CAN Do
- Read and write the system clipboard (via IPC)
- Navigate the current pane to any URL
- Make HTTP requests to any origin (standard browser CORS rules apply)
- Load any
https://resource - Read image files from disk (via the
read_fileIPC message, limited to 5 MB) - Send input to the terminal PTY
- Open and close panels
- Use localStorage for persistence (scoped to
jarvis://localhostorigin) - Start window drag operations
- Log debug events to the Rust log
What Plugins CANNOT Do
- Access the
file://protocol directly - Execute
javascript:URLs - Use
data:URLs for navigation - Access other plugins’ files via path traversal
- Bypass the IPC allowlist (unknown message kinds are rejected)
- Read arbitrary files from disk (only recognized image formats via
read_file) - Execute arbitrary system commands
Hot Reload
Editing Plugin Files (HTML/JS/CSS)
Since files are read from disk on every request, changes to your plugin’s source files take effect the next time the plugin is loaded. You can:- Press Escape to go back to the terminal, then re-open the plugin from the palette.
- Press
Cmd+R/Ctrl+Rto refresh the webview in-place (this key is not intercepted by Jarvis).
Adding or Removing Plugins
Create or delete the plugin folder under~/.config/jarvis/plugins/, then trigger “Reload Config” from the command palette. The discovery runs again and the palette updates accordingly.
Editing plugin.toml
Changes to the manifest (name, category, entry) require a “Reload Config” to take effect in the palette, since the manifest is only read during discovery.Troubleshooting
Plugin doesn’t appear in the palette
- Check the folder location — verify the plugin is in the correct platform-specific directory.
- Check that
plugin.tomlexists in the plugin’s folder root. - Check for TOML syntax errors — look for
"Failed to parse plugin manifest"warnings in the Jarvis log. - Reload config — open the palette and select “Reload Config”.
- Check for read errors — look for
"Failed to read plugin manifest"warnings in the log.
Plugin loads but shows a blank page
- Check the entry point — does
index.html(or your custom entry) exist in the plugin folder? - Open DevTools (right-click then “Inspect”, or enable via
advanced.developer.inspector_enabled) and check the console for errors. - Check the URL — the plugin loads at
jarvis://localhost/plugins/{folder-name}/{entry}. Make sure the folder name and entry file match.
IPC bridge is not available
- Check
window.jarvis— it should exist even before your script runs. - Make sure you are loading via
jarvis://, notfile://orhttp://. The IPC bridge is only injected for webviews created by Jarvis.
CSS variables not working
- Use fallback values — always write
var(--color-primary, #cba6f7)with a fallback. - Check in DevTools that the
:rootstyles are being injected.
Escape key closes my plugin unexpectedly
Pressing Escape when a plugin is loaded navigates back to the previous page. This is by design (same behavior as exiting a game). If you need Escape inside your plugin, capture it at the document level withstopPropagation, but be aware the capture-phase IPC listener may still see it.
Plugin assets return 404
- Check relative paths — assets should be relative to your HTML file:
<img src="icon.png">not<img src="/icon.png">. - Check file extensions — unknown extensions are served as
application/octet-streamwhich may not render correctly. - No directory traversal —
../paths outside your plugin folder will 404.