Repository navigation
Layout Modes
Floating, BSP, master-stack, scrolling columns and zoom, and how to switch between them.
Tiling is a switch: on or off. When it is on, the panes follow one of three layouts. When it is off, the panes float and you place them yourself. Zoom sits on top of all of them and shows one pane large for a moment.
Try it live: the Layouts track lets you switch between them in your browser.
| Mode | What it does |
|---|---|
| BSP | A tree of splits. Any number of panes, each resizable on its own. The default. See BSP Tiling. |
| Master-stack | One or more master panes on one side, and the other panes in a stack beside them. |
| Scrolling | Niri-style columns on a strip wider than the screen. The screen scrolls instead of every pane shrinking. |
| Floating | Tiling off. Panes can overlap, and you move, snap and resize them. |
Open, close and focus panes below to see how each layout places them:
Tip
This part is interactive on the docs site. Open it on tuios.dev.
Turn tiling on or off:
| Key | Where |
|---|---|
| t | Window management mode |
| Ctrl+B Space | Any mode |
Pick the layout from the command palette (Ctrl+P):
Layout: BSP tilingLayout: master-stackLayout: scrolling (niri-style)Layout: disable tiling
Picking a layout also turns tiling on. There is no default key for a layout. Bind one yourself if you switch often.
The layout mode belongs to the session. It is kept in the daemon's session state, so a scrolling session is still scrolling after you detach and reattach, and turning tiling off does not forget which layout you had. The layout itself (the BSP tree, the scrolling columns) is kept per workspace, so each of the nine workspaces has its own.
startup.layout sets the mode a new session starts in: bsp, master-stack or scrolling. startup.tiled (default true) decides whether a new session starts tiled at all. A session that already exists keeps its own mode.
[startup]
layout = "scrolling"
tiled = trueFrom a script, tuios set-layout turns tiling on or off and tidies the splits:
tuios set-layout --tiling true
tuios set-layout --equalize
tuios set-layout --rotateIt needs an attached client.
The master-stack layout puts the first panes of a workspace in a master area and the other panes in a stack beside it. The master area is for the pane you work in most, such as an editor. The layout is close to dwm's tile layout and Hyprland's master layout.
| Option | Default | Meaning |
|---|---|---|
appearance.master_position |
left |
The side the masters take: left, right, top, bottom or center
|
appearance.master_count |
1 |
How many panes are masters (1 to 9). The masters share the master area in equal parts |
appearance.master_ratio |
50 |
The share of the screen the master area takes, as a percent (10 to 90) |
appearance.master_grid |
true |
With one master on the left, show four or more panes as an equal grid. false keeps the master and the stack at every pane count |
With center, the first stack pane goes to the right, the next to the left, and so on. With only one stack pane, the master goes to the left. Two panes on a screen that is taller than it is wide stack one above the other.
Each workspace starts with these options. The keys below change the workspace on screen, and the workspace keeps the change. In a daemon session every attached client shows the same layout.
| Key | Action | What it does |
|---|---|---|
| Ctrl+B L o | cycle_master_position |
Move the masters to the next side |
| Ctrl+B L Enter | swap_with_master |
Swap the focused pane with the master. On the master, swap with the first stack pane |
| Ctrl+B L m | focus_master |
Focus the master |
| Ctrl+B L i | add_master |
Make one more pane a master |
| Ctrl+B L d | remove_master |
Make one pane fewer a master |
| < / > (window mode) |
resize_master_shrink, resize_master_grow
|
Narrow or widen the master area |
| = (window mode), Ctrl+B = |
equalize_splits, prefix_equalize_splits
|
Put the master back at master_ratio and give every other pane an equal share |
tuios set-layout --master-position center
tuios set-layout --masters 2A resize goes into the workspace's splits: the master ratio, and the share of every other pane. That covers each master, each stack pane, and each row and pane of the grid. The keyboard resizes, the percentage resizes and a mouse drag on a divider all do this, so the resize survives a retile. A terminal resize is a retile too. When a pane opens or closes, the part of the layout it changes goes back to equal shares.
With the master in the center, a resize moves one divider, and the next retile puts the master back in the middle at its new width. The splits are session state, so every client attached to the session lays the workspace out the same way.
In every layout, a workspace keeps its splits when the session's size changes while the workspace is off screen. When you switch back, the panes fit the new size.
The scrolling layout is modeled on the niri window manager. Each pane is a column on a horizontal strip, and the screen is a window onto that strip. A new pane gets a new column right after the focused one, and the strip scrolls to show it. The other columns keep their width.
A column holds one pane by default. It can hold several stacked vertically, which split its height evenly. Closing the last pane in a column removes the column and moves focus to the column on its left.
| Input | Action |
|---|---|
| Alt+Left / Alt+Right (terminal mode) | Focus the column to the left or right |
| h / l (window mode) | Focus the column to the left or right |
| j / k (window mode), Alt+Down / Alt+Up (terminal mode) | Focus the pane below or above in a column of stacked panes |
| Alt+P / Alt+N | Focus the column to the left or right |
| H / L, or Ctrl+Left / Ctrl+Right (window mode) | Move the focused column left or right along the strip |
| < / > (window mode) | Shrink or grow the focused column by four cells |
| Alt or Shift + mouse wheel | Scroll the strip sideways |
Moving focus with the keyboard scrolls the strip by the least amount that shows the whole focused column, with four cells of its neighbour peeking in beside it. It does not center the column: when all of it and that margin are already on screen, the strip stays put. Clicking a column that is partly off screen brings all of it on screen (appearance.niri_click_reveals). With focus_follows_mouse on, hovering does the same (appearance.niri_hover_reveals).
The plain mouse wheel belongs to the pane under the pointer. The strip only scrolls while Alt or Shift is held. This stops a trackpad's sideways drift from sliding the strip while you scroll a pane's history.
These have palette entries and no default key. The action names can be bound in [keybindings].
| Palette entry | Action | What it does |
|---|---|---|
Scroll: cycle column width |
scroll_cycle_width |
Cycle the focused column through 33%, 50%, 55%, 67% and 90% of the screen, and 100% when scroll_column_max is 100
|
Scroll: maximize column width |
scroll_maximize |
Widen the focused column to scroll_column_max, the widest the strip gives it |
Scroll: move window into the column below |
scroll_consume |
Pull the pane from the next column into the focused one, stacked below |
Scroll: move window out to its own column |
scroll_expel |
Move the focused pane out of its column into a new column to the right. Focus follows it, so expel undoes a consume |
| none |
scroll_focus_left, scroll_focus_right
|
Focus the column to the left or right |
| none |
scroll_move_left, scroll_move_right
|
Move the focused column left or right |
A column's width is a share of the screen until you resize it with < or >, which pins it to a cell count. Each press changes the width by four cells, from 20 cells up to scroll_column_max. Cycling the width unpins it again.
| Key | Default | Meaning |
|---|---|---|
appearance.scroll_column_width |
55 |
Width of a new column, as a percent of the screen (20 to 100) |
appearance.scroll_column_max |
90 |
The widest a column may be set, as a percent. 100 lets a column fill the screen, and the next column no longer peeks in |
appearance.niri_scroll_cells |
8 |
Cells the strip moves per wheel event (1 to 200) |
appearance.niri_reverse_scroll |
false |
Reverse the wheel direction on the strip |
appearance.niri_click_reveals |
true |
Clicking a partly hidden column scrolls it fully into view |
appearance.niri_hover_reveals |
true |
With focus-follows-mouse, hovering does the same |
The default column width of 55% is over half on purpose: two columns never quite fit, so the strip reads as something you scroll.
The scroll offset and the columns are part of the session. Every client attached to the session sees the same columns at the same offset, and the columns come back after a session switch or a reattach. A session switch forgot my column widths tells how the columns became session state.
Shared borders: with shared_borders on, columns draw no border of their own. One divider stands between two columns. Drag it to set the width of the column on its left. Windows stacked in one column have a divider between them too, but they always share the column's height evenly.
Limits: the strip always animates when it slides, even with animations off, because a jump is hard to follow.
With tiling off, panes float. Drag a pane by its title bar to move it, or Alt+drag anywhere on it. Right-drag resizes it from the nearest corner. In window management mode:
| Key | Action |
|---|---|
| h / l | Snap to the left or right half |
| f | Snap to full screen |
| u | Unsnap |
| Ctrl+B L 1-4 | Snap to a corner |
Dragging a pane to a screen edge or corner snaps it there too. See Keybindings.
The palette entry Toggle floating lifts only the focused pane out of the tiling and lets it float over the others. Run it again to put the pane back.
For a short-lived floating pane that closes when its program exits, use a popup:
tuios popup --width 90% --height 80% -- htopZoom shows the focused pane over the layout. By default it takes 95% of the screen, so the edges of the layout stay visible around it. The layout underneath is not changed.
| Key | Where |
|---|---|
| z | Window management mode |
| Ctrl+B z | Any mode |
On a tiled pane, the third title bar control toggles zoom too (appearance.window_button_zoom).
| Key | Default | Meaning |
|---|---|---|
appearance.zoom_size |
95 |
Share of the screen a zoomed pane takes, as a percent (50 to 100). 100 is fullscreen |
appearance.zoom_max_width |
0 |
Width cap in cells. 0 means no cap. Useful on an ultrawide display |
appearance.zoom_borderless |
false |
Show the zoomed pane on the whole pane region, with no border and no title bar. The program in the pane gets the full size. The dock and the rail stay on the screen. With it on, zoom_size and zoom_max_width have no effect |
appearance.zoom_follows_focus |
true |
Moving focus while zoomed zooms the pane you moved to |
appearance.zoom_animation |
true |
Slide between the tile and the zoomed box instead of switching in one frame |
appearance.gap keeps empty cells between neighbouring panes, in every tiling mode (0 to 8, default 0). It is an inner gap only: there is no margin at the screen edge.
The layout prefix sizes the focused pane to a percentage:
| Key | Action |
|---|---|
| Ctrl+B L 5-9 | Width to 50%, 60%, 70%, 80% or 90% of the content area |
| Ctrl+B L Shift+5-9 | Height to 50% through 90% |
Each percentage is its own action (resize_width_50 to resize_width_90, resize_height_50 to resize_height_90), so any of them can be rebound in [keybindings.layout_prefix]. The layout still applies its minimum pane size and gaps. In the scrolling layout only the width actions do anything.
gap, master_ratio, scroll_column_width and shared_borders decide how many cells each pane gets, so they are shared across the session: change one on any client and every attached client follows. Purely visual settings such as the theme, border style and glyphs stay per client.
The palette entry All windows opens a searchable list of every pane on every workspace, with a short preview of each. It has no default key.
| Key | Action |
|---|---|
| Type | Filter by title, workspace number, preview text or directory |
| Up / Down, Ctrl+P / Ctrl+N | Move the selection |
| Enter | Go to the pane: switch workspace, restore it if minimized, focus it |
| Ctrl+U | Clear the query |
| Esc | Close |
The preview is the first three non-empty lines of the pane, taken when the list opens. The directory is only read on Linux.
Multifocus sends your typing to several panes at once, like tmux's synchronize-panes. Use it to run the same command on several hosts.
| Input | Action |
|---|---|
| Ctrl+Shift+click on a pane | Add or remove it from the set |
Palette: Toggle multifocus, or action toggle_multifocus_active
|
Add or remove the focused pane |
Palette: Toggle multifocus on all panes, or action toggle_multifocus_all
|
Add every visible pane on the workspace. When all of them are in the set, remove them. Panes on other workspaces stay in the set |
Palette: Clear multifocus
|
Empty the set |
The two actions have no default key. This example binds them to Ctrl+B y and Ctrl+B Y:
[keybindings.prefix_mode]
toggle_multifocus_active = ["y"]
toggle_multifocus_all = ["Y"]To open many panes with multifocus on in one step, use tuios xpanes. It opens one pane per item on a new workspace, tiled, and puts all of them in the set. This is like tmux-xpanes:
tuios xpanes --ssh web1 web2 web3
printf 'a\nb\nc\n' | tuios xpanes -c 'echo {}'
tuios xpanes -ss --interval 1 -c 'curl -s https://{}/health' web1 web2 web3With -c, TUIOS replaces {} with the item, in shell quotes, and types the command into the shell of each pane. The shell stays when the command stops. -s runs the command with no shell and holds the pane until you press Enter. -ss closes the pane when the command stops. --interval waits that many seconds between the panes. --session names the session, because -s is speedy mode, as in tmux-xpanes. -l picks tiled, even-horizontal or even-vertical. --no-sync leaves multifocus off. The layout and multifocus need a client attached to the session. To close all the panes again, use the close_workspace action or tuios close-workspace. See Close every pane on a workspace.
From a script, tuios run-command SetMultifocus <window>... sets the set to exactly these windows. With no window, it empties the set.
Panes in the set get a distinct border colour. A notification shows the size of the set when you change it. With appearance.dim_unfocused on, the panes in the set are not dimmed. Set appearance.dim_multifocus = true to dim them like the other panes.
While the set is not empty and you are in terminal mode, every key that goes to the focused pane also goes to each pane in the set. A paste goes to each pane in the set too. A pane gets a bracketed paste only when its program turned bracketed paste on. A paste into a scrolled pane first returns it to live output, as a typed key does. In copy mode, TUIOS drops the paste. Keys that TUIOS handles itself, such as the leader key, are not copied.
Limits:
- Nothing is copied in window mode.
- A key goes only to the panes of the set that are on the current workspace and are not minimized. The other panes stay in the set and get keys again when they are on screen.
- The set belongs to your client. Other clients do not share it. It does not survive a detach, and a session switch clears it.
- The set follows panes, not positions. A swap keeps the same panes in the set. A pane that closes leaves the set.
- A popup cannot join the set.
Multi copy mode is copy mode on every pane of the multifocus set at the same time. Use it to search the output of many panes at once and copy the same part from each pane. Each pane has its own cursor and its own selection. Each key moves all of them together.
To start it, add two or more panes to the multifocus set and focus one of them. Then press Ctrl+B [, the copy mode key. When the focused pane is not in the set, the key starts copy mode on one pane. When multifocus is on, the palette entry Multi copy mode also starts it, and the prefix menu shows Multi copy (N) on the [ line.
The dock shows MULTI 3, where 3 is the number of panes. After a search, it shows MULTI 2/3: the search found text in 2 of the 3 panes.
| Key | Action |
|---|---|
| h j k l, w b e, g G and the other copy mode motions | Move the cursor in every pane |
| / or ?, then text, then Enter | Search every pane. Each pane goes to its own match |
| n / N | Go to the next or previous match in each pane |
| v / V | Start a character or line selection in every pane that has a match |
| Esc | End the selection. A second Esc, or q, leaves copy mode in every pane |
| y | Copy the selection of each pane to the clipboard, as one block |
| Y | Save the selection of each pane to a file |
| Tab | Change the format: plain, markdown, json |
| Shift+Up / Shift+Down | Scroll every pane one line |
A pane where the search finds no text loses the multifocus border colour. With a theme, the pane is also dimmed. It keeps its cursor. It takes no motions or selections until a later search finds text in it. Ctrl+L clears the search.
y copies only the selected text of each pane. A pane without a selection adds nothing. The panes are in window order.
| Format | What it writes |
|---|---|
plain |
The selected lines of each pane, one pane after the other. Each pane ends with a newline. Nothing is added. Use it when each line already names its host |
markdown |
For each pane, a ## Pane N: title heading and the lines in a fenced code block |
json |
An array with one object for each pane: {"pane": N, "window_id": "...", "title": "...", "lines": ["..."]}. The pane number is the index that tuios list-windows --json shows |
The format starts as appearance.selection.multi_format (default plain). Tab changes it until you leave multi copy mode.
appearance.selection.copy_command and a [[keybindings.copy_pipe]] key also work in multi copy mode. The command gets the text that y copies, in the current format, one time for all panes. See Pipe a yank through a command.
Y opens a prompt at the bottom of the focused pane. The default path is ~/tuios-copy-<date>-<time>.<ext>, with txt, md or json for the format. Type or paste a path. Press Enter to save, or Esc to cancel.
- A relative path starts in the directory of the focused pane, when its shell reports one. Otherwise it starts in your home directory. The line above the prompt shows the full path.
- TUIOS does not write over a file that exists. It tells you when the folder does not exist or when the path is a folder.
- The dock shows the path of the file it wrote. In the web client and over SSH, the file is on the machine that runs TUIOS, and the dock names that machine.
A pane title can come from a remote shell. In the markdown and json formats, TUIOS removes control, bidirectional and zero-width characters from the title and cuts it to 200 characters.
Limits:
- Soft-wrapped lines are joined by a width estimate, as in copy mode on one pane.
- Multi copy mode ends when focus moves to a pane that is not in it.
- A pane that you minimize or move to another workspace leaves multi copy mode.
- The clipboard gets the copy through OSC 52. Some terminals cut a long OSC 52 copy. Above 100 KB, the dock tells you. Use Y to save a large copy to a file.
- BSP Tiling: Splits, preselection, edge resizing and layout templates
- Keybindings: Every default key, by mode
- Configuration: The appearance and startup tables
- Session Rail: The sidebar of sessions, panes, files and agents
- Introduction
- Getting Started
- Learn tuios, hands on
- Keybindings
- Hints
- Finding a Pane
- Links
- TUIOS vs Others
Windows and Layout
Sessions and Machines
Agents
- Agents
- Inbox and Review
- Agent Messaging
- Program Status
- Worktrees
- Checkpoints
- Ship
- Push Notifications
- Turn Off Agent Features
Scripting
Recording
Reference