bmux_windows_plugin_api 0.0.1-alpha.1

Typed public API of the bmux windows plugin (BPDL-generated bindings)
// 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;
}