Repository navigation
Configuration
Every table and option in the TUIOS config.toml, with defaults and how changes apply.
TUIOS reads ~/.config/tuios/config.toml. If XDG_CONFIG_HOME is set, the file is $XDG_CONFIG_HOME/tuios/config.toml. On the first run, TUIOS writes a config.toml with comments and no settings, except [startup].
tuios config path # print the path
tuios config edit # open it in $EDITOR
tuios config reset # overwrite it with the defaults, after askingWrite only what you want to change. Missing keys and missing tables take their defaults.
[appearance]
theme = "nord"
[keybindings.window_management]
new_window = ["ctrl+t"]config.toml can include other files. TUIOS also reads the *.toml files in a config.d directory next to config.toml.
include = ["shared.toml", "?work.toml"] # put this before the first table- A relative path is relative to the file that has the
includeline. A name that starts with?is optional. - The included files come first, then the
config.dfiles in name order, thenconfig.toml. A later file wins. -
config.tomlholds only the keys that you changed. TUIOS writes a change to the file that sets the key, and keeps its comments. - TUIOS never writes a read-only file, such as a file from Nix or home-manager.
- Add
disabled = trueto an array entry to remove an earlier entry with the samenameorkey.
tuios config files # every file, in merge order
tuios config origin [key] # the file that sets each key
tuios config prune --dry-run # the keys of config.toml that have their default valueAn old config.toml sets every key and hides the other files. Run tuios config prune to remove its default values.
For the merge rules, read-only setups and a dotfiles example, see Split the Config.
Two things live outside the file: custom themes in ~/.config/tuios/themes/ and custom glyph sets in ~/.config/tuios/glyphs/.
Note
The binary is the source of truth
tuios list-options prints every settable option with its type, default and accepted values. It reads the same registry the validator and the settings page use, so it always matches the build you run.
| Table | What it holds |
|---|---|
[appearance] |
Theme, backgrounds, borders, windows, mouse, tiling, dock. Sub-tables scrollbar, selection and sidebar
|
[notifications] |
How long messages stay up, and agent alerts |
[keybindings] |
The leader key and 23 sections of bindings |
[daemon] |
Daemon log level, agent detection, resuming agents, saved history and the size of a shared session |
[startup] |
How a new session starts |
[workspaces] |
What happens when a workspace becomes empty |
[launcher] |
How the app launcher starts a graphical program |
[tape] |
Project tapes |
[debug] |
The keycast overlay |
[screenshot] |
Capture format, frame and fonts |
[screensaver] |
The idle animation |
[hints] |
What hints mode labels, the label letters and how much the pane dims |
[panes] |
The keys of the pane labels that Ctrl+B Q shows |
[scratch] |
The size of the scratch terminal that Ctrl+B g shows |
[pip] |
The size and corner of the picture-in-picture view that p pins |
[spotlight] |
The beam that dims all but one part of the screen |
[dock] |
Which components the bar draws, and your own |
[hosts] |
Other machines this daemon links to, and what each may do here |
[tailscale] |
Which tailnet machines tuios hosts add offers |
[agents] |
The switch for agent features, approvals from the Inbox, risk rules, what a pane may do, the away recap, the queue and checkpoints |
[notify] |
Push notifications to your phone for Inbox items |
[hooks] |
Shell commands run on events |
TUIOS watches the file. A save is applied about 200 ms later, with no restart. The command palette also has a Reload Config entry.
A file with an error is not applied. The running settings stay, and the error is shown on screen.
Most of the file applies live. These parts do not:
| Change | When it applies |
|---|---|
[hooks] |
The daemon reads it at start. Run tuios kill-server after editing a hook the daemon runs |
[daemon] |
The daemon reads it at start. Run tuios kill-server after editing it. tuios set-config daemon.log_level also changes the log level of a running daemon |
[startup] |
The next new session |
scrollback_lines |
Panes opened afterwards |
| Custom theme files | See Custom themes |
The daemon also follows the file for [hosts], [agents], [notify] and preferred_shell. Removing a host closes its link at once. A change that gives panes or other machines more waits for tuios config apply. This covers a new host, a wider [agents.permissions] and a wider link policy. See Changes that wait.
tuios list-options # every path, with default and accepted values
tuios list-options --section sidebar # one group
tuios list-options --search "pane bg" # fuzzy search, best match first
tuios get-config appearance.border_style
tuios set-config appearance.border_style thick
tuios config browse # search the options and set one, in an explorertuios config browse shows each option with its type, default, current value and source. Press / to search. Press Enter on an option to set it the same way as set-config. q or Esc leaves it. It needs a running daemon. See Explorers.
set-config changes a running session. With a client attached, the client applies the value and writes it to the config. With no client attached, the daemon keeps the value for this session and does not write the file. A path the registry does not know is refused. An [appearance] option also answers to its bare name, so border_style and appearance.border_style are the same path. set-config cannot reach [keybindings], [hooks], [hosts], [tailscale], [agents] or the [dock] lists, because those are maps and lists rather than single values. For [agents] this is also a safeguard: a program in a pane cannot loosen it through TUIOS. dock.clock.format is the one [dock] key it can set.
The settings page edits every option in the registry, in the same groups list-options prints. Open it with:
-
, in window mode (
open_settings) -
Ctrl+B , (
prefix_settings) - Settings in the Ctrl+P command palette
Most rows apply as you change them. Colour options open a colour picker. Two rows open editors of their own: the rail's sections and the dock's components.
The page reopens on the tab, row and search you left it on. 1 to 9 go to a tab. A row whose value in force differs from its default has a dot after its name. That includes the rows the page draws by hand, such as the theme, glyph set, shared borders, pane gap, the master and column ratios, the rail's sections, max FPS, show keys, spotlight and the screen saver effect. For the pane geometry rows the value compared is the session's, not the config's. Backspace (or Delete) resets a changed row to its default, and its description says what the default is. The hand-drawn rows have no reset: Backspace on one says so, and you change it back with its own control. Ctrl+Z undoes the last change made on the page.
/, or any letter the page does not use, searches every tab. It ranks each row by its name, its config key, its value and its description, and each result names its tab. In the results, ←, → and Enter change the row where it is, Tab goes to the row on its own tab, and Esc clears the search. The command palette reaches the same rows by name (settings: pane background), and tuios list-options --search runs the same search from a shell.
Note
A save changes only the key
The settings page writes only the key that you changed. Your comments, blank lines and key order stay. When the config has more than one file, the change goes to the file that sets the key. See Split the config into several files.
Interface flags override the file for one run. They are accepted by tuios, tuios new, tuios attach, tuios ssh and tuios tape play.
| Flag | Option |
|---|---|
--theme |
appearance.theme |
--border-style |
appearance.border_style |
--dockbar-position |
appearance.dockbar_position |
--window-title-position |
appearance.window_title_position |
--window-button-style |
appearance.window_button_style |
--window-button-position |
appearance.window_button_position |
--hide-window-buttons |
appearance.hide_window_buttons |
--hide-scrollbar |
appearance.hide_scrollbar |
--shared-borders |
appearance.shared_borders |
--scrollback-lines |
appearance.scrollback_lines |
--zoom-max-width |
appearance.zoom_max_width |
--no-animations |
appearance.motion = "none" |
--confirm-quit |
appearance.confirm_quit |
--show-clock, --show-cpu, --show-ram
|
appearance.show_clock, show_cpu, show_ram
|
--show-keys |
debug.show_key_events |
--ascii-only |
none. Draws ASCII instead of Nerd Font glyphs |
All keys below go under [appearance].
| Key | Default | Values | What it does |
|---|---|---|---|
theme |
"" |
a theme id, none, omarchy or omarchy/NAME
|
Colour theme. Empty keeps your terminal's own colours. On a machine with Omarchy, empty uses the Omarchy theme (Added in v0.10.0). See Omarchy |
glyphs |
"default" |
a glyph set id | Shapes of the chrome. See Glyph sets |
border_style |
"rounded" |
see below | Border drawn around every pane |
border_focused_color |
"" |
#RRGGBB |
Override the focused border colour |
border_unfocused_color |
"" |
#RRGGBB |
Override the unfocused border colour |
shared_borders |
false |
bool | Draw one border between neighbouring tiled panes, in every layout |
gap |
0 |
0 to 8 | Empty cells between tiled panes |
dim_unfocused |
0 |
0 to 90 | Fade panes you are not in, in percent |
dim_foreground (Added in v0.10.0)
|
true |
bool | Let dim_unfocused dim the text of a pane you are not in, as well as its background. false keeps the text at full strength and dims only the background |
dim_multifocus |
false |
bool | Dim the panes in the multifocus set like other panes you are not in |
session_colors |
true |
bool | Give each session its own colour on the rail and in the switcher |
session_border |
false |
bool | Also draw that colour on every pane border |
zen_mode |
"disabled" |
disabled, always, mouse
|
Hide borders never, always, or while the mouse is idle |
panel_padding |
2 |
1 to 6 | Columns of padding inside overlay panels |
motion |
"full" |
none, basic, full
|
How much moves. basic animates window slides, zoom and the copy sweep. full adds the fade of a dialog opening (truecolor only), the shimmer on a working agent's row and the confetti. none moves nothing |
animation_speed (Added in v0.10.0)
|
100 |
25 to 400 | The speed of the window animations, in percent: minimize and restore, snap, swap and zoom. 200 runs them in half the time. 0 reads as 100. A value out of range moves to the nearest end. motion = "none" still turns them off |
theme_transition (Added in v0.10.0)
|
"none" |
none, circle, fade
|
How a theme change shows. circle grows the new theme from the middle of the screen. fade changes every cell from the old colours to the new colours, and needs a truecolor terminal. none changes the theme in one frame. motion = "none" and --no-animations turn it off |
theme_transition_duration (Added in v0.10.0)
|
450 |
100 to 2000 | The time a theme transition takes, in milliseconds. animation_speed scales it |
modal_dim |
30 |
0 to 90 | Dim the screen behind a dialog, in percent. 0 turns it off. At 16 colours the text goes faint instead |
max_fps |
0 |
0, 10 to 240, or "auto"
|
The highest frame rate TUIOS draws at. 0 means 60. See below
|
wrap_lists |
true |
bool | Moving past the end of a list wraps to the other end |
animations_enabled, the key motion replaces, is still read from older files: false reads as motion = "none", and true changes nothing. Where both are set, motion wins.
Values: rounded (default), normal, thick, double, block, outer-half-block, inner-half-block, ascii, hidden, glyphs.
glyphs uses the border of the active glyph set. hidden also hides the window buttons and the scrollbar, because both are drawn on the border.
max_fps is the highest frame rate TUIOS draws at. Your terminal and your monitor can show fewer frames. A value above what they can show does not make TUIOS smoother.
[appearance]
max_fps = 144 # a number from 10 to 240
# max_fps = "auto" # the refresh rate of your display
# max_fps = 0 # 60, the default| Value | Frame rate |
|---|---|
0 |
60. This is the default |
10 to 240
|
That number. A value outside the range moves to the nearest end |
"auto" |
The refresh rate of your display, from 10 to 240. If TUIOS cannot find the rate, it uses 60 |
The settings page offers Auto, 30, 60, 90, 120, 144, 165 and 240. With Auto selected, the row shows the rate in use, for example Auto (144). A change applies at once, with no restart.
- What you see. TUIOS sends a frame to your terminal, and the terminal draws it on its own schedule. kitty and Ghostty draw at most once for each refresh of the monitor, so a 60 Hz monitor shows at most 60 frames a second. Over SSH, the network and the remote terminal set the limit too.
-
What it costs. TUIOS draws only when something on the screen changes. The terminal library under it still wakes at
max_fpswhile TUIOS is idle. An idle TUIOS at 240 wakes about three times as often as at 60. -
How auto finds the rate. TUIOS looks once in the background when it starts, and again when the config file reloads. Until the answer arrives, it draws at 60. On Wayland it asks
hyprctlon Hyprland,nirion niri andwlr-randron other wlroots compositors. On X11, or on another compositor, it asksxrandr --current. On macOS it askssystem_profiler SPDisplaysDataType. On Windows it does not look, and uses 60. - Several displays. A terminal window cannot tell which display it is on, so auto uses the highest rate among them.
-
Remote clients. Over SSH, in
tuios-weband in the SSH server, auto uses 60, because the displays on the machine are not the ones you see.
A cell with no background of its own is transparent, so your terminal's own background shows through. That is the default everywhere: pane content left on the default background, the space between panes, the borders, the dock and the rail. These options paint those cells instead, one surface at a time or all at once.
| Key | Surface | Default |
|---|---|---|
background |
Every surface below that is not set on its own | "off" |
pane_background |
Pane content, the thin scrollbar over it, and the scrollback browser |
"" (follows background) |
desktop_background |
Behind and between panes: gaps, the space around floating panes, an empty workspace and its welcome screen | "" |
window_chrome_background |
Pane borders and title bars, and the lines between shared borders | "" |
dock_background |
The dock | "" |
sidebar.background |
The rail, under [appearance.sidebar]
|
"" |
Each takes off, theme or a #RRGGBB colour:
-
offpaints nothing, so your terminal's background shows through. -
themepaints the active theme's background, with the theme's foreground on default-coloured text. With no theme set, it behaves asoff. -
#RRGGBBpaints that colour. With a theme set, default-coloured text takes the theme's foreground, lightened or darkened until it reads on the colour.
A surface's own option wins whenever it holds a value, off included. Left empty, it follows background:
[appearance]
background = "theme" # every surface on the theme's background
dock_background = "#11111b" # the dock a shade darker
pane_background = "off" # panes keep the terminal's own background
[appearance.sidebar]
background = "#181825"Only cells with no background of their own are painted. A background a program chose, the selection and search marks, and the colours the chrome sets itself always win. A value that is not a keyword or a #RRGGBB colour reads as off for that surface, with a validation warning.
While the pane background paints a colour, a program that asks the terminal for its colours (OSC 10 and 11) is told the colours it is drawn on, so it can pick a light or dark palette. All six options apply live, reach every attached client, and are on the Backgrounds tab of the settings page, where x clears a surface back to following background.
| Key | Default | Values | What it does |
|---|---|---|---|
window_title_position |
"top" |
top, bottom, hidden
|
Where the title is drawn. hidden also disables rename |
window_title_format |
"" |
template | Title template. See below |
window_button_style |
"dots" |
dots, pill
|
macOS-style traffic lights, or a filled pill. On the left, the pill puts close at the outer corner: close, zoom, minimize |
window_button_position |
"left" |
left, right
|
Which end of the title bar holds the buttons |
hide_window_buttons |
false |
bool | Hide the minimize, zoom and close buttons |
window_button_zoom |
true |
bool | Show the zoom button on tiled panes |
hide_scrollbar |
false |
bool | Hide the scrollbar on the pane border |
confirm_quit |
false |
bool | Always confirm on quit, not only when processes are running |
preferred_shell |
"" |
path | Shell for new panes. Empty picks one for your platform |
new_window_inherit_cwd |
true |
bool | Start a new window in the focused pane's directory. On Windows, the shell must report its folder with OSC 7 or OSC 9;9 |
new_window_follow_ssh |
false |
bool | A split or new window of a pane that runs ssh runs the same ssh. See below |
whichkey_enabled |
true |
bool | Show the which-key popup after the leader key |
whichkey_position |
"bottom-right" |
bottom-right, bottom-left, top-right, top-left, center
|
Where the popup opens |
prefix_repeat_time |
500 |
0 to 5000 | Milliseconds the prefix stays armed after a repeatable command. 0 turns it off |
A template for every title. It expands three placeholders:
-
{title}: the custom name, or the title the program set -
{index}: the window's 1-based position in its workspace -
{cwd}: the window's working directory
[appearance]
window_title_format = "{index}: {title} ({cwd})"Any other {...} produces a validation warning and is drawn as written.
| Key | Default | Values | What it does |
|---|---|---|---|
click_to_type |
"double" |
double, single, off
|
In window mode, what starts typing in a pane: a double click, a single click, or no click. A single click always focuses |
auto_enter_terminal_on_focus |
"off" |
off, targeted, all
|
Start typing when a keyboard focus command lands on a pane. targeted covers numbered select and arrows, all adds Tab |
focus_follows_mouse |
false |
bool | Focus the pane under the pointer |
alt_drag |
true |
bool | Alt + left-drag moves a pane |
right_click_opens_menu |
false |
bool | In terminal mode, a plain right-click opens the pane menu |
copy_on_select |
true |
bool | Copy a mouse selection when you release the button |
word_characters |
"@-./_~?&=%+#" |
string | Punctuation that counts as part of a word on double-click |
links |
"all" |
all, marked, off
|
The links TUIOS finds. marked finds only OSC 8 links. See Links
|
link_click |
"both" |
both, ctrl, shift, off
|
The click that opens a link. both is Ctrl+click and Shift+click |
link_hover (Added in v0.9.3)
|
"safe" |
safe, always
|
The hover underline and label on a link. safe skips a pane whose program tracks the mouse. always draws them in every pane |
link_opener |
"" |
a command | The command that opens a web link. TUIOS puts the URL where %s is, or at the end. Empty uses $BROWSER, then the system opener |
nvim_navigation |
false |
bool | Let the terminal-mode focus keys move through Neovim splits first. See Neovim pane navigation |
kitty_placeholders |
"auto" |
auto, on, off
|
Draw images placed with kitty Unicode placeholders |
Ctrl+click on a link opens it. Ctrl+drag still moves the pane: a link opens only when the pointer does not move. Only http, https, mailto, ftp and ftps links go to the opener. TUIOS copies other links. Under ssh, on a remote client, or on a machine with no desktop, TUIOS copies the URL, and your terminal can still open the link from the OSC 8 that TUIOS writes. If the opener fails, the dock says so and TUIOS copies the URL. See Links for the opener, the safety rules and a table of terminals.
| Key | Default | Values | What it does |
|---|---|---|---|
scrollback_lines |
10000 |
100 to 1000000 | Lines each pane keeps. Read when the pane is created |
scroll_lines |
3 |
1 to 50 | Lines per mouse wheel notch |
| Key | Default | Values | What it does |
|---|---|---|---|
tiling_scheme |
"spiral" |
spiral, longest_side, alternate, smart_split
|
BSP scheme a workspace starts with the first time it is tiled. A workspace already tiled keeps its own scheme. See BSP tiling |
master_ratio |
50 |
10 to 90 | The share of the screen the master panes take in the master-stack layout, in percent |
master_position |
"left" |
left, right, top, bottom, center
|
The side the master panes take. With center, the stack panes go to the right and the left in turn |
master_count |
1 |
1 to 9 | How many panes are master panes |
master_grid |
true |
bool | With one master on the left, show four or more panes as a grid |
scroll_column_width |
55 |
20 to 100 | Column width in the scrolling layout, in percent |
scroll_column_max |
90 |
20 to 100 | Widest a scrolling column may be set to. 100 lets a column fill the screen |
niri_scroll_cells |
8 |
1 to 200 | Cells the scrolling layout moves per wheel event |
niri_reverse_scroll |
false |
bool | Reverse the wheel in the scrolling layout |
niri_click_reveals |
true |
bool | Clicking a partly hidden column brings it fully on screen |
niri_hover_reveals |
true |
bool | With focus_follows_mouse, hovering does the same |
zoom_size |
95 |
50 to 100 | Share of the screen a zoomed pane takes, in percent. Below 100 the layout stays visible at the edges. 100 is fullscreen |
zoom_max_width |
0 |
cells | Width cap for a zoomed pane. 0 means no cap |
zoom_borderless |
false |
bool | Show a zoomed pane on the whole pane region, with no border and no title bar. The pane's program gets the full size. With it on, zoom_size and zoom_max_width have no effect |
zoom_animation |
true |
bool | Slide a pane into and out of zoom |
zoom_follows_focus |
true |
bool | Moving focus while zoomed zooms the pane you move to |
drop_preview (Added in v0.9.3)
|
"fill" |
fill, outline, off
|
The highlight while you drag a pane in BSP. fill draws a frame in the accent colour, with the border glyphs of the panes, and tints the area inside it (Added in v0.10.0). outline draws only the frame. off draws nothing, and a drop still splits or swaps. See Moving a window with the mouse
|
drop_preview_delay (Added in v0.9.3)
|
"120ms" |
a duration, or 0
|
How long the pointer stays on one drop zone before its highlight shows. 0 shows it at once |
The layout a new session starts in is startup.layout. See The startup table. Each workspace starts with the master-stack options, and the layout keys change the workspace on screen. See Master-stack.
| Key | Default | Values | What it does |
|---|---|---|---|
dockbar_position |
"top" |
top, bottom, hidden
|
Edge the dock sits on |
dock_workspace_tabs |
true |
bool | Show the clickable workspace strip |
dock_workspace_tab_format |
"" |
template | Tab label with {index} and {name}, and {icon} and {zoom} (Added in v0.10.0). Empty means {name}. See Workspace tab style
|
dock_workspace_label_max |
12 |
cells, 0 for no cap |
Longest workspace tab label. The cap counts the characters of the tab format |
dock_workspace_tooltip |
true |
bool | Show a truncated workspace name in full on hover |
dock_pill_caps |
true |
bool | Rounded caps on every dock pill: the mode chip, the workspace pills and the minimized windows. false draws flat pills |
dock_compact |
false |
bool | Draw the dock as one row with no rule. The panes get one more row |
dock_mode_icon_window |
unset | text, "" or default
|
Icon in the mode pill in window mode. Unset or default uses the icon of the glyph set. An empty value shows no icon |
dock_mode_icon_terminal |
unset | text, "" or default
|
Icon in the mode pill in terminal mode |
dock_mode_icon_tiling |
unset | text, "" or default
|
Icon in the mode pill while tiling is on |
show_clock |
false |
bool | Show the clock |
clock_format |
"15:04:05" |
Go time layout | Clock format, for example 15:04 or Mon 3:04PM
|
show_cpu |
false |
bool | Show a CPU graph |
show_ram |
false |
bool | Show RAM use |
hide_clock |
false |
bool | Deprecated. Use show_clock
|
dock_workspace_label_max stops one long workspace name from pushing the other tabs off the dock. Set it to 0 to show the whole name. The workspace strip then scrolls. A dock with no room for one full tab still shows the current tab, cut to fit.
dock_compact works with the dock at the top and at the bottom. It has no effect when dockbar_position is hidden. The change applies at once. A compact dock does not show the rule that tells how long a dock message stays. When clients with different docks share a session, the panes keep the largest dock height. A client with a compact dock shows a blank row in that case.
The mode pill icons can be any text up to 8 cells wide. A wide character, such as 終, counts as two cells. The dock does not accept control characters. For an icon that it does not accept, the dock shows the default icon and the config check shows a warning. The pill adds one space on each side of the icon. If an icon is empty and the mode has no other text, the dock shows no pill in that mode. While tiling is on, the pill also shows the next split direction, V or H.
[appearance]
dock_mode_icon_window = "WM"
dock_mode_icon_terminal = ">_"
dock_mode_icon_tiling = ""To remove the whole mode pill, remove "mode" from the left list in the [dock] table. You can also set the icons on the settings page, in the Dock section, or with tuios set-config appearance.dock_mode_icon_window WM. The change applies at once.
To reorder the dock or add your own cells, see The dock table.
Two rail options sit directly in [appearance]:
| Key | Default | What it does |
|---|---|---|
global_session |
true |
Offer a session on the rail that holds panes from several machines, once a second machine is reachable |
git_dirty |
true |
Count staged, changed and untracked paths in the rail's git section. This is the only part of that section that walks the working tree |
The rest are in [appearance.sidebar].
[appearance.scrollbar]:
| Key | Default | Values | What it does |
|---|---|---|---|
style |
"track" |
track, thin
|
A full-height track, or a hairline thumb over the content |
thumb |
"" |
one-cell glyph | Thumb glyph. Empty uses the style's own |
track |
"" |
one-cell glyph, or none
|
Track glyph. Empty uses the style's own |
tint |
"quiet" |
quiet, border, muted, or #RRGGBB
|
Bar colour |
[appearance.selection] sets the colours TUIOS draws over pane content to mark text.
| Key | Default | What it sets |
|---|---|---|
bg |
"#45475A" |
Background of selected text |
fg |
"" |
Selected text. Empty keeps the text's own colour |
bold |
false |
Also draw selected text bold |
search_bg, search_fg
|
"#8A6D2F", "#F5E7C8"
|
Every search match |
match_bg, match_fg
|
"#E5A93D", "#1C1B19"
|
The match the cursor is on |
cursor_bg, cursor_fg
|
"#39C5CF", "#08222B"
|
The copy mode cursor |
flash |
true |
Sweep a band of light over text you just copied. appearance.motion = "none" also turns it off |
flash_color |
"" |
Colour of the sweep |
flash_ms |
420 |
Length of the sweep, 80 to 3000 ms |
flash_style |
"diagonal" |
diagonal, diagonal-reverse, horizontal, vertical
|
copy_entry |
"cursor" |
Where the copy cursor starts. cursor is the terminal cursor, usually the prompt line, as in tmux. center is the first column of the middle row |
multi_format |
"plain" |
The format that multi copy mode starts with: plain, markdown or json
|
osc52_write |
"focused" |
What OSC 52 from a pane does to the host clipboard: off, ask, focused or on. See Clipboard writes from a pane
|
copy_command |
"" |
A command that gets each yank in copy mode on stdin. The clipboard gets its stdout. Empty copies the selection as it is. See Pipe a yank through a command |
An empty foreground keeps the colour the program wrote. An empty background falls back to the default.
A program in a pane sets the clipboard with OSC 52. An editor uses OSC 52 to yank over ssh. Any output can also carry OSC 52, for example a file that a program prints. appearance.selection.osc52_write controls what happens.
| Value | What happens |
|---|---|
off |
The host clipboard does not change. |
ask |
The dock shows a message. Click the message to copy the text. |
focused |
The focused pane copies. A write from another pane shows a message in the dock. This is the default. |
on |
Every pane copies. This is the behaviour of v0.8.0. |
[appearance.selection]
osc52_write = "focused"- When the focused pane copies text with a line break or a control character, the dock shows one line for that pane. A one-line copy shows nothing.
- Each pane keeps one clipboard ask. A click copies only the text that the dock shows. If the text changes first, click the new message.
- A pane that runs without the daemon keeps its own copy of the text. The program reads it back with an OSC 52 query. A pane under the daemon keeps no copy, so an OSC 52 query there always gets an empty answer.
- TUIOS drops a clipboard reply that answers no read request of TUIOS.
Pastes, notifications, OSC 66 text and link addresses lose their control characters. A paste into a pane keeps its tabs and line breaks. Text that holds ESC[201~ cannot end a bracketed paste early.
[appearance.sidebar] configures the rail at the side of the screen.
| Key | Default | Values | What it does |
|---|---|---|---|
enabled |
true |
bool | Show the rail |
position |
"right" |
right, left, hidden
|
Edge the rail sits on |
width |
24 |
columns | Preferred width on a wide screen |
background |
"" |
off, theme, #RRGGBB
|
The rail's background. Empty follows appearance.background. See Backgrounds
|
agent_rest_fold |
"1h" |
a duration, or off
|
How long an agent row rests (idle, unknown, or done and seen) before it folds into one +N at rest line |
subagent_rows (Added in v0.9.3)
|
"rows" |
rows, tree, count
|
How an agent row shows its subagents. rows draws a row for each subagent under its agent's row. tree draws the same rows on tree lines. count draws the count alone. See Subagent rows
|
subagent_collapse (Added in v0.9.3)
|
"off" |
off, expanded, collapsed
|
Draw a heading line, such as ▾ 4 subagents, over the subagent rows of each agent. The value is the state at the start |
subagent_fields (Added in v0.9.3)
|
"mark,name,time,note" |
a comma list of mark, name, type, time, note
|
The parts each subagent row shows. type is the agent type, such as Explore. note is the second line |
subagent_max_rows (Added in v0.9.3)
|
0 |
0 to 16 | The most lines the subagents of one agent use, the +N more line included. 0 means no limit |
subagent_hide_finished (Added in v0.9.3)
|
"off" |
a duration, or off
|
Remove the row of a finished subagent this long after it ends |
header_case (Added in v0.9.3)
|
"lowercase" |
lowercase, uppercase
|
How the headers of the rail sections read. lowercase is the quiet default. uppercase draws every header as a bold heading, with a rule to the edge of the rail |
sections |
"sessions:25,terminals,files:25,agents:34" |
layout string | Which sections to draw, in what order. See below |
show_glyphs |
true |
bool | Agent-state glyph on each row |
show_counts |
true |
bool | Window count on each session row |
show_numbers |
false |
bool | Switch number (the one switch_session_N opens) before each local session name, for sessions 1 to 9 |
marquee |
true |
bool | Scroll a hovered row's long title |
tooltips |
true |
bool | Label the collapsed strip on hover |
file_icons |
true |
bool | A Nerd Font icon per file type in the files section |
file_icon_colors |
true |
bool | Draw each icon in its file type's colour |
folder_click |
"navigate" |
navigate, cd, both
|
What a click on a folder row does |
file_actions |
true |
bool | Allow create, rename, delete, copy, cut and paste |
file_delete |
"trash" |
trash, permanent
|
Where d sends a file |
editor |
"" |
a command | The terminal editor that opens a text file. Empty uses $EDITOR, then $VISUAL, then vi. See The file editor
|
Deprecated keys still load: show_windows, show_agents and workspaces in this table, and the flat sidebar_* keys in [appearance]. They are folded into the current keys when the file is read.
One string sets which sections the rail draws, their order from the top, and the share of the rail each may take.
[appearance.sidebar]
sections = "sessions:25,terminals,files:25,agents:34"- Separate sections with commas. Add
:Nfor a share in percent. - The section names are
sessions,terminals,files,agents,gitandcustom. Leave a name out to hide that section. See The custom section. - A share is a ceiling, not a reservation. A section only takes the lines its rows fill. A section with no share takes what the others leave.
-
spacerdraws nothing and takes lines. It is the only name you can repeat. A spacer with a share keeps that share. A spacer with no share takes the lines nothing else wants, which pushes what follows to the bottom.
[appearance.sidebar]
# Sessions at the top, files at the bottom
sections = "sessions:30,terminals,spacer,files:40"An unknown name, a bad percent or a repeated section is dropped and reported as a config warning.
The settings page has an editor for this: the Sections row under Sidebar. Arrows select, Shift+arrows move, Enter adds or removes a section, ←/→ change the share, r restores the default and u undoes your edits.
The files section lists the focused pane's directory. It is read again when the shell changes directory, when the focus moves and when a client attaches. It is also read again when a file in the listed folder is added, removed or renamed, by the pane or by another program. tuios asks the system to report these changes (inotify on Linux, kqueue on macOS and the BSDs, ReadDirectoryChangesW on Windows). Nothing polls, and a change to the contents of a file does not cause a read. For a pane on another machine, the list changes only when the shell changes directory.
folder_click decides what a click on a folder does: navigate opens it in the listing, cd sends the focused pane there, both does both. A cd is only typed into a pane that is at a shell prompt.
The file keys are in [keybindings.sidebar_files] and work while the rail has the keyboard and the cursor is on a file row. See Keybindings. Each action opens a centred dialog. The delete dialog opens on Cancel, so Enter on an untouched dialog deletes nothing.
Delete uses the freedesktop.org trash in your home directory, so your desktop's trash shows the file and can restore it. D deletes permanently. Set file_delete = "permanent" to make d permanent too. The per-volume trash is not implemented.
Y copies the absolute path of the selected file or folder. A folder's context menu has a Copy path row that does the same.
On Windows, a shell under MSYS2 or Cygwin reports its folder as /c/work. TUIOS reads that as C:\work, by the rules of cygpath, so the files section lists the right folder.
Shift+Enter on a text file opens it in a terminal editor. editor sets the command. The settings page shows it as File editor under Sidebar.
[appearance.sidebar]
editor = "micro"- Other terminal editors and arguments work too, such as
nanoorvim -f. - Quote a path or an argument that contains spaces. TUIOS splits the command into arguments and runs no shell.
- TUIOS adds the full path of the file at the end.
- When
editoris empty, TUIOS uses$EDITOR, then$VISUAL, thenvi. - File links and hints open files with the same command.
- TUIOS reads the first 512 bytes of a file to check that it is text. It refuses binary files and special files.
The custom section shows the output of a command you write. Place custom in sections and describe it in [appearance.sidebar.custom]:
[appearance.sidebar]
sections = "sessions:25,terminals,custom:35,agents:30"
[appearance.sidebar.custom]
title = "Brief"
command = "agent-brief render"
refresh = "event:window-focused,agent-state"| Key | What it does |
|---|---|
title |
The section's heading. Defaults to Custom
|
command |
Run with sh -c. Every line of stdout is a row |
refresh |
once (the default), a duration such as "30s" (one second at least), or event:TYPE[,TYPE...] with the dock's event types. push is refused |
The command follows the rules of a dock component. It runs in the client, under a three second timeout, with bounded output. SGR colour stays, and every other control sequence is removed. A command that fails, times out or prints nothing leaves the title over an empty section. Five failures in a row stop it until the config reloads or you run tuios refresh-dock rail/custom. tuios list-dock-components lists it as rail/custom. Rows are cut to the rail's width and to the section's lines, with … +N for the rows below.
Each run gets the client's environment and these variables: TUIOS_SESSION, TUIOS_SOCKET, TUIOS_RAIL_SECTION (custom), TUIOS_RAIL_WIDTH, TUIOS_RAIL_HEIGHT, TUIOS_ACTIVE_PANE_ID and TUIOS_ACTIVE_PANE_CWD. An event that arrives during a run causes one more run after it, so a quick focus change never leaves the old pane's rows on screen. The command does not run while the rail is hidden or folded.
sections works with tuios set-config. [appearance.sidebar.custom] is read from the file only, so a program in a pane cannot change the command.
(Added in v0.9.3)
The rail can show on both edges of the screen at the same time. [appearance.sidebar] still configures the original rail. Add [appearance.sidebar.left] or [appearance.sidebar.right] to configure one edge:
[appearance.sidebar]
position = "right"
sections = "sessions,terminals,custom"
[appearance.sidebar.custom]
command = "right-summary"
[appearance.sidebar.left]
enabled = true
width = 28
sections = "sessions,files,custom"
[appearance.sidebar.left.custom]
command = "left-summary"| Key | What it does |
|---|---|
enabled |
Show a rail on this edge. The edge opposite position stays hidden until you set enabled = true
|
width |
The preferred width of the rail on this edge, in columns |
sections |
The section layout of this edge. See sections |
custom |
A table with the command of this edge's custom section |
session (Added in v0.10.0)
|
A daemon session whose panes this rail shows in place of its sections. See A daemon session in a rail |
session_workspace (Added in v0.10.0)
|
0, the default, shows the session rail on every workspace. 1 to 9 shows it only on that workspace |
- On the edge of
position, the edge table overrides only the keys it sets. The other keys come from[appearance.sidebar]. - On the other edge, a key you do not set takes its default. An unset
sectionstakes the default section order. - With
position = "hidden", an edge table withenabled = truestill draws. When both edges are on, the left one is the original rail. - Each rail has its own scroll, keyboard focus and custom command. Both read the same sessions, files and git state.
- On a narrow screen, the second rail closes before the original rail.
The edge tables are read from the file only. tuios set-config cannot change them, because a custom command runs outside the panes. Ctrl+B H shows or hides the second rail. See Session Rail.
(Added in v0.10.0)
A rail can show the panes of a local daemon session. The panes stack top to bottom in the rail. You type in them as in any other pane. Make the sessions, then name them in the edge tables:
tuios new side-left --detach
tuios new side-right --detach[appearance.sidebar.left]
enabled = true
width = 36
session = "side-left"
[appearance.sidebar.right]
session = "side-right"
session_workspace = 2 # show this rail only on workspace 2- Click a pane to type in it. With the rail focused, Ctrl+B - and Ctrl+B c make a new pane in that session, below the others. Ctrl+B n and p change the active pane.
- A rail that is too short to give each pane six rows shows a group of panes around the active one.
- The rail does not resize the panes of the session. It crops them. Another client of the session keeps its size.
- While a session is in the rail, the rail's custom command does not run. An empty
sessiongives the rail its sections back. - A workspace switch, a hidden rail or a detach does not stop the programs in the session. A daemon restart does.
- A session that does not exist yet shows an error in the rail. The rail tries again when the session starts.
- Settings, then Sidebar, has a Left session and a Right session field.
[appearance.sidebar.agent_row] chooses which tokens an agent row shows and styles each one, with optional rules that match on the token's value:
[appearance.sidebar.agent_row]
tokens = ["session", "need", "harness", "name", "elapsed", "context", "subagents", "pr", "meta", "now", "message"]
[[appearance.sidebar.agent_row.elapsed.rule]]
gt = 30 # minutes in the current state
fg = "warning"The tokens above are the shipped list. The other tokens are state, host and prompt. A tokens list you wrote keeps its own order and gets no new tokens, so add subagents or pr to it to see them. need says what a row wants from you, such as approval or question. context shows ctx 84% once the agent's context is 80% full. now is what a working agent is doing, such as Bash: go test ./.... subagents shows how many subagents a Claude Code agent has at work, such as 3 subagents, on any row while any run. When the subagents have rows of their own, it reads 3 of 4 run. pr shows the pull request of the session's worktree branch and its checks, such as PR #12 open pass. meta draws the other keys the pane reported with tuios set-agent-meta, and $ followed by a key, such as $model or $cost, places one key. See Session Rail.
Each rule has one test: equals, contains, starts_with, gt or lt. The first matching rule wins. A mistake in this table is a warning, not an error.
The actions split_ssh_horizontal, split_ssh_vertical and new_window_ssh open a pane on the machine that the focused pane is connected to with ssh. See Keybindings.
appearance.new_window_follow_ssh makes the ordinary actions do the same: split_horizontal, split_vertical and new_window. When the focused pane does not run ssh, these actions open an ordinary pane. The default is false.
[appearance]
new_window_follow_ssh = truetuios finds ssh in the foreground of the pane. ssh can run from a shell, or from sshpass, autossh or mosh. tuios reads only the processes that you own in the pane. It runs the ssh or mosh that it finds on its own PATH, with no shell on this machine. It never runs the program that the pane runs.
The new ssh gets the same environment as an ordinary new pane. If the ssh that tuios follows has an SSH_AUTH_SOCK that names your own agent socket, the new ssh gets that value too. An agent that your shell started in the pane then works in the split. The socket must not be a link, and its folder must be yours and closed to other users.
The new ssh line keeps only what it needs to reach the same host as the same user. It always gets -o ControlMaster=no. Your ~/.ssh/config applies to it as usual, so a setting that you put there still works in the split.
tuios follows an ssh line only when each -o option is on one of two lists. The new pane keeps these options:
AddressFamily, BatchMode, CertificateFile, CheckHostIP, Ciphers, Compression, ConnectionAttempts, ConnectTimeout, EscapeChar, HostKeyAlgorithms, HostName, IdentitiesOnly, IdentityFile, KbdInteractiveAuthentication, KexAlgorithms, LogLevel, MACs, PasswordAuthentication, Port, PreferredAuthentications, ProxyJump, PubkeyAcceptedAlgorithms, PubkeyAuthentication, ServerAliveCountMax, ServerAliveInterval, StrictHostKeyChecking, TCPKeepAlive and User.
The new pane does not get these options, and tuios still follows the line:
- Forwards and sessions:
LocalForward,RemoteForward,DynamicForward,GatewayPorts,ExitOnForwardFailure,ClearAllForwardings,Tunnel,TunnelDevice,RemoteCommand,SessionType,StdinNull,ForkAfterAuthenticationandRequestTTY. - Files, sockets, environment and forwarding:
ControlMaster,ControlPersist,ControlPath,UserKnownHostsFile,GlobalKnownHostsFile,HostKeyAlias,IdentityAgent,SendEnv,SetEnv,ForwardAgent,ForwardX11andForwardX11Trusted. - The flags
-A,-X,-Y,-S,-L,-R,-D,-W,-w,-N,-f,-n,-T,-t,-Mand-s.
These ssh lines get an ordinary pane:
- An
-ooption that is not on the two lists above.ProxyCommand,LocalCommand,XAuthLocationandMatchare examples. - An option or a value with a control character, such as a line break.
-
-E,-F,-I,-G,-V,-Qand-O, and an option that tuios does not know. - A
-JorProxyJumphop that is not[user@]host[:port]or thessh://form, made of letters, digits and._:@[]-, or a hop that starts with-. - A mosh destination that starts with
-. - mosh with
--ssh,--clientor--server. - ssh that another program starts, such as
scp,rsync,gitorsftp.
Put a ProxyCommand or a ProxyJump in ~/.ssh/config to use it with a split. A split with -J on the command line is followed.
The new pane starts in the remote folder when the remote shell reports its folder with OSC 7. The host name in the report must be the same as the host in the ssh line, or as the HostName that ssh -G gives for that line. reachy-mini matches pollen@reachy-mini. When ~/.ssh/config makes prod an alias for box.example.com, a report from box.example.com or from box matches ssh prod. A report from box.other.com does not. When there is no match, the new pane starts in the remote home folder.
fish sends the report by default. For bash, add this line to ~/.bashrc on the remote machine:
PROMPT_COMMAND='printf "\033]7;file://%s%s\033\\" "$HOSTNAME" "$PWD"'${PROMPT_COMMAND:+;$PROMPT_COMMAND}For zsh, add these lines to ~/.zshrc on the remote machine:
autoload -Uz add-zsh-hook
_tuios_osc7() { printf '\e]7;file://%s%s\e\\' "$HOST" "$PWD" }
add-zsh-hook precmd _tuios_osc7To go to the folder, tuios adds -t, -o RemoteCommand=none and the command exec sh -c 'cd "FOLDER" ...' to ssh. This works with every login shell that can start sh. If the folder is gone, the new pane starts in the home folder. A folder name with a quote, $, a backslash or ! is not used. A Windows folder, such as C:/Users/x or a network share //server/share, is not used either, because a Windows ssh server has no sh. The new pane then starts in the home folder.
A theme sets the chrome's colours. A glyph set sets its shapes: border corners, window buttons, rules, separators and rail marks.
[appearance]
glyphs = "heavy"tuios list-glyphs # every set, and every role name
tuios list-glyphs heavy # what this set draws, role by role
tuios set-config appearance.glyphs heavy| Id | What it is |
|---|---|
default |
Rounded frame, Nerd Font powerline caps, ✕ and □ buttons |
unicode |
Box drawing and geometric shapes, with no Nerd Font glyphs |
heavy |
One stroke weight heavier throughout |
ascii |
7-bit ASCII only |
A set's border is used only when border_style = "glyphs". This keeps a set from silently overriding a border style you chose. A set that defines only some border runes gets the rounded border for the rest.
Save <id>.json in the glyphs directory. tuios list-glyphs prints its path.
{
"display_name": "Mine",
"inherits": "heavy",
"bullet": "◦",
"focus": "▐",
"border": {
"top_left": "╔",
"top_right": "╗",
"bottom_left": "╚",
"bottom_right": "╝"
}
}Every field is optional. The id defaults to the file name. A role you do not set comes from the inherited set, and finally from default. Inheritance goes up to eight levels deep.
The roles:
-
Window buttons:
close,maximize,minimize,dot,pill_left,pill_right -
Rules and separators:
rule,separator,arrow_left,arrow_right,dash_rule -
Rail marks:
focus,attention,bullet,add,collapse,expand,fold_open,fold_shut,tree_branch,tree_last -
Files section:
folder,parent,file -
Scrollbar:
scrollbar_thumb,scrollbar_track -
Text:
ellipsis,sigil -
Border:
border.top,border.bottom,border.left,border.right, the four corners, and the junctionsmiddle,middle_top,middle_bottom,middle_left,middle_right
Most roles must be exactly one cell wide, because the window buttons' click areas are fixed offsets. A glyph of the wrong width falls back to the default. separator, ellipsis, collapse and expand may be any width. To see what was dropped:
tuios list-glyphs mine --json | jq -r '.problems[]?'The directory is read again on every lookup, so a new file can be selected at once. A file that does not parse is skipped and listed under problems.
--ascii-only replaces non-ASCII glyphs role by role, so a set keeps every role it already spelled in ASCII. A glyph set never changes colours, and it does not change the dock's icons.
The settings page has a Glyph set row under Theme. Its picker applies each set as you move through the list.
342 themes ship with TUIOS.
tuios --list-themes # every theme id
tuios --preview-theme dracula # the 16 ANSI colours
tuios list-themes --filter gruvbox # from a running daemon, with --json
tuios list-themes catppuccin_mocha # colours and their contrast[appearance]
theme = "dracula"With no theme, TUIOS uses your terminal's own colours. A theme only changes ANSI colours 0 to 15. 256-colour and truecolor output is drawn as the program wrote it.
To pick one in the app, press Enter on the Theme row of the settings page, or choose Theme Picker in the command palette. The picker previews as you move and Esc restores the theme you had. It has no key binding.
Save one theme per JSON file in ~/.config/tuios/themes/. Subdirectories are ignored.
{
"id": "my-theme",
"display_name": "My Theme",
"fg": "#e0def4",
"bg": "#191724",
"black": "#26233a",
"red": "#eb6f92",
"green": "#31748f",
"yellow": "#f6c177",
"blue": "#9ccfd8",
"purple": "#c4a7e7",
"cyan": "#ebbcba",
"white": "#e0def4"
}-
iddefaults to the file name, lowercased.display_namedefaults toid. - The colour names are
black,red,green,yellow,blue,purple,cyan,white, each with abright_variant, plusfg,bgandcursor. Notepurple, notmagenta. - A missing colour gets the xterm default. A missing
bright_*colour copies its normal colour, so set them if you want bright text to look different. - An optional
chromeobject sets the UI colours directly (accent,accent_bright,success,warning,error,info,surface) without changing the pane palette. - A file that does not parse is skipped and logged.
tuios import-theme <file> converts a kitty, Ghostty, Alacritty or WezTerm scheme into a file here.
Selecting an id that is not registered yet, with set-config or the settings page, reads the directory again first. So does tuios list-themes. The picker builds its list when it opens.
[notifications] sets how long messages stay on screen. 0 uses the built-in default.
| Key | Default | What it does |
|---|---|---|
duration |
0 |
Seconds an info or success message stays up |
warning_duration |
0 |
Seconds a warning stays up |
error_duration |
0 |
Seconds an error stays up when error_sticky is off |
error_sticky |
true |
Errors stay until you press Esc |
[notifications.agent] controls what happens when an agent in a pane changes state.
| Key | Default | What it does |
|---|---|---|
enabled |
true |
Turn every agent alert on or off |
notify |
true |
Send a desktop notification through the terminal (OSC 9, so it works over SSH) |
dock |
true |
Show the alert in the dock. Click it to go to the pane |
sound |
false |
Make the alert audible |
sound_mode |
"audio" |
audio plays a cue, bell sends BEL, both does both |
sound_cooldown_seconds |
3 |
Shortest gap between two sounds, across all panes |
command |
"" |
Shell command to run on an alert |
settle_seconds |
2 |
Wait this long, and drop the alert if the pane leaves the state |
suppress_focused |
true |
No alert for the pane you are looking at |
quiet_hours |
"" |
HH:MM-HH:MM in local time when nothing alerts |
[notifications.agent.states] turns alerts on per state: needs_input, errored and done default to true. idle and working default to false.
To hear an alert, set sound = true. sound_mode then decides how:
-
audioplays a short cue through the audio player of the machine that runs TUIOS:paplay,pw-playoraplayon Linux,afplayon macOS, PowerShell on Windows. If the machine has no player, the alert is silent and TUIOS shows no message. -
bellsends a BEL character to your terminal. Your terminal decides what a bell does, and some terminals make no sound. In kitty, check thatenable_audio_bellisyes. Other terminals have a similar bell setting. -
bothdoes the two.
Over SSH or in the browser, audio plays on the server, not where you sit. Use bell there. See Agents.
[notifications.agent.sounds] replaces the two built-in cues with your own files: needs_input (also used for errored) and done (also used for idle). A path that does not exist falls back to the built-in cue.
The after-agent-state hook follows the same rules, including the settle wait.
[notifications.mail] controls the alerts for mail to you and for session notices. A key that you leave out uses the same key from [notifications.agent].
| Key | Default | What it does |
|---|---|---|
enabled |
follows notifications.agent.enabled
|
Turn mail alerts on or off |
notify |
follows notifications.agent.notify
|
Send a desktop notification |
dock |
follows notifications.agent.dock
|
Show the alert in the dock. Click it to open the thread |
sound |
follows notifications.agent.sound
|
Make the alert audible |
between_agents |
false |
Also alert on a message from one agent to another |
TUIOS warns when between_agents is on and mail alerts are off.
The sound mode, the cooldown, the cue files and the quiet hours always come from [notifications.agent]. tuios set-config notifications.mail.sound "" clears a key, and get-config then prints (follows notifications.agent.sound). See Agent Messaging.
The prefix key. The default is ctrl+b.
[keybindings]
leader_key = "ctrl+a"alt+space is already the app launcher.
opt+ and option+ mean alt+ on every platform, cmd+ and command+ mean super+, and control+ means ctrl+. See Modifier spellings. Press the leader two times in terminal mode to send it to the pane.
Each section is a table under [keybindings]. Each action takes a list of keys. The Keybindings page lists every default.
| Section | When it is active |
|---|---|
window_management |
Window mode: windows, sessions, zoom, copy |
workspaces |
Window mode: switch and move between workspaces |
layout |
Window mode: snapping, tiling, swaps, resizes, splits, preselect |
mode_control |
Window mode: enter terminal mode, help, settings, quit |
restore_minimized |
Window mode: restore a minimized window by number |
system |
Window mode: the spotlight |
navigation |
Empty. Kept so older files still parse |
terminal_mode |
Terminal mode, no prefix |
global |
Both modes: the command palette and the launcher |
prefix_mode |
After the leader key |
window_prefix |
After leader, t |
minimize_prefix |
After leader, m |
workspace_prefix |
After leader, w |
layout_prefix |
After leader, L |
tape_prefix |
After leader, T |
debug_prefix |
After leader, D |
script |
While a tape plays |
sidebar |
While the rail has the keyboard |
sidebar_files |
While the rail's cursor is on a file row. Checked before sidebar
|
sidebar_agents |
While the rail's cursor is on an agent row. Checked before sidebar
|
inbox |
While the Inbox is open |
inbox_peek |
While a prompt is open over the Inbox |
mail |
While the mailbox is open |
lists (Added in v0.10.0)
|
The keys that move the cursor in every list. See Lists and the settings page |
gui (Added in v0.10.0)
|
The keys of a native GUI client that uses tuios gui-bridge. A terminal client does not read it |
-
Modifiers:
ctrl+,alt+,shift+,super+. Alsoopt+andoption+foralt+,cmd+andcommand+forsuper+, andcontrol+forctrl+, on every platform. -
Special keys:
enter,return,esc,escape,tab,space,backspace,delete,up,down,left,right,home,end,pgup,pageup,pgdown,pagedown,f1tof12. - Any single character: letters, digits, symbols, and accented letters.
-
Held modifiers:
leftalt,rightalt,leftctrl,leftsuperand the like, forhold_window_modeonly. They need the Kitty keyboard protocol.
super+ only reaches TUIOS in terminals that speak the Kitty keyboard protocol. On Linux, opt+1 is alt+1.
new_window = ["n", "ctrl+t"] # several keys for one action
close_window = [] # no key: the key goes to the paneAn empty list is different from a missing line. A missing action gets its default back at the next load. An empty list stays empty.
shift+1 and ! are the same key, and so are shift+a and A. Binding one spelling binds both. Do not bind the two spellings to different actions.
On macOS, the workspace and preselect defaults use opt+, and terminal_mode adds opt+tab, opt+shift+tab and opt+esc. Writing alt+ on macOS works but gives an advisory warning. Elsewhere the defaults use alt+. See Keybindings.
| Key | Default | What it does |
|---|---|---|
log_level |
"off" |
off, errors, basic, messages, verbose or trace
|
agent_autodetect |
true |
Detect an agent CLI running in a pane and show its state |
agent_detect_seconds |
0 |
Seconds between checks. 0 means 2. A negative number turns detection off |
agent_binaries |
[] |
Extra program names to treat as agents, added to the built-in list |
resume_agents |
"ask" |
After a daemon restart: ask puts a Resume row in the Inbox for each pane whose agent was running, auto types each harness's resume command into the new shell, off does neither |
persist_scrollback |
true |
Save each pane's history with its session, and show it above a divider after a daemon restart or a reboot. The files can hold secrets. false stops this, and the daemon deletes the saved history when it next starts. See Pane history
|
persist_scrollback_lines |
0 |
Most history lines one pane saves. 0 means 1000 |
persist_scrollback_kb |
0 |
Most KiB one pane's saved history takes on disk, compressed. 0 means 2048 |
window_size |
"smallest" |
The size of a session that has more than one client: smallest, largest or latest (the client that last had input). The names are those of the tmux window-size option. See Session size
|
display (Added in v0.9.3)
|
"follow" |
"follow" gives the new panes of a daemon with no display the display of the local client. "off" gives them none. See The display of the client
|
respond_from_shell |
false |
Let tuios respond, run from a shell outside every pane, answer an agent's prompt. A caller inside a pane is refused either way. Not settable with set-config
|
To change window_size for one session while it runs, use tuios set-config daemon.window_size latest -s NAME. That value is not saved to the file. A change in the file applies when the daemon starts again.
(Added in v0.9.3)
A daemon that a host link starts over ssh has no WAYLAND_DISPLAY or DISPLAY. Its panes have none either, so wl-copy, wl-paste, xclip and image paste fail. With display = "follow", the default, the new panes of such a daemon get the display of the local client that attached to the session or used it last.
[daemon]
display = "follow"- A daemon that has a
WAYLAND_DISPLAYor aDISPLAYof its own keeps it. The option does nothing then. - Each session has a link to the Wayland socket of the client:
wayland-<session id>.sock, in the folder of the daemon socket. A new pane getsWAYLAND_DISPLAYset to the full path of the link. The link moves to the socket of the newest client. - A new pane also gets
DISPLAYandXAUTHORITYof the client. - A shell that started before a desktop client attached has no display. Run
eval "$(tuios display-env 2>/dev/null)"in that shell, or add the line to your shell rc. - When the client detaches, the session uses the display of the client before it. When no attached client has a display, tuios removes the link.
- tuios uses the display of
tuios attachandtuios newonly. The client must run on this machine, as your user, and outside every pane. tuios ignores a client that comes through a host link, the tuios SSH server ortuios-web. -
DISPLAYmust be:Nor:N.S. tuios ignores a forwarded display such aslocalhost:10.0. - The option is read from the file only.
tuios set-configcannot change it.
tuios set-config daemon.log_level messages changes the level of a running daemon at once. verbose and trace also log pane content, window titles and paths. tuios logs reads the log.
[startup] decides how a new session starts. It never changes a running session.
| Key | Default | What it does |
|---|---|---|
daemon |
true |
A bare tuios attaches to a daemon session instead of running standalone |
tiled |
true |
Start with tiling on |
layout |
"bsp" |
Tiling layout: bsp, master-stack or scrolling
|
open_default_window |
false |
Open one window when a session starts empty |
start_in_terminal_mode |
false |
Start in terminal mode when a window is present |
--standalone and TUIOS_NO_DAEMON=1 override daemon for one run or one shell. If the daemon will not start, a bare tuios runs standalone and says so.
Note
Older config files keep their behaviour
daemon and tiled default to true in a new install. A config file that already exists and does not mention them reads them as false, so an upgrade keeps the floating, standalone start it had. Add the keys to opt in.
In the same way, v0.8.0 changed eight appearance defaults: the rail is on, on the right, 24 columns wide; the dock and the window titles are on top; a zoomed pane takes 95% of the screen; a double click starts typing; and the scrollbar is a track. A config file that sets one of these keys keeps its value. The first-run file older releases wrote names most of them, so such an install keeps the old look until you remove those lines.
A session keeps its own layout once it is running. Attaching never rearranges a session to match your config.
When the last pane on the workspace on screen closes, TUIOS shows the workspace you came from. This happens when the pane's program stops, when you close the pane, when tuios xpanes -ss closes it, and with tuios close-workspace.
| Key | Default | What it does |
|---|---|---|
return_when_empty |
true |
Show the workspace you came from when the workspace on screen becomes empty. false keeps the empty workspace on screen |
new_window_when_empty |
false |
Open a pane when you switch to a workspace that has no panes, as the new-window key does |
TUIOS goes back in this order:
- For a workspace that
tuios xpanesopened, the workspace where you rantuios xpanes. - The workspace you showed last that still has panes. After a chain of empty workspaces, TUIOS goes back past each one.
- The workspace with the lowest number that has panes.
When no workspace has panes, TUIOS shows the splash screen. The workspace on screen is the same for every client of the session, so every client goes back. A pane that moves to another workspace does not close, so TUIOS does not go back.
[workspaces]
return_when_empty = falseThe daemon reads the setting when it starts and when the file changes. tuios set-config workspaces.return_when_empty false changes it while TUIOS runs.
By default, a workspace with no panes shows the splash screen. To open a pane there when you switch to it, as tabs do in other multiplexers, turn on new_window_when_empty:
[workspaces]
new_window_when_empty = trueThe new pane starts in the folder of the pane that had focus on the workspace you came from. It starts in the session's start folder (the folder that tuios new --cwd gave) when you came from a workspace with no pane, from a scratch terminal, or from a pane on another machine. With appearance.new_window_follow_ssh = true, a pane that runs ssh gives a new pane that runs the same ssh, as the new-window key does.
A pane opens only when you switch: with a workspace key, a click on the dock, the workspace switcher, the pane navigator or the command palette. These switches do not open a pane:
-
move_and_followand other moves that take a pane with them. -
tuios xpanes,tuios select-workspace,tuios run-commandand tape scripts. - The switch back that
return_when_emptymakes. - The start of TUIOS and an attach to a session.
A workspace gets one pane, also when two clients switch to it at the same time. When you switch away before the pane opens, you stay where you went.
Each client reads the setting from its config file, and a change applies at once. The settings page lists it under Daemon, and tuios set-config workspaces.new_window_when_empty true changes it while TUIOS runs.
The daemon must be from the same release or newer. An older daemon opens no pane. Run tuios kill-server and start TUIOS again to load the new daemon.
The app launcher starts a desktop entry in a new pane. A graphical program opens its own window, so its pane stays empty. Set launcher.gui_command to start such an entry with another command. The launcher adds the entry's argv to that command. Entries with Terminal=true and programs from $PATH still open in a pane.
[launcher]
gui_command = "tuios-wayland launch --" # or "niri msg action spawn --"Warning
Use a command that runs an argv
Do not use a command that joins its arguments into a shell line, such as swaymsg exec -- or hyprctl dispatch exec. With such a command, the text of a desktop entry becomes shell code. Any installed package can write a desktop entry.
[tape] controls project tapes: a .tuios.tape file in a directory. See Tape Scripting.
| Key | Default | What it does |
|---|---|---|
autorun |
"ask" |
off ignores project tapes. ask shows a badge and runs nothing until you choose. auto runs a trusted, unedited tape on entry |
auto_review |
false |
Open the review dialog when a tape is found, instead of only showing the badge |
A tape edited after you trusted it is untrusted again.
| Key | Default | What it does |
|---|---|---|
show_key_events |
false |
Show the keycast of recent keypresses |
--show-keys and Ctrl+B D k toggle the same overlay. See Showkeys.
[screenshot] sets the defaults for tuios screenshot and the in-app capture (Ctrl+B C). Each key has a command flag that overrides it for one capture.
| Key | Default | Values | What it does |
|---|---|---|---|
format |
"png" |
png, svg, ansi, html, txt
|
Output format |
directory |
"~/Pictures/tuios" |
path | Where captures are saved |
copy |
true |
bool | Try to copy the capture to the clipboard |
preview |
true |
bool | Open the preview panel after a capture |
frame |
"window" |
window, plain, none
|
A window card, a plain card, or nothing |
background |
"auto" |
auto, none, #RRGGBB, #RRGGBB..#RRGGBB
|
Backdrop. auto derives it from the theme. Two colours make a gradient |
padding |
48 |
0 to 128 | Space around the card, in pixels |
radius |
10 |
0 to 32 | Card corner radius, in pixels |
shadow |
true |
bool | Soft shadow under the card |
controls |
"auto" |
auto, macos, glyphs, none
|
Window buttons on the card |
title_format |
"{title}" |
template | Title bar text, with {title}, {index} and {cwd}
|
font_family |
"JetBrains Mono, monospace" |
string | Font to use when your terminal does not report its own |
font_file |
"" |
path | Font file to draw with. Wins over every other font choice, and is embedded in SVG and HTML output |
scale |
2 |
1 to 4 | PNG size multiplier |
cursor |
false |
bool | Draw the cursor cell |
[screenshot]
format = "svg"
frame = "plain"
background = "none"
padding = 24png and svg carry the frame. ansi and txt are plain output, so the frame keys do not apply.
[screensaver] animates the screen after a period with no input. It is off by default.
| Key | Default | What it does |
|---|---|---|
enabled |
false |
Start after idle_minutes with no input |
idle_minutes |
10 |
Minutes of quiet before it starts, 1 to 240 |
effect |
"random" |
The effect, or random for a different one each time |
while_busy |
false |
Start even while a pane runs a command or an agent |
[screensaver]
enabled = true
idle_minutes = 5
effect = "matrix"There are 36 effects. tuios list-options screensaver.effect prints them. The settings page previews each effect on your current screen.
S in window mode starts the screen saver now, whatever enabled says.
[hints] sets what hints mode (Ctrl+B F) labels.
| Key | Default | Values | What it does |
|---|---|---|---|
builtins |
"all" |
all, none, or names such as url,path,sha
|
The built-in patterns to look for |
patterns |
[] |
list of Go regular expressions | Your own patterns. A group named match sets the part that is copied |
alphabet |
"asdfghjkl" |
lowercase letters | The letters of the labels, easiest first |
open |
true |
bool | Let Ctrl and a label open a URL or a path |
dim |
60 |
10 to 90 | How much the text around the labels dims, in percent |
all_panes |
false |
bool | Put labels on all panes on the workspace, not only on the focused pane. See All panes |
See What hints mode finds for the pattern names.
[panes] sets the keys of the pane labels (Ctrl+B Q) and the first layout of the pane navigator (Ctrl+B /).
| Key | Default | Values | What it does |
|---|---|---|---|
label_keys |
"1234567890" |
letters a to z and digits |
The keys the labels are made of, first pane first. With more panes than keys, a label has two keys |
navigator_layout |
"tree" |
tree, flat, cards
|
The layout the pane navigator opens in. v changes it while the navigator is open |
[scratch] sets the size of the scratch terminal that Ctrl+B g shows in a popup. See Scratch terminal.
| Key | Default | Values | What it does |
|---|---|---|---|
width |
"80%" |
cells (100) or percent (80%) |
Width of the scratch terminal |
height |
"80%" |
cells (30) or percent (80%) |
Height of the scratch terminal |
The old session key is no longer used. TUIOS ignores it, and the config warnings tell you to remove it.
[pip] sets the size and the first corner of the picture-in-picture view. See Picture in picture.
| Key | Default | Values | What it does |
|---|---|---|---|
width |
40 |
12 to 200 | Width of the view in cells, border included. The view gets smaller on a small screen |
height |
12 |
3 to 100 | Height of the view in cells, border included |
corner |
"bottom-right" |
bottom-right, bottom-left, top-right, top-left
|
The corner the view goes to first. The view moves to a different corner when the line you type reaches it |
[paste_buffers]
limit = 20 # how many unnamed buffers to keep. 0 keeps none.
max_kb = 16384 # most KiB all buffers hold together, at most 262144. 0 uses 16384.limit counts only the buffers that TUIOS names (buffer0, buffer1). Past max_kb, the oldest unnamed buffer goes first, then the oldest named one. See Paste buffers on tuios.dev.
[[keybindings.command]] binds a key to a command that you write: a scratch terminal, a popup, a pane or a command with no window. A mistake in an entry is a warning, and TUIOS ignores that entry. See Command keys.
[spotlight] lights one part of the screen and dims the rest, for screen sharing. B in window mode, or Ctrl+B B in any mode, turns it on and off. While it is on, Esc in window mode or a click on the Spotlight chip in the dock turns it off. See Spotlight.
| Key | Default | Values | What it does |
|---|---|---|---|
enabled |
false |
bool | Turn the spotlight on |
follow |
"mouse" |
mouse, cursor
|
Follow the pointer, or the focused pane's cursor |
radius |
10 |
2 to 200 | Half the beam's height, in rows |
dim |
75 |
10 to 95 | How much an unlit cell is dimmed, in percent |
edge |
"hard" |
hard, soft
|
Cut the beam off, or fade it. A fade sends about three times the bytes |
shake |
false |
bool | Shake the mouse left and right to toggle the beam |
follow = "cursor" sends fewer bytes to a remote client.
[dock] sets what the bar draws: three ordered lists of component names, plus a table for each component that needs one. Edit it in the file, or with the Dock > Components editor on the settings page. set-config cannot change the lists.
[dock]
left = ["mode", "workspaces", "trail", "tape"]
center = ["windows"]
right = ["transfers", "notifications", "copy-help", "cpu", "ram", "clock", "session-controls"]
[dock.clock]
format = "15:04"
[dock.custom.branch]
command = "git branch --show-current"
refresh = "event:after-focus-change"
on-click = "tuios new-window log -- git log --oneline -20"These lists are the default. A missing list uses the default. An empty list (left = []) draws nothing on that side.
The built-in components are mode, workspaces, trail, tape, windows, transfers, notifications, copy-help, cpu, ram, clock, session-controls and session (Added in v0.10.0). Listing one lets it draw. It still needs its own condition, so cpu still needs show_cpu. copy-help shows the keys of copy mode, multi copy mode and hints mode while the mode is open. See Mode keys in the dock.
transfers (Added in v0.9.3) shows the copies between machines. It shows nothing while no copy exists. One copy is a bar and a percent, [####--] 61%. More copies are a count and a percent, 3 copies 42%. waiting means a link is down. 1 failed stays until you open the Copies panel. A click opens the Copies panel.
[dock.clock] takes format, a Go time layout. A layout without seconds refreshes once a minute.
(Added in v0.10.0) When the center list has a clock or a custom cell, the centre is at the middle of the terminal. A notification does not move it, and it does not shorten the minimized pane labels. On a crowded bar, the centre moves left to keep the cells on the right on the screen. Click a cut notification to read the full message.
(Added in v0.10.0) session shows the session name, the workspace and the pane, such as main 2:1. It is in no default list. [dock.session] takes format, with {session}, {workspace} and {pane}. It also takes the cell style keys fg, bg, bold, cap_left and cap_right. [dock.custom.NAME] takes the same style keys. Colours are #rrggbb.
(Added in v0.10.0)
By default the workspace strip draws rounded pills. [dock.tabs] can draw flat tabs, the way a tmux status line draws its window list:
[appearance]
dock_workspace_tab_format = "{name}{zoom} {icon}"
[dock.tabs]
style = "flat" # pill (default) or flat
separator = " • " # drawn between flat tabs
separator_fg = "#4c3743"
active_fg = "#272e33"
active_bg = "#a7c080"
active_bold = true
inactive_fg = "#4f5b58"
number_glyphs = ["I", "II", "III"] # {icon}: the first entry is workspace 1
zoom_marker = " [ ]" # {zoom} on the current workspace while a pane is zoomed
show_add = false # keep the "+" tab in the flat styleinactive_bg and inactive_bold set the other tabs. Colours are #rrggbb, and they are overrides. With no colour set, a flat strip draws the current tab in bold on the theme's accent, and the other tabs in dim text. TUIOS ignores a colour that is not #rrggbb and shows a warning. A workspace past the end of number_glyphs shows its number. The flat style shows even one workspace, and drops the + tab unless show_add is on. With no [dock.tabs] table, the strip does not change.
[dock.custom.NAME] defines a cell. Put custom/NAME in a list to place it.
| Key | Default | What it does |
|---|---|---|
command |
required | Run with sh -c. The first line of output is the cell text |
refresh |
"once" |
once, a duration such as "30s", push, or event:TYPE[,TYPE]
|
on-click |
"" |
Run with sh -c when the cell is clicked |
max-width |
24 |
Widest the cell may be, in cells |
fg, bg, bold (Added in v0.10.0)
|
unset | The text colour, the ground colour and bold text of the cell. Colours are #rrggbb
|
cap_left, cap_right (Added in v0.10.0)
|
unset | Text drawn before and after the cell, in the colour of its ground |
With push, the command keeps running and each line it prints replaces the cell. With event:, the command runs again when the daemon reports one of those hook events.
A component whose command fails is hidden. tuios list-dock-components shows what each component last did and why a cell is missing. tuios refresh-dock [name] runs a component again now.
A command runs in the client, with a three second timeout. TUIOS reads at most 64 KiB of its output and stops the command when it prints more. SGR colour stays. TUIOS removes other control sequences, C1 controls, bidirectional and invisible characters, and bytes that are not UTF-8. Wide characters and emoji stay.
[hosts] names the other machines this daemon links to. Each host is a table.
[hosts.build]
addr = "gaurav@buildbox"
[hosts.work]
addr = "workstation"
connect_timeout = 5| Key | Default | What it does |
|---|---|---|
addr |
required | Anything ssh accepts, including an ssh_config alias. A host with no addr, or with one that starts with -, is ignored and logged |
connect_timeout |
10 |
Seconds one dial may take before the host counts as unreachable |
command |
"" |
The tuios binary on the host. Empty means the link finds it on the PATH, at the usual install paths, or through a login shell |
ssh_options |
[] |
Extra ssh arguments, placed before the address. Only safe options are accepted. See Rules for ssh_options |
repos_root |
"" |
Where the host keeps its checkouts, as the host reads it, such as ~/src. fan --host, worktree new --host and start-agent look for a repository there by its origin URL |
You can also manage the table from the shell. These commands edit the file:
tuios hosts add build gaurav@buildbox
tuios hosts test build
tuios hosts remove buildA change that gives a machine less applies at once, also on links that are open. A new host, a host that dials another way, or a change that gives a machine more waits for tuios config apply from a terminal outside TUIOS. A daemon restart also applies it. So tuios hosts add from a pane waits. A host that you add with "Add a host" on the settings page applies at once. See Changes that wait.
ssh_options accepts only options that cannot run code or write files on this machine. Write each option as -o Keyword=value, with one plain value and no quotes.
- Accepted examples:
-p,-i,-l,-J,Port,User,HostName,HostKeyAlias,IdentityFile,StrictHostKeyChecking, theServerAliveoptions and the algorithm lists. - Refused:
ProxyCommand,LocalCommand, the known hosts files,ControlPath,-Fand socket forwards. - Refused: port forwards (
-L,-R,-D). -
-JandProxyJumptake host names only.ForwardAgenttakes onlyyesorno. - A host
addrorcommandcannot start with a dash.
TUIOS drops a host with a refused option. tuios hosts and the Inbox name the reason. Put a refused option in ~/.ssh/config, and name that host in addr.
[hosts.build]
addr = "gaurav@buildbox"
ssh_options = ["-J", "bastion", "-o", "ServerAliveInterval=30"]Warning
Upgrading from v0.8.0
After the upgrade, look in the Inbox or run tuios hosts. A host with a refused option is dropped. Move the option, such as ProxyCommand or LocalForward, into ~/.ssh/config. Keep port forwards in ~/.ssh/config too.
The daemon runs ssh with BatchMode on, so a link never asks for a password or about a host key. Run ssh to the host once by hand to accept its key.
The same [hosts.NAME] table, read on the machine a link arrives at, also says what the machine of that name may do there. [hosts."*"] covers every machine with no table of its own.
[hosts.laptop]
addr = "laptop" # optional: without it nothing is dialled
allow = ["list", "mail", "open", "write", "respond"]
hold_mail = true
hosted_grace = "10m"
[hosts."*"]
allow = ["list", "mail"]| Capability | What it lets the other machine do here |
|---|---|
list |
Read sessions, windows, captures, agent state, the Inbox, prompts, waits and events |
mail |
Send and read agent mail, and use the stash |
open |
Start sessions, windows, worktrees, fans and agents |
write |
Type into panes, close and move windows, set options and names, report agent state, attach, and pull a worktree's work |
respond |
Answer for you: prompts, held approvals, ask-human questions, dismissing Inbox items and passing on held mail. Also type, from a pane on the other machine, into a pane here that waits on a prompt. The person at the other machine, outside every pane, needs only write for that |
files (Added in v0.9.3)
|
Read and write files here, and copy files in and out. Writes land only in the folders of files_roots. See Files over a link
|
list also lets the other machine stream the bytes of a pane (stream-pane). (Added in v0.9.3)
With no table, a machine may list, mail, open, write and files. respond is opt-in. An allow that is set replaces that list, and allow = [] allows nothing. A table that sets allow without files keeps that machine from your files. Anything not allowed is refused with forbidden.
To relay on to this machine's own hosts, a machine needs list, mail, open, write and respond. A table written for an older tuios that allows those five still relays.
hold_mail = true holds mail from that machine to an agent here in your Inbox, marked held for NAME, until you pass it on with p. hosted_grace is how long a pane this machine runs for the other one (a window opened there with --host) keeps running after the link drops: a duration, "0" to end it with the link, 10 minutes when unset, at most 24 hours.
The name is the one the other machine gives for itself, its host name up to the first dot, unless the ssh key it logs in with pins one with command="tuios stdio-proxy --as laptop",restrict. Only a pinned name is a boundary. A change that allows less applies to the next call on every link. A change that allows more waits for tuios config apply.
(Added in v0.9.3)
A machine with files can read your files and write new ones. Three keys in its [hosts.NAME] table set the limits:
[hosts.laptop]
allow = ["list", "files"]
files_roots = ["~/Downloads/tuios", "~/dev"] # where it may write
files_read_roots = ["~/share"] # where it may read
files_min_free = "5GiB" # the space a write must leave free| Key | Default | What it does |
|---|---|---|
files_roots |
one receive folder | The folders the machine may write in. The default is ~/Downloads/tuios on macOS and Windows, and $XDG_DOWNLOAD_DIR/tuios (or ~/Downloads/tuios) on other systems. tuios makes it on first use |
files_read_roots |
not set | The folders the machine may read. Not set means every file you can read, apart from the read deny list. When set, the machine also reads the folders of files_roots
|
files_min_free |
"1GiB" |
The free space a write must leave on its disk. A whole number with B, KB, MB, GB, TB, KiB, MiB, GiB or TiB. "0" turns the check off |
A path is absolute or starts with ~. Some places are always closed, whatever the roots say:
-
Writes never go into
~/.ssh,~/.gnupg, cloud and git credentials, shell start files such as.bashrcand.zshrc, login items, crontabs,/proc, or tuios's own config and state. -
Reads never return keys, credentials, keyrings, shell histories, browser profiles,
/proc, or tuios's own state. A public*.pubkey in~/.sshis the one exception. - A file with more than one name (a hard link) is never read over a link. Copy it to a new file to share it.
A refused write says where writes may land and which key to change. A deny list can never name every secret on a machine, so files_read_roots is the stronger choice.
Other limits:
- One machine may have at most 8 write streams and 64 unfinished copies here at once. Past that, a write is
busy, and a copy waits and tries again. - Files dropped on a pane go to a private drop folder. It holds at most 256 MiB for all drops, for 24 hours.
- A copy another machine starts here keeps to the same rules, also after tuios restarts. When
filesis taken away, its copies here stop. - A change that widens
files_rootsorfiles_read_roots, removesfiles_read_roots, or lowersfiles_min_freewaits fortuios config apply. A change that narrows applies at once.
Warning
Give files only to machines you trust
A machine with files can write any file inside its roots and read any file outside the deny list. A file copy can run code: a binary in a folder on your PATH, or a config file that a program reads. Give files, and a wide files_roots, only to a machine you trust as you trust your own.
See Remote Hosts for the copies themselves.
Once a host is linked, you can list its sessions and agents (tuios ls --all-hosts, tuios list-agents --host build), attach to or create sessions on it (tuios attach --host build api, tuios new --host build), and open a pane whose process runs there (tuios new-window --host build). See tuios hosts.
A host behind NAT, with no port forward and no jump host, is a network problem to solve under TUIOS. Any tool that pipes stdio to a remote port works as an ssh ProxyCommand. ssh_options refuses ProxyCommand, so name it in ~/.ssh/config.
Tailscale's tailcat is one such tool. It carries a connection over WireGuard with no Tailscale account and no open port. Run it on the far side against sshd:
tailcat --serve=22Then add a host to ~/.ssh/config that uses it:
Host laptop-tailcat
HostName laptop
User gaurav
ProxyCommand tailcat <token> 22
Name that host in the host entry:
[hosts.laptop]
addr = "laptop-tailcat"ssh still decides who may connect: the tunnel only carries bytes to sshd, which asks for your key as usual. A leaked tailcat token gives reachability, not access. TUIOS adds no identity system of its own and opens no listening port.
[tailscale] changes which tailnet machines tuios hosts add and tuios hosts tailnet offer as addresses. It never adds a host by itself.
| Key | Default | What it does |
|---|---|---|
enabled |
true |
Offer tailnet machines. Empty on a machine that is not on a tailnet |
addr |
"dns" |
Address form: dns (MagicDNS name), name (short name) or ip
|
user |
"" |
ssh login to put before every address |
users |
none | Per-machine logins, keyed by short name. Wins over user
|
os |
platforms that run TUIOS | Operating systems to offer. An empty list offers every machine |
offline |
false |
Also offer machines that are offline |
self |
false |
Also offer this machine |
shared |
false |
Also offer machines shared into your tailnet |
include, exclude
|
[] |
Glob patterns on the short name and the MagicDNS name. exclude wins |
max |
built in | Most machines to offer |
socket |
"" |
Path to the tailscaled socket, if it is not in the usual place |
[tailscale]
user = "ubuntu"
exclude = ["*-pad-*"]
[tailscale.users]
build = "admin"[agents] sets how TUIOS treats the coding agents in its panes. Except for enabled, it is outside list-options, and set-config cannot change it, so a program in a pane cannot loosen it. The daemon reads it at start and again whenever the file changes. See Inbox and Review for what each part does.
[agents]
enabled = falseenabled (default true) is the one switch for every agent feature. With it off, TUIOS is only a multiplexer: no agent detection, no agents section on the rail, no Inbox, mail, approvals or agent alerts, and agent commands stop with a message. The change applies at once. It is the first row of the settings page, "Agent features", and tuios set-config agents.enabled false from a shell outside TUIOS also sets it. A process in a pane cannot change it. See Turn Off Agent Features.
[agents.approvals]
enabled = ["claude-code", "opencode"] # harnesses whose permission prompts the Inbox may answer
hold_seconds = 120 # 10 to 300
hold_plans = true # a plan follows enabled
[agents.approvals.risk]
builtin = true # keep the shipped risk rules
panes_may_allow = false # a pane with the respond grant may not allow a risky call
[[agents.approvals.risk.rule]]
name = "kubectl apply"
tools = ["Bash"] # empty: every tool
pattern = '\bkubectl\s+(apply|delete)\b' # RE2, matched per command
[agents.permissions]
mode = "strict" # open (default) or strict
grants = ["read", "write", "fan"] # what a pane holds under strict
[agents.recap]
mode = "toast" # toast, inbox or off
away = "10m"
test_patterns = ["go test", "pytest"]
[agents.queue]
max = 8 # at most 64
[agents.checkpoints]
enabled = true
keep = 50 # at most 1000
max_untracked_mb = 50 # a negative value means no limit| Key | Default | What it does |
|---|---|---|
approvals.enabled |
[] |
Harnesses whose permission prompts wait for an answer from the Inbox: claude-code (or claude), opencode and kilo. Empty turns it off |
approvals.hold_seconds |
120 |
How long a prompt waits for your answer before the harness asks in its own pane |
approvals.hold_plans |
true |
Also hold a plan an agent asks you to approve when it leaves plan mode |
approvals.risk.builtin |
true |
Keep the shipped risk rules |
approvals.risk.panes_may_allow |
false |
Let a pane holding the respond grant allow a risky call |
approvals.risk.rule |
none | Your own rules: a name, the tools it applies to, and an RE2 pattern
|
permissions.mode |
"open" |
open gives a pane started with no grants of its own admin. strict gives it grants. Any other value reads as strict
|
permissions.grants |
["read", "write", "fan"] |
What such a pane holds under strict. An empty list gives nothing. Add files to let a pane copy files near its work. See Copying files with the files grant
|
recap.mode |
"toast" |
Where the away recap shows: toast in the dock and the Inbox, inbox only in the Inbox, off only in tuios agent-log --recap
|
recap.away |
"10m" |
How long you must have been away from a pane for the dock to show its recap |
recap.test_patterns |
built in | Commands that count as a test run in the recap, such as go test and npm test
|
queue.max |
8 |
Messages one pane's queue holds |
checkpoints.enabled |
true |
Save the git work tree of an agent's pane each time the agent finishes a turn that changed a file. See Checkpoints |
checkpoints.keep |
50 |
How many checkpoints one pane keeps, at most 1000 |
checkpoints.max_untracked_mb |
50 |
Leave an untracked file larger than this many megabytes out of a checkpoint. tuios checkpoint list names the files left out, and a restore does not change them |
After turning on approvals.enabled, run tuios integration install for that harness again, so its hook is the version that waits. The grants are read, write, fan, respond, files and admin. See What a pane may do.
A change to [agents.permissions] that gives panes less applies at once. A change that gives more, such as strict to open, waits. Run tuios config apply from a terminal outside TUIOS, or restart the daemon. See Changes that wait.
[notify] sends a push notification when the Inbox gets an item that waits for you. The daemon sends it, so it works when no client is attached. The providers are ntfy, Pushover and a webhook.
[notify]
web_url = "https://term.example.com/" # tuios-web, for a link to the item
[notify.ntfy]
url = "https://ntfy.sh/a-long-random-topic"
token_file = "~/.config/tuios/ntfy-token" # optional| Key | Default | What it does |
|---|---|---|
enabled |
true |
false turns every notification off. Nothing is sent without a provider |
web_url |
"" |
The address of tuios-web. Each notification links to web_url/inbox?item=ID
|
content |
"summary" |
summary sends the kind, the pane name and the line the Inbox shows. title sends only the kind and the session |
quiet_active_seconds |
120 |
Hold a notification while you typed at an attached client in this many seconds. 0 sends at once |
cooldown_seconds |
60 |
The shortest time between two notifications for the same pane and kind |
max_per_hour |
30 |
The most notifications in one hour. 0 sets no limit |
allow_http_redirects |
false |
Let a provider redirect to a plain http address |
[notify.triggers] selects the Inbox kinds: approval, plan, question and ask are on by default, mail, errored and finished are off. Like [hosts], [notify] is not in list-options, and tuios set-config cannot change it. A new address or a new source for a secret waits for tuios config apply from a terminal outside TUIOS. tuios notify test sends a test notification through each provider. See Push Notifications for the providers and the secrets.
(Added in v0.9.3)
[notify.webpush] configures the pushes the daemon sends to a phone that registered for Web Push. It needs no provider.
[notify.webpush]
subject = "https://example.com/tuios-contact" # optional
allow_insecure = false| Key | Default | What it does |
|---|---|---|
subject |
https://tuios.dev/push |
The VAPID subject: an https URL or a mailto: address that a push service can use to contact you |
allow_insecure |
false |
Let tuios send to a push service on a loopback or private address, by https or http. A change to true waits for tuios config apply
|
See Push Notifications.
[hooks] runs shell commands on window, workspace, session and agent events. Event names use hyphens, such as after-new-window. See Hooks and tuios list-hooks.
The file is checked when it loads. Problems are errors or warnings.
Errors are keys TUIOS cannot read, such as "ctrl+nope". TUIOS ignores each of these keys and loads the rest of the file. See When a key is wrong.
Warnings keep the key. They cover unknown values (which fall back to the default), unknown title placeholders, actions with no key, two actions on one key in the same section, and the macOS alt+ advice.
TUIOS shows the number of problems when it starts, for example 2 config problems, see the log viewer. The log viewer (leader D l) shows each problem. If the file is not valid TOML, TUIOS uses the built-in defaults.
TUIOS ignores a key that it cannot read and loads the rest of the file. The action gets its default key when it has no other key. If another action already has that default key, the action has no key. A leader that TUIOS cannot read changes to ctrl+b. tuios keybinds doctor and tuios config apply list the ignored keys. See When a key is wrong on tuios.dev.
TUIOS reads opt+ and option+ as alt+ on every platform. On Linux, opt+1 is alt+1, so one config.toml works on both. See One config on macOS and Linux on tuios.dev.
My config seems ignored. TUIOS ignores only the keys that it cannot read. Open the log viewer (leader D l), or run tuios keybinds doctor, to see them. Run tuios config path to confirm which file TUIOS reads. See My config seems ignored on tuios.dev.
A setting changed nothing.
- Check the table of what applies live. A saved keybinding works at the next key press.
- Check that the file parsed. Look for an error notification, or run
tuios keybinds list. - Check whether a command-line flag is overriding it.
- For a hook, check the event name spelling with
tuios list-hooks.
A key does nothing. Run tuios keybinds explain <key> to see every place it is bound, and tuios keybinds doctor for conflicts.
[keybindings.window_management]
new_window = ["ctrl+t"]
close_window = ["ctrl+w"]
next_window = ["ctrl+tab"]
prev_window = ["ctrl+shift+tab"][keybindings]
leader_key = "ctrl+a"
[keybindings.prefix_mode]
prefix_new_window = ["c"]
prefix_split_horizontal = ["S"]
prefix_split_vertical = ["V"]
prefix_session_switcher = ["s"]
prefix_scrollback = ["/"]In this example, S and s are taken by the splits and the switcher, so the scrollback browser moves to /. Keys inside one section must not collide.
[appearance]
theme = "nord"
border_style = "hidden"
dockbar_position = "hidden"
shared_borders = true
whichkey_enabled = false
window_title_format = "{index}: {title}"
scrollback_lines = 50000
max_fps = 120- Keybindings: Every default key, by section
- Hooks: Run shell commands on TUIOS events
- CLI Reference: Every command and flag
- 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