A terminal multiplexer with tiling panes, tabs and workspaces, written in Go.
Website · Install · Keys · Configuration · How it works
A real session, captured through slat's own emulator and cut into a film — not a mock-up.
slat tiles one terminal into panes, tabs and workspaces. Your shells live in a
small background process, so closing the window — or losing the SSH
connection — doesn't end anything. Run slat again and it's all there,
output included.
- Panes — split left/right or top/bottom, move between them by direction, swap, resize, zoom
- Tabs and workspaces — group tabs into named workspaces, rename them in place
- Detach and reattach —
ddisconnects; runningslatfrom any terminal reattaches - Survives a reboot — the layout, each pane's directory and the output it had printed are saved and put back when slat starts again
- Scroll and copy mode — page back through a pane's output, search it vim-style, select characters or lines and yank them to your system clipboard (over
sshtoo, via OSC 52) - Mouse — click a pane to focus it, drag a border to resize, wheel through its history; programs that want the mouse themselves get the events, translated to their pane
- Every pane keeps its own screen —
clear, vim or htop in one pane never touch another, and nothing is lost when you split, close or switch - Fast — only changed cells are sent to your terminal; half a million lines of output render in about a third of a second
- New panes open where you are — a split starts in the directory of the pane you split from (Linux)
- Drivable from scripts and AI agents —
slat ls,run,captureandwait --for idlelet anything outside the terminal work a session and know when a pane needs a human (details) - Move panes around — walk a pane through the layout like a tiling window manager; the swap slides instead of jumping
- Rice it — ten palettes or your own colours, six border styles or your own glyphs, a status bar you write as a format string, and animation timing and easing you can tune or switch off entirely
- One checked config file — mistakes are reported when you run
slat, not ignored
| Platform | How |
|---|---|
| Debian / Ubuntu | signed apt repository |
| Arch Linux | package from the latest release |
| Fedora / RHEL | one dnf command |
| macOS, anything with Go | go install |
| Try it without installing | Docker |
Every package comes for both amd64 and arm64.
Add the repository once; after that, apt upgrade keeps slat up to date.
# 1. Trust the repository's signing key
curl -fsSL https://noturbob.github.io/slat/apt/slat.gpg \
| sudo tee /usr/share/keyrings/slat.gpg > /dev/null
# 2. Add the repository
repo=https://noturbob.github.io/slat/apt
echo "deb [signed-by=/usr/share/keyrings/slat.gpg] $repo stable main" \
| sudo tee /etc/apt/sources.list.d/slat.list
# 3. Install
sudo apt update
sudo apt install slatKey fingerprint, or install a single .deb instead
The repository key's fingerprint is:
9040 A503 5BDC A04A 55C3 719A D1A8 1309 D2F2 0668
To install one release without adding the repository:
curl -LO https://github.com/noturbob/slat/releases/latest/download/slat_linux_amd64.deb
sudo apt install ./slat_linux_amd64.debcurl -LO https://github.com/noturbob/slat/releases/latest/download/slat_linux_amd64.pkg.tar.zst
sudo pacman -U slat_linux_amd64.pkg.tar.zstOr from the AUR: yay -S slat.
Download slat_windows_amd64.zip from the
latest release, unzip it and
put slat.exe on your PATH. Windows 10 1809 or later is required (that's when
ConPTY arrived). Panes run cmd.exe by default; for something else:
# %USERPROFILE%\.config\slat\config.toml
shell = "powershell.exe"Windows Terminal is recommended — the old conhost window can't render everything slat draws.
sudo dnf install \
https://github.com/noturbob/slat/releases/latest/download/slat_linux_amd64.rpmWorks on Linux and macOS:
go install github.com/noturbob/slat/cmd/slat@latestOther ways: prebuilt binaries, or build from source
Prebuilt binaries for Linux and macOS are on the releases page.
To build from a checkout:
make build # ./bin/slat
make install # into $(go env GOPATH)/bindocker run -it --rm ghcr.io/noturbob/slatThe shells run inside the container, not on your machine, and the container stops when you detach, so this is for trying slat out. Install it natively for real use. (The image is published from the next release onwards.)
slatThe first run starts the background daemon; later runs attach to it. Press Ctrl+S then ? inside slat to see every key.
Press the prefix (default Ctrl+S), then one key. Everything else goes straight to your shell — including Ctrl+C. Press the prefix twice to send it to the shell. Prefix then ? shows every key inside slat.
| Key | Action |
|---|---|
v / h |
Split left / right, top / bottom |
o / O |
Next / previous pane |
arrows, or k j H L |
Pane above / below / left / right |
s |
Swap with the next pane |
m |
Move mode: h j k l walk the pane around, q leaves |
< > K J |
Move the pane left / right / up / down |
+ / - |
Grow / shrink the pane |
= |
Equalize all sizes |
z |
Zoom the pane to full size (toggle) |
x |
Close the pane |
[ or PageUp |
Scroll and copy mode (see below) |
| Key | Action |
|---|---|
c |
New tab |
n / p |
Next / previous tab |
1–9 |
Go to tab N |
, |
Rename the tab |
X |
Close the tab and all its panes |
| Key | Action |
|---|---|
W |
New workspace |
w / P |
Next / previous workspace |
$ |
Rename the workspace |
| Key | Action |
|---|---|
d |
Detach — the session keeps running |
q |
Quit — ends every shell |
? |
Show all keys |
[ (or PageUp) scrolls back through a pane's own history. The view is
anchored to the output, so it holds still while the program keeps writing.
| Key | Action |
|---|---|
k j h l, arrows |
Move the cursor; the view follows it |
Ctrl+u / Ctrl+d |
Half a screen up / down |
Ctrl+b / Ctrl+f, PageUp / PageDown |
A screen up / down |
g / G |
Oldest line / back to the live screen |
0 / $ |
Start / end of the line |
/ ? |
Search up / down — smart case; the cursor moves to the match |
n / N |
Next / previous match |
v / V |
Select characters / whole lines |
y |
Copy the selection and leave |
Esc |
Drop the selection, then leave |
q |
Leave |
Copying uses OSC 52, which asks your terminal to put the text on the system
clipboard — so it works over ssh, where slat runs on the far machine and
the clipboard is on yours. Some terminals refuse OSC 52 or need it enabled
(set -g set-clipboard on territory); for those, name a local command:
[copy]
osc52 = true # ask the terminal; the only way that works over ssh
command = "wl-copy" # …and/or pipe it locally: xclip -sel clip, pbcopyA reboot used to end everything: the daemon holds your shells in memory, so it outlives a closed window or a dropped connection, but not a restart.
slat now writes the session out — workspaces, tabs, the layout, each pane's working directory and the output it had printed — and puts it back the next time it starts. Panes come back in the same shape, in the same directories, with their old output above the prompt and a dim rule marking where the previous session ended.
What comes back is the output, not the programs. Nothing portable can
restore a running process, so a pane that was running vim comes back as a
shell in the same directory, with what vim had drawn left behind as text.
Quitting with q clears the saved session — ending a session
means ending it. Detaching, closing the window and rebooting do not. The
file lives in $XDG_STATE_HOME/slat/session.json (~/.local/state by
default), is written every 15 seconds while anything changes and again on
the way out, and keeps up to 2000 lines per pane.
restore = false # start clean every timeClick a pane to focus it, drag a border to resize, and the wheel scrolls back through its output. Programs that ask for the mouse — vim, htop, less — receive the events themselves, with the coordinates translated into their pane, so they behave as they do outside slat.
Your terminal hands its clicks to slat while this is on, so its own click-drag selection is unavailable; hold Shift for it in most terminals. To keep the terminal's mouse instead:
mouse = falseEvery command below talks to a running session over its socket, so scripts — and AI coding agents — can drive slat without a terminal attached. Nothing is sent anywhere: slat has no network code.
$ slat ls
*1 idle 1:shell bash ~/src/slat
2 working 1:build go ~/src/slat
$ slat pane new --cmd 'go test ./...' # new pane, your focus stays put
$ slat wait 3 --for idle --timeout 5m # block until it finishes
$ slat capture 3 --lines 20 # read what it printed| Command | What it does |
|---|---|
slat ls |
every pane, with what each one is doing |
slat status [PANE] |
one pane's status |
slat pane new [--split v|h] [--cwd D] [--cmd C] [--target P] [--focus] |
split a pane |
slat pane close PANE |
close a pane |
slat send PANE TEXT [--enter] [--key KEY] |
type into a pane |
slat run PANE COMMAND... |
send a command and press Enter |
slat capture PANE [--lines N] [--history] |
read a pane's text |
slat wait PANE --for idle|input|exit|text=REGEX [--timeout 60s] |
block until something happens |
PANE is an id from slat ls, or active (the default). --json on any
command prints one object with a schema field. Exit codes: 0 ok,
1 error, 2 timed out, 3 that pane is gone.
A pane's status is one of four, which is what makes wait useful:
- idle — the pane's own shell has the terminal: the command finished.
- working — a program is running.
- input — something has gone quiet on a question (
[y/N], a password prompt), so an agent waiting for a build wakes up instead of hanging. Shell builtins count:read -p "Overwrite? [y/N] "is detected even though the shell itself is the foreground process, because the line doesn't end the way a prompt does. - exited — the pane's program is gone.
When a pane starts waiting for input, its tab is marked 2:shell ? in the
status bar, and slat can run a command so you don't have to watch:
[agent]
on_input = "notify-send \"slat: pane %p needs input\" %c"%p is the pane id, %t its tab, %s the status and %c the pane's last
line. Values are shell-quoted for you, so a prompt containing a quote stays
text. The hook runs detached; its output goes to the daemon log, never to your
screen.
Tune the detection under [agent] in the config; see
docs/design/agent-cli.md for the full design.
slat reads ~/.config/slat/config.toml (or $XDG_CONFIG_HOME/slat/config.toml).
Every setting is optional:
prefix = "C-a" # Ctrl + a letter, or C-\ C-] C-^ C-_
shell = "/bin/zsh" # default: $SHELL
status_bar = true
scrollback = 5000 # lines kept per pane; 0 turns it off
mouse = true # false hands the mouse back to your terminal
restore = true # bring the session back after a reboot
animations = true # false turns every animation off
[agent] # how `slat status` reads a pane (see above)
settle = "750ms" # quiet for this long after output = idle
input_after = "10s" # quiet for this long on a prompt = waiting for input
[theme]
name = "catppuccin" # catppuccin, default, dracula, gruvbox, latte, mono, nord, rose-pine, solarized, tokyo-night
accent = "#fabd2f" # override any single colour
[borders]
style = "rounded" # sharp, rounded, heavy, double, dashed, none
# vertical = "┃" # …or set any glyph yourself
[animation]
split = "90ms" # a new pane appearing; 0 turns it off
move = "120ms" # two panes trading places
easing = "out-cubic" # linear, out-quad, out-cubic, out-back
reveal = "curtain" # curtain or none
glyph = "░"
[status]
left = " {workspace} │ {tabs}"
right = "{badge} pane {pane}/{panes} {time} "
tab = " {index}:{name}{alert} "
alert = " ●"
[keybinds]
split-vertical = "|"
split-horizontal = "-"
resize-shrink = "_" # "-" was given away above
zoom = "" # "" unbinds a command[theme]colours arebg,fg,dim,accent,border,tab_bg,tab_fg,tab_active_bgandtab_active_fg, written as a colour name, a palette index (0-255) or#rrggbb. Only what slat draws is themed — pane contents keep whatever colours the programs in them use.[status]takesleft,right,tabandalert. The sides accept{workspace}{workspace_index}{workspace_count}{workspaces}{tabs}{tab}{tabs_count}{pane}{panes}{badge}{time}{seconds}{date};tabaccepts{index}{name}{alert}. Everything else is literal text, so a Nerd Font glyph goes straight in — and each placeholder is drawn in the colour that suits it, so there's no colour markup to write.- A mistake anywhere — an unknown colour, a glyph two columns wide, a
placeholder that doesn't exist, an easing curve that doesn't either — is
reported when you run
slat, never quietly drawn. - Fonts belong to your terminal, not to slat. slat draws characters; which font paints them is Kitty's, Alacritty's or WezTerm's business. Point that at a Nerd Font and its glyphs work here like any other character.
- Ready-made setups live in
docs/rices/— copy one and edit it, or send yours in. - Keybinds you don't set keep their defaults.
- Giving a default key to another command takes it away from the default command, so bindings never silently collide.
- An unknown setting, an unknown command name, a key that isn't a single
character, or two commands on one key is an error, shown when you run
slat.
config.example.toml lists every command with its default.
terminal ── slat (client) ──unix socket──▶ slat daemon
raw keys ─────────────────────────────▶ ├─ workspaces / tabs / layout tree
◀── only the changed cells ───────────── ├─ per-pane terminal emulator
└─ shells on PTYs
- Daemon. The first
slatre-executes itself as a background daemon that owns every shell. Its socket lives in$XDG_RUNTIME_DIR(or the temp dir) and is only accessible to you. Attaching from a second terminal takes over from the first, liketmux attach -d. The daemon's own errors go to the.logfile next to its socket. - Emulation. Each pane's output is fed into slat's own terminal emulator
(
internal/vt), which keeps that pane's screen. It handles the xterm features shells and full-screen programs use: colors (16, 256 and 24-bit), wide characters and emoji, scroll regions, the alternate screen, line drawing and cursor queries. - Rendering. On every change slat composes a frame from the panes, borders, status bar and overlays, compares it with what your terminal already shows, and sends only the difference. Since frames are built from state rather than patched step by step, a split, close, resize or reattach can't leave stale or missing text behind.
- Input modes. The active pane's cursor-key mode and bracketed paste are mirrored onto your terminal, so arrow keys in vim and multi-line pastes into your shell behave as they do outside slat.
cmd/slat/ entry point: starts or attaches to the daemon
internal/
daemon/ socket server, attach / detach, session lifetime
client/ raw mode, alternate screen, stdin/stdout ⇄ daemon
proto/ length-prefixed frames from client to daemon
app/ the session: input → actions, frame composition
vt/ terminal emulator for each pane
ui/ frame, diffing renderer, status bar, help, borders
session/ workspaces, tabs, focus, removing dead panes
layout/ binary-tree tiling
pane/ a shell on a PTY with its emulator
input/ prefix key and command table
config/ TOML loading and validation
website/ the site at noturbob.github.io/slat (Next.js, built by CI)
docs/design/ how the agent CLI was designed
docs/rices/ ready-made configurations, one of which the tests read
packaging/aur/ the AUR package
packaging/apt/ builds the signed apt repository
None of website/, docs/ artwork, packaging/ or .github/ reaches the
source archive — .gitattributes keeps git archive (and
so the Debian tarball) down to what actually builds slat.
Releases are built by GoReleaser when a v* tag is pushed:
binaries, .deb, .rpm and Arch packages for Linux and macOS on amd64 and arm64.
The release then republishes the website and the apt repository to GitHub Pages
(pages.yml), signed with the APT_GPG_PRIVATE_KEY secret.
make test runs go vet and the tests with the race detector. The tests in
internal/app drive real shells through splits, closes, overlays and
workspace changes and check what a terminal would display.
- A restored session brings back output, not running programs: nothing portable can restore a process, so each pane comes back as a fresh shell in the same directory.
- On Windows, ConPTY has no foreground process group, so a pane's status
comes from output timing alone:
slat statussays idle or working, never which program is running, andslat lsshows the directory a pane started in rather than where its shell has since moved.
- Go 1.23+ to build
- Linux, macOS, or Windows 10 1809 and later
slat is free and always will be. If it saves you time, you can buy me a coffee on Ko-fi.
MIT
