panes 0.7.0

Renderer-agnostic layout engine with declarative ergonomics
Documentation

crates.io docs.rs CI license

You keep solving the same panel layout problem — splits, stacks, grids, resize — from scratch in every project.

panes is a spatial layout engine that computes rectangles without rendering them.

Describe panels in rows, columns, and presets. panes solves the geometry via Taffy's flexbox engine and hands back a map of PanelId → Rect. No framework. No widget system. No opinions about pixels.

Proof

use panes::{layout, grow, fixed};

// Game HUD: health bar pinned at 40px, viewport fills the rest
let layout = layout! {
    col {
        row {
            panel("viewport", grow: 1.0)
        }
        row(gap: 4.0) {
            panel("health", fixed: 40.0)
            panel("inventory", grow: 1.0)
            panel("minimap", fixed: 48.0)
        }
    }
}?;

let resolved = layout.resolve(800.0, 600.0)?;
for entry in resolved.panels() {
    println!("{}: {:?}", entry.kind, entry.rect);
}

Install

cargo add panes

Usage

Build custom layouts or pick from 15 presets. Pass any coordinate system — pixels, logical points, terminal cells.

// Custom — full control with the layout macro
let layout = layout! {
    row(gap: 8.0) {
        panel("editor", grow: 2.0)
        col {
            panel("chat")
            panel("status", fixed: 3.0)
        }
    }
}?;
// Preset — one-liner for common patterns
Layout::master_stack(["editor", "chat", "status"]).master_ratio(0.6).gap(1.0)
// Runtime — add/remove panels, focus navigation, frame diffing
let mut rt = Layout::master_stack(["editor", "chat", "status"])
    .master_ratio(0.6).gap(1.0).into_runtime()?;

rt.add_panel("terminal".into())?;          // strategy-managed
rt.add_panel_adjacent("split".into())?;    // auto-splits focused panel
rt.focus_next();
rt.focus_direction_current(FocusDirection::Right);

let frame = rt.resolve(80.0, 24.0)?;
let diff = frame.diff();

Adapters convert rects to renderer-native types:

Crate Target Rect type
panes-ratatui ratatui ratatui::layout::Rect (u16, edge-rounded)
panes-egui egui egui::Rect (f32)
panes-css Browser CSS declarations (browser solves layout)
panes-wasm Canvas/JS WasmRect (f64)

Each adapter provides a panels() iterator that yields PanelEntry { id, kind, rect, kind_index } — no hashmap, no cross-referencing:

for entry in panes_ratatui::panels(&resolved) {
    println!("{}: {} at {:?}", entry.id, entry.kind, entry.rect);
}

panes-ratatui also provides focused_panels(), which pairs each entry with a focus bool — decorations (_tab / _title) light up automatically when their content panel is focused.

Performance

LayoutRuntime resolves a 100-panel flat layout in ~1µs on the hot path — roughly 3x over raw Taffy. The abstraction layer (tree compilation, rect resolution, frame diffing) adds minimal overhead.

See benches/ for methodology.

Documentation

See the User Guide for the full API: all 15 presets, the layout macro, TOML configuration, runtime mutations, frame diffing, animation, and render adapters.

License

MIT or Apache 2.0, at your option. See LICENSE-MIT and LICENSE-APACHE.