Overview
Jarvis uses a binary split tree to manage pane layout within the application window. Every visible pane occupies a leaf node in the tree, and interior nodes describe how their two children are divided—either horizontally (side by side) or vertically (top and bottom).The tiling system lives in the
jarvis-tiling crate and is intentionally decoupled from rendering and platform windowing.Layout Configuration
Layout settings control the visual appearance of the tiling grid:Core Layout Settings
Gap between panels in pixels (valid range: 1–20)
Border radius in pixels (valid range: 0–20)
Inner pane padding in pixels (valid range: 0–40)
Maximum number of panels (valid range: 1–10)
Default panel width as fraction of screen (valid range: 0.3–1.0)
Scrollbar width in pixels (valid range: 1–10)
Panel border width in pixels (valid range: 0.0–3.0)
Screen-edge padding in pixels (valid range: 0–40)
Opacity for unfocused panels (valid range: 0.0–1.0)
Configuration Example
config.toml
The Split Tree
The layout is represented by a tree structure:Split Tree Data Model
Split Tree Data Model
Splitting Panes
Manual Split
You can split panes horizontally (side by side) or vertically (top/bottom):- Horizontal split (
SplitHorizontal): places panes side by side - Vertical split (
SplitVertical): places panes top and bottom
Auto-Split Logic
Theauto_split_direction() method chooses the split direction based on the focused pane’s aspect ratio:
- If
width >= height→ Horizontal (split side by side) - If
width < height→ Vertical (split top/bottom)
The
max_panels configuration value caps the total number of panes that can exist simultaneously.Split Behavior
When a split is performed:Focus Management
Focus Operations
focus_next()
Move focus to the next pane in depth-first order, wrapping around
focus_prev()
Move focus to the previous pane, wrapping from first to last
focus_direction(dir)
Move focus to the neighbor in a specific direction (doesn’t wrap)
focus_pane(id)
Focus a specific pane by its numeric ID
Focus Indicators
Whenlayout.inactive_opacity < 1.0, unfocused panes are rendered with reduced opacity. The layout.border_width setting controls whether pane borders are visible (default 0.0 means no border).
config.toml
Zoom Mode
Zoom mode makes a single pane fill the entire content area, hiding all other panes.Behavior
- Toggle on:
zoom_toggle()setszoomed = Some(focused_id) - Toggle off: calling
zoom_toggle()again setszoomed = None - Zooming requires at least 2 panes
- Any split operation automatically cancels zoom
- Closing a pane cancels zoom
When zoomed, only the zoomed pane is positioned; all other panes remain at their previous positions (off-screen).
Resizing Panes
Pane sizes are controlled by theratio field on Split nodes.
Keyboard Resize
Use keyboard shortcuts to resize the focused pane:config.toml
[0.1, 0.9], preventing any pane from being resized to invisibility.
Mouse Drag Resize
Mouse drag resize is a three-phase interaction:Hit testing (cursor move)
On cursor movement, the system:
- Computes all split borders from the current tree
- Hit-tests the cursor position (6-pixel hit zone)
- Changes cursor icon to
ColResizeorRowResizewhen hovering a border
Drag start (mouse button press)
When pressing the mouse button over a border, the system records:
- The border being dragged
- The start position
Drag update (cursor move while dragging)
While dragging, each cursor movement:
- Computes the pixel delta from the drag start
- Converts pixels to a ratio delta
- Calls
adjust_ratio_between()to update the split node - Repositions webviews
Pane Stacks (Tabs)
A single leaf position in the tree can host multiple panes as a stack (tabbed interface).Stack Operations
push_to_stack(kind, title)
Add a new pane of the given kind to the stack at the focused leaf position
cycle_stack_next()
Advance to the next tab, wrapping around
cycle_stack_prev()
Go to the previous tab, wrapping around
Only the active pane in the stack is visible; others are preserved in memory.
Opacity Configuration
Transparency settings for different UI layers:Background layer opacity (valid range: 0.0–1.0)
Panel opacity (valid range: 0.0–1.0)
Orb visualizer opacity (valid range: 0.0–1.0)
Hex grid background opacity (valid range: 0.0–1.0)
HUD overlay opacity (valid range: 0.0–1.0)
Opacity Example
config.toml
UI Chrome: Tab Bar and Status Bar
UI chrome elements surround the content area and affect the viewport available for tiling.Tab Bar
The tab bar sits at the top of the window with a default height of 32 pixels.Status Bar
The status bar sits at the bottom of the window with a default height of 24 pixels.Show the status bar
Status bar height in pixels (valid range: 20–48)
Show panel toggle buttons (left side)
Show online user count (right side)
Background color (CSS color string)
Chrome Layout
Auto-Open Panels
Configure panels to open automatically when Jarvis starts:config.toml
Panel Kinds
Terminal
Interactive terminal emulator (xterm.js)
Chat
Chat interface pane
Assistant
AI assistant panel
WebView
General-purpose web content pane
Auto-Open Configuration
Panel type:
terminal, assistant, chat, settings, presenceCommand to run (empty =
$SHELL)Arguments for the command
Panel title
Working directory (empty =
$HOME)To disable auto-open entirely, set an empty array:
Multi-Panel Workspace Example
config.toml
Advanced: Layout Engine
The layout engine converts the split tree into concrete pixel rectangles:Algorithm
Algorithm
-
Apply outer padding — The viewport is inset by
outer_paddingon all four sides -
Recurse through the tree — At each
Splitnode:- Subtract the
gapfrom the available dimension - Multiply the remaining space by
ratioto get the first child’s size - The second child gets the remainder
- The gap is placed between the two children
- Subtract the
-
Leaf nodes emit
(pane_id, bounds)directly
Gap and Padding Calculation
Troubleshooting
Panels not splitting
Panels not splitting
Check:
- Current panel count <
layout.max_panels - Keybinds are correctly configured
- Not in zoom mode (splits cancel zoom)
Gaps too large/small
Gaps too large/small
Adjust
layout.panel_gap (valid range: 1–20 pixels)Can't see inactive panes
Can't see inactive panes
Increase
layout.inactive_opacity or disable effects.inactive_pane_dimResize not working
Resize not working
- Check that you’re dragging on the split border (6-pixel hit zone)
- Ratios are clamped to [0.1, 0.9] to prevent invisible panes