Plugin development Reference

Slot catalog

Every UI slot where a plugin can contribute interface — rail, command palette, settings, dock panels, pane banners, terminal menu, and the + New menu.

Slots are named places in the UI where a plugin contributes interface. You add to a slot with ctx.slots.contribute(slot, item) from the renderer context. Each contribution is tracked and removed automatically when the plugin deactivates.

ctx.slots.contribute("commands", {
  id: "my-plugin.do-thing",
  title: "My Plugin: Do Thing",
  keywords: "demo example",
  run: () => ctx.log("did the thing"),
});

(ctx.commands.register(cmd) is sugar for contributing to the commands slot.)

Slots

SlotItemMap (src/renderer/plugins/slot-registry.ts):

SlotItem shapeRenders at
sidebar.actions{ title, icon, onClick, testid?, badge? }Left rail footer buttons (non-view actions)
sidebar.sections{ id, render }Left sidebar, between tab list and footer (expanded only)
commands{ id, title, keywords?, run }Command palette (also dispatchable by id)
settings.sections{ title, render }Settings panel, after built-in sections
dock.panels{ id, title, icon?, render, useBadgeCount?, headerActions? }The workspace dock, as a FIRST-CLASS panel: orders, collapses, drags between docks and persists like Files/Changes. Each also gets a multi-lit launcher button on the left rail (useBadgeCount? drives its badge; the button toggles the panel’s visibility — several can be lit at once)
overlays{ id, render }App overlay layer (manage your own visibility)
pane.banners{ id, render(sessionId) }Per-terminal-pane top strip (render null when idle)
pane.footerBanners{ id, render(sessionId) }Per-terminal-pane footer strip — home of the built-in connection-status banner (render null when idle)
terminal.contextMenu{ label, run }Terminal right-click menu, appended group
workspace.cardAccessory{ id, render({ tabId }) }Each workspace card’s trailing edge in the expanded left rail (e.g. an extra badge)
workspace.flyoutSessionRow{ id, render({ tabId, paneId, paneTabId, sessionId }) }Each session row in the workspace flyout
newTab.entries{ id, label, glyph?, section?, order?, run?(target) | picker }A tab kind in the + New dropdown (opened by the titlebar/rail + New and each pane +), beside the built-in local Terminal
agents.rowActions{ id, label, run(sessionId) }The Agents hub’s per-agent row overflow menu (an extra action on a listed agent, e.g. “open in editor”). sessionId is null for a not-yet-spawned tab

Overlays vs. panels

Overlay contributions that should participate in the central Escape handler and terminal focus-reclaim must report their open state via useReportOverlay from src/renderer/plugins/overlay-authority.ts (the Sprites / SSH overlays show the pattern).

Feature management surfaces (Sprites / SSH / Agents) are docked panels — contribute them via dock.panels and toggle them from the multi-lit launcher buttons on the left rail (several can be open at once); they are not overlays. The overlays slot is for true modals only (e.g. the sprite chooser, the SSH host-picker and connection manager). Modal overlays that should participate in the central Escape handler / terminal focus-reclaim must still report their open state via useReportOverlay.

The built-in Settings and Notifications panels remain right-edge slide-overs and use useExclusiveRightPanel; plugin features no longer claim the right edge.

Plugins can also intercept new-session requests (New Tab, pane +) through src/renderer/workspace/new-session-bridge.ts — registrations are plain unregister functions, so wrap them with ctx.track.

The + New dropdown (newTab.entries)

A newTab.entries item adds a tab kind to the + New dropdown — opened by the titlebar / rail + New and each pane +, beside the built-in local Terminal.

  • section: "local" (with Terminal) or "remote" (below a separator, default); order sorts within a section.
  • Provide EITHER run(target) (a leaf action — open + close) OR picker (a drill-in { render({ target, onClose, onBack }) } searchable panel; use the shared NewTabPickerList).
  • target is { kind: "newTab" } or { kind: "paneTab", paneId }.

The Sprites / SSH plugins add Sprite / SSH drill-in pickers here.

For rendering whole pane-tabs and the dock-vs-grid movable panel helpers, see Pane surfaces & panels.