Widgets allow plugins to render content in the CLI status bar, title bar, info bar, and footer.
| Zone | Location | Description |
|---|---|---|
titleBarLeft | Title bar (left) | Left side of the title bar |
titleBarRight | Title bar (right) | Right side of the title bar |
statusBarLeft | Status bar (left) | Left side of the status bar |
statusBarRight | Status bar (right) | Right side of the status bar |
infoBar | Info bar | Below the main content area |
footer | Footer | Bottom of the screen |
type UIWidget interface {
Render(width int) []WidgetSpan
}
type WidgetSpan struct {
Text string
Style string // "default", "bold", "dim", "accent", "muted", "green", "red", "yellow", "blue"
}
The Render method receives the available width and returns styled spans.
For widgets that need per-workDir rendering (e.g., git branch):
type WorkDirRenderer interface {
RenderForWorkDir(width int, workDir string) []WidgetSpan
}
If implemented, RenderForWorkDir is called instead of Render when a workDir is available.
Permission required: ui.contribute
func (p *MyPlugin) Activate(ctx plugin.PluginContext) error {
widget := &MyWidget{}
return ctx.ContributeUI("my-widget", "statusBarRight", widget, 100)
}
Parameters:
widgetID: Unique widget identifier (per plugin)zone: Widget zone (see table above)widget: UIWidget implementationpriority: Ordering within zone (lower = leftmost, default 100)
ctx.UpdateWidget("my-widget") // Trigger a re-render
The WidgetRegistry manages all registered widgets:
type WidgetRegistry struct { ... }
func NewWidgetRegistry() *WidgetRegistry
func (wr *WidgetRegistry) Register(pluginID, widgetID, zone string, provider UIWidget, priority int)
func (wr *WidgetRegistry) Unregister(pluginID, widgetID string)
func (wr *WidgetRegistry) UnregisterAll(pluginID string)
func (wr *WidgetRegistry) RefreshWidget(pluginID, widgetID string, width int, renderFn RenderFunc)
func (wr *WidgetRegistry) RefreshAllWidgets(width int, renderFn RenderFunc)
func (wr *WidgetRegistry) NotifyUpdated()
func (wr *WidgetRegistry) OnUpdated(fn func())
Script plugins declare widgets in plugin.json:
{
"contributes": {
"ui": [
{
"id": "git-branch",
"slot": "statusBarRight",
"priority": 50,
"description": "Show current git branch"
}
]
}
}
The script’s stdout is rendered as widget content. The XBOT_WIDGET_ID environment variable identifies which widget to render.
Web plugins contribute widgets via the web_ui protocol. Web widgets use structured spans (WebWidgetSpan) with semantic styles, not ANSI codes.
type GitBranchWidget struct{}
func (w *GitBranchWidget) Render(width int) []plugin.WidgetSpan {
branch := getCurrentBranch()
return []plugin.WidgetSpan{
{Text: " ", Style: "default"},
{Text: branch, Style: "accent"},
}
}
func (w *GitBranchWidget) RenderForWorkDir(width int, workDir string) []plugin.WidgetSpan {
branch := getBranchForDir(workDir)
return []plugin.WidgetSpan{
{Text: " ", Style: "default"},
{Text: branch, Style: "accent"},
}
}
- PluginContext API — UIContributor interface
- Permissions —
ui.contributepermission - API Reference: Widget Zones — Complete zone reference
- Cookbook: Widget Development — Step-by-step guide