Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Augani/kael/llms.txt

Use this file to discover all available pages before exploring further.

Kael’s workspace system gives you a composable, serializable multi-pane layout out of the box. You define panels that declare their preferred dock area, register them with a Workspace, and Kael handles grouping, tab management, and bounds computation automatically. When the user rearranges panels or resizes a dock, you can serialize the entire layout to JSON in a single call and restore it on the next launch.

Core types

DockArea

DockArea is a Copy enum that identifies the four regions where panels can live:
rust
pub enum DockArea {
    Left,    // side navigation / file tree
    Right,   // inspector / properties
    Bottom,  // terminal / output / problems
    Center,  // main content (default)
}
DockArea::Center is the default variant; panels placed there share the main content area.

TabGroup

A TabGroup owns the ordered list of panel IDs that occupy one DockArea. Exactly one panel is active at a time.
rust
pub struct TabGroup {
    pub dock_area: DockArea,
    pub panel_ids: Vec<String>,
    pub active_panel_id: Option<String>,
}
You rarely construct TabGroup manually — Workspace creates and updates groups automatically as you add or move panels.

The Panel trait

Any type you want to dock must implement Panel:
rust
pub trait Panel: Send + Sync {
    fn id(&self) -> &str;
    fn title(&self) -> &str;
    fn dock_area(&self) -> DockArea;
}
The id must be unique across the workspace — it is the stable key used for serialization and tab management.

Pre-built panel implementations

Kael ships three concrete Panel implementations in kael::panels so you can get started without writing boilerplate:
rust
use kael::panels::SidebarPanel;

let panel = SidebarPanel::new("explorer", "Explorer");
// panel.dock_area() == DockArea::Left
SidebarPanel always docks to DockArea::Left. Use it for file trees, project lists, and navigation.

Setting up a workspace

1

Create the workspace

rust
use kael::Workspace;

let mut workspace = Workspace::new();
2

Register panels

Call add_panel for each panel. Kael creates a TabGroup for each DockArea the first time a panel targets it, and subsequent panels sharing the same area are appended to that group.
rust
use kael::panels::{SidebarPanel, InspectorPanel, BottomPanel};

workspace.add_panel(Box::new(SidebarPanel::new("explorer", "Explorer")));
workspace.add_panel(Box::new(SidebarPanel::new("search", "Search")));
workspace.add_panel(Box::new(InspectorPanel::new("inspector", "Inspector")));
workspace.add_panel(Box::new(BottomPanel::new("terminal", "Terminal")));
After these four calls the workspace contains two TabGroups in Left (with explorer active), one in Right, and one in Bottom.
3

Activate a panel

Call set_active on the relevant TabGroup to bring a panel to the front:
rust
for group in workspace.tab_groups_mut() {
    if group.dock_area == DockArea::Left {
        group.set_active("search");
    }
}
4

Remove a panel

remove_panel returns the boxed panel if it existed. Empty groups are pruned automatically.
rust
let removed = workspace.remove_panel("search");
assert!(removed.is_some());
5

Move a panel to a different area

rust
workspace.move_panel("inspector", DockArea::Left)?;
move_panel returns Err if the panel ID is not registered.

Computing dock bounds

compute_dock_layout converts the current panel configuration and a viewport size into pixel Bounds for each dock area. Pass the result to your rendering code to position panes:
rust
use kael::{px, size};

let viewport = size(px(1440.0), px(900.0));
let dock = workspace.compute_dock_layout(viewport);

// dock.left  -> Some(Bounds { origin: (0, 0), size: (288, 900) })
// dock.right -> None  (no panels in Right)
// dock.center -> Bounds { origin: (288, 0), size: (1152, 720) }
// dock.bottom -> Some(Bounds { ... })
The default size ratios are 0.2 (20 %) for Left, Right, and Bottom. Override them via layout_mut().sizes:
rust
use kael::workspace::DockArea;

workspace.layout_mut().sizes.insert(DockArea::Left, 0.25);
workspace.layout_mut().sizes.insert(DockArea::Bottom, 0.3);

Persisting and restoring layout

save_layout serializes the current panel positions, tab groups, and dock sizes to a pretty-printed JSON string. restore_layout reads that string and reconciles it with whichever panels are currently registered — panels present in the saved layout but not registered are silently skipped.
rust
// --- save ---
let json = workspace.save_layout()?;
std::fs::write("workspace.json", &json)?;

// --- restore on next launch ---
let json = std::fs::read_to_string("workspace.json")?;

let mut workspace = Workspace::new();
// re-register all panels first ...
workspace.add_panel(Box::new(SidebarPanel::new("explorer", "Explorer")));
workspace.add_panel(Box::new(InspectorPanel::new("inspector", "Inspector")));
workspace.add_panel(Box::new(BottomPanel::new("terminal", "Terminal")));

// then restore — overrides positions and sizes from disk
workspace.restore_layout(&json)?;
Always register your panels before calling restore_layout. The method only repositions panels it can find in the current panels map; it cannot recreate panel objects from JSON alone.
The WorkspaceLayout type that backs the serialized format is itself Serialize + Deserialize, so you can embed it inside a larger application-level config struct if needed.

Writing a custom panel

If none of the pre-built panels fit, implement Panel directly:
rust
use kael::workspace::{DockArea, Panel};

struct OutputPanel {
    id: String,
    title: String,
}

impl Panel for OutputPanel {
    fn id(&self) -> &str { &self.id }
    fn title(&self) -> &str { &self.title }
    fn dock_area(&self) -> DockArea { DockArea::Bottom }
}

workspace.add_panel(Box::new(OutputPanel {
    id: "output".to_string(),
    title: "Output".to_string(),
}));
Use a static string or a const for the panel id to avoid typos when calling remove_panel, move_panel, or set_active.

Build docs developers (and LLMs) love