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 asking

Write only what you want to change. Missing keys and missing tables take their defaults.

config.toml can include other files, and TUIOS also reads the *.toml files in ~/.config/tuios/config.d/. config.toml wins over all of them. See Split the Config.

[appearance]
theme = "nord"

[keybindings.window_management]
new_window = ["ctrl+t"]

Two things live outside the file: custom themes in ~/.config/tuios/themes/ and custom glyph sets in ~/.config/tuios/glyphs/.

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.

Tables

TableWhat 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
[review]How a review of an agent's changes opens
[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

Applying changes

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:

ChangeWhen it applies
[hooks]The daemon reads it at start. Run tuios restart-server after editing a hook the daemon runs
[daemon]The daemon reads it at start. Run tuios restart-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_linesPanes opened afterwards
Custom theme filesSee 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.

Changing a setting at runtime

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 explorer

tuios 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. The next client that attaches applies it and writes it to the config. 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 in-app settings page

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.

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 How TUIOS saves a change.

Command-line flags

Interface flags override the file for one run. They are accepted by tuios, tuios new, tuios attach, tuios ssh and tuios tape play.

FlagOption
--themeappearance.theme
--border-styleappearance.border_style
--dockbar-positionappearance.dockbar_position
--window-title-positionappearance.window_title_position
--window-button-styleappearance.window_button_style
--window-button-positionappearance.window_button_position
--hide-window-buttonsappearance.hide_window_buttons
--hide-scrollbarappearance.hide_scrollbar
--shared-bordersappearance.shared_borders
--scrollback-linesappearance.scrollback_lines
--zoom-max-widthappearance.zoom_max_width
--no-animationsappearance.motion = "none"
--confirm-quitappearance.confirm_quit
--show-clock, --show-cpu, --show-ramappearance.show_clock, show_cpu, show_ram
--show-keysdebug.show_key_events
--ascii-onlynone. Draws ASCII instead of Nerd Font glyphs

Appearance

All keys below go under [appearance].

Theme and chrome

KeyDefaultValuesWhat it does
theme ""a theme idColour theme. Empty keeps your terminal's own colours
glyphs Added in v0.8.0"default"a glyph set idShapes of the chrome. See Glyph sets
border_style "rounded"see belowBorder drawn around every pane
border_focused_color Added in v0.8.0""#RRGGBBOverride the focused border colour
border_focused_color_window Added in v0.11.0""a colourThe focused border in window mode. It wins over border_focused_color. See Focused border colour
border_focused_color_terminal Added in v0.11.0""a colourThe focused border in terminal mode. It wins over border_focused_color
border_unfocused_color Added in v0.8.0""#RRGGBBOverride the unfocused border colour
frame_panels Added in v0.11.0falseboolFrame the palette, the switchers, the Agents view, the settings page and the other large panels in border_style. See Dialog and panel frames
shared_borders falseboolDraw one border between neighbouring tiled panes, in every layout
gap Added in v0.8.000 to 8Empty cells between tiled panes
dim_unfocused Added in v0.8.000 to 90Fade panes you are not in, in percent
dim_foreground Added in v0.10.0trueboolLet 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 Added in v0.8.1falseboolDim the panes in the multifocus set like other panes you are not in
session_colors Added in v0.8.0trueboolGive each session its own colour on the rail and in the switcher
session_border Added in v0.8.0falseboolAlso draw that colour on every pane border
zen_mode Added in v0.8.0"disabled"disabled, always, mouseHide borders never, always, or while the mouse is idle
panel_padding Added in v0.8.021 to 6Columns of padding inside overlay panels
motion Added in v0.8.0"full"none, basic, fullHow 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.010025 to 400, or 0The speed of window animations, in percent: minimize and restore, snap, swap and zoom. 200 runs them in half the time. 0 means 100. A value outside the range moves to the nearest end. Overlay fades and the copy sweep keep their own timing
theme_transition Added in v0.10.0"none"none, circle, fadeHow 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.0450100 to 2000The time a theme transition takes, in milliseconds. animation_speed scales it
modal_dim Added in v0.8.0300 to 90Dim the screen behind a dialog, in percent. 0 turns it off. At 16 colours the text goes faint instead
popup_dim Added in v0.11.000 to 90Dim the screen behind a focused popup from tuios popup, in percent. 0 turns it off. tuios popup --dim N sets it for one popup. A TML popup uses modal_dim
max_fps Added in v0.8.000, 10 to 240, or "auto"The highest frame rate TUIOS draws at. 0 means 60. See below
wrap_lists Added in v0.8.0trueboolMoving 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.

border_style

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.

Focused border colour

The focused pane's border shows the mode. By default it is the theme's cyan in window mode and its green in terminal mode. Three keys set it:

[appearance]
border_focused_color_window = "accent"
border_focused_color_terminal = "#ff9e64"
# border_focused_color = "#89b4fa"   # both modes
  • A key for one mode wins over border_focused_color in that mode.
  • An empty key uses the next key. After the three keys, the theme sets the colour.
  • A value is a hex colour, a palette index from 0 to 255, a slot name such as blue, or a role name such as accent.
  • border_focused_color also replaces the colours for copy mode and for an agent that waits. The keys for one mode do not.
  • Added in v0.11.0 On many themes the cyan and the green have the same lightness. When you set no colour, the terminal-mode border is then a step darker, or a step lighter when darker is too faint. You can see the mode without the hue. To keep one colour for both modes, set border_focused_color.
  • At 16 colours the default borders use bright cyan and bright green from your terminal's palette.

The settings page has a row for each key, in the Appearance section. See Border colours.

Dialog and panel frames Added in v0.11.0

The small dialogs, such as rename, confirm and new space, draw their frame in border_style. The large panels, such as the command palette, the switchers, the Agents view and the settings page, have no frame. Set frame_panels = true to give them the same frame.

[appearance]
border_style = "double"
frame_panels = true
  • rounded, normal, thick, double and ascii draw their own corners and lines.
  • hidden draws no frame. A dialog keeps its size, and its title and key hints stay in place. A frame that is the only edge of a region is drawn as rounded. An example is a panel at 16 colours.
  • block, outer-half-block and inner-half-block draw the rounded frame.
  • glyphs draws the border of the active glyph set.
  • At 16 colours a panel always has a frame, because its background is the background of your terminal.

appearance.pane_overlays Added in v0.12.0 draws dialogs and panels like pane windows. Every panel gets a frame in the focused pane's border colour. The frame and its content sit on the background of an empty pane cell. The selected row and input fields are steps of that background. The option is off by default. Under hidden, at 16 colours, or with no theme on a terminal that reports no background, a dialog keeps its shade.

appearance.overlay_border_color Added in v0.12.0 sets a colour for every dialog and panel frame: #89b4fa, a slot name such as bright_cyan, or a role such as accent.

[appearance]
pane_overlays = true
overlay_border_color = "accent"

max_fps

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
ValueFrame rate
060. This is the default
10 to 240That 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_fps while 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 hyprctl on Hyprland, niri on niri and wlr-randr on other wlroots compositors. On X11, or on another compositor, it asks xrandr --current. On macOS it asks system_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-web and in the SSH server, auto uses 60, because the displays on the machine are not the ones you see.

Backgrounds

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.

KeySurfaceDefault
background Added in v0.8.0Every surface below that is not set on its own"off"
pane_background Added in v0.8.0Pane content, the thin scrollbar over it, and the scrollback browser"" (follows background)
desktop_background Added in v0.8.0Behind and between panes: gaps, the space around floating panes, an empty workspace and its welcome screen""
window_chrome_background Added in v0.8.0Pane borders and title bars, and the lines between shared borders""
dock_background Added in v0.8.0The dock""
sidebar.background Added in v0.8.0The rail, under [appearance.sidebar]""

Each takes off, theme or a #RRGGBB colour:

  • off paints nothing, so your terminal's background shows through.
  • theme paints the active theme's background, with the theme's foreground on default-coloured text. With no theme set, it behaves as off.
  • #RRGGBB paints 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.

Windows and titles

KeyDefaultValuesWhat it does
window_title_position "top"top, bottom, hiddenWhere the title is drawn. hidden also disables rename
window_title_format Added in v0.8.0""templateTitle template. See below
host_title Unreleased""templateThe title of the terminal that tuios runs in, from the focused pane. Empty sets no title. See below
window_button_style Added in v0.8.0"dots"dots, pillmacOS-style traffic lights, or a filled pill. On the left, the pill puts close at the outer corner: close, zoom, minimize
window_button_position Added in v0.8.0"left"left, rightWhich end of the title bar holds the buttons
hide_window_buttons falseboolHide the minimize, zoom and close buttons
window_button_zoom Added in v0.8.0trueboolShow the zoom button on tiled panes
hide_scrollbar Added in v0.8.0falseboolHide the scrollbar on the pane border
confirm_quit falseboolAlways confirm on quit, not only when processes are running
preferred_shell ""pathShell for new panes. Empty picks one for your platform
new_window_inherit_cwd Added in v0.8.0trueboolStart 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 Added in v0.9.0falseboolA split or new window of a pane that runs ssh runs the same ssh. See below
whichkey_enabled trueboolShow the which-key popup after the leader key
whichkey_position "bottom-right"bottom-right, bottom-left, top-right, top-left, centerWhere the popup opens
prefix_repeat_time Added in v0.8.05000 to 5000Milliseconds the prefix stays armed after a repeatable command. 0 turns it off

window_title_format

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.

host_title

host_title sets the title of the terminal that tuios runs in, such as the title bar of Ghostty. It is the same as set-titles in tmux. The title follows the focused pane, the workspace and the session, and it changes when the pane's own title changes.

PlaceholderWhat it holds
{title}The focused pane's name, as the title on its border shows it
{index}The pane's position in its workspace, from 1
{cwd}The pane's working directory
{session}The session's name as the rail shows it. Without a daemon, tuios
{workspace}The workspace's name, or its number. With a scratch group open, the workspace that it shows over
[appearance]
host_title = "{title} · {session}"
  • With no focused pane on the workspace in view, the title is the session's name.
  • The title is also the session's name when the template uses {title} and the pane has no name, as for a new shell, and when the template comes out empty.
  • A title that a tool sets with herdr's terminal title set wins while it is set.
  • Each client sets the title of its own terminal.
  • When you empty the option while a client is attached, tuios clears the title that it set.

The option is on the settings page, in the Appearance section. tuios set-config applies it at once.

Mouse and input

KeyDefaultValuesWhat it does
click_to_type Added in v0.8.0"double"double, single, offIn 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 Added in v0.8.0"off"off, targeted, allStart typing when a keyboard focus command lands on a pane. targeted covers numbered select and arrows, all adds Tab
focus_follows_mouse Added in v0.8.0falseboolFocus the pane under the pointer
alt_drag Added in v0.8.0trueboolAlt + left-drag moves a pane
right_click_opens_menu Added in v0.8.0falseboolIn terminal mode, a plain right-click opens the pane menu
copy_on_select Added in v0.8.0trueboolCopy a mouse selection when you release the button
word_characters Added in v0.8.0"@-./_~?&=%+#"stringPunctuation that counts as part of a word on double-click
links Added in v0.8.0"all"all, marked, offThe links TUIOS finds. marked finds only OSC 8 links. See Links
link_click Added in v0.9.0"both"both, ctrl, shift, offThe click that opens a link. both is Ctrl+click and Shift+click
link_opener Added in v0.9.0""a commandThe 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 Added in v0.9.0falseboolLet the terminal-mode focus keys move through Neovim splits first. See Neovim pane navigation
link_hover Added in v0.9.3"safe"safe, alwaysThe hover underline and label on a link. safe skips a pane whose program tracks the mouse. always draws them in every pane
kitty_placeholders Added in v0.8.0"auto"auto, on, offDraw images placed with kitty Unicode placeholders
image_symbols Added in v0.9.0"auto"auto, octant, sextant, quadrant, half, offThe block characters for a sixel image on a terminal without graphics. See Images Without Graphics

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.

Scrolling

KeyDefaultValuesWhat it does
scrollback_lines 10000100 to 1000000Lines each pane keeps. Read when the pane is created
scroll_lines Added in v0.8.031 to 50Lines per mouse wheel notch

Tiling and zoom

KeyDefaultValuesWhat it does
tiling_scheme Added in v0.8.3"spiral"spiral, longest_side, alternate, smart_splitBSP scheme a workspace starts with the first time it is tiled. A workspace already tiled keeps its own scheme. See BSP tiling
master_ratio Added in v0.8.05010 to 90The share of the screen the master panes take in the master-stack layout, in percent
master_position Added in v0.8.5"left"left, right, top, bottom, centerThe side the master panes take. With center, the stack panes go to the right and the left in turn
master_count Added in v0.8.511 to 9How many panes are master panes
master_grid Added in v0.8.5trueboolWith one master on the left, show four or more panes as a grid
scroll_column_width Added in v0.8.05520 to 100Column width in the scrolling layout, in percent
scroll_column_max Added in v0.8.09020 to 100Widest a scrolling column may be set to. 100 lets a column fill the screen
niri_scroll_cells Added in v0.8.081 to 200Cells the scrolling layout moves per wheel event
niri_reverse_scroll Added in v0.8.0falseboolReverse the wheel in the scrolling layout
niri_click_reveals Added in v0.8.0trueboolClicking a partly hidden column brings it fully on screen
niri_hover_reveals Added in v0.8.0trueboolWith focus_follows_mouse, hovering does the same
zoom_size Added in v0.8.09550 to 100Share of the screen a zoomed pane takes, in percent. Below 100 the layout stays visible at the edges. 100 is fullscreen
zoom_max_width Added in v0.8.00cellsWidth cap for a zoomed pane. 0 means no cap
zoom_borderless Added in v0.9.0falseboolShow 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 Added in v0.8.0trueboolSlide a pane into and out of zoom
zoom_follows_focus Added in v0.8.0trueboolMoving focus while zoomed zooms the pane you move to
drop_preview Added in v0.9.3"fill"fill, outline, offThe highlight while you drag a pane in BSP. fill draws a frame and tints the area inside it. 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 0How 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.

Dock

KeyDefaultValuesWhat it does
dockbar_position "top"top, bottom, hiddenEdge the dock sits on
dock_workspace_tabs Added in v0.8.0trueboolShow the clickable workspace strip
dock_workspace_tab_format Added in v0.8.0""templateTab 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 Added in v0.9.012cells, 0 for no capLongest workspace tab label. The cap counts the characters of the tab format
dock_workspace_tooltip Added in v0.8.0trueboolShow a truncated workspace name in full on hover
dock_pill_caps Added in v0.8.0trueboolRounded caps on every dock pill: the mode chip, the workspace pills and the minimized windows. false draws flat pills
dock_compact Added in v0.9.0falseboolDraw the dock as one row with no rule. The panes get one more row
dock_mode_icon_window Added in v0.9.0unsettext, "" or defaultIcon 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 Added in v0.9.0unsettext, "" or defaultIcon in the mode pill in terminal mode
dock_mode_icon_tiling Added in v0.9.0unsettext, "" or defaultIcon in the mode pill while tiling is on
dock_mode_split_letter Added in v0.11.0trueboolShow V or H after the tiling icon in the mode pill. The letter is the direction of the next split
show_clock falseboolShow the clock
clock_format Added in v0.8.0"15:04"Go time layoutClock format, for example 15:04 or Mon 3:04PM
show_cpu falseboolShow a CPU graph
show_ram falseboolShow RAM use
hide_clock falseboolDeprecated. 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. Set dock_mode_split_letter = false to show the tiling icon alone. With the default icons, the pill then has the same width as the pill of the leader key, so the workspace tabs do not move when you press the leader.

[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.

Workspace tab style 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 style
KeyWhat it does
stylepill (the default) or flat
separator, separator_fgThe text between flat tabs, and its colour
active_fg, active_bg, active_boldThe current tab
inactive_fg, inactive_bg, inactive_boldThe other tabs
number_glyphsThe text of {icon} for each workspace. A workspace past the end of the list shows its number
zoom_markerThe text of {zoom} on the current workspace while a pane is zoomed
show_addKeep the + tab in the flat style
caps Added in v0.12.0true gives each flat tab the rounded ends of a pill. A tab with no background gets a blank column in place of each end. There are no ends under ASCII glyphs or with appearance.dock_pill_caps = false
active_underline Added in v0.12.0Underline the current tab. When it is not set, a pill is underlined and a flat tab is not

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. 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.

active_fg, active_bg, inactive_fg, inactive_bg and inactive_bold also apply to pills. A pill with no colour keeps the theme's look.

appearance.dock_mode_pill Added in v0.12.0 sets what the mode pill shows while tiling is on. tiling (the default) shows the tiling icon. input shows the terminal icon or the window icon, and the split letter after it shows that tiling is on.

To reorder the dock or add your own cells, see The dock table.

Calm mode Added in v0.12.0

appearance.calm turns calm mode on. Nothing moves or blinks, and only the messages that need you show in the dock. Ctrl+B Z turns it on and off and saves the choice here. Calm mode sits over appearance.motion. When you turn calm mode off, your motion level comes back. Calm mode and appearance.motion = "none" also stop the motion of TML surfaces. A surface then shows each part at once.

[appearance]
calm = true

Rail options

Two rail options sit directly in [appearance]:

KeyDefaultWhat it does
global_session Added in v0.8.0trueOffer a session on the rail that holds panes from several machines, once a second machine is reachable
git_dirty Added in v0.8.0trueCount 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].

The scrollbar

[appearance.scrollbar]:

KeyDefaultValuesWhat it does
style Added in v0.8.0"track"track, thinA full-height track, or a hairline thumb over the content
thumb Added in v0.8.0""one-cell glyphThumb glyph. Empty uses the style's own
track Added in v0.8.0""one-cell glyph, or noneTrack glyph. Empty uses the style's own
tint Added in v0.8.0"quiet"quiet, border, muted, or #RRGGBBBar colour

Selection colours

[appearance.selection] sets the colours TUIOS draws over pane content to mark text.

KeyDefaultWhat it sets
bg Added in v0.8.0"#45475A"Background of selected text
fg Added in v0.8.0""Selected text. Empty keeps the text's own colour
bold Added in v0.8.0falseAlso 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 Added in v0.8.0trueSweep a band of light over text you just copied. appearance.motion = "none" also turns it off
flash_color Added in v0.8.0""Colour of the sweep
flash_ms Added in v0.8.0420Length of the sweep, 80 to 3000 ms
flash_style Added in v0.8.0"diagonal"diagonal, diagonal-reverse, horizontal, vertical
copy_entry Added in v0.8.1"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 Added in v0.8.1"plain"The format that multi copy mode starts with: plain, markdown or json
osc52_write Added in v0.8.1"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 Added in v0.8.3""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.

Clipboard writes from a pane

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.

ValueWhat happens
offThe host clipboard does not change.
askThe dock shows a message. Click the message to copy the text.
focusedThe focused pane copies. A write from another pane shows a message in the dock. This is the default.
onEvery 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.

The session rail

[appearance.sidebar] configures the rail at the side of the screen.

KeyDefaultValuesWhat it does
enabled Added in v0.8.0trueboolShow the rail
position Added in v0.8.0"right"right, left, hiddenEdge the rail sits on
width Added in v0.8.024columnsPreferred width on a wide screen
background Added in v0.8.0""off, theme, #RRGGBBThe rail's background. Empty follows appearance.background. See Backgrounds
cursor_background Added in v0.11.0""a colourThe background of the row under the cursor while the rail has the keyboard. Empty keeps the theme's background. See The cursor row
agent_rest_fold Added in v0.8.0"1h"a duration, or offHow 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, countHow 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, collapsedDraw 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, noteThe 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.300 to 16The 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 offRemove the row of a finished subagent this long after it ends
header_case Added in v0.9.3"lowercase"lowercase, uppercaseHow 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 Added in v0.8.0"sessions:25,terminals,files:25,agents:34"layout stringWhich sections to draw, in what order. See below
show_glyphs Added in v0.8.0trueboolAgent-state glyph on each row
show_counts Added in v0.8.0trueboolWindow count on each session row
show_numbers Added in v0.9.0falseboolSwitch number (the one switch_session_N opens) before each local session name, for sessions 1 to 9
marquee Added in v0.8.0trueboolScroll a hovered row's long title
tooltips Added in v0.8.0trueboolLabel the collapsed strip on hover
file_icons Added in v0.8.0trueboolA Nerd Font icon per file type in the files section
file_icon_colors Added in v0.8.0trueboolDraw each icon in its file type's colour
folder_click Added in v0.8.0"navigate"navigate, cd, bothWhat a click on a folder row does
file_actions Added in v0.8.0trueboolAllow create, rename, delete, copy, cut and paste
file_delete Added in v0.8.0"trash"trash, permanentWhere d sends a file
editor Added in v0.8.2""a commandThe 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.

sections

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 :N for a share in percent.
  • The section names are sessions, terminals, files, agents, git and custom. 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.
  • spacer draws 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

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.

appearance.sidebar.file_drag Added in v0.12.0 lets you drag a row of the files section onto a pane. It is on by default. See Drag a file into a pane.

The file editor

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 nano or vim -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 editor is empty, TUIOS uses $EDITOR, then $VISUAL, then vi.
  • 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

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"
KeyWhat it does
title Added in v0.9.0The section's heading. Defaults to Custom
command Added in v0.9.0Run with sh -c. Every line of stdout is a row
refresh Added in v0.9.0once (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.

The pane section Added in v0.12.0

The rail can show a live terminal under its own heading. Put pane in sections, and describe it in [appearance.sidebar.pane]. tuios reads this table from the file only.

[appearance.sidebar]
sections = "sessions:25,terminals,files:25,pane,agents:34"

[appearance.sidebar.pane]
title   = "git"
command = "watch -t git status -sb"
height  = 12
KeyWhat it does
title Added in v0.12.0The heading of the section. The default is pane
command Added in v0.12.0The program the pane runs, through sh -c (cmd /C on Windows)
height Added in v0.12.0The most rows the pane takes. 0 or unset takes the share of the section, such as pane:30

See The pane section.

Group the main checkout Added in v0.12.0

appearance.sidebar.group_main = true puts a repository's main checkout under the repository row with its worktrees. The session is labelled by its branch. Enter or a click on the repository row then opens the main checkout's session. Fold group on the row's right-click menu folds the group. The default is false.

Session peek Added in v0.12.0

When the pointer or the rail cursor is on a session row, the terminals section shows the panes of that session. appearance.sidebar.peek = false turns this off. The terminals section then shows the panes of the attached session.

Two edges 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"
KeyWhat it does
enabledShow a rail on this edge. The edge opposite position stays hidden until you set enabled = true
widthThe preferred width of the rail on this edge, in columns
sectionsThe section layout of this edge. See sections
customA table with the command of this edge's custom section
session Added in v0.10.0The name of a daemon session to show in this rail. Its panes stack top to bottom in place of the sections. Empty shows the sections. See A daemon session in a rail
session_workspace Added in v0.10.0Show the session rail only on this workspace, 1 to 9. 0, the default, shows it on every 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 sections takes the default section order.
  • With position = "hidden", an edge table with enabled = true still 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.

Each edge table also takes the five subagent_* keys. A key that the edge table does not set uses the value from [appearance.sidebar]. See Subagent rows.

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.

Agent rows

[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.

Splits that follow ssh

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 = true

tuios 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.

Lines that are not followed

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, ForkAfterAuthentication and RequestTTY.
  • Files, sockets, environment and forwarding: ControlMaster, ControlPersist, ControlPath, UserKnownHostsFile, GlobalKnownHostsFile, HostKeyAlias, IdentityAgent, SendEnv, SetEnv, ForwardAgent, ForwardX11 and ForwardX11Trusted.
  • The flags -A, -X, -Y, -S, -L, -R, -D, -W, -w, -N, -f, -n, -T, -t, -M and -s.

These ssh lines get an ordinary pane:

  • An -o option that is not on the two lists above. ProxyCommand, LocalCommand, XAuthLocation and Match are examples.
  • An option or a value with a control character, such as a line break.
  • -E, -F, -I, -G, -V, -Q and -O, and an option that tuios does not know.
  • A -J or ProxyJump hop that is not [user@]host[:port] or the ssh:// form, made of letters, digits and ._:@[]-, or a hop that starts with -.
  • A mosh destination that starts with -.
  • mosh with --ssh, --client or --server.
  • ssh that another program starts, such as scp, rsync, git or sftp.

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.

Start in the remote folder

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_osc7

To 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.

Glyph sets

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
IdWhat it is
defaultRounded frame, Nerd Font powerline caps, ✕ and □ buttons
unicodeBox drawing and geometric shapes, with no Nerd Font glyphs
heavyOne stroke weight heavier throughout
ascii7-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.

Writing a set

A set can name the marks of the agent states: state_working, state_needs_input, state_idle, state_done, state_errored, state_unknown and state_waiting. A mark may be one cell or two, so state_needs_input = "✋" is valid. A state the set leaves alone keeps the shipped mark. Of the built-in sets, only ascii names these marks.

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 junctions middle, 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.

Added in v0.11.0 A session name, a workspace name or a pane title can hold Nerd Font icons. TUIOS shows each icon in one cell, on the rail, in the dock and in the rest of the chrome. On the Linux console and under --ascii-only, TUIOS removes the icons from what it draws, because they show as empty boxes there. The name itself does not change.

The settings page has a Glyph set row under Theme. Its picker applies each set as you move through the list.

Themes

345 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.

Custom themes

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"
}
  • id defaults to the file name, lowercased. display_name defaults to id.
  • The colour names are black, red, green, yellow, blue, purple, cyan, white, each with a bright_ variant, plus fg, bg and cursor. Note purple, not magenta.
  • 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 chrome object 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

[notifications] sets how long messages stay on screen, how they look and which ones the dock shows. For a time, 0 uses the built-in default: 2 seconds for info and success messages, 8 for warnings and 15 for errors. A time can be up to 3600 seconds.

KeyDefaultWhat it does
duration Added in v0.8.00Seconds an info or success message stays up
warning_duration Added in v0.8.00Seconds a warning stays up
error_duration Added in v0.8.00Seconds an error stays up when error_sticky is off
error_sticky Added in v0.8.0trueErrors stay until you press Esc
bell Added in v0.11.0"toast"What a pane's bell does: toast, forward, both or off. See The pane bell
style Added in v0.12.0"full"full shows a coloured bar and a line that counts down. plain shows the icon and the text only
level Added in v0.12.0"info"The least severe message the dock shows: info, warning or error
log_hidden Added in v0.12.0truefalse keeps the messages that level hides out of the log too

A message that level hides still goes to the log viewer (Ctrl+B D l). Warnings and errors always go to the log. A message from a pane, an agent or mail does not use level. [notifications.agent] and [notifications.mail] control those messages.

The pane bell Added in v0.11.0

A program in a pane rings the bell when it prints the BEL character. For example, printf '\a' rings it. notifications.bell sets what TUIOS does then:

[notifications]
bell = "forward"   # or "toast", "both", "off"
ValueWhat it does
toastShow "bell" in the dock. This is the default
forwardSend the bell to the terminal that TUIOS runs in. That terminal then plays its sound, flashes, or marks the window or tab
bothDo the two
offDo neither
  • TUIOS sends the bell for every pane, also for the pane you are in. It sends at most one bell from each pane in 200 milliseconds.
  • Each client sends the bell to its own terminal. Over tuios ssh and in tuios-web, the bell goes to that client, not to the machine that runs TUIOS.
  • Your terminal decides what a bell does. In Ghostty, for example, set bell-features to choose a sound, an attention request or a title mark.
  • The workspace bell marker does not change with this setting. Set bell_marker under [dock.tabs] to change it.

The settings page has this setting in the Alerts tab, as "Pane bell".

Agent alerts

[notifications.agent] controls what happens when an agent in a pane changes state.

KeyDefaultWhat it does
enabled Added in v0.8.0trueTurn every agent alert on or off
notify Added in v0.8.0trueSend a desktop notification through the terminal (OSC 9, so it works over SSH)
dock Added in v0.8.0trueShow the alert in the dock. Click it to go to the pane
sound Added in v0.8.0falseMake the alert audible
sound_mode Added in v0.8.0"audio"audio plays a cue, bell sends BEL, both does both
sound_cooldown_seconds Added in v0.8.03Shortest gap between two sounds, across all panes
command Added in v0.8.0""Shell command to run on an alert
settle_seconds Added in v0.8.02Wait this long, and drop the alert if the pane leaves the state
suppress_focused Added in v0.8.0trueNo alert for the pane you are looking at
quiet_hours Added in v0.8.0""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:

  • audio plays a short cue through the audio player of the machine that runs TUIOS: paplay, pw-play or aplay on Linux, afplay on macOS, PowerShell on Windows. If the machine has no player, the alert is silent and TUIOS shows no message.
  • bell sends a BEL character to your terminal. Your terminal decides what a bell does, and some terminals make no sound. In kitty, check that enable_audio_bell is yes. Other terminals have a similar bell setting.
  • both does 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.

Mail alerts

[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].

KeyDefaultWhat it does
enabled Added in v0.8.1follows notifications.agent.enabledTurn mail alerts on or off
notify Added in v0.8.1follows notifications.agent.notifySend a desktop notification
dock Added in v0.8.1follows notifications.agent.dockShow the alert in the dock. Click it to open the thread
sound Added in v0.8.1follows notifications.agent.soundMake the alert audible
between_agents Added in v0.8.1falseAlso 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.

Keybindings

leader_key

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.

If TUIOS cannot read the leader, the leader is ctrl+b until you correct it. See When a key is wrong.

lock Added in v0.11.0

Lock mode gives every key in terminal mode to the pane, except the leader.

[keybindings]
lock = true
KeyDefaultWhat it does
lock Added in v0.11.0falsetrue gives every key to the pane in terminal mode, except the leader. The leader unlocks for one action, and then TUIOS locks again. A repeatable action keeps its menu open until you press Esc. That menu takes only repeatable keys. Any other key goes to the pane. The zellij preset sets true

See Lock mode.

nest_exit_key Added in v0.12.0

The key that ends nest mode. The default is "f12". A key tuios cannot read goes back to "f12", and a key that programs type is reported as a problem.

Sections

Each section is a table under [keybindings]. Each action takes a list of keys. The Keybindings page lists every default.

SectionWhen it is active
window_managementWindow mode: windows, sessions, zoom, copy
workspacesWindow mode: switch and move between workspaces
layoutWindow mode: snapping, tiling, swaps, resizes, splits, preselect
mode_controlWindow mode: enter terminal mode, help, settings, quit
restore_minimizedWindow mode: restore a minimized window by number
systemWindow mode: the spotlight
navigationEmpty. Kept so older files still parse
terminal_modeTerminal mode, no prefix
globalBoth modes: the command palette and the launcher
prefix_modeAfter the leader key
window_prefixAfter leader, t
minimize_prefixAfter leader, m
workspace_prefixAfter leader, w
layout_prefixAfter leader, L
tape_prefixAfter leader, T
debug_prefixAfter leader, D
scriptWhile a tape plays
sidebarWhile the rail has the keyboard
sidebar_filesWhile the rail's cursor is on a file row. Checked before sidebar
sidebar_agentsWhile the rail's cursor is on an agent row. Checked before sidebar
inboxWhile the Inbox is open
inbox_peekWhile a prompt is open over the Inbox
mailWhile the mailbox is open
lists Added in v0.10.0The cursor keys of every list: the palette, the launcher, the pickers, the menus and the settings page. See List keys
gui Added in v0.10.0Keys of the native GUI client. The terminal client does not read them

Key syntax

  • Modifiers: ctrl+, alt+, shift+, super+. Also opt+ and option+, which mean alt+, cmd+ and command+, which mean super+, and control+, which means ctrl+. These spellings work on every platform.
  • Special keys: enter, return, esc, escape, tab, space, backspace, delete, up, down, left, right, home, end, pgup, pageup, pgdown, pagedown, f1 to f12.
  • Any single character: letters, digits, symbols, and accented letters.
  • Held modifiers: leftalt, rightalt, leftctrl, leftsuper and the like, for hold_window_mode only. 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. See One config on macOS and Linux.

new_window = ["n", "ctrl+t"]   # several keys for one action
close_window = []              # no key: the key goes to the pane

An 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.

Keyboard layout and Option characters

[keybindings]
keyboard_layout = "us"   # us, other
option_glyphs = "auto"   # auto, alt, bind, type
KeyDefaultWhat it does
keyboard_layout Added in v0.9.2usus lets a binding on opt+shift+7 also match opt+&, as on a US keyboard. Set other for AZERTY, QWERTZ and other layouts. A key that the terminal reports through the Kitty keyboard protocol uses that report.
option_glyphs Added in v0.9.2auto Added in v0.10.0What a character that macOS Option typed does. auto reads the keyboard layout of the Mac when tuios starts. On a US or ABC layout it runs the Option binding for the character. On other layouts the character goes to the pane. A keyboard_layout that you set overrides the layout of the Mac. alt runs the binding only when the terminal also reports Alt. bind always runs it. type always sends the character to the pane.

See Shifted digits and AZERTY and One Option key for typing.

Platform defaults

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.

The daemon table

KeyDefaultWhat it does
log_level "off"off, errors, basic, messages, verbose or trace
agent_autodetect Added in v0.8.0trueDetect an agent CLI running in a pane and show its state
agent_detect_seconds Added in v0.8.00Seconds between checks. 0 means 2. A negative number turns detection off
agent_binaries Added in v0.8.0[]Extra program names to treat as agents, added to the built-in list
resume_agents Added in v0.8.0"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 Added in v0.8.3trueSave 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 Added in v0.8.30Most history lines one pane saves. 0 means 1000
persist_scrollback_kb Added in v0.8.30Most KiB one pane's saved history takes on disk, compressed. 0 means 2048
window_size Added in v0.8.5"latest"The size of a session that has more than one client: latest (the client that last had input), smallest or largest. The names are those of the tmux window-size option. See Session size
single_client Added in v0.9.0falseKeep one client for each session. Each attach detaches the other clients of the session, as tuios attach -d does. See One client for each session
ssh_agent Added in v0.9.0"off""follow" gives each session a stable link to the ssh agent of the client used last. New panes get SSH_AUTH_SOCK set to the link. See SSH Agent
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 Added in v0.8.0falseLet 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

A change to single_client, ssh_agent or display in the file applies to the next attach.

The display of the client 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_DISPLAY or a DISPLAY of 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 gets WAYLAND_DISPLAY set to the full path of the link. The link moves to the socket of the newest client.
  • A new pane also gets DISPLAY and XAUTHORITY of the client. DISPLAY is a name, not a path, so a pane keeps the value it started with.
  • 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. See tuios display-env.
  • 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, and new panes get no display.
  • tuios uses the display of tuios attach and tuios new only. The client must run on this machine, as your user, and outside every pane. tuios ignores a client that comes through a host link, a client of the tuios SSH server and a client of tuios-web.
  • A WAYLAND_DISPLAY name such as wayland-1 is looked up in the XDG_RUNTIME_DIR of the client, then in the one of the daemon, then in /run/user/<uid>. The client does not need XDG_RUNTIME_DIR.
  • The Wayland socket must be a Unix socket that you own. DISPLAY must be :N or :N.S, and tuios ignores a forwarded display such as localhost:10.0. XAUTHORITY must be a file that you own.
  • The option is read from the file only. tuios set-config cannot change it.

To change window_size for one session while it runs, use tuios set-config daemon.window_size smallest -s NAME. That value is not saved to the file. A change in the file applies when the daemon starts again.

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.

The startup table

[startup] decides how a new session starts. It never changes a running session.

KeyDefaultWhat it does
daemon Added in v0.8.0trueA bare tuios attaches to a daemon session instead of running standalone
tiled Added in v0.8.0trueStart with tiling on
layout Added in v0.8.0"bsp"Tiling layout: bsp, master-stack or scrolling
open_default_window Added in v0.8.0falseOpen one window when a session starts empty
start_in_terminal_mode Added in v0.8.0falseStart in terminal mode when a window is present
session Added in v0.12.0""The session a plain tuios attaches to. tuios makes the session if it is gone. Empty attaches to the most recent session
window Added in v0.12.0""The pane, by name or id, that a plain tuios focuses in session. When no pane has that name, the focus does not change

session and window change a plain tuios only. Other commands use the session you give them. session uses the daemon even when daemon is false.

--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.

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.

The workspaces table

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.

KeyDefaultWhat it does
return_when_empty Added in v0.9.0trueShow the workspace you came from when the workspace on screen becomes empty. false keeps the empty workspace on screen
new_window_when_empty Added in v0.9.0falseOpen a pane when you switch to a workspace that has no panes, as the new-window key does

TUIOS goes back in this order:

  1. For a workspace that tuios xpanes opened, the workspace where you ran tuios xpanes.
  2. The workspace you showed last that still has panes. After a chain of empty workspaces, TUIOS goes back past each one.
  3. 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 = false

The 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.

A pane on an empty workspace

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 = true

The 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_follow and other moves that take a pane with them.
  • tuios xpanes, tuios select-workspace, tuios run-command and tape scripts.
  • The switch back that return_when_empty makes.
  • 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 restart-server to load the new daemon.

The launcher table

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 --"

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.

launcher.keep_open Added in v0.12.0 keeps the pane of a launched program open after it exits. A line under the output shows the exit status. The option needs a daemon session.

[launcher]
keep_open = true

The tape table

[tape] controls project tapes: a .tuios.tape file in a directory. See Tape Scripting.

KeyDefaultWhat it does
autorun Added in v0.8.0"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 Added in v0.8.0falseOpen the review dialog when a tape is found, instead of only showing the badge

A tape edited after you trusted it is untrusted again.

The debug table

KeyDefaultWhat it does
show_key_events Added in v0.8.0falseShow the keycast of recent keypresses

--show-keys and Ctrl+B D k toggle the same overlay. See Showkeys.

The screenshot table

[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.

KeyDefaultValuesWhat it does
format Added in v0.8.0"png"png, svg, ansi, html, txtOutput format
directory Added in v0.8.0"~/Pictures/tuios"pathWhere captures are saved
copy Added in v0.8.0trueboolTry to copy the capture to the clipboard
preview Added in v0.8.0trueboolOpen the preview panel after a capture
frame Added in v0.8.0"window"window, plain, noneA window card, a plain card, or nothing
background Added in v0.8.0"auto"auto, none, #RRGGBB, #RRGGBB..#RRGGBBBackdrop. auto derives it from the theme. Two colours make a gradient
padding Added in v0.8.0480 to 128Space around the card, in pixels
radius Added in v0.8.0100 to 32Card corner radius, in pixels
shadow Added in v0.8.0trueboolSoft shadow under the card
controls Added in v0.8.0"auto"auto, macos, glyphs, noneWindow buttons on the card
title_format Added in v0.8.0"{title}"templateTitle bar text, with {title}, {index} and {cwd}
font_family Added in v0.8.0"JetBrains Mono, monospace"stringFont to use when your terminal does not report its own
font_file Added in v0.8.0""pathFont file to draw with. Wins over every other font choice, and is embedded in SVG and HTML output
scale Added in v0.8.021 to 4PNG size multiplier
cursor Added in v0.8.0falseboolDraw the cursor cell
[screenshot]
format = "svg"
frame = "plain"
background = "none"
padding = 24

png and svg carry the frame. ansi and txt are plain output, so the frame keys do not apply.

The screensaver table

[screensaver] animates the screen after a period with no input. It is off by default.

KeyDefaultWhat it does
enabled Added in v0.8.0falseStart after idle_minutes with no input
idle_minutes Added in v0.8.010Minutes of quiet before it starts, 1 to 240
effect Added in v0.8.0"random"The effect, or random for a different one each time
while_busy Added in v0.8.0falseStart 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. A plugin can add a saver Unreleased: set effect = "plugin/ID/SAVER". See A screen saver in a plugin. The settings page previews each effect on your current screen.

S in window mode starts the screen saver now, whatever enabled says.

The hints table

[hints] sets what hints mode (Ctrl+B F) labels.

KeyDefaultValuesWhat it does
builtins Added in v0.8.1"all"all, none, or names such as url,path,shaThe built-in patterns to look for
patterns Added in v0.8.1[]list of Go regular expressionsYour own patterns. A group named match sets the part that is copied
alphabet Added in v0.8.1"asdfghjkl"lowercase lettersThe letters of the labels, easiest first
open Added in v0.8.1trueboolLet Ctrl and a label open a URL or a path
dim Added in v0.8.16010 to 90How much the text around the labels dims, in percent
all_panes Added in v0.8.2falseboolPut 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.

The panes table

[panes] sets the keys of the pane labels (Ctrl+B Q) and the first layout of the pane navigator (Ctrl+B /).

KeyDefaultValuesWhat it does
label_keys Added in v0.9.0"1234567890"letters a to z and digitsThe keys the labels are made of, first pane first. With more panes than keys, a label has two keys
navigator_layout Added in v0.9.0"tree"tree, flat, cardsThe layout the pane navigator opens in. v changes it while the navigator is open
navigator_search Added in v0.12.0falsetrue, falseOpen the navigator ready to type a search. Esc with nothing typed closes it
drag_and_drop Added in v0.12.0falsetrue, falseTurn on drag and drop between panes over kitty's protocol (OSC 72)
drag_and_drop_desktop Unreleasedfalsetrue, falseWith drag_and_drop, drag files between the desktop and the panes. Needs kitty 0.47 or later. See below

With drag_and_drop on, a program that speaks the protocol, such as yazi 26.9 or later, can drag files from its pane. Hold the left button on a file in terminal mode, and carry it to another pane. A pane whose program speaks the protocol takes the drop itself. Any other pane gets the paths as a paste, quoted for its shell, by the rules of a drag from the rail. tuios reads no file, and refuses a drop back into the pane the drag came from. The switch is off because the protocol is still settling.

Drag and drop with the desktop Unreleased

Set drag_and_drop_desktop = true with drag_and_drop = true to drag files between the desktop and the panes. The terminal that tuios runs in must speak the same protocol. Today that is kitty 0.47 or later. tuios asks the terminal once at startup.

[panes]
drag_and_drop = true
drag_and_drop_desktop = true
  • From the desktop: drag a file from a file manager or a browser onto the tuios window. It lands on the pane under the pointer. A program that speaks the protocol takes it. Any other pane gets the path as a paste, by the rules above.
  • To the desktop: a file that a program in a pane drags, such as yazi, becomes a drag on the desktop. Drop it on any other window. The desktop shows the file names and the pane they come from.
  • tuios takes a drag only when the press went to a pane that offers drags. You still move a pane, select text and drag a row of the rail as before.
  • The bridge is for a terminal on this machine. A client over SSH, through tuios-web, or attached to the daemon of another machine does not use it.
  • Both switches apply without a restart.

The path_picker table Added in v0.12.0

[path_picker] sets show_hidden. With true, the path picker shows the names that start with a dot when it opens. The default is false.

The worktree table Added in v0.12.0

[worktree] path sets where tuios worktree new, New worktree... on the rail and tuios fan put a worktree. The value is a path template. Empty is the default. See Where a worktree goes.

[worktree]
path = "{{ repo_path }}/.worktrees/{{ branch | slug }}"

The review table Added in v0.12.1

[review] sets how a review opens.

KeyDefaultWhat it does
focus Added in v0.12.1"diff"The side that has the keys when a review opens: diff or files
scope Unreleased"base"What a review shows when it opens: base or uncommitted
[review]
focus = "files"
scope = "uncommitted"

base shows the changes since the base. In a plain repository, the base is the merge base with the upstream branch, so commits that you did not push yet are part of the review. uncommitted shows only the changes that are not committed. In the review, u still switches between the two, and b still sets another base. The compare view of two attempts does not use scope.

Both keys are on the settings page, in the Behavior section. tuios set-config review.scope uncommitted applies from the next review. tuios reads another value as the default and shows a warning when the config loads. tuios set-config refuses it.

The tasks table Added in v0.12.0

[tasks] sets how the task board works.

KeyDefaultWhat it does
stages Added in v0.12.0every stageThe stages the board shows. The order stays todo, plan, doing, verify, review, shipped, done
auto_stage Added in v0.12.0truefalse leaves every stage to you. tuios then moves no card on its own
[tasks]
stages = ["todo", "doing", "verify"]
auto_stage = false

The scratch table

[scratch] sets the size of the scratch terminal that Ctrl+B g shows in a popup. See Scratch terminal.

KeyDefaultValuesWhat it does
width Added in v0.8.2"80%"cells (100) or percent (80%)Width of the scratch terminal
height Added in v0.8.2"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.

The pip table

[pip] sets the size and the first corner of the picture-in-picture view. See Picture in picture.

KeyDefaultValuesWhat it does
width Added in v0.8.34012 to 200Width of the view in cells, border included. The view gets smaller on a small screen
height Added in v0.8.3123 to 100Height of the view in cells, border included
corner Added in v0.8.3"bottom-right"bottom-right, bottom-left, top-right, top-leftThe corner the view goes to first. The view moves to a different corner when the line you type reaches it

The paste_buffers table

[paste_buffers] sets how many paste buffers the daemon keeps. The daemon reads it when it starts and when the file changes.

KeyDefaultValuesWhat it does
limit Added in v0.9.0200 or moreHow many unnamed buffers (buffer0, buffer1) to keep. Past it, the oldest goes. 0 keeps none, and a yank goes only to the clipboard
max_kb Added in v0.9.0163840 to 262144The most KiB that all buffers hold together. Past it, the oldest unnamed buffer goes first, then the oldest named one. 0 uses 16384

A smaller limit removes the oldest buffers at once. A yank larger than max_kb does not become a buffer, but it still goes to the clipboard.

Command keys

[[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.

The spotlight table

[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.

KeyDefaultValuesWhat it does
enabled Added in v0.8.0falseboolTurn the spotlight on
follow Added in v0.8.0"mouse"mouse, cursorFollow the pointer, or the focused pane's cursor
radius Added in v0.8.0102 to 200Half the beam's height, in rows
dim Added in v0.8.07510 to 95How much an unlit cell is dimmed, in percent
edge Added in v0.8.0"hard"hard, softCut the beam off, or fade it. A fade sends about three times the bytes
shake Added in v0.8.0falseboolShake the mouse left and right to toggle the beam

follow = "cursor" sends fewer bytes to a remote client.

The dock table

[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. 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 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 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.

Custom components

[dock.custom.NAME] defines a cell. Put custom/NAME in a list to place it.

KeyDefaultWhat it does
commandrequiredRun 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-width24Widest the cell may be, in cells
fg, bg, bold Added in v0.10.0unsetThe text colour, the ground colour and bold text of the cell. Colours are #rrggbb
cap_left, cap_rightunsetText 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.

SSH server limits Added in v0.12.0

The [ssh] table sets the limits of the tuios ssh server. The server reads it when it starts. The defaults do not stop a normal login.

[ssh]
handshake_timeout = "120s" # time to finish the login; 0 turns the limit off
idle_timeout = "0"         # close a connection with no traffic for this long; 0 is off
max_handshakes = 32        # connections that log in at the same time; 0 is no limit
max_sessions = 64          # sessions open at the same time; 0 is no limit
KeyDefaultWhat it does
handshake_timeout Added in v0.12.0"120s"Close a connection that does not log in within this time, as LoginGraceTime does in OpenSSH. The limit ends when the login is complete
idle_timeout Added in v0.12.0"0"Close a connection after this time with no traffic in either direction. A session that shows a clock stays open
max_handshakes Added in v0.12.032When this many connections are logging in, close a new connection at once. A connection that is logged in does not count
max_sessions Added in v0.12.064When this many sessions are open, over all connections, refuse a new session. The client sees a message

A duration is a value such as 90s, 30m or 2h. A value that does not parse, or a negative value, gives a warning and the default. tuios ssh --handshake-timeout, --idle-timeout, --max-handshakes and --max-sessions replace these values for one run. The server writes the limits it uses to its log when it starts. tuios set-config cannot change this table.

The hosts table

[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
KeyDefaultWhat it does
addr Added in v0.8.0requiredAnything ssh accepts, including an ssh_config alias. A host with no addr, or with one that starts with -, is ignored and logged
connect_timeout Added in v0.8.010Seconds one dial may take before the host counts as unreachable
command Added in v0.8.0""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 Added in v0.8.0[]Extra ssh arguments, placed before the address. Only safe options are accepted. See Rules for ssh_options. -A forwards your ssh agent to the host. See SSH Agent
repos_root Added in v0.8.0""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 build

A 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.

Rules for ssh_options

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, the ServerAlive options and the algorithm lists.
  • Refused: ProxyCommand, LocalCommand, the known hosts files, ControlPath, -F and socket forwards.
  • Refused: port forwards (-L, -R, -D).
  • -J and ProxyJump take host names only. ForwardAgent takes only yes or no.
  • A host addr or command cannot 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"]

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.

What another machine may do here

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"]
CapabilityWhat it lets the other machine do here
listRead sessions, windows, captures, agent state, the Inbox, prompts, waits and events
mailSend and read agent mail, and use the stash
openStart sessions, windows, worktrees, fans and agents
writeType into panes, close and move windows, set options and names, report agent state, attach, and pull a worktree's work
respondAnswer 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.3Read 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.

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
KeyDefaultWhat it does
files_roots Added in v0.9.3one receive folderThe 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 Added in v0.9.3not setThe 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 Added in v0.9.3"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
files_max_entries Added in v0.12.020000The most new files and folders the machine may make here in ten minutes. Past the bound, a new file or folder is busy, and a copy waits and goes on. 0 turns the bound 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 .bashrc and .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 *.pub key in ~/.ssh is the one exception.
  • Reads also never return ~/.gitconfig, ~/.config/git, shell start files or ~/.config/environment.d. The list also covers the snap and flatpak browser folders, Thunderbird, Signal, Discord, Slack, Telegram, VS Code and common crypto wallets. A folder copy over a link leaves these entries out.
  • 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.

The read rules and files_read_roots also apply to a kitty image that a program names by path in a pane that runs on this machine for the other machine. See Kitty images in the pane.

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 files is taken away, its copies here stop.
  • A change that widens files_roots or files_read_roots, removes files_read_roots, or lowers files_min_free waits for tuios config apply. A change that narrows applies at once.
  • A file that another machine writes here gets the sender's permission bits under this machine's umask. It never gets group or world write. The execute bits stay. This holds for a paired device too.
  • A machine with files cannot write the config folders of the agent tools that tuios integrates. It also cannot read the logins and API keys of more agent tools and cloud tools.

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.

Using a linked host

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.

Reaching a host you cannot ssh to directly

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=22

Then 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.

The tailscale table

[tailscale] changes which tailnet machines tuios hosts add and tuios hosts tailnet offer as addresses. It never adds a host by itself.

KeyDefaultWhat it does
enabled Added in v0.8.0trueOffer tailnet machines. Empty on a machine that is not on a tailnet
addr Added in v0.8.0"dns"Address form: dns (MagicDNS name), name (short name) or ip
user Added in v0.8.0""ssh login to put before every address
usersnonePer-machine logins, keyed by short name. Wins over user
os Added in v0.8.0platforms that run TUIOSOperating systems to offer. An empty list offers every machine
offline Added in v0.8.0falseAlso offer machines that are offline
self Added in v0.8.0falseAlso offer this machine
shared Added in v0.8.0falseAlso offer machines shared into your tailnet
include, exclude[]Glob patterns on the short name and the MagicDNS name. exclude wins
max Added in v0.8.0built inMost machines to offer
socket Added in v0.8.0""Path to the tailscaled socket, if it is not in the usual place
[tailscale]
user = "ubuntu"
exclude = ["*-pad-*"]

[tailscale.users]
build = "admin"

The agents table

[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.

Turn off agent features

[agents]
enabled = false

enabled (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 that stop reporting Added in v0.12.0

A hooked agent reports at each prompt and each tool call. When an agent that reported working sends no report for silent_after, tuios shows it as unknown with the reason silent. Its row reads "No report for N min".

[agents]
silent_after = "10m"   # default; "0" or "off" turns it off

The value is a Go duration, such as 90s or 20m. A value that does not parse reads as 10m, with a warning. The daemon reads the value when it starts and when the file changes. See Agents that stop reporting.

Fold the Working group Added in v0.12.0

fold_working = true opens the agents view with the Working group folded. A folded group shows its heading, its count and the state mark of each agent. F folds or opens any group. tuios keeps that choice in sidebar.json in its state folder, and the choice wins over fold_working. The settings page has this option as "Fold Working".

The other agent tables

[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.mail]
wake = true                             # type one line into an idle agent with unread mail

[agents.checkpoints]
enabled = true
keep = 50                               # at most 1000
max_untracked_mb = 50                   # a negative value means no limit
KeyDefaultWhat it does
approvals.enabled Added in v0.8.0[]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 Added in v0.8.0120How long a prompt waits for your answer before the harness asks in its own pane
approvals.hold_plans Added in v0.8.0trueAlso hold a plan an agent asks you to approve when it leaves plan mode
approvals.risk.builtin Added in v0.8.0trueKeep the shipped risk rules
approvals.risk.panes_may_allow Added in v0.8.0falseLet a pane holding the respond grant allow a risky call
approvals.risk.rule Added in v0.8.0noneYour own rules: a name, the tools it applies to, and an RE2 pattern
permissions.mode Added in v0.8.0"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 Added in v0.8.0["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 Added in v0.8.0"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 Added in v0.8.0"10m"How long you must have been away from a pane for the dock to show its recap
recap.test_patterns Added in v0.8.0built inCommands that count as a test run in the recap, such as go test and npm test
queue.max Added in v0.8.08Messages one pane's queue holds
mail.wake Added in v0.11.0trueType one line into an agent at rest that has unread mail, so that it reads the mail. false stops it. A change to false applies at once. A change to true waits for tuios config apply from outside TUIOS, or a restart. See Agent Messaging
checkpoints.enabled Added in v0.9.0trueSave the git work tree of an agent's pane each time the agent finishes a turn that changed a file. See Checkpoints
checkpoints.keep Added in v0.9.050How many checkpoints one pane keeps, at most 1000
checkpoints.max_untracked_mb Added in v0.9.050Leave 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.

The notify table

[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
KeyDefaultWhat it does
enabled Added in v0.9.0truefalse turns every notification off. Nothing is sent without a provider
web_url Added in v0.9.0""The address of tuios-web. Each notification links to web_url/inbox?item=ID
content Added in v0.9.0"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 Added in v0.9.0120Hold a notification while you typed at an attached client in this many seconds. 0 sends at once
cooldown_seconds Added in v0.9.060The shortest time between two notifications for the same pane and kind
max_per_hour Added in v0.9.030The most notifications in one hour. 0 sets no limit
allow_http_redirects Added in v0.9.0falseLet 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.

Web Push 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
KeyDefaultWhat it does
subject Added in v0.9.3https://tuios.dev/pushThe VAPID subject: an https URL or a mailto: address that a push service can use to contact you
allow_insecure Added in v0.9.3falseLet 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

[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.

Validation

The file is checked when it loads. Problems are errors or warnings.

Errors are keys TUIOS cannot read, such as "ctrl+nope" or "ctrl+". 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.

A file that does not parse

If config.toml is not valid TOML, TUIOS cannot read any of it. At startup, TUIOS uses the built-in defaults and shows a config problem. If you save such a file while TUIOS runs, TUIOS keeps the config that it has.

When a key is wrong

TUIOS ignores a key that it cannot read, such as ctrl+nope. It loads the rest of the file. Your other keys, the leader and the other settings stay as you wrote them.

The action of an ignored key gets a key as follows:

  • If the action has another key that TUIOS can read, the action keeps that key.
  • If the action has no other key, it gets its default key.
  • If another action already has that default key, the action has no key.
  • If TUIOS cannot read leader_key, the leader is ctrl+b until you correct it.

TUIOS does not change your file. Correct the key, then save. The change applies at once.

To find the ignored keys:

  • Look for the config problems notification when TUIOS starts.
  • Open the log viewer (leader D l). Each line names the key and its file.
  • Run tuios keybinds doctor. It lists each key by file and says what TUIOS does instead. See Keybindings.
  • Run tuios config apply. It lists the keys it ignored.

The same rule applies to keys in an included file and in config.d.

One config on macOS and Linux

You can use one config.toml on a Mac and on a Linux machine, for example from a dotfiles repo. TUIOS reads opt+ and option+ as alt+ on every platform. On Linux, opt+1 is alt+1.

[keybindings.workspaces]
switch_workspace_1 = ["opt+1"]   # Option+1 on macOS, Alt+1 on Linux

tuios keybinds doctor lists these keys under OPT KEYS READ AS ALT. This is for information. You do not need to change them.

Troubleshooting

My config seems ignored.

  1. Run tuios config path to confirm which file TUIOS reads. Run tuios config files to see every file that TUIOS reads.
  2. Look for the config problems notification when TUIOS starts. Open the log viewer (leader D l) to see each problem.
  3. Run tuios keybinds doctor. It lists each key that TUIOS cannot read, and the leader that TUIOS uses. TUIOS ignores only those keys. See When a key is wrong.
  4. If the log says the file does not parse, correct the TOML. TUIOS uses the defaults until then.
  5. On Linux, a key copied from a Mac works. opt+ is alt+ on every platform.

A setting changed nothing.

  1. Check the table of what applies live. A saved keybinding works at the next key press.
  2. Check that the file parsed. Look for an error notification, or run tuios keybinds list.
  3. Check whether a command-line flag is overriding it.
  4. Check whether a later file sets the key. Run tuios config origin <key>. An old config.toml that sets every key hides the other files. Run tuios config prune to remove its default values. See Split the Config.
  5. 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.

Examples

Browser-style keys

[keybindings.window_management]
new_window = ["ctrl+t"]
close_window = ["ctrl+w"]
next_window = ["ctrl+tab"]
prev_window = ["ctrl+shift+tab"]

Screen-style leader

[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.

A quiet, dense layout

[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

tuios lives only at tuios.dev and github.com/Gaurav-Gosain/tuios. tuios has no X or Twitter account. An account that offers a tuios token or presale is not this project.

On this page

TablesApplying changesChanging a setting at runtimeThe in-app settings pageCommand-line flagsAppearanceTheme and chromeborder_styleFocused border colourDialog and panel frames Added in v0.11.0max_fpsBackgroundsWindows and titleswindow_title_formathost_titleMouse and inputLinksScrollingTiling and zoomDockWorkspace tab style Added in v0.10.0Calm mode Added in v0.12.0Rail optionsThe scrollbarSelection coloursClipboard writes from a paneThe session railsectionsThe files sectionThe file editorThe custom sectionThe pane section Added in v0.12.0Group the main checkout Added in v0.12.0Session peek Added in v0.12.0Two edges Added in v0.9.3Agent rowsSplits that follow sshLines that are not followedStart in the remote folderGlyph setsWriting a setThemesCustom themesNotificationsThe pane bell Added in v0.11.0Agent alertsMail alertsKeybindingsleader_keylock Added in v0.11.0nest_exit_key Added in v0.12.0SectionsKey syntaxKeyboard layout and Option charactersPlatform defaultsThe daemon tableThe display of the client Added in v0.9.3The startup tableThe workspaces tableA pane on an empty workspaceThe launcher tableThe tape tableThe debug tableThe screenshot tableThe screensaver tableThe hints tableThe panes tableDrag and drop with the desktop UnreleasedThe path_picker table Added in v0.12.0The worktree table Added in v0.12.0The review table Added in v0.12.1The tasks table Added in v0.12.0The scratch tableThe pip tableThe paste_buffers tableCommand keysThe spotlight tableThe dock tableCustom componentsSSH server limits Added in v0.12.0The hosts tableRules for ssh_optionsWhat another machine may do hereFiles over a link Added in v0.9.3Using a linked hostReaching a host you cannot ssh to directlyThe tailscale tableThe agents tableTurn off agent featuresAgents that stop reporting Added in v0.12.0Fold the Working group Added in v0.12.0The other agent tablesThe notify tableWeb Push Added in v0.9.3HooksValidationWhen a key is wrongOne config on macOS and LinuxTroubleshootingExamplesBrowser-style keysScreen-style leaderA quiet, dense layoutRelated