// Public, typed contract of the windows plugin. Other plugins that
// depend on `bmux_windows_plugin_api` import these interfaces rather
// than the windows plugin implementation itself.
//
// Four interfaces:
// - `windows-state` — read-only queries about panes and windows.
// - `windows-commands` — mutating operations on panes and windows.
// - `windows-events` — event stream of window/pane lifecycle changes.
// - `windows-list` — reactive state channel carrying the ordered
// window list for tab-bar rendering + positional navigation.
plugin bmux.windows version 1;
capability WINDOWS_READ = bmux.windows.read;
capability WINDOWS_WRITE = bmux.windows.write;
@capability(WINDOWS_READ)
interface windows-state {
// A pane's current lifecycle status.
variant pane-status {
@default running,
exited { exit-code: i32 },
}
// Snapshot of a single pane's state.
record pane-state {
id: uuid,
session-id: uuid,
focused: bool,
zoomed: bool,
name: string?,
status: pane-status,
}
// Snapshot of a floating pane surface exposed through the windows UX facade.
record floating-pane-state {
id: uuid,
pane-id: uuid,
session-id: uuid,
anchor-pane-id: uuid?,
context-id: uuid?,
client-id: uuid?,
x: u16,
y: u16,
w: u16,
h: u16,
z: i32,
layer: string,
scope: string,
visible: bool,
}
// Snapshot of a window. Windows are a plugin-layered view over
// sessions/contexts; the id is the underlying context id.
record window-entry {
id: string,
name: string,
active: bool,
}
// Pane-level queries.
query pane-state(id: uuid) -> pane-state?;
query focused-pane(session: uuid) -> uuid?;
query zoomed-pane(session: uuid) -> uuid?;
query list-panes(session: uuid) -> list<pane-state>;
query list-floating-panes(session: uuid?) -> list<floating-pane-state>;
// Window-level queries.
query list-windows(session: string?) -> list<window-entry>;
}
@capability(WINDOWS_WRITE)
interface windows-commands {
// Errors a focus command can produce.
variant focus-error {
pane-not-found,
focus-denied { reason: string },
}
// Errors a close command can produce.
variant close-error {
pane-not-found,
close-denied { reason: string },
}
// Errors a window-level command can produce.
variant window-error {
not-found,
denied { reason: string },
failed { reason: string },
}
// Errors a pane mutation command (split/launch/resize/zoom/
// restart) can produce.
variant pane-mutation-error {
pane-not-found,
session-not-found,
invalid-argument { reason: string },
denied { reason: string },
failed { reason: string },
}
// Acknowledgement returned by mutating window commands. When a new
// window is created, `id` carries the new context id; other
// commands may leave it empty.
record window-ack {
ok: bool,
id: string?,
}
// Acknowledgement returned by pane-mutation commands. `pane-id`
// carries the freshly-created pane id for split/launch; other
// commands leave it empty.
record pane-ack {
ok: bool,
pane-id: uuid?,
}
// Acknowledgement returned by `zoom-pane`. Carries the pane that
// was toggled plus the post-toggle zoom state so callers can
// render an accurate status without a follow-up query.
record pane-zoom-ack {
pane-id: uuid,
zoomed: bool,
}
// Direction in which a pane split/focus action moves.
enum pane-direction {
@default horizontal,
vertical,
left,
right,
up,
down,
}
// Directional intent for resizing a pane. Increase/decrease grow or
// shrink the focused pane automatically; physical directions move
// the nearest boundary on that side.
enum pane-resize-direction {
@default increase,
decrease,
left,
right,
up,
down,
}
// Directional intent for moving a floating pane.
enum floating-pane-move-direction {
@default right,
left,
up,
down,
}
// Placement for moving a window relative to another window.
enum window-move-placement {
@default before,
after,
}
// Selector identifying a session or pane. Exactly one of `id`,
// `name`, or `index` should be populated. `id` wins when multiple
// are set. `index` is only meaningful for pane selectors scoped
// to a session.
record selector {
id: uuid?,
name: string?,
index: u32?,
}
// Pane-level commands.
command focus-pane(id: uuid) -> result<unit, focus-error>;
command close-pane(id: uuid) -> result<unit, close-error>;
// Selector-targeted variants. These accept the full `selector`
// record (id / name / index / active-by-empty) so callers that
// address panes by position within a session don't have to resolve
// an id first.
command focus-pane-by-selector(session: selector?, target: selector)
-> result<pane-ack, pane-mutation-error>;
command close-pane-by-selector(session: selector?, target: selector)
-> result<pane-ack, pane-mutation-error>;
// Close the currently-active pane in the targeted session.
// Equivalent to `close-pane-by-selector` with an empty target, but
// gives consumers a dedicated name that documents the "close active
// pane" semantic.
command close-active-pane(session: selector?)
-> result<pane-ack, pane-mutation-error>;
// Focus the next/previous pane in the layout relative to the
// currently-active pane. The direction is interpreted by the host
// today; once the windows plugin owns focus ordering internally,
// this shifts to a plugin-local lookup.
command focus-pane-in-direction(session: selector?, direction: pane-direction)
-> result<pane-ack, pane-mutation-error>;
// Split / launch / resize / zoom / restart.
// Each targets a session (by uuid or name) and an existing pane
// (likewise), plus operation-specific extras.
command split-pane(
session: selector?,
target: selector?,
direction: pane-direction,
ratio-pct: u32?,
) -> result<pane-ack, pane-mutation-error>;
command launch-pane(
session: selector?,
target: selector?,
direction: pane-direction,
name: string?,
program: string,
args: list<string>,
) -> result<pane-ack, pane-mutation-error>;
command resize-pane(
session: selector?,
target: selector?,
direction: pane-resize-direction,
cells: u16,
) -> result<pane-ack, pane-mutation-error>;
command zoom-pane(
session: selector?,
) -> result<pane-zoom-ack, pane-mutation-error>;
command restart-pane(
session: selector?,
target: selector?,
) -> result<pane-ack, pane-mutation-error>;
command create-floating-pane(
session: selector?,
target: selector?,
x: u16?,
y: u16?,
w: u16?,
h: u16?,
z: i32?,
layer: string?,
scope: string?,
program: string?,
args: list<string>,
) -> result<pane-ack, pane-mutation-error>;
command move-floating-pane(
session: selector?,
target: selector,
x: u16,
y: u16,
) -> result<pane-ack, pane-mutation-error>;
command resize-floating-pane(
session: selector?,
target: selector,
w: u16,
h: u16,
) -> result<pane-ack, pane-mutation-error>;
command move-active-floating-pane(
session: selector?,
direction: floating-pane-move-direction,
cells: u16,
) -> result<pane-ack, pane-mutation-error>;
command resize-active-floating-pane(
session: selector?,
direction: pane-resize-direction,
cells: u16,
) -> result<pane-ack, pane-mutation-error>;
command focus-floating-pane(session: selector?, target: selector)
-> result<pane-ack, pane-mutation-error>;
command raise-floating-pane(session: selector?, target: selector)
-> result<pane-ack, pane-mutation-error>;
command lower-floating-pane(session: selector?, target: selector)
-> result<pane-ack, pane-mutation-error>;
command close-floating-pane(session: selector?, target: selector)
-> result<pane-ack, pane-mutation-error>;
command focus-next-floating-pane(session: selector?)
-> result<pane-ack, pane-mutation-error>;
command raise-active-floating-pane(session: selector?)
-> result<pane-ack, pane-mutation-error>;
command lower-active-floating-pane(session: selector?)
-> result<pane-ack, pane-mutation-error>;
command close-active-floating-pane(session: selector?)
-> result<pane-ack, pane-mutation-error>;
command set-floating-pane-z(
session: selector?,
target: selector,
z: i32,
) -> result<pane-ack, pane-mutation-error>;
command set-floating-pane-layer(
session: selector?,
target: selector,
layer: string,
) -> result<pane-ack, pane-mutation-error>;
// Window-level commands.
command new-window(name: string?) -> result<window-ack, window-error>;
command rename-window(name: string) -> result<window-ack, window-error>;
command kill-window(target: string, force-local: bool) -> result<window-ack, window-error>;
command kill-all-windows(force-local: bool) -> result<window-ack, window-error>;
command switch-window(target: string) -> result<window-ack, window-error>;
command move-window(source: uuid, target: uuid, placement: window-move-placement)
-> result<window-ack, window-error>;
}
interface windows-events {
// A single event describing a pane-level state change.
variant pane-event {
// A pane gained focus.
focused { pane-id: uuid },
// A pane lost focus.
unfocused { pane-id: uuid },
// A pane was zoomed to fill its session viewport.
zoomed { pane-id: uuid },
// A zoomed pane was restored.
unzoomed { pane-id: uuid },
// A new pane was opened.
opened { pane-id: uuid, session-id: uuid },
// A pane was closed.
closed { pane-id: uuid },
// The pane's lifecycle status changed (e.g., running -> exited).
status-changed { pane-id: uuid },
}
events pane-event;
}
// Reactive state channel carrying the ordered list of windows in the
// order consumers should render them (and navigate through). The
// windows plugin owns the authoritative window order (persisted in its
// plugin-local storage under `windows.order`) and publishes a fresh
// snapshot on every mutation: new-window, rename-window,
// switch-window, move-window, kill-window, kill-all-windows,
// goto-window, close-current-window, next/prev/last window.
//
// Subscribers (notably the attach tab bar) consume this via
// `EventBus::subscribe_state::<WindowListSnapshot>` to observe the
// current ordered list synchronously plus a live-update receiver.
// This lets the tab bar render the exact order the plugin maintains
// and lets next/prev navigation walk the same order without a
// round-trip query per frame.
//
// Payload semantics:
// - `windows` is the ordered list of live windows; consumers render
// positional indices (`1:`, `2:`, …) from this order.
// - `active` on each entry is `true` for the current active window
// (as resolved by the plugin's effective-current-context logic) and
// `false` otherwise.
// - `revision` is a monotonic counter advancing on every publish;
// consumers can use it to deduplicate or order updates.
interface windows-list {
record window-list-snapshot {
windows: list<window-list-entry>,
revision: u64,
}
record window-list-entry {
id: uuid,
name: string,
active: bool,
}
@state events window-list-snapshot;
}