Session Rail
The sidebar that lists sessions, machines, panes, files, agents and git state.
The session rail is a sidebar down one edge of the screen. It lists your sessions, the panes in the current session, the files in the focused pane's directory, the agents at work, and the git state of the focused pane. Everything on it can be reached with the mouse or the keyboard.
Try it live: the Workspaces track opens the rail and walks it in your browser.
Showing and hiding it
The rail is on by default, on the right edge, 24 columns wide.
| How | What it does |
|---|---|
| Ctrl+B b | Show or hide the rail |
Palette: Toggle sidebar | The same |
| Ctrl+B H Added in v0.9.3 | Show or hide the second rail, on the other edge. See Both edges |
appearance.sidebar.enabled | Whether a new session shows it. Default true |
tuios set-config appearance.sidebar.enabled false
tuios set-config appearance.sidebar.position leftIn a daemon session, the rail belongs to the session. When you show or hide it on one client, every client of the session shows or hides it, and the panes take the new space on every client. A new session takes the setting of the first client that attaches. When you toggle the rail, TUIOS also keeps the new value in your config for the next new session. A reload of the config file does not change the rail of a running session. The width of the rail stays per client. See The rail with more than one client.
position is right (default), left or hidden. width is the preferred width in columns on a wide screen (default 24). A config file that sets enabled, position or width keeps its value, so an install from before v0.8.0 whose file names them keeps the old rail. On a narrow screen, or after <, the rail collapses to a one-column strip of marks. Hover the strip for a label (tooltips).
Both edges Added in v0.9.3
You can show a rail on each edge of the screen. Each rail has its own width, sections and custom command. Turn on the second edge in config.toml:
[appearance.sidebar]
position = "right"
sections = "sessions,terminals,agents"
[appearance.sidebar.left]
enabled = true
width = 28
sections = "files,git"| How | What it does |
|---|---|
| Ctrl+B b | Show or hide the original rail |
| Ctrl+B H | Show or hide the second rail. It does not turn on an edge that has no enabled = true |
| e, while a rail has the keyboard | Move the keyboard to the rail on the other edge, and back |
| Drag the inner border | Change the width of that rail, for this client only |
Both rails read the same sessions, files and git state. Each rail has its own scroll, cursor and mouse areas. On a narrow screen the second rail closes first. Every key of the edge tables is on the configuration page.
A daemon session in a rail Added in v0.10.0
A rail can show a daemon session in place of its sections. The panes of the session, on its current workspace, stack top to bottom in the rail. Make the sessions first, then name them in config.toml:
tuios new side-left --detach
tuios new side-right --detach[appearance.sidebar]
enabled = true
position = "right"
width = 36
[appearance.sidebar.right]
session = "side-right"
[appearance.sidebar.left]
enabled = true
width = 36
session = "side-left"Settings, then Sidebar, has a session name, a width and a workspace for each edge. An empty session name shows the sections again.
- Click a pane in the rail to type in it. Each pane has its own title bar, and its close and minimize buttons act on that pane.
- With the rail focused, Ctrl+B - and Ctrl+B c make a pane in that session, under the others. Ctrl+B | also stacks the new pane under the others. Ctrl+B n and p change the active pane. Zoom makes a pane larger inside the rail.
- When the rail cannot give each pane six rows, it shows a group of panes around the active pane.
- Added in v0.10.0
session_workspace = 0, the default, shows the rail on every workspace.session_workspace = 2shows it only on workspace 2. - The rail does not change the size of the session. It cuts the panes to fit, and another client of the session keeps its size.
- While a session is in a rail, the custom command of that rail does not run.
A switch of workspace, a hidden rail or a detach does not stop the session or its programs. A daemon restart does. The name can be set before the session exists. The rail then shows an error and tries again when the session is made.
The sections
| Section | What it lists |
|---|---|
sessions | Every session, grouped by machine when you have remote hosts. Worktree sessions sit under their repository, labelled by branch. A global group appears once a second machine is reachable |
terminals | The panes of the current session |
files | The focused pane's directory. The list updates when a file is added, removed or renamed. See the files section |
agents | Every pane running a coding agent, with its state. See Agents |
git | The focused pane's repository, branch, and how far it is ahead of or behind its upstream |
custom | The output of a command you write, one row per line. See The custom section |
pane Added in v0.12.0 | A live terminal under its own heading. See The pane section |
appearance.sidebar.sections is one string that says which sections the rail draws, in what order, and the most each may take:
[appearance.sidebar]
sections = "sessions:25,terminals,files:25,agents:34"A name left out is a section the rail does not draw. The percent is a ceiling, not a reservation. spacer is an empty block you can repeat to add a gap or push the next section to the bottom. The git and custom sections are not in the default layout, so add them by name:
[appearance.sidebar]
sections = "sessions:25,terminals,git,files:25,agents:34"The settings page (, in window mode) has an editor for this under Sidebar, Sections. The full syntax is on the configuration page.
The last section in the layout is pinned to the bottom of the rail. A divider row sits above it. Drag the divider, or put the cursor on it and press < or >, to move the split. A double click on it, or Enter, restores the layout's own share.
appearance.git_dirty (default true) adds counts of staged, changed and untracked paths to the git section. It is the only part of the section that walks the working tree, so turn it off on a very large repository.
A section with more rows than it can show Added in v0.9.3
A section shows as many rows as fit. Its last line is … +N for the rows that do not show. The mouse wheel over a section scrolls it. The keyboard cursor scrolls the section to stay in view.
While rows are out of view, the section draws a scroll thumb on the rail's edge rule, next to its rows. The thumb uses a cell of the rule, so the rows keep their full width.
- The size of the thumb shows the part of the rows that is on the screen.
- The thumb is at the first line only at the top of the section, and at the last line only at the bottom.
- A hover preview of the panes of another session gets a thumb too. When the terminals section is above the sessions section, the preview keeps the height of the section, so the session row under the pointer does not move.
- The collapsed strip and a section of one line have no thumb.
The thumb uses the scrollbar_thumb glyph of the glyph set: ┃ by default, # with --ascii-only and █ in the heavy set. When the thumb and the rule have the same glyph, as with a heavy border, the thumb is █ (# with --ascii-only). appearance.scrollbar and appearance.hide_scrollbar change only the scroll bar of a pane.
Using it with the keyboard
Move the keyboard into the rail with s in window management mode, or Ctrl+B e from anywhere. The same keys take you back to the panes. Esc also leaves.
While the rail has the keyboard, these keys from [keybindings.sidebar] apply:
| Key | Action |
|---|---|
| j / k, Down / Up | Move the cursor |
| g / G, Home / End | First or last row |
| l / h, Right / Left | Next or previous section |
| Tab / Shift+Tab | Cycle sections |
| Enter | Activate the row: attach the session, or focus the pane |
| 1-9 | Jump to a session by its position |
| J / K | Move a session or machine down or up the rail |
| n | New session. Asks which machine when there is more than one |
| t | New terminal in the session |
| r | Rename the pane under the cursor |
| c | Recolour the pane under the cursor |
| m | Open the menu for the row under the cursor |
| x | Open that menu on its Close or Kill entry |
| f | Agents: show all sessions, or only this one |
| o | Agents: sort by what needs you (you, the default), by priority (pri), or by most recent (rec) |
| i | Open the mailbox for the pane under the cursor |
| / | Search every pane in every session. @state filters by agent state |
| < / > | Collapse or expand the rail. On the divider, move the split |
| ? | Show the rail's keys |
On a row of the files section, [keybindings.sidebar_files] answers first: a create, r rename, d delete to the trash, D delete permanently, y copy, x cut, p paste, Y copy the absolute path.
On an agent row, [keybindings.sidebar_agents] answers first:
| Key | Action |
|---|---|
| r | Reply to the agent. The line opens under the Inbox, and the message is typed when the agent is at rest |
| v | Review the pane's changes |
| u | Mark the pane's finished turn unread again, for every client |
| z | Snooze the pane's Inbox item |
| x | Drop the newest queued message. u within 10 seconds queues it again. With nothing queued, x opens the menu as on any row |
| Space | Open or close the subagent rows of the agent. It needs a subagent heading. See Shape the rows |
Every key can be rebound. See Configuration.
The cursor row Added in v0.11.0
While the rail has the keyboard, the row under the cursor has a lighter background. At 256 colours and truecolor, its text is also bold and underlined in the accent colour. The underline starts after the state glyph, so the marks in the gutter keep their meaning. At 16 colours the cursor row is in reverse video.

appearance.sidebar.cursor_background sets the background of that row:
[appearance.sidebar]
cursor_background = "#45475a" # or a slot name, such as "blue", or a role name, such as "accent"- Empty, the default, keeps the theme's background for the cursor row.
- The value is a hex colour, a palette index from
0to255, a slot name or a role name. - The text colour does not change. Choose a background that your text is readable on.
- The option sets only the cursor row while the rail has the keyboard. The row under the mouse pointer and the cursor of a rail without the keyboard keep their own background.
- At 16 colours the option has no effect.
- The option is on the settings page, in the Sidebar section.
The focused pane's row in the terminals section carries the focus mark. When the rail does not show that row, the agents row of the pane carries the mark. This occurs when sections has no terminals, when the terminals section has no room for the row, and when the terminals section shows the panes of another session.
Quick look Added in v0.12.0
Put the rail cursor on a row of the files section and press Space. The preview opens over the panes. Right-click a row and pick Preview to look at that row.
The preview shows:
- code with syntax colours and line numbers,
- Markdown as formatted text,
- a picture, a page of a PDF or a frame of a film,
- JSON laid out for reading, and CSV and TSV as a table,
- the files in an archive,
- the names in a folder.
A binary file shows its first 16 KiB in hex. A long text opens at once, and tuios reads more of it as you move. Line numbers after a jump to the end are estimates, and the preview says so. A search or a count stops after 5 seconds and says how far it went. Press n to go on.
- A PDF shows one page and a bar with the page number. Click the bar to go to a page. At 16 colours the preview shows the words of the page.
- A film shows one frame and a bar with the time. Click or drag the bar to move to a time. There is no playback. Press t to show 7 small frames from across the film. At 16 colours the preview shows the facts of the film.
- A GIF: J and K step through its frames.
- A picture uses Kitty graphics when the terminal supports them. Other terminals get block characters. At 16 colours the preview names the picture and does not draw it.
A PDF page needs pdftoppm, and a film frame needs ffmpeg, on the machine that has the file. For a listing on another machine, that machine reads the file and sends the preview. Its host policy must allow files.
↑ and ↓ (or j and k) move the cursor to the next row, and the preview shows that row. Space or Esc closes the preview. A click outside it closes it too. When the rail loses the keyboard, the preview closes.
These keys work while the cursor is in the list. The preview shows the keys that work now. Every other key goes to the rail.
| Key | Action |
|---|---|
| J K | Move half a page, one page of a PDF, or 5 percent of a film |
| Ctrl+D Ctrl+U | Move half a page |
| Mouse wheel over the preview | Move 3 lines, one page of a PDF, or 1 percent of a film |
| t | Show or hide the small frames of a film |
| v | Show the next view: the diff of a changed file, then the details |
| Tab | Move the keyboard into the file |
| f | Fill the area of the panes, or go back |
Inside the file, every key acts on the preview:
| Key | Action |
|---|---|
| j k | Move one line, one page of a PDF, or 1 second of a film |
| h l | Move 1 second of a film, or one column of a table |
| v | Show the next view: the diff of a changed file, then the details |
| t | Show or hide the small frames of a film |
| d u | Move half a page |
| g G | Go to the start or the end, or the first or last page |
| / | Search in a text. Lower case ignores case |
| n N | Go to the next match, or the one before |
| : | Go to a line number, or a page of a PDF |
| w | Wrap long lines, or cut them |
| f | Fill the area of the panes, or go back |
| Esc or Tab | Move the keyboard back to the list |
| Space | Close the preview |
Change these keys in [keybindings.quick_look] Unreleased and [keybindings.quick_look_inside].
Drag a file into a pane Added in v0.12.0
Press a row of the files section, move the pointer out of the rail, and release the button over a pane. The pane gets the path. I on a row does the same for the focused pane. A drag never copies or moves the file.
- While you drag, a heavy frame in the accent colour shows the pane under the pointer. A pane that does not take the path gets the frame in the warning colour.
- A label at the pointer shows the name, the pane and the program in the pane.
- Esc cancels the drag. A release over the rail, the dock or an empty area does nothing.
- After the drop, the pane has the focus and the mode does not change.
tuios quotes the path for the program in the pane. It knows sh, bash, rbash, zsh, dash, ksh, ksh93, mksh, lksh, oksh, loksh, pdksh, posh, yash, ash, hush, fish, nu, csh, PowerShell, elvish and cmd. Other programs get POSIX single quotes. A name with a backslash before a quote or another backslash, or a name that ends in a backslash, gets its quotes and backslashes outside the single quotes. Then fish behind tmux or script reads the path as a POSIX shell does. A folder path ends in /, and a space follows the path. Hold Alt when you release a folder to type a cd to it.
tuios does not paste the path in these cases. The label and a dock note say why:
- The pane is on another machine.
- The pane runs ssh or mosh, or its shell reports a folder on another machine.
- The file is in a listing from another machine.
- The name holds a line break or a control character.
- The pane is in copy mode.
To turn the drag off, set appearance.sidebar.file_drag = false.
The pane section Added in v0.12.0
The rail can run a program in a real terminal under its own heading. Put pane in sections and describe the program in [appearance.sidebar.pane]. See the keys.
- The program runs in the session, so it survives a detach. Tiling, layouts, the terminals section and
tuios list-windowsleave the pane out.tuios list-windows --raillists it. - The pane is as wide as the rail's content and as tall as the lines the section gets. When the rail is hidden, folded, narrower than 10 columns or gives the section no lines, the program keeps running at its last size.
- Click the pane, or press P in window mode (
focus_rail_paneAdded in v0.12.0), to type into it. Alt+Esc, a click outside the pane or the leader key gives the keyboard back. Esc goes to the program. - A program that asks for the mouse gets clicks, motion and the wheel.
- The pane shows text only. Kitty images are not drawn in the rail.
- When the program exits, the pane closes and the section says so. Click the line to start it again.
- A session has one rail pane, and only the main rail draws it. If two clients name different commands, the last one to ask replaces the pane.
Edit and search files
Shift+Enter, or Edit in a file's context menu, opens the row in a new pane. The pane runs the editor from Settings, Sidebar, File editor, or $EDITOR when that setting is empty.
Ctrl+B f searches file names below the focused pane's directory. Ctrl+F does the same below the folder the rail shows, while the rail has focus. Type a name or a relative path and press Enter. tuios opens the rail, opens the file's folder, and selects the file.
The search stops after about 2.5 seconds and skips .git, node_modules, .venv and target. Its title says partial when it did not search the whole tree.
Using it with the mouse
- Click a session to switch to it, or a pane to focus it.
- Added in v0.10.0 Click a session of another machine to open it. When the link to that machine is up, the dock says
Connecting to HOST. When the link is down, TUIOS saysReconnecting to HOSTand opens the session when the link comes up. The wait ends after one minute, and TUIOS says why. See Remote Hosts. - Click the
+on a section header to make a session or a terminal. The+beside a machine makes a session on that machine. - Right-click a row for its menu. A folder's menu has Copy path, which copies its absolute path.
- Right-click the header of a repository, or the row of a worktree session, and select New worktree... to make a worktree session. See Worktrees.
- Hover a row whose title is too long and it scrolls (
marquee). - Click a folder in the files section to walk into it (
folder_click).
Agent rows
The agents section lists every pane running an agent, in every session. By default it is sorted by what each one needs from you, in four groups: needs you (an approval, a question, or an error), finished and not yet seen, working, and at rest. Inside a group, rows keep the order the panes were opened, so a row moves only when what it wants from you changes.
Each row shows the pane's state glyph and its name. A row says how long it has been in its state once that is five minutes or more, or when the cursor or pointer is on it. The second line says the one thing the row's group is about:
- Working: what the agent is doing now, such as
Bash: go test ./..., when its hooks report it. - Needs you: a word for what it needs (
approval,question,risky,plan) and what the pane asks. - Finished, not yet seen: the first line of what the agent last said.
- At rest: nothing.
ctx 84% appears once the agent's context is 80% full or more, and 1 queued at the right edge while messages wait in its queue.
│ agents 2 need you
│▎▲ review 4m
│ risky · rm -rf bu…
│ ■ docs
│ Updated CHANGELOG…
│ ● api 1 queued
│ ctx 84% · Bash: go…Subagent rows Added in v0.9.3
While a Claude Code agent has subagents at work, each subagent gets a row under its agent's row. The agent's row says how many run, such as 3 of 4 run.
agents
◊ lead
claude · 3 of 4 run
● Research tmux
Bash: go test ./…
● Audit the rail
Read: internal/a…
■ Write the docs
done · 14 tools- The first line of a subagent row is what the agent asked it to do.
- The second line, on a tall section, is the tool it runs now, or how it ended.
- The mark is
●running,■done,×failed and○stopped (*,#,xandowith ASCII glyphs). - A finished row is muted. It goes one minute after the subagent ends, or five minutes after it fails.
- A click on a subagent row focuses its agent's pane. The keyboard cursor skips subagent rows.
An agent at rest whose subagents still run shows ◊ (% with ASCII glyphs) in the working colour, and its state reads waiting. The title bar and the switcher show the same mark. The daemon still keeps the pane done, so wait-for and the done alert work as before.
When the section is short, the rail first drops the finished subagent rows. Then it shows the newest few with a line such as +2 more. Then it shows none. It never hides an agent row to show a subagent row.
appearance.sidebar.subagent_rows = "count" draws only the count, such as 3 subagents. "tree" draws the same rows on tree lines. The settings page has these options on its Sidebar tab. The rows need the Claude Code integration.
Shape the rows Added in v0.9.3
Four more options change how the rows look. Each default draws the rows as shown above.
| Key | Values | Default | What it does |
|---|---|---|---|
subagent_collapse | off, expanded, collapsed | off | expanded and collapsed draw a heading line, ▾ 4 subagents, over the rows of each agent. The value is the state at the start |
subagent_fields | a comma list of mark, name, type, time, note | mark,name,time,note | The parts each row shows. type is the agent type, such as Explore. note is the second line |
subagent_max_rows | 0 to 16 | 0 | The most lines the subagents of one agent use, the +N more line included. 0 means no limit |
subagent_hide_finished | a duration such as 30s, or off | off | Remove the row of a finished subagent this long after it ends |
[appearance.sidebar]
subagent_rows = "tree"
subagent_collapse = "expanded"
subagent_fields = "mark,name,time"
subagent_max_rows = 4
subagent_hide_finished = "20s"With subagent_rows = "tree", the rows look like this:
agents
◊ lead
claude · 4 of 4 run
├─ ● Research tmux
│ Bash: go test ./…
├─ ● Audit the rail
│ Read: internal/a…
└─ ● Check CI
Bash: gh run list- A click on the heading, or Enter on it, opens or closes the rows. Space does the same with the cursor on the agent row or on its heading. The action is
agent_subagents, in[keybindings.sidebar_agents]. It does nothing without a visible heading. - A closed heading shows
▸. On a tall section, its second line names the subagents. The rail keeps the state of each pane until the client stops. - The daemon keeps a finished subagent for 60 seconds, or 5 minutes when it failed.
subagent_hide_finishedcan remove a row earlier. It cannot keep a row longer than the daemon does. - Each of the five
subagent_*options can be set again in[appearance.sidebar.left]or[appearance.sidebar.right], for the rail on that edge. A key that the edge table does not set uses the value from[appearance.sidebar]. tuios set-configand the settings page refuse a badsubagent_fields,subagent_hide_finishedoragent_rest_foldvalue. Asubagent_fieldslist that names no field gives a warning when the config loads.
On a rail 30 columns wide or narrower, a pane running an agent is listed once, in the agents section, and the terminals section keeps the panes that run no agent. Rows that have rested longer than agent_rest_fold (an hour by default) fold into one +N at rest line. Enter or a click on it shows them again. A row that needs you, a working agent, the focused pane, a pane with messages queued and a pane whose agent has subagents at work never fold.
[appearance.sidebar.agent_row] picks which tokens a row shows, in what order, and how each is coloured. It is a table, so set it in config.toml rather than with set-config:
[appearance.sidebar.agent_row]
# the shipped list; state, host, prompt and $key are the others
tokens = ["session", "need", "harness", "name", "elapsed", "context", "subagents", "pr", "meta", "now", "message"]
[[appearance.sidebar.agent_row.elapsed.rule]]
gt = 30
fg = "warning"pr shows the pull request of the session's worktree branch and its checks, such as PR #12 open pass. A tokens list you wrote gets no new tokens, so add subagents or pr to it to see them.
A rule has exactly one test (equals, contains, starts_with, gt or lt) and sets fg, bold or both. For elapsed, gt and lt compare minutes. The first matching rule wins, with at most eight per token.
On a narrow rail
When a row does not fit, the rail shares its columns between the name and the other parts of the row, such as the state, the time or the queue count.
- A part is shown whole or not at all. The rail never cuts a part short.
- The parts at the right end of the row have priority. When one part does not fit, the next part can still use the space.
- The name keeps at least eight characters and an ellipsis. It then takes every column that the other parts leave.
- The harness before a name, and the branch of a session, stay only while the whole name fits beside them. When the harness goes, the session before it goes too. So
api/deploynever reads as a harness calledapi. - The queue count on an agent row takes the longest form that fits:
2 queued, then2q.
A machine that does not answer keeps its header row. The right end of the row says why:
offline, orseen 3m agofor a machine that answered before and then dropped off.no daemon,no tuiosorversionfor a machine that answered and refused.connectingorreconnectingwhile TUIOS tries the link.
When messages wait for the machine, the count comes first, as in 3 queued, offline. On a narrow rail, the row shows 3 queued, and then the count in place of part of the name. With no messages waiting, a narrow row shows ✕ (x with ASCII glyphs), so a down machine does not look like one that is up. A machine that is connecting gets no mark.
Other options
| Key | Default | Meaning |
|---|---|---|
show_glyphs | true | Agent state glyph on each row |
show_counts | true | Pane count on each session row |
show_numbers | false | Show the switch number before each local session name. The number is the one switch_session_N opens, for sessions 1 to 9 |
marquee | true | Scroll a hovered row's long title |
tooltips | true | Label the collapsed strip on hover |
file_icons | true | A Nerd Font icon per file type |
file_icon_colors | true | Draw each file icon in its type's colour |
folder_click | "navigate" | navigate, cd (send the focused pane there) or both. TUIOS types a cd only into a shell at its prompt |
file_actions | true | Allow create, rename, delete, copy, cut and paste |
file_delete | "trash" | trash or permanent |
file_drag Added in v0.12.0 | true | Drag a file row onto a pane. See Drag a file into a pane |
group_main Added in v0.12.0 | false | Put a repository's main checkout under the repository row. See Group the main checkout |
peek Added in v0.12.0 | true | Show the panes of the session under the pointer or the cursor. See Session peek |
agent_rest_fold | "1h" | How long an agent row rests before it folds. A duration, or off |
subagent_rows Added in v0.9.3 | "rows" | rows, tree or count. How an agent row shows its subagents |
subagent_collapse Added in v0.9.3 | "off" | off, expanded or collapsed. A heading over the subagent rows |
subagent_fields Added in v0.9.3 | "mark,name,time,note" | The parts of a subagent row |
subagent_max_rows Added in v0.9.3 | 0 | The most lines one agent's subagents use. 0 means no limit |
subagent_hide_finished Added in v0.9.3 | "off" | A duration after which a finished subagent leaves the rail, or off |
header_case Added in v0.9.3 | "lowercase" | lowercase or uppercase. Applies to every rail header |
background | "" | The rail's background: off, theme or #RRGGBB. Empty follows appearance.background |
cursor_background Added in v0.11.0 | "" | The background of the cursor row while the rail has the keyboard. See The cursor row |
"cd here" types a cd only into a pane whose shell is at its prompt. In daemon mode, the daemon does this check. TUIOS types no cd for a folder whose name holds a quote, a backslash or a control character. The dock says when it did not type.
All of these live under [appearance.sidebar]. appearance.session_colors gives each session its own colour on the rail, and appearance.session_border carries that colour onto every pane border.
Older config keys
The flat sidebar_* keys and the show_windows and show_agents booleans still load. They are folded into [appearance.sidebar] and sections when the file is read. Use the new keys in new configs.
Related
BSP Tiling
Tile windows with binary space partitioning, and switch to the master-stack or scrolling layouts.
Themes and Appearance
Colour themes, custom and imported themes, chrome colours, glyph sets, spacing, dimming and the spotlight.
tuios lives only at tuios.dev and github.com/Gaurav-Gosain/tuios. tuios has no X or Twitter account. An account that offers a tuios token or presale is not this project.