Configuration

Every table and option in the TUIOS config.toml, with defaults and how changes apply.

TUIOS reads one TOML file, ~/.config/tuios/config.toml. If XDG_CONFIG_HOME is set, the file is $XDG_CONFIG_HOME/tuios/config.toml. TUIOS writes a commented default file on first run.

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.

[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, 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 19 sections of bindings
[daemon]Daemon log level and agent detection
[startup]How a new session starts
[tape]Project tapes
[debug]The keycast overlay
[screenshot]Capture format, frame and fonts
[screensaver]The idle animation
[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
[tailscale]Which tailnet machines tuios hosts add offers
[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
Keybindings other than leader_keyThe next client: detach and attach again, or restart
[hooks]The daemon reads it at start. Run tuios kill-server after editing a hook the daemon runs
[daemon]The daemon reads it at start. Run tuios kill-server after editing it. tuios set-config daemon.log_level also changes the log level of a running daemon
[startup]The next new session
scrollback_linesPanes opened afterwards
Custom theme filesSee Custom themes

The daemon also follows the file, but only for [hosts] and preferred_shell. Adding or removing a host opens or closes its link at once.

Changing a setting at runtime

tuios list-options                       # every path, with default and accepted values
tuios list-options --section sidebar     # one group
tuios get-config appearance.border_style
tuios set-config appearance.border_style thick

set-config changes a running session and does not write the file. A path the registry does not know is refused. An [appearance] option also answers to its bare name, so border_style and appearance.border_style are the same path. set-config cannot reach [keybindings], [hooks], [hosts], [tailscale] or the [dock] lists, because those are maps rather than single values. 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 settings page rewrites the whole file

Each change is saved by writing the full config to config.toml. Your comments, key order and formatting are replaced by the generated header and every option with its value. tuios keybinds unbind and tuios keybinds free write the file the same way. Keep a copy in version control if you edit the file by hand.

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.animations_enabled = false
--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"default"a glyph set idShapes of the chrome. See Glyph sets
border_style"rounded"see belowBorder drawn around every pane
border_focused_color""#RRGGBBOverride the focused border colour
border_unfocused_color""#RRGGBBOverride the unfocused border colour
shared_bordersfalseboolShare one border between adjacent tiled panes
gap00 to 8Empty cells between tiled panes
dim_unfocused00 to 90Fade panes you are not in, in percent
session_colorstrueboolGive each session its own colour on the rail and in the switcher
session_borderfalseboolAlso draw that colour on every pane border
zen_mode"disabled"disabled, always, mouseHide borders never, always, or while the mouse is idle
panel_padding21 to 6Columns of padding inside overlay panels
animations_enabledtrueboolAnimate transitions
max_fps00, or 10 to 120Frame rate cap. 0 means 60

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.

Windows and titles

KeyDefaultValuesWhat it does
window_title_position"bottom"bottom, top, hiddenWhere the title is drawn. hidden also disables rename
window_title_format""templateTitle template. See below
window_button_style"dots"dots, pillmacOS-style traffic lights, or a filled pill
window_button_position"left"left, rightWhich end of the title bar holds the buttons
hide_window_buttonsfalseboolHide the minimize, zoom and close buttons
window_button_zoomtrueboolShow the zoom button on tiled panes
hide_scrollbarfalseboolHide the scrollbar on the pane border
confirm_quitfalseboolAlways confirm on quit, not only when processes are running
preferred_shell""pathShell for new panes. Empty picks one for your platform
new_window_inherit_cwdtrueboolStart a new window in the focused pane's directory
whichkey_enabledtrueboolShow 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_time5000 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.

Mouse and input

KeyDefaultValuesWhat it does
click_to_type"single"single, double, offIn window mode, whether a click on a pane starts typing in it
auto_enter_terminal_on_focus"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_mousefalseboolFocus the pane under the pointer
alt_dragtrueboolAlt + left-drag moves a pane
right_click_opens_menufalseboolIn terminal mode, a plain right-click opens the pane menu
copy_on_selecttrueboolCopy a mouse selection when you release the button
word_characters"@-./_~?&=%+#"stringPunctuation that counts as part of a word on double-click
links"all"all, marked, offWhich links Shift+click opens. See below
kitty_placeholders"auto"auto, on, offDraw images placed with kitty Unicode placeholders

Shift+click on a link opens it. Shift is the terminal's "this click is mine" modifier, so it works even when the program in the pane uses the mouse.

  • marked finds only OSC 8 hyperlinks, where the program says a run of cells is a link.
  • all (default) also finds plain http, https and file URLs in the text.
  • off finds none.

Only http, https, mailto, ftp and ftps links go to your desktop's opener. Any other scheme is copied to the clipboard instead, with a notification. A file:// link opens a file in a new pane running your editor, and a directory in the rail's files section. A remote client cannot open links, so it copies the address.

Scrolling

KeyDefaultValuesWhat it does
scrollback_lines10000100 to 1000000Lines each pane keeps. Read when the pane is created
scroll_lines31 to 50Lines per mouse wheel notch

Tiling and zoom

KeyDefaultValuesWhat it does
master_ratio5030 to 70Master pane width in the master-stack layout, in percent
scroll_column_width5520 to 100Column width in the scrolling layout, in percent
scroll_column_max9020 to 100Widest a scrolling column may be set to
niri_scroll_cells81 to 200Cells the scrolling layout moves per wheel event
niri_reverse_scrollfalseboolReverse the wheel in the scrolling layout
niri_click_revealstrueboolClicking a partly hidden column brings it fully on screen
niri_hover_revealstrueboolWith focus_follows_mouse, hovering does the same
zoom_size10050 to 100Share of the screen a zoomed pane takes, in percent
zoom_max_width0cellsWidth cap for a zoomed pane. 0 fills the screen
zoom_animationtrueboolSlide a pane into and out of zoom
zoom_follows_focustrueboolMoving focus while zoomed zooms the pane you move to

The layout a new session starts in is startup.layout. See The startup table.

Dock

KeyDefaultValuesWhat it does
dockbar_position"bottom"bottom, top, hiddenEdge the dock sits on
dock_workspace_tabstrueboolShow the clickable workspace strip
dock_workspace_tab_format""templateTab label with {index} and {name}. Empty means {name}
dock_workspace_tooltiptrueboolShow a truncated workspace name in full on hover
dock_pill_capsfalseboolPowerline caps on the dock's pills instead of flat ends
show_clockfalseboolShow the clock
clock_format"15:04:05"Go time layoutClock format, for example 15:04 or Mon 3:04PM
show_cpufalseboolShow a CPU graph
show_ramfalseboolShow RAM use
hide_clockfalseboolDeprecated. Use show_clock

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

Rail options

Two rail options sit directly in [appearance]:

KeyDefaultWhat it does
global_sessiontrueOffer a session on the rail that holds panes from several machines, once a second machine is reachable
git_dirtytrueCount 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"thin"thin, trackA hairline thumb, or a full-height track
thumb""one-cell glyphThumb glyph. Empty uses the style's own
track""one-cell glyph, or noneTrack glyph. Empty uses the style's own
tint"quiet"quiet, border, muted, or #RRGGBBBar colour

Selection colours

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

KeyDefaultWhat it colours
bg"#45475A"Background of selected text
fg""Selected text. Empty keeps the text's own colour
boldfalseAlso 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
flashtrueSweep a band of light over text you just copied
flash_color""Colour of the sweep
flash_ms420Length of the sweep, 80 to 3000 ms
flash_style"diagonal"diagonal, diagonal-reverse, horizontal, vertical

An empty foreground keeps the colour the program wrote. An empty background falls back to the default.

The session rail

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

KeyDefaultValuesWhat it does
enabledfalseboolShow the rail
position"left"left, right, hiddenEdge the rail sits on
width28columnsPreferred width on a wide screen
sections"sessions:25,terminals,files:25,agents:34"layout stringWhich sections to draw, in what order. See below
show_glyphstrueboolAgent-state glyph on each row
show_countstrueboolWindow count on each session row
marqueetrueboolScroll a hovered row's long title
tooltipstrueboolLabel the collapsed strip on hover
file_iconstrueboolA Nerd Font icon per file type in the files section
file_icon_colorstrueboolDraw each icon in its file type's colour
folder_click"navigate"navigate, cd, bothWhat a click on a folder row does
file_actionstrueboolAllow create, rename, delete, copy, cut and paste
file_delete"trash"trash, permanentWhere d sends a file

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 and git. Leave a name out to hide that 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. Nothing polls, so a file written by another program appears on the next read.

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.

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", "harness", "name", "elapsed", "message"]

[[appearance.sidebar.agent_row.elapsed.rule]]
gt = 30          # minutes in the current state
fg = "warning"

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.

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

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.

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

Themes

342 themes ship with TUIOS.

tuios --list-themes                   # every theme id
tuios --preview-theme dracula         # the 16 ANSI colours
tuios list-themes --filter gruvbox    # from a running daemon, with --json
tuios list-themes catppuccin_mocha    # colours and their contrast
[appearance]
theme = "dracula"

With no theme, TUIOS uses your terminal's own colours. A theme only changes ANSI colours 0 to 15. 256-colour and truecolor output is drawn as the program wrote it.

To pick one in the app, press Enter on the Theme row of the settings page, or choose Theme Picker in the command palette. The picker previews as you move and Esc restores the theme you had. It has no key binding.

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. 0 uses the built-in default.

KeyDefaultWhat it does
duration0Seconds an info or success message stays up
warning_duration0Seconds a warning stays up
error_duration0Seconds an error stays up when error_sticky is off
error_stickytrueErrors stay until you press Esc

Agent alerts

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

KeyDefaultWhat it does
enabledtrueTurn every agent alert on or off
notifytrueSend a desktop notification through the terminal (OSC 9, so it works over SSH)
docktrueShow the alert in the dock. Click it to go to the pane
soundfalseMake the alert audible
sound_mode"audio"audio plays a cue, bell sends BEL, both does both
sound_cooldown_seconds3Shortest gap between two sounds, across all panes
command""Shell command to run on an alert
settle_seconds2Wait this long, and drop the alert if the pane leaves the state
suppress_focusedtrueNo alert for the pane you are looking at
quiet_hours""HH:MM-HH:MM in local time when nothing alerts

[notifications.agent.states] turns alerts on per state: needs_input, errored and done default to true. idle and working default to false.

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

Keybindings

leader_key

The prefix key. The default is ctrl+b.

[keybindings]
leader_key = "ctrl+a"

alt+space is already the app launcher.

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

Key syntax

  • Modifiers: ctrl+, alt+, shift+, super+. On macOS, also opt+ and option+, which mean the same as alt+.
  • 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. cmd+ is not valid. On Linux, opt+ is an error.

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.

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_autodetecttrueDetect an agent CLI running in a pane and show its state
agent_detect_seconds0Seconds between checks. 0 means 2. A negative number turns detection off
agent_binaries[]Extra program names to treat as agents, added to the built-in list

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
daemontrueA bare tuios attaches to a daemon session instead of running standalone
tiledtrueStart with tiling on
layout"bsp"Tiling layout: bsp, master-stack or scrolling
open_default_windowfalseOpen one window when a session starts empty
start_in_terminal_modefalseStart in terminal mode when a window is present

--standalone and TUIOS_NO_DAEMON=1 override daemon for one run or one shell. If the daemon will not start, a bare tuios runs standalone and says so.

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.

A session keeps its own layout once it is running. Attaching never rearranges a session to match your config.

The tape table

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

KeyDefaultWhat it does
autorun"ask"off ignores project tapes. ask shows a badge and runs nothing until you choose. auto runs a trusted, unedited tape on entry
auto_reviewfalseOpen 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_eventsfalseShow 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"png"png, svg, ansi, html, txtOutput format
directory"~/Pictures/tuios"pathWhere captures are saved
copytrueboolTry to copy the capture to the clipboard
previewtrueboolOpen the preview panel after a capture
frame"window"window, plain, noneA window card, a plain card, or nothing
background"auto"auto, none, #RRGGBB, #RRGGBB..#RRGGBBBackdrop. auto derives it from the theme. Two colours make a gradient
padding480 to 128Space around the card, in pixels
radius100 to 32Card corner radius, in pixels
shadowtrueboolSoft shadow under the card
controls"auto"auto, macos, glyphs, noneWindow buttons on the card
title_format"{title}"templateTitle bar text, with {title}, {index} and {cwd}
font_family"JetBrains Mono, monospace"stringFont to use when your terminal does not report its own
font_file""pathFont file to draw with. Wins over every other font choice, and is embedded in SVG and HTML output
scale21 to 4PNG size multiplier
cursorfalseboolDraw 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
enabledfalseStart after idle_minutes with no input
idle_minutes10Minutes of quiet before it starts, 1 to 240
effect"random"The effect, or random for a different one each time
while_busyfalseStart even while a pane runs a command or an agent
[screensaver]
enabled = true
idle_minutes = 5
effect = "matrix"

There are 36 effects. tuios list-options screensaver.effect prints them. The settings page previews each effect on your current screen.

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

The spotlight table

[spotlight] lights one part of the screen and dims the rest, for screen sharing. b in window mode toggles it.

KeyDefaultValuesWhat it does
enabledfalseboolTurn the spotlight on
follow"mouse"mouse, cursorFollow the pointer, or the focused pane's cursor
radius102 to 200Half the beam's height, in rows
dim7510 to 95How much an unlit cell is dimmed, in percent
edge"hard"hard, softCut the beam off, or fade it. A fade sends about three times the bytes
shakefalseboolShake 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  = ["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, notifications, copy-help, cpu, ram, clock and session-controls. Listing one lets it draw. It still needs its own condition, so cpu still needs show_cpu.

[dock.clock] takes format, a Go time layout. A layout without seconds refreshes once a minute.

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

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.

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
addrrequiredAnything ssh accepts, including an ssh_config alias. A host with no addr is ignored and logged
connect_timeout10Seconds one dial may take before the host counts as unreachable
command""The tuios binary on the host. Empty means the link finds it on the PATH, at the usual install paths, or through a login shell
ssh_options[]Extra ssh arguments, placed before the address

You can also manage the table from the shell. These commands edit the file, and the daemon applies the change at once:

tuios hosts add build gaurav@buildbox
tuios hosts test build
tuios hosts remove build

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.

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, and ssh_options is where you name it.

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 name it in the host entry:

[hosts.laptop]
addr = "gaurav@laptop"
ssh_options = ["-o", "ProxyCommand=tailcat <token> 22"]

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
enabledtrueOffer tailnet machines. Empty on a machine that is not on a tailnet
addr"dns"Address form: dns (MagicDNS name), name (short name) or ip
user""ssh login to put before every address
usersnonePer-machine logins, keyed by short name. Wins over user
osplatforms that run TUIOSOperating systems to offer. An empty list offers every machine
offlinefalseAlso offer machines that are offline
selffalseAlso offer this machine
sharedfalseAlso offer machines shared into your tailnet
include, exclude[]Glob patterns on the short name and the MagicDNS name. exclude wins
maxbuilt inMost machines to offer
socket""Path to the tailscaled socket, if it is not in the usual place
[tailscale]
user = "ubuntu"
exclude = ["*-pad-*"]

[tailscale.users]
build = "admin"

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 bindings TUIOS cannot parse, such as "cmd+t" or "ctrl+". At startup they print to stderr:

Config error in [window_management]: cmd+t - invalid modifier: cmd

One error at startup discards the whole file

If the file has an error when TUIOS starts, TUIOS uses the built-in defaults for everything, not just the broken key. The message prints before the screen is taken over, so it is easy to miss. Run tuios keybinds list to see it: that command prints Using default keybindings... when the file was rejected.

A file saved with an error while TUIOS runs is not applied, and the error is shown on screen.

Warnings keep the file. 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 them on screen.

Troubleshooting

The file is not loading. Run tuios config path to confirm which file TUIOS reads, then tuios keybinds list to see parse errors.

A setting changed nothing.

  1. Check the table of what applies live. Keybindings need a new client.
  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. 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

On this page