Renderer context
The PluginRendererContext API for a plugin's renderer.js entry — slots, commands, surfaces, and the tab-close interceptor.
A plugin’s renderer entry (renderer.js) receives a PluginRendererContext as
the ctx argument to activate(ctx). It runs inside the app’s renderer and is
where you contribute UI to slots, register
palette commands and pane surfaces,
listen to renderer hooks, and call into your
plugin’s main half.
PluginRendererContext
| API | Purpose |
|---|---|
ctx.hooks.on(name, fn) | Listen to a renderer hook (see the Hook catalog) |
ctx.slots.contribute(slot, item) | Contribute UI to a slot (see the Slot catalog) |
ctx.commands.register(cmd) | Palette command (sugar for the commands slot); prefix ids <plugin-id>. |
ctx.surfaces.register(type, def) | Register a pane SURFACE renderer (see Pane surfaces & panels); prefix types <plugin-id>. |
ctx.workspace.openSurface(type, {state?, title?}) | Open one of this plugin’s surfaces as a pane-tab |
ctx.workspace.onTabClosing(fn) | Intercept a pane-tab close (below) |
ctx.invoke(name, payload?) | Call this plugin’s main ctx.ipc.handle(name) |
ctx.react | The app’s React instance |
ctx.track(d) | Tie an arbitrary disposable to the plugin lifetime |
Use ctx.react for all elements — const h = ctx.react.createElement. Never
bundle your own React; the renderer entry is plain ESM with no imports.
Tab-close interceptor
ctx.workspace.onTabClosing(fn) is different from the fire-and-forget renderer
hooks: it’s an interceptor that can take over
(veto / replace) a pane-tab close.
The callback receives { tab, running, close }:
tab— the pane-tab being closed.running— the busy label (foreground process or detected agent), ornullwhen idle.close()— performs the actual layout close.
Return true to claim the close: the core’s default confirm dialog is
skipped and you become responsible for calling close() yourself (or not, if the
user cancels). Return false to fall through to the next interceptor / the
default flow. The first interceptor to claim wins.
onTabClosing returns a tracked Disposable, so the interceptor is removed when
the plugin deactivates.
const sub = ctx.workspace.onTabClosing(({ tab, running, close }) => {
if (!running) return false; // idle — let the default flow handle it
// show your own Kill / Keep-running / Cancel prompt, then call close() yourself
showPrompt({ onKill: () => close() });
return true; // claimed
});
The Sprites core plugin uses this to prompt Kill / Keep-running / Cancel when closing a sprite tab.
Next
See the Slot catalog for every UI surface you can contribute to, and Pane surfaces & panels for rendering whole pane-tabs and building movable feature panels.