# Tmux Configuration
Personal tmux setup on macOS with tmux 3.6a, [[Ghostty]], and [[Neovim]].
Config file: `~/.tmux.conf`
> [!info] Architecture Mental Model
> - A **server** runs in the background and holds all state
> - **Sessions** are independent workspaces (e.g., "project-A", "notes")
> - Each session has **windows** (like browser tabs)
> - Each window has **panes** (split views within a tab)
> - You **detach** from a session (it keeps running) and **attach** later
## Key Settings
| Setting | Value | Why |
|---------|-------|-----|
| Prefix | `Ctrl+Space` | Easy to press, no finger stretch |
| Mouse | Enabled | Click panes, scroll, drag to resize |
| Copy mode | Vi keys | Natural for Neovim users |
| Escape time | 10ms | Default 500ms causes Neovim input lag |
| History | 50,000 lines | Default 2,000 is too small |
| Base index | 1 | Window/pane numbering starts at 1 |
| True color | `tmux-256color` + `:RGB` | Full color support in Ghostty |
| Clipboard | OSC 52 + pbcopy fallback | Ghostty supports OSC 52 natively |
| Scrollbars | Enabled | tmux 3.6 visual scroll indicator |
| `allow-passthrough on` | enabled | OSC sequences (notifications, clipboard) pass through to cmux/Ghostty |
## Cheat Sheet
### Session Management
| Keys | Action |
|------|--------|
| `tmux` | Start a new session |
| `tmux new -s name` | Start a named session |
| `tmux ls` | List sessions |
| `tmux attach -t name` | Attach to a session |
| `prefix d` | Detach from current session |
| `prefix
| Rename session |
| `prefix s` | Switch between sessions |
### Windows (tabs)
| Keys | Action |
|------|--------|
| `prefix c` | New window (in current path) |
| `prefix 1-9` | Switch to window by number |
| `prefix n` / `prefix p` | Next / previous window |
| `prefix ,` | Rename window |
| `prefix &` | Close window |
| `prefix w` | Window overview (picker) |
### Panes (splits)
| Keys | Action |
|------|--------|
| `prefix \|` | Split vertically (side by side) |
| `prefix -` | Split horizontally (top/bottom) |
| `prefix h/j/k/l` | Navigate panes (vim-style) |
| `Ctrl+h/j/k/l` | Navigate panes (works across Neovim too) |
| `prefix H/J/K/L` | Resize panes (repeatable) |
| `prefix x` | Close pane |
| `prefix z` | Toggle pane zoom (fullscreen) |
| Mouse drag border | Resize pane |
### Copy Mode (scrollback)
| Keys | Action |
|------|--------|
| `prefix [` | Enter copy mode |
| `v` | Begin selection |
| `Ctrl+v` | Rectangle selection |
| `y` | Yank to clipboard |
| `q` | Exit copy mode |
| Mouse scroll | Scroll (auto-enters copy mode) |
| `/` | Search forward (in copy mode) |
| `?` | Search backward (in copy mode) |
### Plugins
| Keys | Action |
|------|--------|
| `prefix I` | Install new plugins |
| `prefix U` | Update plugins |
| `prefix Ctrl+s` | Save session (tmux-resurrect) |
| `prefix Ctrl+r` | Restore session (tmux-resurrect) |
### Config
| Keys | Action |
|------|--------|
| `prefix r` | Reload `~/.tmux.conf` |
## Installed Plugins
Managed via ==TPM== (Tmux Plugin Manager) at `~/.tmux/plugins/tpm`.
| Plugin | Purpose |
|--------|---------|
| `tmux-yank` | System clipboard integration |
| `tmux-resurrect` | Save/restore sessions across restarts |
| `tmux-continuum` | Auto-save sessions every 15 min, auto-restore on start |
| `vim-tmux-navigator` | Seamless `Ctrl+h/j/k/l` across Neovim and tmux panes |
| `catppuccin/tmux` | Catppuccin Mocha theme |
> [!tip] Session Persistence
> `tmux-resurrect` + `tmux-continuum` make sessions survive reboots.
> Sessions auto-save every 15 minutes. On tmux start, the last session auto-restores.
> Manual save: `prefix Ctrl+s`. Manual restore: `prefix Ctrl+r`.
## macOS Prerequisite
Disable the `Ctrl+Space` system shortcut:
**System Settings > Keyboard > Keyboard Shortcuts > Input Sources** — uncheck "Select the previous input source".
Only needed if you use `Ctrl+Space` as prefix. No functionality lost if you use a single keyboard layout.
## Neovim Integration
The `vim-tmux-navigator` plugin is installed on the tmux side. To complete the setup, install the corresponding Neovim plugin:
```lua
-- lazy.nvim
{ "christoomey/vim-tmux-navigator" }
```
This enables `Ctrl+h/j/k/l` to move seamlessly between Neovim splits and tmux panes without thinking about which one you are in.
## Common Workflows
### Start a new project workspace
```bash
tmux new -s myproject
```
### Detach and come back later
```
prefix d # detach
tmux attach -t myproject # reattach later (even after closing terminal)
```
### Split into editor + terminal layout
```
prefix | # vertical split (editor left, terminal right)
prefix - # horizontal split on right pane (terminal top, logs bottom)
prefix h # go back to left pane
```
### Scroll through output
```
prefix [ # enter copy mode
k/j or PgUp/PgDn # scroll
v # select text
y # copy to clipboard
prefix ] # paste
```
## cmux Integration
[[cmux Configuration|cmux]] runs as the outer terminal; tmux runs *inside* cmux workspaces. The two layers are complementary:
| Responsibility | cmux | tmux |
|----------------|------|------|
| Workspace sidebar | ✓ | — |
| Notifications | ✓ | — |
| Session persistence | — | ✓ resurrect/continuum |
| vim-tmux-navigator | — | ✓ |
`allow-passthrough on` is required so that OSC escape sequences (OSC 777 notifications, OSC 52 clipboard) generated by processes inside tmux pass through to cmux/Ghostty rather than being absorbed by tmux.
Catppuccin status bar module config (in `.tmux.conf`, before the TPM init line):
```tmux
set -g @catppuccin_window_status_style "rounded"
set -g @catppuccin_window_text " #W"
set -g @catppuccin_window_current_text " #W"
set -g @catppuccin_status_left ""
set -g @catppuccin_status_right "#{E:@catppuccin_status_session}"
```
This gives rounded window tabs with window names and session name on the right — no clock clutter.
## Verification Commands
```bash
# Check true color support (should show smooth gradient)
printf "\x1b[38;2;255;100;0mTRUECOLOR\x1b[0m\n"
# Check tmux version
tmux -V
# List active sessions
tmux ls
# Check loaded plugins
ls ~/.tmux/plugins/
# Test OSC passthrough to cmux (run inside tmux inside cmux)
printf '\e]777;notify;tmux passthrough;OSC 777 reached cmux\a'
```
## Field notes from agent sessions (2026-11-10)
Scripts and agents that drive tmux hit failure modes that the interactive cheat sheet above does not cover.
- `tmux attach-session -t <name>` always fails with exit 1 when the caller already runs inside tmux. Use `tmux switch-client -t <name>` whenever `$TMUX` is set.
- `tmux set-environment` can serve as a sidecar metadata store attached to a named session. In one session-manager CLI, environment get and set calls made up 40 of 73 tmux API calls. The tmux server therefore becomes the effective session-state store.
- Zellij has no equivalent of `tmux set-environment` and `tmux show-environment` for per-session metadata. A design that stores state in tmux needs an external store when you port it to Zellij: a file, an exported environment variable, or a socket API.
- Non-interactive tmux tests have two traps. The `script` command alone dies at stdin EOF because the pane exits. Pipe `tail -f /dev/null` in front to keep stdin open. A nested attach also fails with the error `sessions should be nested with care`. Prove the client state with `tmux list-clients`.
- `set-titles-string "#H:#S.#I.#P #W"` puts host, session, window, and pane into the terminal title. A tool that binds to the terminal needs all four fields in the format. Session and window alone do not identify the pane.
### Observations
- [pattern] `attach-session` fails for any caller already inside tmux, so an agent terminal needs `switch-client` for every session jump. #tmux #agent-tooling (source: agent sessions, 2026-07)
- [pattern] `set-environment` turns the tmux server into a per-session key-value store, which removes the need for a separate state file. #tmux #state-management (source: agent sessions, 2026-03)
- [limitation] Zellij cannot replace the tmux environment store, so a port needs a file, an exported environment variable, or a socket API. #zellij #portability (source: agent sessions, 2026-03)
- [testing] Keep stdin open with `tail -f /dev/null` and verify clients with `tmux list-clients` when you test tmux without a human at the keyboard. #tmux #testing (source: agent sessions, 2026-08)
- [config] `set-titles-string "#H:#S.#I.#P #W"` carries host, session, window, and pane, which is the minimum a terminal-title-binding tool needs. #tmux #terminal (source: agent sessions, 2026-10)