Authoring a plugin
Build, test, and bundle your own PaceBar provider plugin.
This guide walks through writing a provider plugin. For the full contract, see the plugin schema and the host API reference.
Anatomy
A plugin is a directory with three files:
plugins/<id>/
plugin.json manifest (required)
plugin.js entry script (required)
icon.svg icon, currentColor (required)Write the manifest
plugin.json declares your id, name, and the shape of your output so the UI
can render skeletons before the probe finishes:
{
"schemaVersion": 1,
"id": "my-provider",
"name": "My Provider",
"version": "0.0.1",
"entry": "plugin.js",
"icon": "icon.svg",
"lines": [
{ "type": "badge", "label": "Plan", "scope": "overview" },
{ "type": "progress", "label": "Usage", "scope": "overview", "primary": true },
{ "type": "text", "label": "Details", "scope": "detail" }
]
}Mark one progress line primary to surface it in the
tray icon.
Implement the probe
Register on the global object and return metric lines. Use the host API for I/O — plugins can't reach the network or disk directly.
globalThis.__pacebar_plugin = {
id: "my-provider",
probe(ctx) {
let resp;
try {
resp = ctx.host.http.request({
method: "GET",
url: "https://api.example.com/usage",
timeoutMs: 5000,
});
} catch (e) {
throw "Request failed. Check your connection.";
}
if (resp.status !== 200) throw "Request failed (HTTP " + resp.status + ").";
const data = JSON.parse(resp.bodyText);
return {
lines: [
ctx.line.badge({ label: "Plan", text: data.plan }),
ctx.line.progress({
label: "Usage",
used: data.used,
limit: data.limit,
format: { kind: "percent" },
}),
],
};
},
};Add an icon
Provide icon.svg using fill="currentColor" so it themes correctly in light
and dark mode. Set the provider's real brand color in the manifest where
applicable.
Bundle it
Bundled plugins live under src-tauri/resources/bundled_plugins/<id>/. Run the
bundling script and PaceBar will pick up the new provider:
bun run bundle:pluginsTips
- Wrap host calls in
try/catchand throw short, user-friendly strings (notErrorobjects) — the message becomes the error badge. - Validate API responses before reading nested fields.
- Keep probes fast; users wait on every refresh.
- Use
ctx.app.pluginDataDirfor any per-plugin state.
See the host API reference for the full ctx.host.*
surface (HTTP, filesystem, crypto, keychain, SQLite, env, and ccusage).