# Introduction URL: https://tuios.dev/docs > TUIOS is a terminal multiplexer and window manager with workspaces, tiling, persistent sessions and a vim-style modal interface. import { Callout } from 'fumadocs-ui/components/callout'; import { Card, Cards } from 'fumadocs-ui/components/card'; import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; TUIOS (Terminal UI Operating System) runs many shells inside one terminal. Each shell gets its own window. You arrange windows with tiling or by hand, group them into nine workspaces, and detach and come back later without losing anything. tuios with four tiled panes, and the sidebar listing the session, its files and the agents in it ## Install ```bash brew install tuios ``` ```bash yay -S tuios-bin ``` ```bash curl -fsSL https://raw.githubusercontent.com/Gaurav-Gosain/tuios/main/install.sh | bash ``` ```bash go install github.com/Gaurav-Gosain/tuios/cmd/tuios@latest ``` Nix, Docker, release binaries and building from source are covered in [Getting Started](/docs/getting-started#install). ## Run it ```bash tuios ``` Press n to open a window, i to type in it, and Alt+Esc to get back to window management. Ctrl+P opens the command palette, which lists every command. [Getting Started](/docs/getting-started) walks through the rest. `tuios` starts a background daemon and attaches to a session in it. Close the terminal and the session keeps running. Run `tuios` again to get it back. ## What you get **Windows and layout** * Two modes, modelled on vim: window management mode for arranging windows, terminal mode for typing into one. * BSP tiling by default, with explicit splits, preselection, swapping and resizing. Master-stack and a niri-style scrolling layout are one palette command away. * Nine workspaces, zoom, minimize, layout templates and optional shared borders. * A session rail on the right lists your sessions, panes, files and agents. * A command palette (Ctrl+P) and an app launcher (Alt+Space). **Sessions** * A daemon owns the shells, so detaching costs nothing and several terminals can attach to one session at once. * Window layout and working directories come back after a daemon restart or a reboot. Running programs and scrollback do not. See [Sessions](/docs/sessions). * Sessions and single panes can live on other machines, reached over ssh. **Scripting** * A JSON [control protocol](/docs/control-protocol) on the daemon socket, with blocking waits and an event stream. * CLI commands for the same things: `send-text`, `capture-pane`, `wait-for`, `new-window` and more. * [Tape scripts](/docs/tape-scripting) to record and replay workflows, and [hooks](/docs/hooks) that run shell commands on window, session and agent events. **Agents** * Panes that run coding agents report whether the agent is working, idle or waiting for you. See [Agents](/docs/agents). * One [Inbox](/docs/agent-inbox) lists every approval, question and finished turn waiting for you, in every session, and answers them from where you are. * Review what an agent changed, leave notes on its lines, and send them back. Fan one prompt out to several agents in git worktrees and keep the best result. **Terminal** * Vim-style copy mode over 10,000 lines of scrollback per window, plus mouse wheel scrolling and selection. * [Multi copy mode](/docs/layout-modes#multi-copy-mode) searches every pane of the multifocus set at once and copies the selection of each pane. * [Hints](/docs/hints) put a label on each URL, path and hash in a pane. Type the label to copy the text. * Kitty graphics (including `mpv --vo=kitty` video), sixel passthrough, the kitty keyboard protocol and synchronized output. * 342 built-in themes with a live picker, and an in-app settings page. **Remote access** * `tuios ssh` serves TUIOS over SSH with public key authentication. * `tuios-web`, a separate binary, serves it to a browser. See [Web Terminal](/docs/web). ## Where to go next ## Community * Source, issues and discussions: [Gaurav-Gosain/tuios](https://github.com/Gaurav-Gosain/tuios) on GitHub. * License: MIT. * [tuios-courier](https://github.com/Gsirawan/tuios-courier) by [Gsirawan](https://github.com/Gsirawan): agent mail between machines through a relay over HTTPS, for networks that block ssh. It is a separate community project. See [Agent messaging](/docs/agent-messaging#without-ssh). # Getting Started URL: https://tuios.dev/docs/getting-started > Install TUIOS, open your first windows, and learn the keys you need on day one. import { Callout } from 'fumadocs-ui/components/callout'; import { Card, Cards } from 'fumadocs-ui/components/card'; import { Step, Steps } from 'fumadocs-ui/components/steps'; import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; This page takes you from nothing to a working session in about five minutes. [Learn tuios](/learn) runs the real app in your browser, with a pretend shell. Press the keys yourself, five minutes, nothing to install. You need a terminal with true color support. A [Nerd Font](https://www.nerdfonts.com) makes the icons render; without one, start TUIOS with `--ascii-only`. ## Install macOS and Linux: ```bash brew install tuios ``` On Linux, the `tuios-ghostty` cask installs `tuios` built with the [libghostty-vt](/docs/architecture#terminal-emulation-internalvt) emulator. The cask is available from the next release. Both casks install a `tuios` binary. Uninstall one before you install the other. There is no macOS ghostty cask. ```bash brew install gaurav-gosain/tap/tuios-ghostty ``` ```bash yay -S tuios-bin ``` Linux and macOS. Installs the `tuios` binary into `/usr/local/bin`, `~/.local/bin` or `~/bin`, whichever it can write to. ```bash curl -fsSL https://raw.githubusercontent.com/Gaurav-Gosain/tuios/main/install.sh | bash ``` ```bash nix run github:Gaurav-Gosain/tuios/v0.8.5#tuios # a release nix run github:Gaurav-Gosain/tuios#tuios # the newest commit on main ``` Put a release tag after the repo name to build that release. Without a tag, Nix builds the newest commit on `main`. The flake builds for `x86_64-linux`, `aarch64-linux` and `aarch64-darwin`. On an Intel Mac, use a release binary. Needs Go 1.26.6 or newer, the version `go.mod` names. ```bash go install github.com/Gaurav-Gosain/tuios/cmd/tuios@latest ``` ```bash docker run -it --rm ghcr.io/gaurav-gosain/tuios:latest ``` Pre-built binaries for Linux, macOS, Windows and FreeBSD are on the [releases page](https://github.com/Gaurav-Gosain/tuios/releases). These binaries use the pure Go terminal emulator. Archives named `tuios-ghostty_*` use the [libghostty-vt](/docs/architecture#terminal-emulation-internalvt) emulator backend. From v0.8.1, they are available for Linux, Windows and macOS on Apple silicon and Intel. The v0.8.0 release had no macOS ghostty archive. Check the install: ```bash tuios --version ``` The first line names the version and the terminal emulator backend the binary was built with, for example `tuios version 0.8.5 [pure-Go backend]`. If you installed with the script or a release binary, `tuios update` installs the newest release over the binary (`tuios update --check` only reports). For Homebrew, AUR, Nix and Go installs, use that tool to update. `tuios update` detects them and prints the right command instead. The browser terminal is a separate binary, `tuios-web`. See [Web Terminal](/docs/web) to install it. ### Build from source ```bash git clone https://github.com/Gaurav-Gosain/tuios.git cd tuios go build -o tuios ./cmd/tuios ``` `./scripts/install.sh` builds and installs into `~/.local/bin`. It builds the pure Go emulator by default, which needs nothing beyond Go. Run `./scripts/install.sh ghostty` for the libghostty-vt emulator backend, which needs `zig`. ## Your first session ### Start TUIOS ```bash tuios ``` The first run creates a session called `session-0` and shows a welcome screen with four keys on it: You are in **window management mode**. Keys here arrange windows. Nothing is sent to a shell yet. The dock runs along the top edge, and the session rail down the right edge lists your sessions, panes and files, and later your agents. Ctrl+B b hides the rail. ### Open a window Press n. A window opens with your shell in it. ### Type in it Press i (or Enter) to enter **terminal mode**. Your keys now go to the shell. Run a command: ```bash ls ``` ### Go back to window management Press Alt+Esc (Opt+Esc on macOS), or Ctrl+B then Esc. A bare Esc stays in terminal mode and goes to the program, so vim and other full-screen programs keep working. ### Open two more windows Press n twice. Tiling is on by default, so each new window takes half of the focused one and all three stay visible. Move between them with Tab and Shift+Tab, the number keys 1 to 9, or Alt+arrow keys to move to the neighbour in that direction. You can also click a window to focus it, and double-click it to start typing in it. ### Try another workspace Press Alt+2 (Opt+2 on macOS) to switch to workspace 2. It is empty. Open a window there, then press Alt+1 to go back. ### Detach, and come back Press Ctrl+B then d. TUIOS exits, but the session and its shells keep running in the background daemon. Run `tuios` again and everything is where you left it. Press Ctrl+P in either mode to open the command palette and search every command by name. Press Ctrl+B and wait half a second: a panel lists every key the prefix accepts next. Ctrl+B then ? opens the full help. ## The two modes | Mode | What your keys do | How to get there | | ----------------- | -------------------------------------------- | ----------------------------------------------------------------------------- | | Window management | Create, close, move, tile and switch windows | Alt+Esc, or Ctrl+B Esc | | Terminal | Go to the program in the focused window | i or Enter | Ctrl+B is the **prefix** key. It works in both modes: press it, release it, then press the next key. You can change it with `keybindings.leader_key` in the [configuration](/docs/configuration). ## Keys for day one On macOS, read Alt as Opt. If an Opt chord types a strange character instead, see the [macOS notes](/docs/keybindings#macos). **Windows** | Key | Action | | --------------------------------------------------------- | --------------------------------------------------------------- | | n | New window (window mode) | | w or x | Close the focused window (window mode) | | r | Rename the focused window (window mode) | | Tab / Shift+Tab | Next / previous window (window mode) | | Alt+N / Alt+P | Next / previous window (both modes) | | Alt+arrow | Focus the window in that direction (both modes) | | 1 to 9 | Focus window by number (window mode) | | z | Zoom the focused window over the layout, and back (window mode) | | m | Minimize to the dock (window mode). M restores all. | The prefix versions work in both modes: Ctrl+B then c (new), x (close), r (rename), n/p (next/previous), an arrow key (focus that way), 0 to 9 (focus by number), z (zoom). **Tiling** | Key | Action | | ------------------------------------------------------------- | ----------------------------------------------------------------------- | | t or Ctrl+B Space | Turn tiling on or off | | - / \| | Split the focused window: new window below / to the right (window mode) | | H J K L | Swap with the neighbour left / down / up / right (window mode) | | = | Make all splits equal (window mode) | [BSP Tiling](/docs/bsp-tiling) covers preselection, resizing and the other layouts. **Workspaces** | Key | Action | | ------------------------------------------------------------ | -------------------------------------------- | | Alt+1 to 9 | Switch to workspace 1 to 9 | | Alt+Shift+1 to 9 | Move the focused window there, and follow it | | Ctrl+B w, then a digit | Switch workspace from the prefix menu | Ctrl+B followed directly by a digit focuses a **window**, not a workspace. **Scrollback and copying** | Key | Action | | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | | Mouse wheel | Scroll the window's history. Typing, or scrolling back to the bottom, returns to live output. | | Ctrl+B \[ | Enter copy mode: move with vim keys, / to search, v to select, y to copy, Esc to leave | | Drag with the mouse | Select text. It is copied when you release. | | Ctrl+click a link | Open it. See [Links](/docs/links). | **Everything else** | Key | Action | | ---------------------------------------------------------------------------- | --------------------------------------------------------------- | | Ctrl+P | Command palette | | Alt+Space or Ctrl+B a | App launcher: start any program on your `$PATH` in a new window | | Ctrl+B S | Session switcher | | Ctrl+B , | Settings page (, in window mode) | | Ctrl+B ? | Help (? in window mode) | | Ctrl+B d | Detach, leaving the session running | | Ctrl+B q | Quit menu: detach, switch session, or kill this session | The full list is on the [Keybindings](/docs/keybindings) page, and `tuios keybinds list` prints the bindings in effect on your machine. ## Sessions `tuios` on its own attaches to the session you used last, and creates one if there is none. To manage several: ```bash tuios new work # create a session called "work" and attach to it tuios ls # list sessions tuios attach work # attach to "work" tuios kill-session work # end "work" and every window in it ``` Inside TUIOS, Ctrl+B S switches between sessions and N (window mode) creates one. If you want a session that lives and dies with this terminal and starts no daemon, run `tuios --standalone`. [Sessions](/docs/sessions) explains both kinds and exactly what survives a restart. ## Make it yours ```bash tuios config edit # open the config file in $EDITOR tuios config path # print where it is tuios --list-themes # list the built-in themes tuios --theme dracula # start with a theme ``` Most settings can also be changed live on the settings page, Ctrl+B ,. See [Configuration](/docs/configuration) for every option. ## Shell completions ```bash # Bash tuios completion bash > /etc/bash_completion.d/tuios # Zsh tuios completion zsh > "${fpath[1]}/_tuios" # Fish tuios completion fish > ~/.config/fish/completions/tuios.fish # PowerShell tuios completion powershell > tuios.ps1 ``` ## Troubleshooting ### Empty space around TUIOS TUIOS fills every cell that the terminal gives it. A blank border around TUIOS comes from the padding of the terminal window, not from TUIOS. To remove it, set the padding of your terminal to zero: | Terminal | Setting | | --------- | --------------------------------------------------------------- | | kitty | `window_padding_width 0` | | Ghostty | `window-padding-x = 0` and `window-padding-y = 0` | | WezTerm | `window_padding = { left = 0, right = 0, top = 0, bottom = 0 }` | | Alacritty | `padding = { x = 0, y = 0 }` under `[window]` | Some terminals also leave a thin gap when the window size is not a whole number of cells. Ghostty `window-padding-balance = true` spreads that gap evenly. ### The screen is dim except around the pointer The spotlight is on. Press Esc in window mode, or Ctrl+B B in any mode. You can also click the Spotlight chip in the dock. See [Spotlight](/docs/themes#spotlight). ### No sound from agent alerts Alerts are silent until you set `notifications.agent.sound = true`. With `sound_mode = "bell"`, your terminal's bell setting decides if you hear it. See [Hear an alert](/docs/agents#hear-an-alert). ## Next steps # Keybindings URL: https://tuios.dev/docs/keybindings > Every default key in TUIOS for window mode, terminal mode, prefix commands, copy mode and the mouse, grouped by the config section that owns it. import { Callout } from 'fumadocs-ui/components/callout'; import { Card, Cards } from 'fumadocs-ui/components/card'; import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; TUIOS has two modes. **Window mode** is for managing panes. **Terminal mode** sends your keys to the focused pane. A new session starts in window mode. The leader key is Ctrl+B. Press it, release it, then press a command key. The leader works in both modes. To send the leader key to the program in the pane, press it two times in terminal mode. The pane gets the leader that you configured, such as Ctrl+A. Every table on this page names the config section that holds the binding and the action name to use in it. Every action is rebindable. See [Customizing](#customizing) at the end. **Try it live:** [Learn tuios](/learn) has you press these keys in the real app, in your browser. [Free play](/learn?t=play) has a cheat sheet you can click. This page lists the defaults. `tuios keybinds list`, `tuios keybinds explain ` and the keybind manager (Ctrl+B k) show what is bound on your machine. Search every default binding, or read the tables by section below: ## Modes Section: `[keybindings.mode_control]`. These keys work in window mode. | Key | Action | Does | | ------------------------------ | --------------------- | ----------------------------------------------------- | | i, Enter | `enter_terminal_mode` | Enter terminal mode | | Esc | `enter_window_mode` | Enter window mode | | ? | `toggle_help` | Toggle the help overlay | | , | `open_settings` | Open the settings page | | q | `quit` | Quit. In a daemon session it opens the quit menu | | none | `hold_window_mode` | Window mode while the key is held. Unbound by default | To leave terminal mode, press Ctrl+B Esc, or the direct key from [Terminal mode](#terminal-mode). `hold_window_mode` needs a terminal that speaks the Kitty keyboard protocol (Ghostty, kitty, WezTerm, foot, Alacritty), because only that protocol reports key releases. Bind it to an ordinary spare key, such as an unused function key from `f1` to `f12`. Binding a modifier such as `leftalt` also works, but then every keystroke in the session arrives as an escape code. ## Window mode ### Windows Section: `[keybindings.window_management]`. | Key | Action | Does | | -------------------------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | | n | `new_window` | New window | | N | `new_session` | New session. Asks which machine when there is more than one | | w, x | `close_window` | Close the focused window | | r | `rename_window` | Rename the focused window | | m | `minimize_window` | Minimize the focused window | | M | `restore_all` | Restore all minimized windows | | z | `toggle_zoom` | Zoom the focused window, or unzoom it | | p | `toggle_pip` | Pin the focused window as the [picture-in-picture view](/docs/sessions#picture-in-picture), or unpin the pinned one | | Tab | `next_window` | Focus the next window | | Shift+Tab | `prev_window` | Focus the previous window | | ; | `last_pane` | Go back to the pane that had focus before. Press it again to return | | 1 to 9 | `select_window_1` to `select_window_9` | Focus a window by number | | c | `copy_selection` | Copy the mouse selection | | s | `focus_sidebar` | Give the keyboard to the session rail | | S | `start_screensaver` | Start the screen saver now | | Alt+Shift+N | `next_session` | Next session | | Alt+Shift+P | `prev_session` | Previous session | | none | `switch_session_1` to `switch_session_9` | Attach the session at that position in the rail. Unbound by default. A position with no session shows a message | Section `[keybindings.restore_minimized]` restores one minimized window by position: | Key | Action | | -------------------------------------------------------------------------------------------------- | ---------------------------------------------- | | Shift+1 to Shift+9 (also ! to () | `restore_minimized_1` to `restore_minimized_9` | Section `[keybindings.system]` has one binding: B is `toggle_spotlight`, which lights one part of the screen and dims the rest. See [Spotlight](#spotlight). ### Workspaces Section: `[keybindings.workspaces]`. There are nine workspaces. The modifier is Option on macOS and Alt elsewhere. | Key | Action | Does | | -------------------------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------- | | Alt+1 to Alt+9 | `switch_workspace_1` to `switch_workspace_9` | Show a workspace | | Alt+Shift+1 to Alt+Shift+9 | `move_and_follow_1` to `move_and_follow_9` | Move the focused window there and follow it | `rename_workspace` opens the rename editor on the current workspace. It has no default key. Ctrl+B w r does the same from the prefix. ### Layout and snapping Section: `[keybindings.layout]`. | Key | Action | Does | | ------------------------------------------ | --------------------------- | ------------------------------------------------------------- | | t | `toggle_tiling` | Turn tiling on or off | | h | `snap_left` | Snap to the left half. In tiling, focus left | | l | `snap_right` | Snap to the right half. In tiling, focus right | | j | `focus_down` | Focus the window below | | k | `focus_up` | Focus the window above | | f | `snap_fullscreen` | Fill the screen | | u | `unsnap` | Restore the size before the snap | | H, Ctrl+← | `swap_left` | Swap with the window to the left | | L, Ctrl+→ | `swap_right` | Swap with the window to the right | | K, Ctrl+↑ | `swap_up` | Swap with the window above | | J, Ctrl+↓ | `swap_down` | Swap with the window below | | \< | `resize_master_shrink` | Narrow the master area | | > | `resize_master_grow` | Widen the master area | | \{ | `resize_height_shrink` | Shorten the focused window from the bottom edge | | } | `resize_height_grow` | Lengthen the focused window from the bottom edge | | . | `resize_master_grow_left` | Widen the master area from the left edge | | \[ | `resize_height_shrink_top` | Shorten the focused window from the top edge | | ] | `resize_height_grow_top` | Lengthen the focused window from the top edge | | none | `resize_master_shrink_left` | Narrow the master area from the left edge. Unbound by default | `resize_master_shrink_left` has no default because , opens settings. Bind it to a free key if you want it. Corner snapping is on the [layout prefix](#layout-prefix), not on the bare digits. ### Moving focus Four keys move focus to the window in a direction. Each direction has a key in window mode, in terminal mode and after the prefix. | Direction | Window mode | Terminal mode | After Ctrl+B | | --------- | ------------ | --------------------------- | ---------------------------------- | | Left | h | Alt+← | ← | | Down | j | Alt+↓ | ↓ | | Up | k | Alt+↑ | ↑ | | Right | l | Alt+→ | → | Focus goes to the nearest window in that direction that faces the focused window. At the edge of a tiled layout, focus stays where it is. With tiling off, a window that does not face the focused window can also get focus. With tiling off, h and l snap the focused window to the left or right half of the screen. j and k always move focus. In the [scrolling layout](/docs/layout-modes#scrolling), left and right move between columns. Up and down move between the windows in one column. If your config already binds j or k in a window mode section, TUIOS keeps your binding and gives `focus_down` or `focus_up` no key. `tuios keybinds doctor` tells you when this happens. ### Neovim pane navigation The optional [tuios-nvim-navigator](https://github.com/Tim4c/tuios-nvim-navigator) plugin lets the terminal-mode focus keys move through Neovim splits first. At the edge of a split, TUIOS moves to the next pane. The feature is off by default. Turn it on: ```toml [appearance] nvim_navigation = true ``` The plugin tells TUIOS when it is active, so the same keys keep their normal TUIOS action in shells and other programs. By default, both projects use Alt+←, Alt+↓, Alt+↑ and Alt+→. If you change the keys on one side, change the other to match `terminal_focus_left`, `terminal_focus_down`, `terminal_focus_up` and `terminal_focus_right`. The plugin sends private OSC 7777 messages. TUIOS reads them and never shows them in the pane: ```text OSC 7777 ; tuios-nvim-navigator ; state ; active BEL OSC 7777 ; tuios-nvim-navigator ; state ; inactive BEL OSC 7777 ; tuios-nvim-navigator ; focus ; BEL ``` `` is `left`, `down`, `up` or `right`. Another Neovim integration can send the same messages. TUIOS accepts a focus request only from the focused pane in terminal mode, and only just after it sent the matching focus key to that pane. Window mode always keeps the focus keys for TUIOS. ### BSP splits Also in `[keybindings.layout]`. See [BSP Tiling](/docs/bsp-tiling). | Key | Action | Does | | ------------------------------------------------------------------------ | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | - | `split_horizontal` | Split the focused pane top and bottom | | \|, \\ | `split_vertical` | Split the focused pane left and right | | R | `rotate_split` | Flip the split direction | | = | `equalize_splits` | Make every split equal. In master-stack, also put the master back at `master_ratio` | | Alt+h / j / k / l | `preselect_left`, `preselect_down`, `preselect_up`, `preselect_right` | Choose where the next window opens | The preselect keys use Option on macOS. The prefix menu has its own copies of the four split actions, and rebinding one set does not change the other. ## Terminal mode Section: `[keybindings.terminal_mode]`. These keys work in terminal mode with no prefix. Everything else goes to the pane. | Key | Action | Does | | ---------------------------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | Alt+n | `terminal_next_window` | Focus the next window | | Alt+p | `terminal_prev_window` | Focus the previous window | | Alt+Esc | `terminal_exit_mode` | Go to window mode | | Alt+arrows | `terminal_focus_left`, `_right`, `_up`, `_down` | Focus the pane in that direction | | Shift+↑ | `terminal_scroll_up` | Scroll into the pane's scrollback | | Shift+↓ | `terminal_scroll_down` | Scroll back toward live output | | Ctrl+Shift+V, Super+V | `terminal_paste_host` | Paste from the host clipboard. An image is pasted as a file path. See [Paste an image](#paste-an-image) | | Key | Action | Does | | ------------------------------------------------------------------------------ | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | Option+Tab, Alt+n | `terminal_next_window` | Focus the next window | | Option+Shift+Tab, Alt+p | `terminal_prev_window` | Focus the previous window | | Option+Esc | `terminal_exit_mode` | Go to window mode | | Alt+arrows | `terminal_focus_left`, `_right`, `_up`, `_down` | Focus the pane in that direction | | Shift+↑ | `terminal_scroll_up` | Scroll into the pane's scrollback | | Shift+↓ | `terminal_scroll_down` | Scroll back toward live output | | Ctrl+Shift+V, Super+V | `terminal_paste_host` | Paste from the host clipboard. An image is pasted as a file path. See [Paste an image](#paste-an-image) | Readline, fish and zsh use Alt+← and Alt+→ to move a word at a time. To give them back, run `tuios keybinds free alt+left` and `tuios keybinds free alt+right`. Ctrl+B then an arrow still moves focus. Plain Ctrl+V is not bound. It reaches the pane, so vim's visual block mode works. Shift+↑ and Shift+↓ scroll the pane into its history only when the program is on the main screen and did not turn on a mouse mode. In other panes, TUIOS sends the keys to the program, so nvim, less and htop get them. In copy mode the keys scroll. This is the same rule as for the mouse wheel, and the same rule as in kitty. ### Paste an image Ctrl+B V pastes the image on your clipboard into the focused pane. TUIOS saves the image to a file on the machine where the pane runs. Then it pastes the path of that file into the pane. The pane can run on this machine or on a [host](/docs/remote-hosts). Claude Code, Codex and other agents read an image from a path in the prompt. The action is `paste_image`, and the command palette has it too. The paste key also pastes an image. When the clipboard holds an image and no text, the paste key pastes the image. When the clipboard holds text, the paste key pastes the text. Some terminals send an empty paste when the clipboard holds only an image. TUIOS then pastes the image. A browser can put a link or text beside a copied image. The paste key then pastes the text. Ctrl+B V always pastes the image. The paste key waits at most 0.3 seconds for the list of clipboard types. When the clipboard tool is slower, or cannot list the clipboard, the paste key pastes text. It then does not ask the tool again until TUIOS restarts. Ctrl+V alone goes to the program in the pane. Claude Code reads the clipboard on Ctrl+V itself, and that only works in a pane on this machine. For a pane on a host, use Ctrl+B V. TUIOS reads the image with the clipboard tool of your system: | System | Tool | | -------------- | ----------------------------------------------- | | Linux, Wayland | `wl-paste` from wl-clipboard | | Linux, X11 | `xclip`. `xsel` cannot read an image | | macOS | `osascript`, or `pngpaste` when it is installed | | Windows | PowerShell | The file is a PNG, JPEG, GIF, WebP, BMP or TIFF image of 8 MB or less. Only you can read it. TUIOS deletes it after one hour, or when the daemon stops. The `paste` folder next to the TUIOS socket keeps at most 50 images and 100 MB. When it is full, TUIOS deletes the oldest image. TUIOS does not read the clipboard when the client runs in a browser, when it runs on another machine through `tuios ssh`, or when the paste comes from `send-keys`. Inside ssh, TUIOS reads only an X display that `ssh -X` forwards. Any other clipboard there is the other machine's. Keys sent to the pane are encoded the way a real terminal encodes them. When the program in the pane turns on application cursor keys (DECCKM), as zsh's line editor does, the arrows, Home and End are sent in their SS3 form (`ESC O H` for Home) instead of the CSI form (`ESC [ H`), so the program's bindings for them work. `tuios send-keys` follows the same rule. `tuios send-keys` also encodes a named key or a chord in the keyboard mode the program asked for. A program that asked for the kitty keyboard protocol or modifyOtherKeys gets that encoding, the same as for a key you press. Vim then reads Ctrl+H as Ctrl+H, not as Backspace. ## Global keys Section: `[keybindings.global]`. These keys work in window mode and terminal mode. | Key | Action | Does | | ------------------------------- | ----------------- | ------------------------ | | Ctrl+P | `command_palette` | Open the command palette | | Alt+Space | `launcher` | Open the app launcher | The **command palette** searches commands, sessions and windows. Start the query with `@` to filter panes by agent state (`@n` for needs input), or with `#` to search actions to rebind. The **app launcher** lists every executable on `$PATH`, ranked by use. Enter runs the selection in a new pane with no shell in between. Tab types the command into the focused shell instead, so you can add arguments. These two keys take Ctrl+P from fish and vim and Alt+Space from readline. Set an action to `[]` to give its key back. On macOS, Option+Space types a non-breaking space, so use Ctrl+B a for the launcher. ## Prefix commands Press Ctrl+B, then the key. Section: `[keybindings.prefix_mode]`. | Key | Action | Does | | --------------------------------------------- | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------- | | c | `prefix_new_window` | New window | | x | `prefix_close_window` | Close the focused window | | r | `prefix_rename_window` | Rename the focused window | | n, Tab | `prefix_next_window` | Next window | | p, Shift+Tab | `prefix_prev_window` | Previous window | | 0 to 9 | `prefix_select_0` to `prefix_select_9` | Jump to a window | | arrows | `terminal_focus_left`, `_right`, `_up`, `_down` | Focus the pane in that direction | | ) | `next_session` | Next session | | ( | `prev_session` | Previous session | | Space | `prefix_toggle_tiling` | Turn tiling on or off | | z | `prefix_fullscreen` | Zoom the focused window | | - | `prefix_split_horizontal` | Split top and bottom | | \|, \\ | `prefix_split_vertical` | Split left and right | | R | `prefix_rotate_split` | Flip the split direction | | = | `prefix_equalize_splits` | Make every split equal. In master-stack, also put the master back at `master_ratio` | | \[ | `prefix_selection` | Enter [copy mode](#copy-mode) | | F | `hints` | Label the text on the pane to copy it. See [Hints](/docs/hints) | | none | `hints_all_panes` | Label the text on every pane of the workspace. See [All panes](/docs/hints#all-panes) | | Q | `display_panes` | Put a label on each pane and focus one by its label. See [Pane labels](/docs/find-a-pane#pane-labels) | | / | `choose_tree` | Find a pane in every session and go to it. See [The pane navigator](/docs/find-a-pane#the-pane-navigator) | | s | `prefix_scrollback` | Open the scrollback browser | | C | `prefix_screenshot` | Take a screenshot | | P | `prefix_command_palette` | Open the command palette | | a | `launcher` | Open the app launcher | | , | `prefix_settings` | Open the settings page | | k | `prefix_keybinds` | Open the keybind manager | | b | `prefix_toggle_sidebar` | Show or hide the session rail | | B | `prefix_toggle_spotlight` | Turn the [spotlight](#spotlight) on or off | | V | `paste_image` | Paste the clipboard image as a file path. See [Paste an image](#paste-an-image) | | g | `toggle_scratch` | Show or hide the [scratch terminal](/docs/sessions#scratch-terminal) | | e | `prefix_explore` | Move the keyboard to the rail, or back | | S | `prefix_session_switcher` | Open the session switcher | | W | `prefix_workspace_switcher` | Open the workspace switcher | | j | `prefix_jump_notif` | Jump to the pane of the newest message | | N | `prefix_last_message` | Show the last message in full. See [Messages](#messages) | | i | `prefix_inbox` | Open the [Inbox](/docs/agent-inbox) | | o | `prefix_next_attention` | Go to the oldest Inbox item that needs you. o again goes to the next | | O | `prefix_next_finished` | Go to the newest finished turn nobody has seen. O again goes to the next older one | | v | `prefix_review` | [Review](/docs/agent-inbox#reviewing-an-agents-changes) the focused pane's changes | | A | `prefix_agents_settings` | Open the [Agents tab](/docs/agents#the-agents-tab) of the settings page | | M | `prefix_mail` | Open the Inbox on its mail. m there opens the whole mailbox | | t | `prefix_window` | [Window prefix](#window-prefix) | | m | `prefix_minimize` | [Minimize prefix](#minimize-prefix) | | w | `prefix_workspace` | [Workspace prefix](#workspace-prefix) | | L | `prefix_layout` | [Layout prefix](#layout-prefix) | | T | `prefix_tape` | [Tape prefix](#tape-prefix) | | D | `prefix_debug` | [Debug prefix](#debug-prefix) | | d | `prefix_detach` | Detach. The session keeps running | | X | `prefix_close_session` | Close the session and its panes | | Esc | `prefix_exit_mode` | Leave terminal mode. Never detaches | | q | `prefix_quit` | Quit. In a daemon session it opens the quit menu: detach, switch session, or kill the session | | ? | `prefix_help` | Toggle help | In terminal mode, pressing the leader twice sends one literal Ctrl+B to the pane. The which-key popup that opens after the leader shows the keys of your config. A key that you rebind shows in its new place. An action that you unbind has no row. A row for a range of keys, such as `1-9`, shows only the keys that are bound. For example, if you unbind the key for workspace 5, the row shows `1-4/6-9`. Set `appearance.whichkey_enabled = false` to turn the popup off. After a command worth repeating, such as an arrow, o or O, the prefix stays armed for 500 ms. Ctrl+B then ← ← ← moves three panes. Change this with `appearance.prefix_repeat_time`. `0` turns it off. ### Scratch terminal Ctrl+B g shows the scratch terminal, a small layout of panes in a box over the current layout. Press it again, also from inside the box, to hide it. The shells keep running between shows. Inside the box the window keys act on its panes: split, move the focus, resize, swap, zoom and close. `[scratch]` sets the size of the box. See [Scratch terminal](/docs/sessions#scratch-terminal). ### Window prefix Ctrl+B t, then the key. Section: `[keybindings.window_prefix]`. | Key | Action | Does | | ------------------------------- | ---------------------- | ------------------------- | | n | `window_prefix_new` | New window | | x | `window_prefix_close` | Close the focused window | | r | `window_prefix_rename` | Rename the focused window | | Tab | `window_prefix_next` | Next window | | Shift+Tab | `window_prefix_prev` | Previous window | | t | `window_prefix_tiling` | Turn tiling on or off | | Esc | `window_prefix_cancel` | Cancel | ### Minimize prefix Ctrl+B m, then the key. Section: `[keybindings.minimize_prefix]`. | Key | Action | Does | | ---------------------------- | ---------------------------------------------------------- | -------------------------------------- | | m | `minimize_prefix_focused` | Minimize the focused window | | 1 to 9 | `minimize_prefix_restore_1` to `minimize_prefix_restore_9` | Restore a minimized window by position | | M | `minimize_prefix_restore_all` | Restore all minimized windows | | Esc | `minimize_prefix_cancel` | Cancel | ### Workspace prefix Ctrl+B w, then the key. Section: `[keybindings.workspace_prefix]`. | Key | Action | Does | | --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------- | | 1 to 9 | `workspace_prefix_switch_1` to `workspace_prefix_switch_9` | Show a workspace | | ! @ # $ % ^ & \* ( | `workspace_prefix_move_1` to `workspace_prefix_move_9` | Move the focused window there and follow it | | r | `workspace_prefix_rename` | Rename the current workspace | | Esc | `workspace_prefix_cancel` | Cancel | ### Layout prefix Ctrl+B L, then the key. Section: `[keybindings.layout_prefix]`. | Key | Action | Does | | -------------------------------------------------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------- | | l | `layout_prefix_load` | Open the layout picker | | s | `layout_prefix_save` | Save the current layout as a template | | 1 to 4 | `snap_corner_1` to `snap_corner_4` | Snap to the top-left, top-right, bottom-left or bottom-right quarter. Floating mode only | | 5 to 9 | `resize_width_50` to `resize_width_90` | Set the focused window's width to 50% to 90% | | Shift+5 to Shift+9 | `resize_height_50` to `resize_height_90` | Set its height to 50% to 90% | | Esc | `layout_prefix_cancel` | Cancel | Actions `resize_width_10` to `resize_width_40` and `resize_height_10` to `resize_height_40` also exist, with no default key. Loading a layout moves the existing windows into place. It does not close any. Extra windows are minimized. `tuios layout` manages saved templates from the shell. ### Tape prefix Ctrl+B T, then the key. Section: `[keybindings.tape_prefix]`. See [Tape Recording](/docs/tape-recording). | Key | Action | Does | | -------------- | --------------------- | ----------------------------------------------- | | m | `tape_prefix_manager` | Open the tape manager | | r | `tape_prefix_record` | Start recording | | s | `tape_prefix_stop` | Stop recording | | t | `tape_prefix_review` | Review the project tape found in this directory | | Esc | `tape_prefix_cancel` | Cancel | While a tape plays, section `[keybindings.script]` is active: Ctrl+P is `script_pause` and pauses or resumes playback. ### Debug prefix Ctrl+B D, then the key. Section: `[keybindings.debug_prefix]`. | Key | Action | Does | | -------------- | ------------------------- | --------------------------------------------- | | l | `debug_prefix_logs` | Toggle the log viewer | | c | `debug_prefix_cache` | Toggle style cache statistics | | k | `debug_prefix_showkeys` | Toggle the [showkeys](/docs/showkeys) overlay | | a | `debug_prefix_animations` | Toggle animations | | Esc | `debug_prefix_cancel` | Cancel | ## Session rail Section: `[keybindings.sidebar]`. Active while the rail has the keyboard: press s in window mode, or Ctrl+B e. | Key | Action | Does | | ----------------------------------------------- | -------------------- | ------------------------------------------------------------------ | | j, ↓ | `cursor_down` | Move down | | k, ↑ | `cursor_up` | Move up | | g, Home | `first` | First row | | G, End | `last` | Last row | | l, → | `expand` | Go to the next section | | h, ← | `collapse` | Go to the previous section | | Enter | `activate` | Open the row | | J, Shift+↓ | `reorder_down` | Move the session or machine down | | K, Shift+↑ | `reorder_up` | Move the session or machine up | | Tab, Shift+Tab | `section` | Cycle the sessions, terminals and agents sections | | 1 to 9 | `jump_1` to `jump_9` | Jump to a row by number | | n | `new_session` | New session | | t | `new_window` | New window | | r | `rename` | Rename the pane or session | | c | `accent` | Set the session's accent colour | | x | `kill` | Open the row's close menu | | m | `menu` | Open the row's menu | | i | `mail` | Open the mailbox for the pane under the cursor | | f | `agents_filter` | Agents section: every session, or only this one | | o | `agents_sort` | Agents section: sort by what needs you, by priority, or by recency | | / | `palette` | Open the command palette | | \< | `narrow` | Narrow the rail | | > | `widen` | Widen the rail | | ? | `help` | Show the rail's help | | Esc, s | `exit` | Give the keyboard back | When the cursor is on a row in the files section, section `[keybindings.sidebar_files]` answers first: | Key | Action | Does | | ---------------- | --------------------- | ------------------------------------------------------------------------------------------------- | | Enter | `file_open` | Open the folder, or copy the file's path | | a | `file_create` | New file. End the name with `/` for a folder | | r | `file_rename` | Rename | | d | `file_delete` | Delete to the trash | | D | `file_delete_forever` | Delete with no trash | | y | `file_copy` | Copy | | x | `file_cut` | Cut | | p | `file_paste` | Paste into this folder | | Y | `file_copy_path` | Copy the absolute path of the file or folder. A folder's context menu has a **Copy path** row too | See [The files section](/docs/configuration#the-files-section). When the cursor is on an agent row, section `[keybindings.sidebar_agents]` answers first. So r and x act on the agent there, and keep their rail meaning on every other row: | Key | Action | Does | | ------------ | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | r | `agent_reply` | Reply to the agent. The message is queued and typed when the agent is at rest | | v | `agent_review` | Review the pane's changes | | u | `agent_unread` | Mark the pane's finished turn unread again, for every client | | z | `agent_snooze` | Open the Inbox on the pane's item, ready to snooze it | | x | `agent_cancel_queued` | Drop the newest queued message. u within 10 seconds queues it again. With nothing queued, x opens the row's menu as usual | Enter on the `+N at rest` line shows the agent rows folded there. ## Inbox and mailbox The [Inbox](/docs/agent-inbox) lists everything waiting for you in every session. Open it with Ctrl+B i. Its keys are in `[keybindings.inbox]`: | Key | Action | Does | | --------------------------------------------------------------------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | j, ↓, Ctrl+N | `inbox_down` | Move down | | k, ↑, Ctrl+P | `inbox_up` | Move up | | PgDn, PgUp | `inbox_page_down`, `inbox_page_up` | Move a page | | g, Home / G, End | `inbox_first`, `inbox_last` | First or last item | | Enter | `inbox_go` | Go to the item's pane, or open a mail thread | | Space | `inbox_peek` | Read an approval's or a question's prompt and answer it from the Inbox | | d, Delete | `inbox_dismiss` | Dismiss the item. u undoes it for 10 seconds | | r | `inbox_reply` | Reply to mail, or to the agent of a finished or errored item | | y | `inbox_resume` | On a resume row, type the conversation's resume command in its pane | | p | `inbox_pass_on` | Pass on mail held from another machine | | f | `inbox_filter` | Show one kind, then the next, then all | | / | `inbox_select` | Narrow the list with a selector, such as `harness:codex needs:you` | | m | `inbox_mailbox` | Open the mailbox | | v | `inbox_review` | Review the changes in the item's pane | | z | `inbox_snooze` | Snooze the item: then 1 15 minutes, 2 an hour, 3 until 9:00 tomorrow, 4 until it changes. On a snoozed item, wake it | | u | `inbox_undo` | Undo the last dismiss or snooze, within 10 seconds | | S | `inbox_show_snoozed` | Show or hide the snoozed items | | n | `inbox_deny_reason` | Deny a held approval, or keep a plan planning, with a reason you type | | J, Ctrl+D / K, Ctrl+U | `inbox_detail_down`, `inbox_detail_up` | Scroll the detail under the list, such as a long plan | | Esc, q | `inbox_close` | Close | On an approval the Inbox holds for you, 1 allows it once, 2 always allows it and 3 denies it. On a question put with `ask-human`, a digit picks that answer. The digits are not bindings. The prompt that Space opens has its own section, `[keybindings.inbox_peek]`: | Key | Action | Does | | ---------------------------------------------- | --------------------- | ----------------------------------------------------- | | a | `peek_approve` | Approve | | A | `peek_approve_always` | Approve and do not ask again, when the menu offers it | | d | `peek_deny` | Deny | | Tab | `peek_type` | Type an answer | | r | `peek_read_again` | Read the prompt again | | Enter | `peek_go` | Go to the pane instead | | Esc, q, Space | `peek_back` | Back to the list | A digit chooses that numbered option of the prompt. The mailbox (m in the Inbox) uses `[keybindings.mail]`: j/k (`mail_down`, `mail_up`), PgDn/PgUp (`mail_page_down`, `mail_page_up`), Enter opens a thread (`mail_open`), r replies (`mail_reply`), o goes to the pane (`mail_focus_pane`), n writes a new message to an agent (`mail_new`) and Esc or q goes back (`mail_back`). The footers name whatever key your config binds. Until an agent has been seen, the prefix menu, the help and the palette leave out the agent keys and entries, so a person who runs no agents is not shown them. The keys work before that too, except Ctrl+B O, which types O into the pane until then. ## The review overlay Ctrl+B v, or v in the Inbox or on a rail agent row, opens the diff of a pane's changes over the whole screen. See [Reviewing an agent's changes](/docs/agent-inbox#reviewing-an-agents-changes). These keys are fixed and are not in the config. | Keys | Does | | --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | | j k, arrows | Move by line. With the file list focused, move through the files | | Space, PgDn, Ctrl+D / PgUp, Ctrl+U | Move a page | | g G | First or last line | | ] \[ | Next or previous hunk, going on into the next or previous file | | } \{ | Next or previous file | | s | One column, or old and new side by side when the screen is wide enough | | h l, ← → | Scroll the code sideways | | Tab | Focus the file list or the diff | | Enter | In the file list, open that file | | c / C | A note on the line under the cursor, or on the whole hunk | | e | Edit the note under the cursor | | x | Resolve (remove) the note under the cursor | | S | Send every unsent note to the agent as one message, typed when it is at rest | | u | Switch between the changes since the base and only the uncommitted ones | | b | Diff from another base | | w | The compare view, for a pane in a fan | | r | Read the diff again | | Esc, q | Close | The compare view lists the attempts of a fan. j/k move, Enter reviews an attempt, m marks one and d diffs the two marked attempts, V runs a command in every attempt, K keeps the attempt under the cursor and removes the others after a y, and Esc, q or w go back to the review. ## Lists and the settings page Every list moves the same way: the palette, the launcher, the settings page, the Inbox, the mailbox, the keybind manager, the pickers, the switchers, the menus and the tape manager. | Keys | Does | | ------------------------------------------------------------------------------------ | --------------------------------------------------------------- | | ↑ ↓, Ctrl+P Ctrl+N | Move one row. Past either end, wrap round | | Home End | First or last row | | PgUp PgDn | A page, stopping at the ends | | k j g G | Up, down, first and last, in a list with no filter to type into | | Ctrl+U | Clear a typed filter | Set `appearance.wrap_lists = false` to stop at the ends instead of wrapping. The settings page (, in window mode, or Ctrl+B ,) reopens on the tab, row and search you left it on: | Keys | Does | | -------------------------------------------------------------------------- | ------------------------------------------------- | | ← →, h l | Change the row's value | | Enter, Space | Toggle, cycle, or open the row's picker or editor | | Tab Shift+Tab, ] \[ | Next or previous tab | | 1 to 9 | Go to that tab | | /, or a letter the page does not use | Search every tab | | Backspace, Delete | Reset the row to its default | | Ctrl+Z | Undo the last change made on the page | | Esc, q | Close | A row whose value differs from its default has a dot after its name, including the hand-drawn rows such as the theme and shared borders, which have no reset. See [The in-app settings page](/docs/configuration#the-in-app-settings-page) for the search. The key hints at the foot of a panel stay on one row. When they do not fit, they get shorter in steps. First, `ctrl+` becomes `^`, `alt+` becomes `M-` and `shift+` becomes `S-`. Then the least important hints go, one whole hint at a time. The way out, such as `esc`, stays. Only when the most important hints still do not fit do their labels go, from the last hint back. A hint is never cut in the middle of a word. ### Mode keys in the dock Copy mode, multi copy mode and hints mode show their keys at the right end of the dock while the mode is open. The keys follow the rule for panel footers, so a narrow screen shows fewer keys and never part of one. The main action and the way out always show. In hints mode, ? shows all of its keys in the help. A message that arrives while a mode is open shows in place of the keys until the message goes. The dock component is `copy-help`. ## Messages A message shows at the right end of the dock. A message that is too long for the dock stops after its last whole word, with `…` and `more`. A word with no space in it, such as a long path, is cut where the room ends. * Put the pointer on a message to hold it. A key press, the terminal losing focus, or 60 seconds ends the hold. * Put the pointer on a long message to see its first lines above the dock. * Click a long message to read all of it in the message view. A click on a pane's request to set the clipboard allows it. * Click a short message from a pane to go to that pane. | Keys | Where | Does | | ------------------------------------------------------------------------------------------------------------ | ------------ | --------------------------------------------------------------------------------------------------------- | | Ctrl+B N | Anywhere | Show the last message from a pane, or the last message too long for the dock, also after it left the dock | | Ctrl+B j | Anywhere | Go to the pane of the newest message | | j k, ↑ ↓, wheel | Message view | Scroll one line | | Ctrl+D Ctrl+U, Space, PgDn PgUp | Message view | Scroll half a page | | g G, Home End | Message view | Go to the first or last line | | y | Message view | Copy the message | | Enter | Message view | Go to the pane the message came from | | Esc, q | Message view | Close the message view | The log viewer (Ctrl+B D l) lists every message the dock showed, and every warning and error. The entry under the cursor shows wrapped under the list. Press Enter, or click an entry, to read it in the message view. E copies the errors and A copies the whole log. ## Spotlight B in window mode turns the spotlight on and off. The spotlight dims the screen outside a circle around the pointer. Ctrl+B B turns it on and off in any mode. The key was b in earlier versions. While the spotlight is on, the dock shows a Spotlight chip with the key that turns it off: | Where | Turn off the spotlight | | ------------- | ----------------------------------------- | | Window mode | Esc | | Terminal mode | Ctrl+B B | | Any mode | Click the Spotlight chip in the dock | In terminal mode Esc goes to the program in the pane, so vim and other programs get it. An open dialog, menu or popup closes before Esc turns the spotlight off. The actions are `toggle_spotlight` and `prefix_toggle_spotlight`. `[spotlight]` sets the size and the dimming. See [Themes](/docs/themes#spotlight). ## Close every pane on a workspace The `close_workspace` action closes every pane on the current workspace, like tmux `kill-window`. Use it to close the panes that `tuios xpanes` opened. It has no default key. The command palette has the entry `Close workspace`. This example binds it to Ctrl+B Alt+X, because Ctrl+B X closes the session: ```toml [keybindings.prefix_mode] close_workspace = ["alt+x"] ``` The action asks first. The dialog shows how many panes close and how many agents are in them. `Cancel` is the default row. Scratch panes stay open. `tuios close-workspace` does the same from a shell. ## Split into the same ssh These actions open a pane on the machine that the focused pane is connected to with ssh: | Action | What it does | | ---------------------- | ------------------------------- | | `split_ssh_horizontal` | Splits the pane top and bottom. | | `split_ssh_vertical` | Splits the pane left and right. | | `new_window_ssh` | Opens a new window. | The new pane runs ssh again, to the same host as the same user. It keeps the connection options, such as the port, the identity file and the jump host. tuios does not run the remote command again. It also removes `-N`, `-f`, `-T`, port forwards and the other options that stop a shell. `mosh` works the same way. Eternal Terminal (`et`) is not followed. When the focused pane does not run ssh, the action opens an ordinary pane. You can use the keys in every pane. Some ssh lines also get an ordinary pane. See [Configuration](/docs/configuration#lines-that-are-not-followed). The actions have no default key. The command palette has an entry for each one. To bind them, add them to a section. This example uses keys for window-management mode: ```toml [keybindings.layout] split_ssh_vertical = ["alt+v"] split_ssh_horizontal = ["alt+s"] new_window_ssh = ["alt+w"] ``` To make the ordinary split and new-window keys do the same, set `appearance.new_window_follow_ssh`. To start the new pane in the remote folder, make the remote shell report its folder. See [Configuration](/docs/configuration#splits-that-follow-ssh). ## Copy mode Press Ctrl+B \[ to enter copy mode. It moves a cursor through the scrollback with vim keys. These keys are fixed and are not in the config, except Home and End. You can add keys that pipe a yank through a command. See [Pipe a yank through a command](#pipe-a-yank-through-a-command). Put a count before a motion to repeat it: `10j`, `5w`. The copy cursor starts on the terminal cursor, which is usually the prompt line. tmux does the same. To start on the middle row, set `appearance.selection.copy_entry` to `center`. When the focused pane is in the [multifocus](/docs/layout-modes#typing-into-several-panes) set, Ctrl+B \[ starts [multi copy mode](/docs/layout-modes#multi-copy-mode) on every pane of the set. | Keys | Does | | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | h j k l, arrows | Move the cursor | | w b e | Next word, previous word, end of word | | W B E | The same, for space-separated words | | 0 ^ $ | Line start, first non-blank, line end | | Home End | Line start, line end. You can rebind these two. See [Line start and line end](#line-start-and-line-end) | | H M L | Top, middle and bottom of the screen | | gg, G | Top of the scrollback, bottom (live output) | | `{n}G` | Go to line `n` | | \{ } | Previous and next paragraph | | % | Matching bracket | | Ctrl+U / Ctrl+D | Half page up, down | | Ctrl+B / Ctrl+F, PgUp / PgDn | Full page up, down | | f F t T then a character | Find a character on the line | | ; , | Repeat the character search, forward or back | | / | Search forward, down to the newest output | | ? | Search backward, up into the scrollback | | n | Next match in the direction of the last search | | N | Next match in the opposite direction | | Esc in the search prompt | Cancel the search and move the cursor back | | Ctrl+L | Clear search highlights | | v V | Start a character or line selection | | y, c | Copy the selection to the clipboard, through `copy_command` when it is set | | i | Leave copy mode and go to terminal mode | | q, Esc | Leave copy mode | The scrollback browser (Ctrl+B s) is a different tool. It lists past commands by their prompt marks, for shells that emit OSC 133. ### Line start and line end Home moves the copy cursor to the start of the line, as 0 does. End moves it to the end of the line, as $ does. Both keys also move the end of a v or V selection. The help lists them next to 0 ^ $. | Action | Default key | | ---------------------- | --------------- | | `copy_mode_line_start` | Home | | `copy_mode_line_end` | End | The keys are in the `copy_mode` section. They work only in copy mode. In the search prompt, they do not move the cursor. 0 and $ are fixed keys, and you cannot rebind them. Do not bind a key here that copy mode already uses, such as y or v, or a key of a `[[keybindings.copy_pipe]]` entry. `tuios keybinds doctor` and the config check warn about such a key. A copy pipe key runs the pipe, and the binding does not run. A key that copy mode uses loses its copy mode action. Ctrl+A and Ctrl+E are not defaults. Ctrl+A is a common leader key, and Ctrl+E scrolls one line in tmux copy mode. To use them, add them to the section: ```toml [keybindings.copy_mode] copy_mode_line_start = ["home", "ctrl+a"] copy_mode_line_end = ["end", "ctrl+e"] ``` ### Search The prompt shows `/` or `?` for the direction. The search starts at the copy cursor. When no match is found in that direction, the search continues from the other end of the buffer. n and N also start at the copy cursor. After you move the cursor, they find the nearest match. When a search finds more than 1000 matches, the prompt shows `1000+`. Two actions start copy mode and open the search prompt with one key. They have no default key. | Action | Palette entry | Does | | --------------------------- | ---------------------------- | --------------------------------------- | | `copy_mode_search_forward` | `Copy mode: search forward` | Start copy mode and open the `/` prompt | | `copy_mode_search_backward` | `Copy mode: search backward` | Start copy mode and open the `?` prompt | They do the same as tmux `bind-key b copy-mode \; send-keys ?`. Bind one in any section. This example puts the backward search on Ctrl+B /: ```toml [keybindings.prefix_mode] copy_mode_search_backward = ["/"] ``` If the pane is already in copy mode, the action opens the prompt and does not move the cursor. In multi copy mode, the prompt opens in each pane of the mode. In copy mode, only a key in the `global`, `terminal_mode` or `window_management` section starts the action. A key after the leader does not, because copy mode uses Ctrl+B for page up. In copy mode, / and ? open the same prompts. TUIOS cannot put two actions on one key. `tuios send-keys` cannot do it either, because a key from `send-keys` does not go to copy mode. ### Pipe a yank through a command A yank can go through a command before it goes to the clipboard. tmux calls this `copy-command` and `copy-pipe`. TUIOS has two parts: * `appearance.selection.copy_command` runs one command on every yank in copy mode. * A `[[keybindings.copy_pipe]]` entry binds a key in copy mode to its own command. ```toml [appearance.selection] copy_command = "tmux-copy-it" [[keybindings.copy_pipe]] key = "p" command = "tr '\n' ' '" cancel = true description = "flatten" ``` The command runs with `sh -c`. It gets the selection on stdin. The clipboard gets what the command writes to stdout. When the selection does not end with a new line, TUIOS removes one new line from the end of the output. The clipboard gets the selection itself in these cases: * The command writes nothing. Use this for a command that sends the text to a socket or a file. * The command stops with an exit code that is not 0. The dock shows the exit code and the first line of stderr. * The command runs for more than 10 seconds. TUIOS stops the command and the processes that it started. TUIOS waits until the command closes its stdout. `xclip` starts a process that stays open after `xclip` stops, and keeps stdout open. TUIOS then waits 1 second more. To prevent the wait, send the stdout of `xclip` to `/dev/null`. This example also puts each yank in the X primary selection: ```toml [appearance.selection] copy_command = "xclip -selection primary >/dev/null" ``` | Field | Does | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `key` | The key in copy mode. Write it as in the other sections | | `command` | The command that gets the selection on stdin | | `cancel` | `false`, the default, keeps copy mode on and the cursor where it is. TUIOS clears the selection. This is tmux `copy-pipe`. `true` leaves copy mode, as q does. This is tmux `copy-pipe-and-cancel` | | `description` | The name of the entry in the dock messages. Optional | The entry takes its key from copy mode. An entry on y replaces the plain yank. Copy mode does not use p or P. When an entry takes q, Esc, v, V, /, ? or a digit, TUIOS shows a warning when it loads the config. When no text is selected, the key shows a message and runs nothing. While you type a search, the key goes into the search. Without `copy_command`, y copies the selection as it is. With `copy_command`, y and c pipe the selection through that command. A `[[keybindings.copy_pipe]]` key always uses its own command. A mouse selection that copies on release does not use `copy_command`. In [multi copy mode](/docs/layout-modes#multi-copy-mode), the command gets the text that y copies: the selection of each pane, joined in the current format. It runs one time for all panes. Use the `json` format to process each pane separately, for example with `jq`. With `cancel = true`, multi copy mode stops in every pane. The command runs on the machine that runs the TUIOS client, the same machine as for a `shell` [command key](#command-keys). For the SSH and web clients, it is the server. The command starts in the folder of the focused pane and gets the variables of a command key: `TUIOS_SESSION`, `TUIOS_SOCKET`, `TUIOS_ACTIVE_PANE_ID` and `TUIOS_ACTIVE_PANE_CWD`. You can use TUIOS while the command runs. ## Mouse | Gesture | Does | | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | Click a pane | Focus it | | Double-click a pane | In window mode, also start typing in it. `appearance.click_to_type` makes it a single click, or turns it off | | Drag a title bar | Move the window. In tiling, swap it with the pane you drop it on | | Alt + drag | Move the window from anywhere inside it (`appearance.alt_drag`) | | Right-drag | Resize the window | | Right-click | In window mode, open the menu for the pane, the dock or the desktop | | Shift or Ctrl + right-click | Open the pane menu even when the program in the pane uses the mouse | | Title bar buttons | Minimize, zoom or close | | Click a dock item | Restore a minimized window | | Wheel up | Enter copy mode and scroll, when the program is not using the mouse | | Drag the right border | Scroll with the scrollbar | | Hover a link | Show where it goes. The label shows the real target, also when the text on the screen is different | | Ctrl + click a link | Open it. Shift+click also opens it, but most terminals keep it for their own selection. See [Links](/docs/links) | In floating mode, drop a window on a screen edge to snap it: the left or right edge for a half, a corner for a quarter, the top center for the full screen. In copy mode, a click moves the cursor and a drag selects. Dragging past the top or bottom of the pane scrolls. Ctrl+drag still moves the pane. A link opens only when the pointer does not move. `appearance.link_click` sets which click opens a link. ## Command keys A `[[keybindings.command]]` entry binds a key to a command that you write. ```toml [[keybindings.command]] key = "prefix+alt+g" type = "scratch" command = "lazygit" description = "Lazygit" width = "80%" height = "80%" ``` * `key` is the key that runs the command. A key that starts with `prefix+` works after the leader (Ctrl+B). Any other key works in window mode and in terminal mode. * `type` is one of these: * `scratch`: a scratch terminal of its own. The key shows it and hides it, as Ctrl+B g does for the built-in one. Each entry has its own layout: you can split it. One scratch terminal is on the screen at a time. When the command exits, its pane closes. When no pane is left, the next press starts the command again. With no `command`, the first pane runs your shell. * `popup`: a popup that closes when the command exits. This is the default. * `pane`: a new pane in the layout, next to the focused pane. It closes when the command exits. * `shell`: the command runs with no window. You do not see its output. When the command fails, the dock shows a message. * `command` runs with `sh -c`, so you can write pipes and quotes. It starts in the folder of the focused pane. * `description` is the name in the command palette and in `tuios keybinds list`. It is optional. * `name` keeps a scratch terminal under a fixed name. It is optional. Without it, tuios makes the name from the description or the command. * `width` and `height` set the size of a scratch or popup entry, in cells (`100`) or percent (`80%`). The default is `80%`. The command gets these variables: | Variable | Value | | ----------------------- | ---------------------------------------- | | `TUIOS_SESSION` | The session in which you pressed the key | | `TUIOS_SOCKET` | The socket of the daemon | | `TUIOS_ACTIVE_PANE_ID` | The pane that had the focus | | `TUIOS_ACTIVE_PANE_CWD` | The folder in which the command starts | A command key never takes a key from a tuios action or from the leader key. When the key is taken, `tuios keybinds doctor` shows the entry as dead. When two entries have the same key, the first entry in `config.toml` runs, and the doctor shows the other as dead. To move an entry to a different key, edit its `key` in `config.toml`. The command palette lists each entry, and `tuios keybinds list` shows them under Commands. A key with no modifier and no `prefix+`, for example `g`, works in terminal mode too. It takes that letter from every pane, so tuios shows a warning. Use `prefix+g` or a key with a modifier, for example `alt+g`. When a scratch command stops in its first moments, for example because the program is not installed, the dock shows its exit code. The next press starts it again. tuios does not start it again by itself. The command and the palette row of an entry change when you save `config.toml`. A changed key works at the next key press, as for the other keybindings. When you remove or rename an entry, or change the description of an entry that has no `name`, the panes of its scratch terminal close at the next reload. The dock shows a message. In a session on a different machine, a `scratch` or `popup` entry does not run. The dock shows a message. A `pane` entry runs on the machine of the session. A `shell` entry runs on the machine that runs the tuios client. Only a key press or the command palette runs a command key. A change to `config.toml` does not run a command. A pane that has the respond grant can press keys as you through `run-command`. It can press a command key, also from a session on a different machine, and a `shell` entry then runs on this machine. Give the respond grant only to a pane that you trust. ### Recipes These are command keys for common tasks. Copy an entry into `config.toml` and change the key to taste. Run `tuios keybinds doctor` after you add one, to check the key is free. **Per-session notes.** This scratch entry opens `$EDITOR` on a notes file named after the session, and makes the notes folder first. It falls back to `vi` when `$EDITOR` is not set. ```toml [[keybindings.command]] key = "prefix+alt+n" type = "scratch" command = "mkdir -p ~/notes && ${EDITOR:-vi} ~/notes/$TUIOS_SESSION.md" description = "Session notes" ``` **Browser pane.** This scratch entry opens a page in terminal-browser. It needs terminal-browser installed, and a terminal with kitty graphics. ```toml [[keybindings.command]] key = "prefix+alt+b" type = "scratch" command = "terminal-browser open https://example.com" description = "Browser" ``` **lazygit.** This is the example at the top of this section. It opens lazygit in the focused pane's folder. ```toml [[keybindings.command]] key = "prefix+alt+g" type = "scratch" command = "lazygit" description = "Lazygit" width = "80%" height = "80%" ``` **Pick a file into the focused pane.** This popup runs `fzf` and types the pick into the pane you came from, with [`tuios send-text`](/docs/cli-reference#send-text) and the pane id from `TUIOS_ACTIVE_PANE_ID`. ```toml [[keybindings.command]] key = "prefix+alt+p" type = "popup" command = "tuios send-text -w \"$TUIOS_ACTIVE_PANE_ID\" \"$(fzf)\"" description = "Pick a file" ``` **Open a project.** This popup runs `fzf` over the folders in `~/dev` and switches to the session of the folder you pick. When the session does not exist, [`tuios switch-session`](/docs/cli-reference#switch-session) makes it in that folder. See [Sessionizer](/docs/sessions#sessionizer). ```toml [[keybindings.command]] key = "prefix+alt+s" type = "popup" command = 'dir=$(find ~/dev -mindepth 1 -maxdepth 1 -type d | fzf) && tuios switch-session --create --cwd "$dir" "$(basename "$dir")"' description = "Open a project" ``` **Copy the pane's folder.** This shell entry copies the focused pane's folder to the clipboard. It tries `pbcopy`, then `wl-copy`, then `xclip`, and uses the first one it finds. ```toml [[keybindings.command]] key = "prefix+alt+y" type = "shell" command = "printf %s \"$TUIOS_ACTIVE_PANE_CWD\" | (pbcopy || wl-copy || xclip -selection clipboard)" description = "Copy pane folder" ``` ## Customizing Bindings live in 23 sections under `[keybindings]` in `config.toml`. Each action takes a list of keys. ```toml [keybindings] leader_key = "ctrl+a" [keybindings.window_management] new_window = ["n", "ctrl+t"] close_window = [] # unbind: the key goes back to the pane ``` An empty list and a missing line are different. A missing action gets its default back at the next load. An empty list stays empty. Key syntax and every section are on the [Configuration](/docs/configuration#keybindings) page. The `[keybindings]` sections can also be in a file that `config.toml` includes, or in a file in `config.d`. The tables merge key by key. A `[[keybindings.command]]` entry in a later file changes the earlier entry with the same `name`, or with the same `key` when one of them has no name. Add `disabled = true` to an entry to remove the earlier entry that it matches. The keybind manager and `tuios keybinds unbind` write a change to the file that sets the action. See [Split the Config](/docs/config-files). ### Modifier spellings A key in `config.toml` can spell a modifier in more than one way. TUIOS reads each spelling as the same key. | Write | TUIOS reads it as | Where | | ------------------ | ----------------- | ------------- | | `opt+`, `option+` | `alt+` | macOS only | | `cmd+`, `command+` | `super+` | All platforms | | `control+` | `ctrl+` | All platforms | The order of the modifiers does not matter, so `shift+ctrl+x` is the same key as `ctrl+shift+x`. This applies to `leader_key`, to every binding table, and to `tuios keybinds explain`, `free` and `unbind`. `tuios keybinds explain opt+f12` prints `opt+f12 (tuios reads it as alt+f12)`. `tuios keybinds doctor` lists each key that TUIOS cannot read. A `super+` chord needs a terminal that sends the Super key. Most macOS terminals keep Command chords for their own menus. Ghostty and kitty send an unbound Command chord through the Kitty keyboard protocol. A `super+` chord on F1 to F12, such as `super+f12`, works the same way. On a Mac keyboard, F12 can also be a media key, and then TUIOS does not get it. Inspect and change bindings from the shell: ```bash tuios keybinds list # the main tables tuios keybinds browse # search every binding in an explorer tuios keybinds doctor # conflicts, and keys taken from the pane tuios keybinds explain alt+left # everything one key does tuios keybinds unbind close_window w # take one key off one action tuios keybinds free alt+left # take a key off every action ``` `doctor` tags each finding with its evidence: `certain` comes from TUIOS routing, `observed` from the live pane, `reference` from a list of common program defaults. `doctor` and `explain` take `--json` and `--guest `. `tuios keybinds browse` opens the rows of `tuios keybinds list --json` in an explorer. Press / to search, and Tab or a click on a tab to show one scope. The detail pane shows the keys, the action, the scope and the description. q or Esc leaves it. Add a search to start with it: `tuios keybinds browse spotlight`. `tuios keybinds list` prints plain text, on a terminal too. The **keybind manager** (Ctrl+B k) shows the same analysis in four tabs: Bindings, Conflicts, Guests and Record. Record captures the next key you press. On a binding, Ctrl+D removes it and Ctrl+X takes its key off every action. A keybinding saved in `config.toml` works at the next key press in the running client. You do not need to restart TUIOS or attach again. Changes made with the keybind manager also apply at once. `tuios keybinds list-custom` compares every keybinding section. It does not report `leader_key`. Use `tuios keybinds doctor` or read the file for that. ## Other keyboard layouts Bindings work with layouts for non-Latin scripts, such as Cyrillic, Greek, Hebrew or Arabic. When no binding matches the character that a key types, TUIOS uses the key at the same position on a US layout. On a Ukrainian layout, the key that types `ш` is the US i key. So Ctrl+B then that key opens the Inbox. * A binding on the typed character wins. You can still bind `ш` yourself. * Text that you type into a pane, a rename or a search keeps the character that you typed. * Ctrl on a non-Latin key sends the control code of the US key to the pane. Ctrl+с is Ctrl+C in the shell. Latin layouts, such as AZERTY, QWERTZ or Dvorak, use the letters on their keys. A letter or a Ctrl chord means the key that it types, not the US key at its position. On Dvorak, Ctrl with the key that types `b` is Ctrl+B, the leader. The showkeys strip, the binding recorder and a tape show the chord the same way. `tuios keybinds explain` checks the key as you write it, so give it the Latin key. This needs a terminal that sends the US-layout key through the Kitty keyboard protocol: Ghostty, kitty, WezTerm or foot. Other terminals send only the typed character. With those terminals, use a Latin layout for TUIOS commands. In window mode, TUIOS tells the terminal to send every key as a code. TUIOS resets this when it stops. After `kill -9` or a dropped ssh connection, the terminal can stay in this mode. The shell then shows codes such as `[97u` when you type. To reset the terminal, run `printf '\033[=0;1u'`, or close the tab. ## Keys sent to a pane In terminal mode, TUIOS sends these keys to the program in the pane: * The keypad keys, with Num Lock on or off. Keypad Enter sends Enter. * Begin, the center key of the keypad with Num Lock off. * F13 and higher. A program that uses the legacy encoding gets them in the form that xterm sends. * Insert, Delete, Page Up and Page Down with their modifiers. Ctrl+Delete stays Ctrl+Delete. A program that uses the Kitty keyboard protocol gets each key with the number that the protocol gives it. Caps Lock, Num Lock and a modifier key pressed alone reach that program only when it asks for every key. ## macOS macOS treats Option as a compose key unless the terminal says otherwise. An Option chord then arrives as a character. TUIOS maps most of those characters back to the chord, which is why Option+1 to Option+9 work with no setup. Two kinds cannot be recovered: * **Dead keys.** Option+e, i, n, u and \` send nothing until a second key. Alt+n (next window in terminal mode) takes two presses. * **Rewritten chords.** Many terminals send Option+← and Option+→ as readline word motions, with no sign that an arrow was pressed. To fix both, turn on your terminal's Option-as-Alt setting: | Terminal | Setting | | ------------ | ------------------------------------------------------- | | Ghostty | `macos-option-as-alt = true` | | Terminal.app | Settings > Profiles > Keyboard > Use Option as Meta Key | | iTerm2 | Settings > Profiles > Keys > Left Option key: Esc+ | | kitty | `macos_option_as_alt yes` | | WezTerm | `send_composed_key_when_left_alt_is_pressed = false` | | Alacritty | `option_as_alt = "Both"` under `[window]` | | VS Code | `terminal.integrated.macOptionIsMeta` | Ghostty also needs `keybind = alt+left=unbind` and `keybind = alt+right=unbind`, because its own keybinds rewrite those arrows first. The prefix works on every terminal with no setup. Ctrl+B then an arrow, n, p, (, ), a or a digit covers every navigation command. Command chords never reach a terminal program. macOS sends them to the menu bar. ## Related # Hints URL: https://tuios.dev/docs/hints > Put a short label on each URL, path, hash, address and number in a pane, then type the label to copy, paste or open the text. import { Callout } from 'fumadocs-ui/components/callout'; import { Card, Cards } from 'fumadocs-ui/components/card'; Hints mode puts a short label on the useful text in the focused pane. Type a label to copy the text. It works like tmux-fingers and the kitty hints kitten. Hints mode can also put labels on all panes at once. See [All panes](#all-panes). ## Use it 1. Press Ctrl+B F. The labels show and the rest of the pane dims. The mode pill in the dock shows `HINTS`, and the other end of the dock shows the keys. 2. Type a label. TUIOS copies the text and closes hints mode. | Keys | Does | | -------------------------------------------------------------------- | ------------------------------------------------------------------------- | | A label, such as a or ls | Copy the text | | The label with Shift, such as A | Copy the text and type it into the pane | | The label with Ctrl, such as Ctrl+A | [Open](#open) a URL or a path | | Backspace | Remove the last letter you typed | | ? | Close hints mode and show all of its keys in the help | | Esc, or the leader key | Close hints mode | | q | Close hints mode, when q is not a label letter | | Ctrl+C, Ctrl+G | Close hints mode, when c or g is not a label letter | The text nearest to the cursor gets the shortest label. The same text gets the same label each time it shows. A key that starts no label does nothing. When c or g is a label letter, Ctrl+C and Ctrl+G open that label. g is in the default letters, so Ctrl+G opens the label `g`. Use Esc to close. The leader key always closes hints mode. You can also open hints mode from the command palette. Search for `hints`. A terminal that reports Caps Lock (the Kitty keyboard protocol) lets TUIOS read an upper case letter from Caps Lock as a plain label letter. In other terminals, turn Caps Lock off before you type a label. Hints mode keeps all input from the pane while the labels show. TUIOS drops a paste, a key release and a mouse move over the pane. A click or the mouse wheel closes hints mode first and then works as usual. Hints mode also closes when its pane closes, when focus moves to a different pane, and when you change the workspace. The copy uses the same path as a mouse copy. TUIOS writes the clipboard with OSC 52. On a local client, it also uses the system clipboard tool. ## All panes The `hints_all_panes` action puts labels on all panes that the workspace shows. This includes tiled panes, floating panes and the focused pane. * TUIOS gives no label to text that you cannot see. This includes a minimized pane, a pane behind a zoomed pane, a pane off the screen, and text under a different pane. * Each label is different on all panes. * The focused pane gets the shortest labels. The panes nearest to it get the next shortest labels. * The same text gets the same label on all panes. A path or a `file://` URL gets one label for each pane, because each pane has its own folder. * A label copies the text from any pane. * Shift and a label types the text into the focused pane. The text can come from a different pane. * Ctrl and a label [opens](#open) the text from the pane that shows it. TUIOS uses that pane's folder and machine. * Hints mode closes when one of these panes closes, moves or changes size. The action has no default key. Run it from the command palette, or bind it: ```toml [keybindings.prefix_mode] hints_all_panes = ["A"] ``` To make Ctrl+B F put labels on all panes, set `hints.all_panes`: ```toml [hints] all_panes = true ``` ## What hints mode finds | Name | Examples | | -------- | ---------------------------------------------------------------- | | `url` | `https://example.com/a`, `git@github.com:user/repo.git` | | `path` | `/etc/hosts`, `./run.sh`, `~/notes.md`, `main.go:12:5` | | `diff` | The file in `diff --git a/x b/x`, `--- a/x` and `modified: x` | | `sha` | `149c8a8f`, a full 40-character hash | | `ip` | `10.0.0.1`, `10.0.0.0/8`, `192.168.1.2:8080`, `fe80::1` | | `uuid` | `550e8400-e29b-41d4-a716-446655440000` | | `color` | `#1e1e2e`, `#fa0` | | `hex` | `0xdeadbeef` | | `number` | Numbers of 4 digits or more | | `email` | `ops@example.com` | | `id` | `pod/web-1`, `deployment.apps/web`, pod names, `sha256:` digests | `path` and `email` accept letters in all scripts, with accents and combining marks. The other built-in patterns use only ASCII. A URL ends at a space, a quote, a backtick or an angle bracket. A `)` or a `]` ends the URL when the URL did not open it. A pair that the URL opens stays in it, as in `https://en.wikipedia.org/wiki/Go_(language)`. TUIOS removes `.,;:!?` from the end. So the markdown badge `[![x](https://a.example/b.svg)](https://a.example/c)` gives two URLs. The pointer uses the same rules to find a link. See [Links](/docs/links). A path that starts with `/` must not come directly after `<`, a letter, a digit or `_`. So `

` in HTML is not a path. Hints mode reads only the text on the screen. If you scroll the pane back, it reads the lines you scrolled to. A URL that wraps onto the next row is one match. TUIOS joins two rows only when the terminal wrapped the text. A line that fills the row and then ends is not joined to the next line. ## Open Ctrl and a label opens the text: * A URL opens in your browser. A remote client (`tuios ssh`, the web client) copies the URL, because it cannot open a browser on your machine. * A path or a `file://` URL opens in a new pane with `$EDITOR`. TUIOS removes a `:line:col` at the end. A relative path starts in the directory of the pane. * Other text is copied. TUIOS opens a file only when the file is on this machine. In these cases, it copies the path and tells you why: * The pane runs on another machine. * The session runs on another machine (`tuios attach` to a host). * The client is remote (`tuios ssh`, the web client). * The pane runs `ssh`, `autossh`, `mosh`, `et`, `telnet`, `tsh`, `kitten` (for example `kitten ssh`), `docker`, `kubectl` or `podman`. * The shell reported a folder on another machine. * The path is relative and a program runs in front of the shell. TUIOS knows only the folder that the shell reported. * The path is relative and TUIOS does not know the folder of the pane. TUIOS never gives the text to a shell. It gives the text to the opener as one argument. Only `http`, `https`, `mailto`, `ftp` and `ftps` URLs open. The opener is the same as for Ctrl+click on a link. See [Links](/docs/links). ## Settings Put the settings in the `[hints]` table of `config.toml`: ```toml [hints] # Built-in patterns: "all", "none", or names such as "url,path,sha". builtins = "all" # More patterns, as Go regular expressions. A group named "match" # sets the part that is copied. patterns = ['JIRA-\d+', 'branch: (?P\S+)'] # The letters of the labels, easiest first. Lowercase letters only. alphabet = "asdfghjkl" # Let Ctrl and a label open the text. open = true # How much the text around the labels dims, in percent (10 to 90). dim = 60 # Put labels on all panes on the workspace, not only on the focused pane. all_panes = false ``` Your own patterns come before the built-in patterns. When a pattern does not compile, TUIOS shows a warning when it reads the config, and hints mode skips that pattern. `hints.builtins`, `hints.alphabet`, `hints.open`, `hints.dim` and `hints.all_panes` are also on the settings page (Selection tab) and work with `tuios set-config`. `hints.patterns` is a list, so you set it in the file. ## Change the key The action is `hints` in `[keybindings.prefix_mode]`. To use a different key, bind it: ```toml [keybindings.prefix_mode] hints = ["f"] ``` If your config already binds F in `[keybindings.prefix_mode]` to a different action, TUIOS keeps your binding and gives `hints` no key. `tuios keybinds doctor` tells you when this happens. ## Related # Finding a pane URL: https://tuios.dev/docs/find-a-pane > Jump to a pane by its label, or find any pane in any session by its name, folder, command or screen text. import { Callout } from 'fumadocs-ui/components/callout'; Two keys take you to a pane without walking through the panes one by one. Pane labels number the panes on the screen, as tmux's `display-panes` does. The pane navigator lists every pane in every session, as tmux's `choose-tree` does, and searches them. ## Pane labels 1. Press Ctrl+B Q. Each pane of the workspace shows a large label with its name under it. 2. Type a label. TUIOS focuses that pane and the labels close. | Keys | Does | | ----------------------------- | ------------------------------------------------------ | | A label, such as 3 | Focus that pane | | Backspace | Take back the last key of a two-key label | | Esc, the leader | Close the labels | | q | Close the labels, when q is not a label key | The labels are digits by default, in the order that `select_window_1` to `select_window_9` count the panes. So label 3 is the pane that 3 selects in window mode. A minimised pane gets no label. With more panes than keys, a label has two keys. Use other keys, such as the home row, in `config.toml`: ```toml [panes] label_keys = "asdfghjkl" ``` Letters `a` to `z` and digits are allowed. The first keys go to the first panes. A key typed with Shift is the same key. When a pane is zoomed, the zoomed pane shows its label, and a list under it shows the labels of the panes the zoom hides. A label of a hidden pane moves the zoom to that pane. A pane that is mostly off the screen, or too small for its label, is in the same list. The labels own the keyboard while they are up. In multifocus, a label key or a paste does not reach any pane. The labels close when the layout changes (a pane opens, closes, moves or zooms) and on a click or the mouse wheel. The action is `display_panes`. Bind it to another key in `[keybindings.prefix_mode]`. ## The pane navigator Press Ctrl+B /. The navigator has three boxes. **Search** is at the top and shows how many panes match, such as `3/7`. **Panes** lists the sessions, workspaces and panes. **Preview** shows the screen of the pane under the cursor, in the colors of that pane. The sessions on your [remote hosts](/docs/remote-hosts) are in the tree too. | Keys | Does | | ---------------------------------- | ------------------------------------------------------ | | j, k, arrows | Move | | l, → | Open a session or a workspace | | h, ← | Close it, or go to its parent row | | Space | Open or close | | Enter | Go to the row: the session, the workspace and the pane | | / | Search | | v | Change the layout: tree, flat, cards | | Esc, q | Close the navigator | A click on a pane goes to it. A click on a session or a workspace opens or closes it. ### What a row shows A session name has the color of the session in the sidebar. Its workspaces and panes hang under it on tree lines. A pane row shows: * a dot with the state of the agent in the pane, if there is one; * the pane name. A pane that has no name and no title shows its folder; * the command that runs in the pane, after a `│`. At a prompt this is the shell. For `ssh`, the host follows when the shell reports its commands (OSC 133); * the folder. The current session and the current pane say `current`. The counts at the right are the panes in each session and workspace. A search shows the matched characters in the accent color. ### Layouts Press v to change how the list shows the panes: | Layout | Shows | | ------- | ------------------------------------------------------------------------------------------------ | | `tree` | Sessions, their workspaces and their panes | | `flat` | One row for each pane, with its session and workspace | | `cards` | Two rows for each pane: the name and the command, then the session, the workspace and the folder | Your client keeps the last layout until it closes. Set the first layout in `config.toml`: ```toml [panes] navigator_layout = "flat" ``` ### Search Press / and type. The search reads each pane's name, title, folder, running command, session and workspace, and the last 40 lines of its screen. So you can find the pane where a build failed, or the server you logged in to, even when you did not name the pane. A pane that matches only by its screen text shows the matching line on its row. Esc stops the search and keeps the results. A second Esc clears the search. A paste in the search line is search text. The current session's panes are read from your client, and their preview is live. The other sessions are read when the navigator opens, so their preview shows the screen as it was then. The preview shows colors and text only. Links, clipboard writes and other control sequences from a pane do not reach your terminal. One load reads at most 150 screens and waits at most 8 seconds. A session that does not answer in time says so on its row. The action is `choose_tree`. ## From a script `list-windows --all` prints the same rows as the navigator: ```bash tuios list-windows --all # every pane of every session tuios list-windows --all --all-hosts --json # and the panes on each host tuios list-windows --all --text 20 --json # with the last 20 lines of each screen ``` Each row has `session`, `workspace`, `workspace_name`, `window_id`, `name`, `title`, `command`, `cwd`, `focused` and `agent_state`. A pane on another machine also has `host` and `"untrusted": true`. `--text` reads with `capture-pane`, so the same [pane grants](/docs/agents#what-a-pane-may-do) apply: a pane that holds only `read` gets its own session, and the JSON has an `errors` entry for the rest. # Links URL: https://tuios.dev/docs/links > Open a link in a pane with Ctrl+click, choose the click and the opener, and see which terminals pass the click to TUIOS. import { Callout } from 'fumadocs-ui/components/callout'; import { Card, Cards } from 'fumadocs-ui/components/card'; Ctrl+click on a link in a pane opens it. This works on every platform, macOS included. TUIOS finds two kinds of link: * An **OSC 8 link** is a link that a program marked, such as `ls --hyperlink`, `gcc`, `delta`, `gh` or an agent CLI. Its text and its target can be different. * A **bare URL** is plain text that starts with `http://`, `https://`, `ftp://`, `ftps://`, `file://`, `ssh://` or `git://`. ## Open a link | Gesture | Does | | ---------------------- | ------------------------------------------------------------------------------------------------------------ | | Hover a link | Underline it and show its target in a label under the pointer | | Ctrl+click | Open the link | | Shift+click | Also open the link. Most terminals keep this click for their own selection, so it often does not reach TUIOS | | Ctrl+drag | Move the pane, as before. A link opens only when the pointer does not move | The label shows the real target, also when the text on the screen is different. After a click, the dock names the host, for example `Opening github.com.` A URL that wraps onto the next row is one link. A URL whose start or end is off the screen also opens whole: TUIOS reads the rest of it from the scrollback or from the rows under the view. [Hints mode](/docs/hints) opens links from the keyboard. Ctrl+B F, then Ctrl and a label, opens that URL. Hints mode and the pointer use the same URL detector, so they find the same links. ## Where a URL ends * A URL ends at a space, a quote, a backtick or an angle bracket. * A `)` or a `]` ends the URL when the URL did not open it. A pair that the URL opens stays in it, as in `https://en.wikipedia.org/wiki/Go_(language)`. * TUIOS removes `.,;:!?` from the end. So the markdown badge `[![x](https://a.example/b.svg)](https://a.example/c)` gives two URLs. ## Settings ```toml [appearance] links = "all" # off, marked (OSC 8 only), all link_click = "both" # both, ctrl, shift, off link_opener = "" # for example "firefox --new-tab" or "open -a Safari %s" ``` | Key | Default | What it does | | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------ | | `links` | `"all"` | The links TUIOS finds. `marked` finds only OSC 8 links. `off` finds none | | `link_click` | `"both"` | The click that opens a link. `both` is Ctrl+click and Shift+click. `off` turns the click off | | `link_opener` | `""` | The command that opens a web link. TUIOS puts the URL where `%s` is, or at the end | All three work with `tuios set-config` and are on the settings page. If `link_opener` is empty, TUIOS uses `$BROWSER`, then the system opener: | System | Opener | | ------------- | ---------- | | macOS | `open` | | Windows | `rundll32` | | WSL | `wslview` | | Other systems | `xdg-open` | TUIOS runs no shell. It gives the URL to the opener as one argument. ## When the link does not open TUIOS watches the opener for 3 seconds. If the opener fails in that time, the dock shows "The link did not open. The address is on your clipboard." The log (Ctrl+B D l) keeps what the opener wrote to stderr. If TUIOS cannot start the opener, the dock names the command and TUIOS copies the URL. If `link_opener` does not parse, the dock tells you to change it. ## Which machine opens the link * A local client opens the link on your machine. * Under `ssh`, TUIOS does not use the system opener, because it would open the browser on the remote machine. TUIOS puts the URL on your clipboard. Set `$BROWSER` or `link_opener` to a command that forwards the URL, if you have one. * A remote client (`tuios ssh`, the web client) puts the URL on your clipboard. * A Linux machine with no `DISPLAY` and no `WAYLAND_DISPLAY` has no desktop. TUIOS puts the URL on your clipboard. In all these cases, TUIOS also sends each link to your terminal as OSC 8. Use the link click of your terminal to open it on your machine. ## OSC 8 to your terminal TUIOS draws each pane into its own frame, so your terminal sees the output of TUIOS and not the output of the program. TUIOS writes every link in the focused pane as OSC 8: * An OSC 8 link keeps its URI and its `id=`. * A bare URL gets an `id=` from TUIOS, so a URL that wraps to the next row is one link. Your terminal can then show and open the real target. In a pane that is not focused, TUIOS writes the OSC 8 links of the program, and your terminal finds the bare URLs itself. ## Safety * Only `http`, `https`, `mailto`, `ftp` and `ftps` links go to the opener. TUIOS copies other links, such as `javascript:`, `data:` or the scheme of an application. * A `file://` link opens in an editor pane only when the file is on this machine. A folder opens in the rail's files section. A `file://` link with the name of another host does not open. * TUIOS refuses an address with control characters or spaces. * TUIOS does not ask before it opens a link. The hover label and the scheme list protect you instead. ## Terminals A terminal sends a click to TUIOS only when the terminal does not use the click itself. Most terminals keep Shift+click for their own selection when a program reads the mouse. That is why Ctrl+click is the main click. | Terminal | Shift+click gets to TUIOS | Ctrl+click gets to TUIOS | The terminal opens OSC 8 links | | ---------------- | --------------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------- | | kitty | No. kitty opens the URL under the pointer | Yes | Yes, with Shift+click or Ctrl+Shift+click | | Ghostty | No. Ghostty extends its selection | Yes. On a link, Ghostty opens it | Yes | | WezTerm | No. Shift bypasses mouse reporting | Yes | Yes | | Alacritty | No. Alacritty opens a hinted URL | Yes | Yes, with Shift+click | | foot | No. Shift bypasses mouse reporting | Yes | Yes, in URL mode (Ctrl+Shift+O) | | iTerm2 | Yes | Yes | Yes | | Windows Terminal | No. Shift bypasses mouse mode | Yes | Yes, with Ctrl+click when it gets the click | | xterm | No. Shift bypasses mouse reporting | Yes | No | On macOS, Ctrl+click opens a link in Ghostty, kitty, WezTerm and Terminal.app. Cmd+click does not open a link in any of them while TUIOS reads the mouse. A program can ask Ghostty for Shift+click with `XTSHIFTESCAPE`, or you can set `mouse-shift-capture = true`. TUIOS does not ask, because that takes Shift selection away from you. ## Related # TUIOS vs Others URL: https://tuios.dev/docs/comparison > How TUIOS compares with tmux, zellij, herdr, GNU screen and the splits in WezTerm, kitty and Ghostty, and when to pick one of them instead. import { Callout } from 'fumadocs-ui/components/callout'; import { Card, Cards } from 'fumadocs-ui/components/card'; TUIOS is a terminal multiplexer that works like a window manager. Shells run in windows that float or tile. A daemon keeps them running when you detach. TUIOS also tracks the coding agents running in its panes. It runs inside any terminal, over SSH, or in a browser. TUIOS is not a terminal emulator: it does not draw fonts or open GUI windows. It is also young. The first commit is from September 2025, and the latest release is v0.8.5 (October 2026). This page describes that release. Each fact about another tool comes from that project's own docs, changelog, manual or source, as of September 2026. The [Sources](#sources) list links them. The versions checked are tmux 3.7c (3.8 is in release candidate), zellij 0.45.1, herdr 0.9.1, GNU screen 5.0.2, WezTerm 20240203, kitty 0.49.0 and Ghostty 1.3.1. The TUIOS facts are checked against the TUIOS source and a current build. There are no speed numbers here, because no side-by-side benchmark has been run. ## At a glance This table covers the tools that run inside a terminal. Terminals with their own splits, such as WezTerm, kitty and Ghostty, are a different kind of tool and have [their own section](#terminals-with-built-in-splits). | | TUIOS | tmux | zellij | herdr | GNU screen | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------- | | Language, licence | Go, MIT | C, ISC | Rust, MIT | Rust, Apache 2.0 | C, GPL 3 | | Binary, macOS arm64 | 23 MB, plus `tuios-web` | 1 MB, with shared libraries | 41 MB | 22 MB | Not measured | | Layout | Floating windows, or tiled: BSP, master-stack, scrolling columns | Tiled panes with preset layouts. Floating panes since 3.7 | Tiled, floating and stacked panes | Tiled splits, with zoom | Split regions | | Grouping | 9 workspaces per session | Windows in a session | Tabs in a session | Workspaces, then tabs | Windows in a session | | Mouse | Always on. Drag, resize, focus | Off by default in 3.7, on in 3.8 | On by default | On by default. Click, drag, menus | Focus a region (`mousetrack`) | | Detach and reattach | Yes, a daemon | Yes, a server | Yes, a server | Yes, a server | Yes | | After a restart | Layout, working directories, pane history and agent conversations | Nothing built in. The tmux-resurrect plugin | Layout and commands. Optional scrollback | Layout, working directories and supported agent conversations. Optional screen history | Nothing | | Several clients | Yes, all can type. Sized to the smallest client | Yes. Read-only clients and per-user access | Yes. A cursor per user, read-only attach | Yes. Each client can view its own tab | Yes. Per-user access lists, read-only possible | | Remote machines | Built-in SSH server. Sessions and panes on other hosts in your own client | ssh in and attach | ssh in, or attach to a zellij web server over HTTPS | ssh in, `--remote`, or several saved machines in one window | ssh in. Also serial and telnet windows | | Browser client | `tuios-web`, a separate binary. No login, read-only flag | None | Built in. Login tokens, read-only tokens | None | None | | Scripting | CLI verbs, JSON protocol, blocking waits, event stream, tapes, hooks | tmux commands, control mode, hooks, formats | `zellij action`, `subscribe`, JSON pane lists, plugin pipes | CLI and JSON socket API, output and agent waits, event subscriptions | `screen -X` and `-Q` | | Coding agents | States, alerts, waits, an Inbox that answers approvals, review with notes, queued prompts, messages between agents, worktrees, fan-out | None | None | States, alerts, prompt and wait, worktrees, conversation restore | None | | Images | Kitty graphics and sixel, passed through. Experimental | Sixel when built with it. Others by passthrough | Sixel and Kitty graphics | Kitty graphics | None | | Config | TOML, live `set-config`, settings page | `tmux.conf`, written as tmux commands | KDL | TOML | `.screenrc` | | Extending | Hooks, dock components, control protocol. No plugins | No plugin system. TPM, a third-party manager | WebAssembly plugins | Plugins: any executable with a manifest | None | | Platforms | Linux, macOS, Windows, FreeBSD, OpenBSD | Linux, macOS, the BSDs, Solaris | Linux, macOS, Windows | Linux, macOS, Windows | Unix-like systems | Binary sizes are for macOS on Apple silicon: tmux 3.7c from Homebrew, the zellij 0.45.1 and herdr 0.9.1 release binaries, and TUIOS built from main with `-ldflags "-s -w"`. ## tmux tmux is the default choice, and for good reason. It is packaged almost everywhere and is often already installed. The binary is about 1 MB. Its control mode (`tmux -C`) lets terminals such as iTerm2 show tmux windows as native tabs and windows. **Where tmux is stronger** * It runs on servers you do not control, with nothing to install. * Read-only clients (`attach -r`) and per-user access (`server-access`) are built in. TUIOS has neither in its terminal client. * It is mature and widely documented, with a large body of shared configs and scripts. * 3.8 is moving fast: floating panes that move and resize with commands, modal panes, JSON layout strings, and `wait-for -E` to wait on hook events. **Where TUIOS is stronger** * Floating windows are the core model, not an add-on. In 3.7 tmux floating panes move and resize only with the mouse. TUIOS also has three tiling modes and zoom. * Layout and working directories come back after a reboot with no plugin. * Programs get a JSON protocol with `list-verbs` for discovery, blocking `wait-for` on output, exit, idle or agent state, and an event stream. * Agent state, alerts and messages between agents. tmux has nothing for agents. * A browser client and a built-in SSH server. **Pick tmux** if you work on many machines you do not own, need read-only sharing, or use a terminal built around control mode. ## zellij zellij is the modern, friendly multiplexer. It shows the available keys on screen, extends through WebAssembly plugins, and ships a web client. **Where zellij is stronger** * It teaches itself: the status bar shows the keys for the current mode. * Plugins are WebAssembly. Rust is the officially supported language, and community projects cover others. * The web client (`zellij web`) has login tokens, read-only tokens, and requires HTTPS on any address other than `127.0.0.1`. `tuios-web` has no login and needs an authenticating proxy in front. * Resurrection brings back the commands in each pane, behind a "Press ENTER to run" prompt, and can save the viewport and scrollback. TUIOS brings back a fresh shell under the pane's saved history, and runs no command again. * Read-only attach and a separate cursor per user. * It supports sixel, and Kitty graphics as of 0.45. TUIOS passes images through, and its support is experimental. **Where TUIOS is stronger** * Overlapping, draggable windows and three tiling modes. zellij floating panes sit above a tiled layout. * Agent tracking, and waits on agent state. * Blocking waits from the CLI on a pattern in the output, on idle and on exit. zellij can block until a pane's command exits, and `zellij subscribe` streams a pane's output for a script to watch. * Sessions on other machines in your own client, without a web server. **Pick zellij** if you want discoverable keys, plugins, or browser access with its own login. ## herdr herdr is the closest tool to TUIOS. It is a multiplexer built for coding agents, released in March 2026. It is more widely used, with about 40k [GitHub stars](https://github.com/herdrdev/herdr) against 3.7k for TUIOS, and it releases more often: 0.9.0 and 0.9.1 both shipped in September 2026. Several of the agent screen rules in TUIOS are adapted from herdr's manifests, which are Apache 2.0. **Where herdr is stronger** * After a server restart it can replay recent screen history if you turn that on. TUIOS restores the layout, a fresh shell, and supported agents' conversations, but not the screen. * It adds new agents often: 0.9.1 added Letta Code and 0.9.0 added Muse. * Plugins: a directory with a `herdr-plugin.toml` manifest and commands in any language, plus a marketplace. * One window holds local work and several saved SSH machines, with one combined agent list. * Each attached client can look at a different tab at its own size. TUIOS draws a session at the size of the smallest client. * `herdr update --handoff` can update the server while panes keep running. This is experimental. **Where TUIOS is stronger** * Floating windows, and BSP, master-stack and scrolling-column layouts. herdr has splits and zoom. * A browser client and a built-in SSH server. herdr uses an SSH client on phones instead. * Tape scripts to drive the UI, and tape recording. * Sixel images as well as Kitty graphics. * Mailboxes and questions between agents, and a stash for files agents share. * Agents that report to herdr, such as Crush, report to TUIOS too, with no install step. * Tools built for herdr's socket API, such as Collie's herdr adapter, work with TUIOS too. See [herdr Compatibility](/docs/herdr-compatibility). * An Inbox that answers held approvals, with risk rules that make a dangerous allow take two presses, and a review of what an agent changed, with notes sent back to it. **Pick herdr** if agents are most of what you run, splits are enough, and you want plugins or screen history after a restart. ### tmux-based agent managers Tools such as [claude-squad](https://github.com/smtg-ai/claude-squad) (1.0.20) are not multiplexers. claude-squad runs each agent in its own tmux session and git worktree, and gives you a list of tasks to review, check out and push. It needs tmux and `gh`. Pick it if you want a task list over tmux. Pick TUIOS or herdr if you want to see the agents' panes side by side. ## GNU screen screen is the oldest of these, and is still maintained. 5.0 added truecolor and rewrote authentication, and 5.0.2 shipped in July 2026. **Where screen is stronger** * It is on many old systems where nothing else is. * A window can open a serial line (`screen /dev/ttyUSB0 115200`) or a telnet connection directly. * Multiuser mode has per-user access lists, including read-only access. **Where TUIOS is stronger**: almost everything else on this page. screen splits into regions, but has no floating windows, no restore after a restart, no JSON interface and no image support. **Pick screen** for serial consoles and old machines. ## Terminals with built-in splits WezTerm, kitty and Ghostty are terminal emulators. They draw their own windows, and their splits and tabs are part of that window. TUIOS, tmux, zellij and herdr run inside a terminal instead, so they work over SSH and in any terminal. You can use both: TUIOS inside kitty, WezTerm or Ghostty gets Kitty graphics from the host. | | WezTerm | kitty | Ghostty | | --------------- | ------------------------------------------------------- | ------------------------------------------------------------- | ----------------------- | | Version checked | 20240203 (latest stable, nightly builds since) | 0.49.0 | 1.3.1 | | Language | Rust | C, Python and Go | Zig | | Splits | Panes, tabs, windows, workspaces | Layouts: splits, tall, fat, grid, stack, horizontal, vertical | Native tabs and splits | | Detach | Yes, through a mux server | No | No | | Remote | SSH and TLS domains to a WezTerm mux on the remote host | `kitten ssh`. No remote persistence | No | | Scripting | `wezterm cli`, Lua events | `kitten @` remote control, JSON protocol | AppleScript, macOS only | | Images | iTerm2, Kitty graphics, sixel | Kitty graphics (kitty created it) | Kitty graphics | | Config | Lua | `kitty.conf` | Plain text config file | | Platforms | Linux, macOS, Windows, FreeBSD, NetBSD | Linux, macOS, BSDs | Linux, macOS | ### WezTerm WezTerm is the one terminal here that is also a real multiplexer. A unix domain runs a mux server that keeps panes alive after the GUI closes. An SSH or TLS domain connects to a WezTerm mux running on another machine, and draws its panes as native tabs. `wezterm cli` splits panes, sends text, reads pane text and lists panes as JSON. The config is a Lua program. * **Stronger than TUIOS**: native GUI rendering, fonts and ligatures, and remote panes with native scrollback and mouse. * **TUIOS is stronger**: it runs in any terminal and over plain SSH, has floating windows and agent tracking, and needs nothing but `tuios` on the remote side. WezTerm needs a matching WezTerm on the remote host. The last stable WezTerm release is from February 2024. * **Pick WezTerm** if you want one program for terminal and multiplexer and you control both ends. ### kitty kitty tiles its own windows in seven layouts and has a remote control protocol (`kitten @`) that can open windows, send text and read their contents. Session files set up tabs, windows and layouts, and since 0.43 kitty can save the current layout as a session file. kitty's FAQ says the one thing it lacks from tmux is remote persistence: close kitty and its shells end. * **Stronger than TUIOS**: GPU rendering, native windows, and it created the Kitty graphics and keyboard protocols that other terminals now use. * **TUIOS is stronger**: detach and reattach, sessions that survive closing the terminal, remote hosts, floating windows and agent tracking. * **Pick kitty** if you work on one machine and do not need sessions to outlive the terminal. ### Ghostty Ghostty has native tabs and splits, and on macOS an AppleScript dictionary for windows, tabs and input. It has no detach and no multiplexer of its own. Pick it as a fast native terminal, and run TUIOS or tmux inside it when you need sessions that persist. ## Coming from tmux TUIOS uses the same leader key as tmux, and several prefix keys match: | Action | tmux | TUIOS | | -------------------- | ------------------------------------------ | ------------------------------------------ | | New window | Ctrl+B c | Ctrl+B c | | Detach | Ctrl+B d | Ctrl+B d | | Copy mode | Ctrl+B \[ | Ctrl+B \[ | | Zoom | Ctrl+B z | Ctrl+B z | | Split left and right | Ctrl+B % | Ctrl+B \| | | Split top and bottom | Ctrl+B " | Ctrl+B - | Many CLI verbs share tmux's names too: `new-window`, `split-window`, `send-keys`, `capture-pane`, `list-windows`, `kill-session`. The difference to learn is the two modes. Keys typed in window management mode move windows. Keys typed in terminal mode go to the shell. i enters terminal mode and Alt+Esc leaves it. The prefix key works in both. [Keybindings](/docs/keybindings) lists everything, and every key can be rebound. **Try it live:** [Coming from tmux](/learn?t=from-tmux) and [Coming from zellij](/learn?t=from-zellij) walk you through the switch in your browser, with the keys you already know side by side. ## Sources **tmux** * [tmux 3.7c release](https://github.com/tmux/tmux/releases/tag/3.7c) and [3.8-rc2](https://github.com/tmux/tmux/releases/tag/3.8-rc2) * [CHANGES](https://github.com/tmux/tmux/blob/master/CHANGES): floating panes in 3.7 and 3.8, mouse on by default in 3.8, `wait-for -E`, sixel fixes * [Manual page source](https://github.com/tmux/tmux/blob/master/tmux.1): `attach -r`, `server-access`, `mouse`, `allow-passthrough`, control mode * [README](https://github.com/tmux/tmux/blob/master/README): supported platforms * [configure.ac](https://github.com/tmux/tmux/blob/master/configure.ac): sixel is off unless built with `--enable-sixel` * [tmux-resurrect](https://github.com/tmux-plugins/tmux-resurrect) and [TPM](https://github.com/tmux-plugins/tpm), both third party * [iTerm2 tmux integration](https://iterm2.com/documentation-tmux-integration.html): tmux windows as native tabs and windows **zellij** * [CHANGELOG](https://github.com/zellij-org/zellij/blob/main/CHANGELOG.md): web client (0.43.0), native Windows, read-only attach, `subscribe` and `list-panes` (0.44.0), Kitty graphics (0.45.0) * [Session resurrection](https://zellij.dev/documentation/session-resurrection.html) * [Web client](https://zellij.dev/documentation/web-client.html) * [CLI actions](https://zellij.dev/documentation/cli-actions.html) * [Plugins](https://zellij.dev/documentation/plugins.html) * [0.45.1 release](https://github.com/zellij-org/zellij/releases/tag/v0.45.1) **herdr** * [Concepts](https://herdr.dev/docs/concepts/): workspaces, tabs, panes, mouse, clients * [Session state and restore](https://herdr.dev/docs/session-state/): restart, screen history, agent restore, live handoff * [Supported agents](https://herdr.dev/docs/agents/) and [agent automation](https://herdr.dev/docs/agent-automation/) * [Plugins](https://herdr.dev/docs/plugins/), [configuration](https://herdr.dev/docs/configuration/) (TOML, Kitty graphics), [connecting machines](https://herdr.dev/docs/connecting-machines/), [install](https://herdr.dev/docs/install/) * [CHANGELOG](https://github.com/herdrdev/herdr/blob/master/CHANGELOG.md) and [0.9.1 release](https://github.com/herdrdev/herdr/releases/tag/v0.9.1) * [claude-squad](https://github.com/smtg-ai/claude-squad) **GNU screen** * [Manual](https://www.gnu.org/software/screen/manual/screen.html): `-X`, `-Q`, `split`, `mousetrack`, `aclchg`, serial and telnet windows * [Release tarballs](https://ftp.gnu.org/gnu/screen/): 5.0.2, July 2026 * [5.0.0 announcement](https://lists.gnu.org/archive/html/info-gnu/2024-08/msg00004.html) **Terminals** * WezTerm: [multiplexing](https://wezterm.org/multiplexing.html), [features](https://wezterm.org/features.html), [CLI](https://wezterm.org/cli/cli/index.html), [releases](https://github.com/wezterm/wezterm/releases) * kitty: [layouts](https://sw.kovidgoyal.net/kitty/layouts/), [remote control](https://sw.kovidgoyal.net/kitty/remote-control/), [sessions](https://sw.kovidgoyal.net/kitty/sessions/), [FAQ](https://sw.kovidgoyal.net/kitty/faq/), [changelog](https://sw.kovidgoyal.net/kitty/changelog/) * Ghostty: [features](https://ghostty.org/docs/features), [1.3.1 release notes](https://ghostty.org/docs/install/release-notes/1-3-1) ## Related # Layout Modes URL: https://tuios.dev/docs/layout-modes > Floating, BSP, master-stack, scrolling columns and zoom, and how to switch between them. import { Callout } from 'fumadocs-ui/components/callout'; import { Card, Cards } from 'fumadocs-ui/components/card'; Tiling is a switch: on or off. When it is on, the panes follow one of three layouts. When it is off, the panes float and you place them yourself. Zoom sits on top of all of them and shows one pane large for a moment. **Try it live:** the [Layouts track](/learn?t=layouts) lets you switch between them in your browser. | Mode | What it does | | ------------ | ------------------------------------------------------------------------------------------------------------------ | | BSP | A tree of splits. Any number of panes, each resizable on its own. The default. See [BSP Tiling](/docs/bsp-tiling). | | Master-stack | One or more master panes on one side, and the other panes in a stack beside them. | | Scrolling | Niri-style columns on a strip wider than the screen. The screen scrolls instead of every pane shrinking. | | Floating | Tiling off. Panes can overlap, and you move, snap and resize them. | Open, close and focus panes below to see how each layout places them: ## Switching modes Turn tiling on or off: | Key | Where | | --------------------------------------------- | ---------------------- | | t | Window management mode | | Ctrl+B Space | Any mode | Pick the layout from the command palette (Ctrl+P): * `Layout: BSP tiling` * `Layout: master-stack` * `Layout: scrolling (niri-style)` * `Layout: disable tiling` Picking a layout also turns tiling on. There is no default key for a layout. Bind one yourself if you switch often. The layout mode belongs to the session. It is kept in the daemon's session state, so a scrolling session is still scrolling after you detach and reattach, and turning tiling off does not forget which layout you had. The layout itself (the BSP tree, the scrolling columns) is kept per workspace, so each of the nine workspaces has its own. `startup.layout` sets the mode a **new** session starts in: `bsp`, `master-stack` or `scrolling`. `startup.tiled` (default `true`) decides whether a new session starts tiled at all. A session that already exists keeps its own mode. ```toml [startup] layout = "scrolling" tiled = true ``` From a script, `tuios set-layout` turns tiling on or off and tidies the splits: ```bash tuios set-layout --tiling true tuios set-layout --equalize tuios set-layout --rotate ``` It needs an attached client. ## Master-stack The master-stack layout puts the first panes of a workspace in a master area and the other panes in a stack beside it. The master area is for the pane you work in most, such as an editor. The layout is close to dwm's tile layout and Hyprland's master layout. | Option | Default | Meaning | | ---------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- | | `appearance.master_position` | `left` | The side the masters take: `left`, `right`, `top`, `bottom` or `center` | | `appearance.master_count` | `1` | How many panes are masters (1 to 9). The masters share the master area in equal parts | | `appearance.master_ratio` | `50` | The share of the screen the master area takes, as a percent (10 to 90) | | `appearance.master_grid` | `true` | With one master on the left, show four or more panes as an equal grid. `false` keeps the master and the stack at every pane count | With `center`, the first stack pane goes to the right, the next to the left, and so on. With only one stack pane, the master goes to the left. Two panes on a screen that is taller than it is wide stack one above the other. Each workspace starts with these options. The keys below change the workspace on screen, and the workspace keeps the change. In a daemon session every attached client shows the same layout. | Key | Action | What it does | | --------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------ | | Ctrl+B L o | `cycle_master_position` | Move the masters to the next side | | Ctrl+B L Enter | `swap_with_master` | Swap the focused pane with the master. On the master, swap with the first stack pane | | Ctrl+B L m | `focus_master` | Focus the master | | Ctrl+B L i | `add_master` | Make one more pane a master | | Ctrl+B L d | `remove_master` | Make one pane fewer a master | | \< / > (window mode) | `resize_master_shrink`, `resize_master_grow` | Narrow or widen the master area | | = (window mode), Ctrl+B = | `equalize_splits`, `prefix_equalize_splits` | Put the master back at `master_ratio` and give every other pane an equal share | ```bash tuios set-layout --master-position center tuios set-layout --masters 2 ``` ### How a resize is kept A resize goes into the workspace's splits: the master ratio, and the share of every other pane. That covers each master, each stack pane, and each row and pane of the grid. The keyboard resizes, the [percentage resizes](#gaps-and-sizing) and a mouse drag on a divider all do this, so the resize survives a retile. A terminal resize is a retile too. When a pane opens or closes, the part of the layout it changes goes back to equal shares. With the master in the center, a resize moves one divider, and the next retile puts the master back in the middle at its new width. The splits are session state, so every client attached to the session lays the workspace out the same way. In every layout, a workspace keeps its splits when the session's size changes while the workspace is off screen. When you switch back, the panes fit the new size. ## Scrolling The scrolling layout is modeled on the [niri](https://github.com/niri-wm/niri) window manager. Each pane is a **column** on a horizontal strip, and the screen is a window onto that strip. A new pane gets a new column right after the focused one, and the strip scrolls to show it. The other columns keep their width. A column holds one pane by default. It can hold several stacked vertically, which split its height evenly. Closing the last pane in a column removes the column and moves focus to the column on its left. ### Moving around | Input | Action | | ------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------- | | Alt+Left / Alt+Right (terminal mode) | Focus the column to the left or right | | h / l (window mode) | Focus the column to the left or right | | j / k (window mode), Alt+Down / Alt+Up (terminal mode) | Focus the pane below or above in a column of stacked panes | | Alt+P / Alt+N | Focus the column to the left or right | | H / L, or Ctrl+Left / Ctrl+Right (window mode) | Move the focused column left or right along the strip | | \< / > (window mode) | Shrink or grow the focused column by four cells | | Alt or Shift + mouse wheel | Scroll the strip sideways | Moving focus with the keyboard scrolls the strip by the least amount that shows the whole focused column, with four cells of its neighbour peeking in beside it. It does not center the column: when all of it and that margin are already on screen, the strip stays put. Clicking a column that is partly off screen brings all of it on screen (`appearance.niri_click_reveals`). With `focus_follows_mouse` on, hovering does the same (`appearance.niri_hover_reveals`). The plain mouse wheel belongs to the pane under the pointer. The strip only scrolls while Alt or Shift is held. This stops a trackpad's sideways drift from sliding the strip while you scroll a pane's history. ### Column commands These have palette entries and no default key. The action names can be bound in `[keybindings]`. | Palette entry | Action | What it does | | ------------------------------------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | `Scroll: cycle column width` | `scroll_cycle_width` | Cycle the focused column through 33%, 50%, 55%, 67% and 90% of the screen, and 100% when `scroll_column_max` is `100` | | `Scroll: maximize column width` | `scroll_maximize` | Widen the focused column to `scroll_column_max`, the widest the strip gives it | | `Scroll: move window into the column below` | `scroll_consume` | Pull the pane from the next column into the focused one, stacked below | | `Scroll: move window out to its own column` | `scroll_expel` | Move the focused pane out of its column into a new column to the right. Focus follows it, so expel undoes a consume | | none | `scroll_focus_left`, `scroll_focus_right` | Focus the column to the left or right | | none | `scroll_move_left`, `scroll_move_right` | Move the focused column left or right | A column's width is a share of the screen until you resize it with \< or >, which pins it to a cell count. Each press changes the width by four cells, from 20 cells up to `scroll_column_max`. Cycling the width unpins it again. ### Settings | Key | Default | Meaning | | -------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------- | | `appearance.scroll_column_width` | `55` | Width of a new column, as a percent of the screen (20 to 100) | | `appearance.scroll_column_max` | `90` | The widest a column may be set, as a percent. `100` lets a column fill the screen, and the next column no longer peeks in | | `appearance.niri_scroll_cells` | `8` | Cells the strip moves per wheel event (1 to 200) | | `appearance.niri_reverse_scroll` | `false` | Reverse the wheel direction on the strip | | `appearance.niri_click_reveals` | `true` | Clicking a partly hidden column scrolls it fully into view | | `appearance.niri_hover_reveals` | `true` | With focus-follows-mouse, hovering does the same | The default column width of 55% is over half on purpose: two columns never quite fit, so the strip reads as something you scroll. The scroll offset and the columns are part of the session. Every client attached to the session sees the same columns at the same offset, and the columns come back after a session switch or a reattach. [A session switch forgot my column widths](/blog/a-session-switch-forgot-my-column-widths) tells how the columns became session state. **Shared borders:** with `shared_borders` on, columns draw no border of their own. One divider stands between two columns. Drag it to set the width of the column on its left. Windows stacked in one column have a divider between them too, but they always share the column's height evenly. **Limits:** the strip always animates when it slides, even with animations off, because a jump is hard to follow. ## Floating With tiling off, panes float. Drag a pane by its title bar to move it, or Alt+drag anywhere on it. Right-drag resizes it from the nearest corner. In window management mode: | Key | Action | | ------------------------------------------------------------------- | ------------------------------ | | h / l | Snap to the left or right half | | f | Snap to full screen | | u | Unsnap | | Ctrl+B L 1-4 | Snap to a corner | Dragging a pane to a screen edge or corner snaps it there too. See [Keybindings](/docs/keybindings). The palette entry `Toggle floating` lifts only the focused pane out of the tiling and lets it float over the others. Run it again to put the pane back. For a short-lived floating pane that closes when its program exits, use a popup: ```bash tuios popup --width 90% --height 80% -- htop ``` ## Zoom Zoom shows the focused pane over the layout. By default it takes 95% of the screen, so the edges of the layout stay visible around it. The layout underneath is not changed. | Key | Where | | ----------------------------------------- | ---------------------- | | z | Window management mode | | Ctrl+B z | Any mode | On a tiled pane, the third title bar control toggles zoom too (`appearance.window_button_zoom`). | Key | Default | Meaning | | ------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `appearance.zoom_size` | `95` | Share of the screen a zoomed pane takes, as a percent (50 to 100). `100` is fullscreen | | `appearance.zoom_max_width` | `0` | Width cap in cells. `0` means no cap. Useful on an ultrawide display | | `appearance.zoom_borderless` | `false` | Show the zoomed pane on the whole pane region, with no border and no title bar. The program in the pane gets the full size. The dock and the rail stay on the screen. With it on, `zoom_size` and `zoom_max_width` have no effect | | `appearance.zoom_follows_focus` | `true` | Moving focus while zoomed zooms the pane you moved to | | `appearance.zoom_animation` | `true` | Slide between the tile and the zoomed box instead of switching in one frame | ## Gaps and sizing `appearance.gap` keeps empty cells between neighbouring panes, in every tiling mode (0 to 8, default `0`). It is an inner gap only: there is no margin at the screen edge. The layout prefix sizes the focused pane to a percentage: | Key | Action | | ------------------------------------------------------------------------------------ | ------------------------------------------------------ | | Ctrl+B L 5-9 | Width to 50%, 60%, 70%, 80% or 90% of the content area | | Ctrl+B L Shift+5-9 | Height to 50% through 90% | Each percentage is its own action (`resize_width_50` to `resize_width_90`, `resize_height_50` to `resize_height_90`), so any of them can be rebound in `[keybindings.layout_prefix]`. The layout still applies its minimum pane size and gaps. In the scrolling layout only the width actions do anything. `gap`, `master_ratio`, `scroll_column_width` and `shared_borders` decide how many cells each pane gets, so they are shared across the session: change one on any client and every attached client follows. Purely visual settings such as the theme, border style and glyphs stay per client. ## Finding a pane The palette entry `All windows` opens a searchable list of every pane on every workspace, with a short preview of each. It has no default key. | Key | Action | | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | | Type | Filter by title, workspace number, preview text or directory | | Up / Down, Ctrl+P / Ctrl+N | Move the selection | | Enter | Go to the pane: switch workspace, restore it if minimized, focus it | | Ctrl+U | Clear the query | | Esc | Close | The preview is the first three non-empty lines of the pane, taken when the list opens. The directory is only read on Linux. ## Typing into several panes Multifocus sends your typing to several panes at once, like tmux's `synchronize-panes`. Use it to run the same command on several hosts. | Input | Action | | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | Ctrl+Shift+click on a pane | Add or remove it from the set | | Palette: `Toggle multifocus`, or action `toggle_multifocus_active` | Add or remove the focused pane | | Palette: `Toggle multifocus on all panes`, or action `toggle_multifocus_all` | Add every visible pane on the workspace. When all of them are in the set, remove them. Panes on other workspaces stay in the set | | Palette: `Clear multifocus` | Empty the set | The two actions have no default key. This example binds them to Ctrl+B y and Ctrl+B Y: ```toml [keybindings.prefix_mode] toggle_multifocus_active = ["y"] toggle_multifocus_all = ["Y"] ``` To open many panes with multifocus on in one step, use [`tuios xpanes`](/docs/cli-reference#xpanes). It opens one pane per item on a new workspace, tiled, and puts all of them in the set: ```bash tuios xpanes --ssh web1 web2 web3 ``` From a script, `tuios run-command SetMultifocus ...` sets the set to exactly these windows. With no window, it empties the set. Panes in the set get a distinct border colour. A notification shows the size of the set when you change it. With `appearance.dim_unfocused` on, the panes in the set are not dimmed. Set `appearance.dim_multifocus = true` to dim them like the other panes. While the set is not empty and you are in terminal mode, every key that goes to the focused pane also goes to each pane in the set. A paste goes to each pane in the set too. A pane gets a bracketed paste only when its program turned bracketed paste on. A paste into a scrolled pane first returns it to live output, as a typed key does. In copy mode, TUIOS drops the paste. Keys that TUIOS handles itself, such as the leader key, are not copied. Limits: * Nothing is copied in window mode. * A key goes only to the panes of the set that are on the current workspace and are not minimized. The other panes stay in the set and get keys again when they are on screen. * The set belongs to your client. Other clients do not share it. It does not survive a detach, and a session switch clears it. * The set follows panes, not positions. A swap keeps the same panes in the set. A pane that closes leaves the set. * A popup cannot join the set. ### Multi copy mode Multi copy mode is copy mode on every pane of the multifocus set at the same time. Use it to search the output of many panes at once and copy the same part from each pane. Each pane has its own cursor and its own selection. Each key moves all of them together. To start it, add two or more panes to the multifocus set and focus one of them. Then press Ctrl+B \[, the copy mode key. When the focused pane is not in the set, the key starts copy mode on one pane. When multifocus is on, the palette entry `Multi copy mode` also starts it, and the prefix menu shows `Multi copy (N)` on the \[ line. The dock shows `MULTI 3`, where 3 is the number of panes. After a search, it shows `MULTI 2/3`: the search found text in 2 of the 3 panes. | Key | Action | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | h j k l, w b e, g G and the other [copy mode](/docs/keybindings#copy-mode) motions | Move the cursor in every pane | | / or ?, then text, then Enter | Search every pane. Each pane goes to its own match | | n / N | Go to the next or previous match in each pane | | v / V | Start a character or line selection in every pane that has a match | | Esc | End the selection. A second Esc, or q, leaves copy mode in every pane | | y | Copy the selection of each pane to the clipboard, as one block | | Y | Save the selection of each pane to a file | | Tab | Change the format: plain, markdown, json | | Shift+Up / Shift+Down | Scroll every pane one line | A pane where the search finds no text loses the multifocus border colour. With a theme, the pane is also dimmed. It keeps its cursor. It takes no motions or selections until a later search finds text in it. Ctrl+L clears the search. y copies only the selected text of each pane. A pane without a selection adds nothing. The panes are in window order. | Format | What it writes | | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `plain` | The selected lines of each pane, one pane after the other. Each pane ends with a newline. Nothing is added. Use it when each line already names its host | | `markdown` | For each pane, a `## Pane N: title` heading and the lines in a fenced code block | | `json` | An array with one object for each pane: `{"pane": N, "window_id": "...", "title": "...", "lines": ["..."]}`. The pane number is the index that `tuios list-windows --json` shows | The format starts as `appearance.selection.multi_format` (default `plain`). Tab changes it until you leave multi copy mode. `appearance.selection.copy_command` and a `[[keybindings.copy_pipe]]` key also work in multi copy mode. The command gets the text that y copies, in the current format, one time for all panes. See [Pipe a yank through a command](/docs/keybindings#pipe-a-yank-through-a-command). Y opens a prompt at the bottom of the focused pane. The default path is `~/tuios-copy--