Skip to content
fukuyoriPublic

About

A tmux-like terminal multiplexer for Windows, macOS and Linux written in Rust.

Topics

Resources

Stars

47 stars

Watchers

0 watching

Forks

Repository files navigation

wtmux

A tmux-like terminal multiplexer for Windows, macOS, and Linux, written in Rust.

License: MIT Platform Version

Download from the Microsoft Store

日本語版 README

4.1.0 Highlights

  • Colour queries are answered (OSC 10/11/12) — a program that asks the terminal for its foreground, background or cursor colour (Neovim uses OSC 11 to tell a dark theme from a light one) now gets the host terminal's colours instead of waiting for a timeout. wtmux learns them from the host at startup; with no host answer it stays silent rather than guess. Needs the bundled ConPTY on Windows (the inbox conhost swallows the query).

  • Half-drawn frames are not painted (mode 2026) — a program that wraps a frame in synchronized-output markers no longer shows the half-drawn frame in its pane; the pane keeps its last complete frame and is repainted when the frame ends (one second at most). Known limits are listed in CHANGELOG.md.

  • DECRQM answered — programs can ask which terminal modes wtmux supports and their state (CSI ? Ps $ p), and the answers reflect what wtmux really tracks.

  • Narrowing a pane no longer pushes the prompt down — rows cleared by writing spaces (how the OpenConsole host clears a line) are no longer counted as content when the pane is rewrapped; wtmux now follows the console's own reflow rules, including the cursor.

  • Bundled ConPTY used only as a pair (Windows) — a conpty.dll without OpenConsole.exe silently ran on the inbox conhost while wtmux --version claimed the bundled one; such a folder is now skipped with a warning, and the Inno Setup installer can no longer package the DLL alone.

  • From 4.0.3: Shifted keys no longer lag (Windows) — in terminals that send keys as text (e.g. the Windows port of Ghostty), +, *, Alt+Arrow and other Shift/Alt combinations are drawn immediately instead of only after the next key press.

  • From 4.0.2: Stable cursor after pane resizes (Windows) — creating and closing panes no longer leaves the cursor rows above the prompt. wtmux now resizes its screen with the same rules as the console buffer that Console-API programs go by. Enlarging a pane keeps the prompt where it is (as in Windows Terminal) instead of moving it to the bottom row.

  • Modern ConPTY, bundled — panes can run in conpty.dll + OpenConsole.exe (microsoft/terminal, MIT) instead of the inbox conhost.exe, which counted combining marks, VS16 emoji, ZWJ sequences and flags as extra cells and shifted positioned output. The Windows packages ship the pair; wtmux --version shows which implementation is active. See Troubleshooting.

  • Grapheme-cluster width model — 👨‍👩‍👧 is one two-cell cluster (was six cells), 🇯🇵 and ガ are one cluster each, and ❤️ is widened to two cells only when the host terminal draws it that way (measured at startup). Design notes in docs/design-width-model.md.

  • From 3.5.1: Emoji sequences survive copy mode — zero-width joiners, variation selectors and combining marks that follow a double-width character (e.g. 👨‍👩‍👧) are no longer dropped when copying.

  • From 3.5.0: Reliable background pane redraws — output produced in inactive windows remains pending until rendered, and switching windows always repaints the complete pane layout.

  • From 3.4.0: key cheat sheet — Prefix + ? opens a scrollable list of every effective binding (after your config.toml overrides) with a one-line description, grouped by Windows / Panes / Layouts / Scrollback / Tools.

  • From 3.4.0: automatic pane titles — panes are named after their working directory (wtmux for D:\home\source\rust\wtmux) and follow cd; duplicates in a window are numbered wtmux:2, wtmux:3. Manual pane renaming was removed, and cwd_prompt_hook is now on by default.

  • From 3.4.0: window reordering — Prefix + . then a digit moves the window (tmux move-window); swap-window is available too. Tab labels show the live position (1:main, 2:shell).

  • From 3.4.0: tmux layout keys — Prefix + Alt+1 … Alt+5 apply the five presets directly, and previous-layout / select-layout -n|-p cycle backwards and forwards.

  • From 3.0.0: configurable key bindings — every key in the prefix table can be reassigned or removed from config.toml the way tmux's bind-key does, and prefix-less bindings (tmux's bind-key -n) are supported through [bind_root]. See Custom Key Bindings. wtmux list-keys (alias lsk) prints the effective table.

  • From 2.3.4: more visible pane borders and titles — pane titles are drawn in the same color as their border, and the unfocused border color was brightened in all eight built-in themes.

  • From 2.3.0-2.3.3: agent workflow tools — the Prefix + m message composer (multi-line floating editor for sending messages to a pane, IME-friendly) and wtmux agents, a monitor CLI showing every pane's WORKING / BLOCKED / DONE / IDLE state with a Nerd Font spinner, also visible in the Prefix + g dashboard.

Features

  • tmux-compatible keybindings - Familiar Ctrl+B prefix commands by default, with configurable shortcuts
  • Multiple tabs (windows) - Create, switch, rename, and manage tabs
  • Split panes - Horizontal and vertical splits with resize support
  • Pane zoom - Toggle full-screen for any pane (v0.4.0: seamless transitions)
  • Layout presets - 5 layouts (even-horizontal, even-vertical, main-horizontal, main-vertical, tiled)
  • Copy mode - vim-like scrollback navigation and text selection
  • Search - Search through scrollback buffer with highlighting
  • Command history - Record and reuse commands with Ctrl+R by default
  • Color schemes - 8 built-in themes (default, solarized, monokai, nord, dracula, gruvbox, tokyo-night)
  • Configuration - TOML config file support
  • Cross-platform PTY backends - ConPTY on Windows, POSIX pty (openpty) on macOS / Linux
  • Multiple shells - cmd.exe, PowerShell, PowerShell 7, WSL on Windows; $SHELL (bash, zsh, fish, ...) on macOS / Linux
  • Encoding support - UTF-8 and Shift-JIS (CP932, Windows only)
  • Robust rendering - Thread-safe output with synchronized updates (v0.4.0)
  • Mouse passthrough - TUI apps receive mouse events (hold Shift for wtmux selection)
  • Kitty keyboard protocol - panes support the disambiguate escape codes and report event types enhancements (CSI u), so apps like neovim, helix or fish (in WSL/ssh) can distinguish Ctrl+I from Tab, Shift+Enter from Enter, and receive key-release events
  • win32-input-mode passthrough - Win32-API console apps in panes receive full key records (Shift+Enter, Ctrl+Enter, key releases) via DECSET 9001, just like under Windows Terminal
  • OSC 52 clipboard & focus events - apps in panes (including over ssh/WSL) can set the host clipboard, and receive CSI I/CSI O focus reports (DECSET 1004) on host-focus and pane-focus changes
  • Extended underlines - curly/double/dotted/dashed underline styles and underline colors (SGR 4:x / 58), so LSP diagnostics in nvim/helix render as intended
  • OSC 8 hyperlinks - links from apps in panes (ls --hyperlink, delta, starship, compiler diagnostics) stay Ctrl+clickable in the host terminal
  • Nerd Font / Powerline support - oh-my-posh, Starship, and Powerline prompts render correctly
  • Shell integration - OSC 133/633 for accurate command history with modern prompts

Screenshots

wtmux running Claude Code and OpenAI Codex in a three-pane layout

wtmux message composer broadcasting input to another pane

Requirements

  • Windows: Windows 10 version 1809 or later (ConPTY support required)
  • macOS / Linux: any modern terminal (iTerm2, Ghostty, WezTerm, kitty, GNOME Terminal, ...) — wtmux runs inside it via the standard POSIX pty
  • Rust 1.70 or later (for building from source)

Installation

Option 1: Download Release

Download from the Releases page:

Windows

  • Microsoft Store — Download wtmux
  • Installer (wtmux-x.x.x-windows-x64.exe) - Recommended for most users
  • Portable (wtmux-x.x.x-windows-x64.zip) - No installation required, just extract and run
  • MSI (wtmux-x.x.x-windows-x64.msi) - For enterprise deployment

macOS

  • Installer package (wtmux-x.x.x-macos-<arch>.pkg) - Signed & notarized; installs /usr/local/bin/wtmux

Linux

  • .deb / .rpm packages - Build with scripts/build-linux-packages.sh (see below)
  • Build from source (see Option 2)

Option 2: Build from Source

git clone https://github.com/fukuyori/wtmux.git
cd wtmux
cargo build --release

# Windows: copy to your preferred location
copy target\release\wtmux.exe C:\your\bin\path\

# macOS / Linux: copy to a directory on your PATH
cp target/release/wtmux /usr/local/bin/

Building Installers (Windows)

# Portable package (ZIP)
.\scripts\build-portable.ps1

# Using Inno Setup (recommended for end users)
# Download from: https://jrsoftware.org/isinfo.php
.\scripts\build-inno-installer.ps1

# Using WiX Toolset (for enterprise deployment)
# Download from: https://wixtoolset.org/releases/
# On WiX Toolset v7, the script accepts the OSMF EULA automatically before building.
.\scripts\build-installer.ps1

# MSIX package (for Windows 10/11)
# Requires Windows 10 SDK
.\scripts\build-msix.ps1              # Unsigned (requires Developer Mode)
.\scripts\build-msix.ps1 -Sign        # Self-signed (for testing)

# Regenerate icon assets after editing assets/wtmux-icon.svg
# The generated .ico is embedded into wtmux.exe and reused by the installers.
.\scripts\generate-icons.ps1

To ship the modern ConPTY with the packages, put conpty.dll and OpenConsole.exe (Microsoft-signed builds from microsoft/terminal, MIT) in vendor\conpty\ before running any of the scripts above; every package then installs them next to wtmux.exe together with LICENSE-ConPTY.txt. Without them the packages are built as before and wtmux uses the inbox conhost.exe. See vendor/conpty/README.md.

Building the macOS Installer

scripts/sign-and-notarize-macos.sh builds a signed & notarized .pkg from target/release/wtmux (requires Developer ID certificates and a notarytool keychain profile):

cargo build --release
./scripts/sign-and-notarize-macos.sh

Building the Linux Packages

scripts/build-linux-packages.sh builds .deb and .rpm packages from target/release/wtmux using cargo-deb and cargo-generate-rpm (package metadata lives in Cargo.toml):

cargo install cargo-deb cargo-generate-rpm  # one-time setup
./scripts/build-linux-packages.sh           # builds both
./scripts/build-linux-packages.sh --deb     # .deb only
./scripts/build-linux-packages.sh --rpm     # .rpm only

Output goes to installer/output/.

Usage

# Default: Multi-pane mode
wtmux

# With PowerShell 7 and UTF-8
wtmux -7 -u

# With WSL
wtmux -w

# Custom shell (e.g. zsh on macOS / Linux)
wtmux -s zsh

# Simple single-pane mode
wtmux -1

# Show help
wtmux --help

Command Line Options

Option Description
-1, --simple Simple single-pane mode
-c, --cmd Use Command Prompt (cmd.exe) (Windows only)
-p, --powershell Use Windows PowerShell (Windows only)
-7, --pwsh Use PowerShell 7 (pwsh.exe) (Windows only)
-w, --wsl Use WSL (Windows only)
-s, --shell <CMD> Custom shell command
--sjis Shift-JIS encoding (default: UTF-8) (Windows only)
-P, --cwd-prompt-hook <on|off> Set shell prompt hook cwd tracking (default: on)
--no-cwd-prompt-hook Disable shell prompt hook cwd tracking
-v, --version Show version
-h, --help Show help
list-keys (lsk) List the effective key bindings

The Windows-only options are hidden on macOS / Linux. There, the default shell is $SHELL (falling back to /bin/sh); use -s or the shell config key to override it.

Keybindings

Prefix commands use Ctrl+B by default (same as tmux), and the prefix key can be changed with prefix_key. The tables below show the default keybindings; every one of them can be reassigned or removed — see Custom Key Bindings.

Windows (Tabs)

Key Action
Ctrl+B, c Create new window
Click [+] in the tab bar Create new window
Ctrl+B, & Kill current window
Ctrl+B, n Next window
Ctrl+B, p Previous window
Ctrl+B, l Toggle last window
Ctrl+B, w Show the window selector
Ctrl+B, 0-9 Select window by number
Ctrl+B, , Rename window
Ctrl+B, . then 1-9 Move window to that position (tmux move-window)
Ctrl+B, ? Key cheat sheet (every effective binding with a short description; j/k/PgUp/PgDn scroll, q/Esc close)

The window selector shows every window with its pane count and tmux-style flags (* current, - last), plus a live preview of the selected window. Use Up/Down or j/k to move, 1-9 to jump to a window by number, Enter to switch, x to kill the selected item (confirm with y), and Esc or q to close the list. Windows expand into a tree: Right/l lists a window's panes as child rows, Left/h folds them; selecting a pane row previews it, and Enter switches to the window with that pane focused. The mouse works too: scroll to move the selection, click a row to switch to it, or click outside the popup to close it.

Panes

Key Action
Ctrl+B, " Split horizontally (top/bottom)
Ctrl+B, % Split vertically (left/right)
Ctrl+B, x Kill current pane
Ctrl+B, o Next pane
Ctrl+B, ; Previous pane
Ctrl+B, ←↑↓→ Move focus to pane in direction
Ctrl+B, Ctrl+←↑↓→ Resize pane
Ctrl+B, z Toggle pane zoom
Ctrl+B, Space Next layout preset
Ctrl+B, Alt+1 … Alt+5 Apply a layout preset directly (see the table below)
Ctrl+B, q Show pane numbers (then 0-9 to select)
Ctrl+B, { Swap with previous pane
Ctrl+B, } Swap with next pane

Layout presets (same names and keys as tmux):

Key Preset Arrangement
Alt+1 even-horizontal All panes side by side, equal width
Alt+2 even-vertical All panes stacked, equal height
Alt+3 main-horizontal One large pane on top, the rest in a row below
Alt+4 main-vertical One large pane on the left, the rest stacked on the right
Alt+5 tiled Grid

Space cycles forward through the presets; previous-layout (no default key, bindable) cycles backward.

Pane titles are automatic: each pane shows the last component of its working directory (e.g. wtmux for D:\home\source\rust\wtmux) and follows cd as you move around. Panes in the same window that share a directory name are numbered wtmux, wtmux:2, wtmux:3, …

Copy Mode

Key Action
Ctrl+B, [ Enter copy mode
Ctrl+B, / Enter search mode

In copy mode:

Key Action
h/j/k/l or arrows Move cursor
0 / $ Line start / end
g / G Top / bottom of buffer
Ctrl+U / Ctrl+D Half page up / down
Ctrl+B / Ctrl+F Full page up / down
Space or v Start/toggle selection
Enter or y Copy selection and exit
/ Search forward
? Search backward
n / N Next / previous match
q or Esc Exit copy mode

Other

Key Action
Ctrl+B, : Command prompt (tmux-style commands, see below)
Ctrl+B, t Theme selector
Esc in theme selector Cancel theme selector
Ctrl+B, m Message composer (multi-line editor, see below)
Ctrl+B, r Reset cursor shape
Ctrl+B, Shift+P Toggle output logging for the focused pane ([LOG])
Ctrl+B, b Send Ctrl+B to application
Esc Cancel prefix mode

Message Composer

Ctrl+B, m opens a floating multi-line editor for composing a message and sending it to the focused pane's application (IME-friendly; handy for AI agents such as Claude Code). Also available as m in the agent dashboard and as the compose-message command.

Key Action
Ctrl+Enter (or Ctrl+S) Send the message
Enter Insert a newline
Esc Close (the unsent draft can be recalled with Ctrl+P)
Shift+Arrows / Shift+Home / Shift+End Extend the selection
Ctrl+A Select all
Ctrl+C / Ctrl+X Copy / cut the selection
Ctrl+V Paste from the clipboard
Ctrl+Z / Ctrl+Y Undo / redo
Ctrl+Home / Ctrl+End Jump to the start / end of the message
Ctrl+U Clear the message
Ctrl+P / Ctrl+N Recall older / newer sent messages
Tab Insert 4 spaces

The mouse works inside the composer too: click places the cursor, drag selects text, the wheel scrolls, and dragging the right or bottom border (or the corner) resizes the popup — the chosen size is kept until wtmux exits. The footer shows a line/character counter.

Command Prompt

Ctrl+B, : opens a tmux-style command prompt on the status bar. Supported commands (tmux abbreviations in parentheses):

Command Action
split-window [-h] (splitw) Split pane; -h = left/right
new-window (neww) Create window
kill-pane (killp) / kill-window (killw) Kill pane / window
next-window / previous-window / last-window (next / prev / last) Switch window
select-window -t <n> (selectw) Select window by number
rename-window <name> (renamew) Rename window
move-window -t <n> (movew) Move the current window to position n (others shift)
swap-window -t <n> (swapw) Swap the current window with the one at position n
select-layout <even-horizontal|even-vertical|main-horizontal|main-vertical|tiled> (selectl) Apply layout preset
select-layout -n / -p, next-layout (nextl), previous-layout (prevl) Next / previous layout preset
list-keys (lsk) Open the key cheat sheet
resize-pane -Z Toggle pane zoom
set synchronize-panes [on|off] Input broadcast
pipe-pane Toggle pane output logging
display-popup [command] (popup) Open a floating popup pane

Results and errors appear as a transient message on the status bar.

Popup (display-popup)

:display-popup [command] — or wtmux display-popup [command...] from any shell — opens a centered floating pane (60% of the terminal) running the command, or your default shell. All input goes to the popup; it closes when the command exits. Ctrl+B, x force-closes a stuck popup. Note: the command is spawned directly, so shell built-ins need an explicit shell (e.g. display-popup cmd /c dir on Windows, display-popup sh -c "ls | head" on macOS / Linux).

Command History

wtmux includes its own command history feature, separate from your shell's built-in history. It records the commands you enter, eliminating the need to retype complex commands repeatedly. The history selector shortcut defaults to Ctrl+R and can be changed with keybindings.history_selector.

Key Action
Ctrl+R Show history search
Enter Execute selected command (replace current input)
Shift+Enter Append with && (run if previous succeeds)
Ctrl+Enter Append with & (background/parallel)

For more details, see: https://qiita.com/spumoni/items/7d43ed7e579d99cfda3e

Shell Integration (Recommended)

wtmux can detect commands precisely — regardless of prompt appearance — when the shell emits OSC 133 / OSC 633 markers. This removes the need for prompt-pattern heuristics and makes command history accurate even with fancy prompts (oh-my-posh, Starship, multi-line prompts, etc.).

PowerShell (automatic)

Add one line to your PowerShell profile ($PROFILE):

$env:TERM_PROGRAM = "vscode"

PowerShell 7 and Windows PowerShell 5 automatically emit OSC 633 markers when TERM_PROGRAM is set to "vscode".

bash / zsh (macOS / Linux / WSL)

Add to ~/.bashrc or ~/.zshrc:

# OSC 133 shell integration for wtmux
__wtmux_precmd() { printf '\e]133;A\e\'; }
__wtmux_preexec() { printf '\e]133;C\e\'; }
PS1='\[\e]133;B\e\\]'"$PS1"
# bash: use PROMPT_COMMAND
PROMPT_COMMAND="__wtmux_precmd;${PROMPT_COMMAND}"
# zsh: use precmd/preexec hooks instead

oh-my-posh

Enable the built-in shell integration in your oh-my-posh config:

{ "osc99": true, "osc7": true, "osc133": true }

Fallback (cmd.exe)

cmd.exe does not support OSC sequences. wtmux automatically falls back to keystroke tracking — intercepting every character before it reaches the shell — which gives accurate results without any shell configuration.


Configuration

wtmux reads configuration from config.toml in its config directory:

OS Location
Windows %LOCALAPPDATA%\wtmux\config.toml (e.g. C:\Users\you\AppData\Local\wtmux\config.toml)
macOS / Linux $XDG_CONFIG_HOME/wtmux/config.toml, or ~/.config/wtmux/config.toml when XDG_CONFIG_HOME is unset

If neither location can be determined, ~/.wtmux/config.toml is used as a fallback. Command history, pane output logs, and VT traces are stored in the same directory.

# Default shell (optional)
# Windows: "cmd", "powershell", "pwsh", "wsl", or full path
# macOS / Linux: command name or full path (default: $SHELL, then /bin/sh)
# shell = "pwsh.exe"

# Codepage for encoding (optional, Windows only)
# codepage = 65001  # UTF-8
# codepage = 932    # Shift-JIS

# Prefix key (default: "C-b" for Ctrl+B)
# prefix_key = "C-a"  # Change to Ctrl+A

# Inject a prompt hook into cmd.exe / PowerShell to publish pane cwd changes.
# Enabled by default: pane titles follow the working directory. Set to false
# if the hook interferes with a custom prompt.
# cwd_prompt_hook = true

# Remove built-in bindings (tmux: unbind-key).
# Being an array, this must appear before the [sections] below.
# unbind = ["d", "P"]

# Color scheme
# Available: default, solarized-dark, solarized-light, monokai, nord, dracula, gruvbox-dark, tokyo-night
color_scheme = "tokyo-night"

# Legacy global keybindings (prefer [bind_root] below)
[keybindings]
# history_selector = "C-r"      # Also accepts "Ctrl+R"
# scrollback_up = "S-PageUp"
# scrollback_down = "S-PageDown"
# scrollback_top = "S-Home"
# scrollback_bottom = "S-End"
# selection_left = "S-Left"
# selection_right = "S-Right"
# selection_up = "S-Up"
# selection_down = "S-Down"
# copy_selection = "C-S-c"      # Also accepts "Ctrl+Shift+C"

# Bindings pressed after the prefix key (tmux: bind-key)
[bind]
# "M-p" = "previous-layout"
# "M-s" = "swap-window"         # then a digit picks the other window
# "C-o" = "swap-pane -D"
# "z"   = ""                    # empty string unbinds

# Bindings pressed without the prefix (tmux: bind-key -n) — the recommended
# place for scrollback / selection / copy keys
[bind_root]
# "S-PageUp" = "scroll-up"
# "C-S-c"    = "copy-selection"
# "C-M-t"    = "select-layout tiled"

# Tab bar settings
[tab_bar]
visible = true

# Status bar settings
[status_bar]
visible = true
show_time = true

# Pane border settings
[pane]
border_style = "single"  # single, double, rounded, none

# Cursor settings
[cursor]
shape = "block"          # block, underline, bar
blink = true

# Scrollback buffer
[scrollback]
lines = 10000

# Agent state hooks (see "AI Agent Integration" below)
[hooks]
# on_agent_blocked = "powershell -NoProfile -Command \"...notify...\""
# on_agent_done = ""

The [keybindings] section is the legacy way to remap these non-prefix shortcuts: the history selector (Ctrl+R by default), scrollback navigation, keyboard selection, and copy-selection. It keeps working, but the same functions are also available as bindable commands (scroll-up, extend-selection, copy-selection, history-selector, ...) — prefer binding them in [bind_root], which allows any key, supports unbinding, and takes precedence over [keybindings].

Custom Key Bindings

[bind], [bind_root] and unbind assign commands to keys the way tmux's bind-key does. Every built-in binding can be overridden or removed.

unbind = ["d"]                 # drop the default Ctrl+B, d (array goes before any [section])

[bind]                         # pressed after the prefix key
"M-p" = "previous-layout"
"M-s" = "swap-window"
"|"   = "split-window -h"
"C-o" = "swap-pane -D"
"z"   = ""                     # empty string unbinds, same as `unbind`

[bind_root]                    # pressed without the prefix
"C-M-Left"  = "select-pane -L"
"C-M-Right" = "select-pane -R"

Key names: a single character (c, %, 4), or one of Space, Enter, Esc, Tab, Backspace, Delete, Insert, Up, Down, Left, Right, Home, End, PageUp, PageDown, F1-F12. Prefix with C- (Ctrl), M- (Alt) or S- (Shift); the Ctrl+ / Alt+ / Shift+ spellings also work. Character case is significant, so P and p are different keys.

Commands:

Group Commands
Windows new-window / kill-window / next-window / previous-window / last-window / select-window -t <n> / move-window [-t <n>] / swap-window [-t <n>] / rename-window / choose-window
Panes split-window [-h] / kill-pane / next-pane / previous-pane / select-pane -L|-R|-U|-D / swap-pane -U|-D / display-panes
Sizing resize-pane -Z (zoom) / resize-pane -L|-R|-U|-D / resize-pane + / resize-pane -
Layout next-layout / previous-layout / select-layout <even-horizontal|even-vertical|main-horizontal|main-vertical|tiled> / select-layout -n|-p / M-1…M-5
Modes copy-mode / search / command-prompt / choose-theme / agent-dashboard / compose-message
Terminal scroll-up [n] / scroll-down [n] / scroll-top / scroll-bottom / extend-selection -L|-R|-U|-D / copy-selection / history-selector
Other set synchronize-panes / pipe-pane / paste-buffer / send-prefix / detach-client / next-attention / reset-cursor / none

Notes:

  • [bind_root] keys are intercepted before reaching the shell and take precedence over [keybindings]
  • A malformed entry is skipped on its own, with the reason printed to stderr at startup
  • wtmux list-keys (alias lsk) prints the effective table in the same syntax [bind] accepts
$ wtmux list-keys
bind      Space        next-layout
bind      Alt+4        select-layout main-vertical
bind      c            new-window
bind_root Ctrl+Alt+Left select-pane -L

Font Settings

[font]
# Font family (leave empty to inherit from host terminal)
# family = "CaskaydiaCove Nerd Font"

# Font size in points (0 = inherit)
# size = 12

# Suppress SGR 1 (Bold) — set to true if Powerline/Nerd Font glyphs
# look misaligned or replaced by boxes.
# See "Troubleshooting" section below.
# suppress_bold = false

Available Color Schemes

  • default - Default terminal colors
  • solarized - Solarized Dark
  • monokai - Monokai Pro
  • nord - Nord
  • dracula - Dracula
  • gruvbox - Gruvbox Dark
  • tokyo-night - Tokyo Night

AI Agent Integration

wtmux monitors every pane and classifies it herdr-style as WORKING / BLOCKED / DONE / IDLE (Prefix + g opens the agent dashboard; WORKING panes animate a Nerd Font circle-slice spinner there). wtmux agents prints the same list from any pane and refreshes four times a second (Ctrl+C quits; --once for a single snapshot) — run it in a spare pane or a display-popup for an always-on monitor. Three more features build on this for running AI coding agents in panes:

Agent state hooks

Run a command whenever a pane's state changes — e.g. raise a Windows toast the moment a background agent blocks on a permission prompt:

# Windows: %LOCALAPPDATA%\wtmux\config.toml
# macOS / Linux: ~/.config/wtmux/config.toml
[hooks]
on_agent_blocked = 'powershell -NoProfile -Command "New-BurntToastNotification -Text \"wtmux\", \"$env:WTMUX_HOOK_TITLE is waiting for input\""'
# macOS notification instead:
# on_agent_blocked = 'osascript -e "display notification \"$WTMUX_HOOK_TITLE is waiting for input\" with title \"wtmux\""'
# Linux notification instead:
# on_agent_blocked = 'notify-send wtmux "$WTMUX_HOOK_TITLE is waiting for input"'
# on_agent_working / on_agent_done / on_agent_idle are also available

Hooks run detached (cmd /C on Windows, sh -c elsewhere) and receive the context via environment variables: WTMUX_HOOK_STATE, WTMUX_HOOK_PREV_STATE, WTMUX_HOOK_PANE (<window>.<pane>), WTMUX_HOOK_WINDOW, WTMUX_HOOK_TITLE (the pane title, i.e. its working directory name).

Ground-truth state reporting (wtmux report-state)

By default pane states are inferred from output heuristics. Tools running inside a pane can instead report the state directly:

wtmux report-state blocked     # the calling pane, via WTMUX_PID / WTMUX_PANE
wtmux report-state -t 1.2 done # explicit <window>.<pane> target

This pairs naturally with agent CLIs that have their own hook systems, and is the recommended setup for Claude Code: its waiting-for-input UI keeps redrawing (spinner, status line), so the output-quiet heuristics tend to leave the pane stuck on WORKING. With hooks the pane flips to BLOCKED the moment Claude Code asks for permission or input. Add to ~/.claude/settings.json:

{
  "hooks": {
    "UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "wtmux report-state working 2>/dev/null || true" }] }],
    "Notification":     [{ "hooks": [{ "type": "command", "command": "wtmux report-state blocked 2>/dev/null || true" }] }],
    "Stop":             [{ "hooks": [{ "type": "command", "command": "wtmux report-state done 2>/dev/null || true" }] }]
  }
}

UserPromptSubmit marks the pane WORKING when a prompt is sent, Notification marks it BLOCKED (waiting for permission / input), and Stop marks it DONE. The 2>/dev/null || true keeps the hooks silent when Claude Code runs outside wtmux.

Reported states override the heuristics (until new output arrives), update the dashboard / status bar / attention flags, and fire [hooks] commands.

Scripting a running instance (send-keys / capture-pane)

External tools — orchestrators, scripts, or another AI agent — can drive a running wtmux:

# Type a command into pane 2 of window 1 and run it
wtmux send-keys -t 1.2 "cargo test" Enter

# Read back what that pane shows (visible screen; -S - adds full scrollback)
wtmux capture-pane -p -t 1.2
wtmux capture-pane -p -t 1.2 -S -

# Open a popup in the running instance
wtmux display-popup "cmd /c dir"

send-keys understands tmux key names (Enter, Escape, Tab, Space, BSpace, Up/Down/Left/Right, Home, End, PageUp, PageDown, C-x, M-x); anything else is sent literally. Without -t, the calling pane (via WTMUX_PANE) or the focused pane is targeted. With a single wtmux running, the instance is found automatically; otherwise pass --pid <pid> (see wtmux list-clients). This pairs with report-state to build claude-squad-style agent orchestration on top of wtmux.

Pane output logging (tmux pipe-pane analog)

Prefix + Shift+P toggles logging of the focused pane's raw output stream to logs/wtmux-<pid>-<window>.<pane>-<epoch>.log under the config directory (%LOCALAPPDATA%\wtmux on Windows, ~/.config/wtmux on macOS / Linux). The status bar shows [LOG] while logging is active. Useful for auditing or replaying an agent's session; the log contains the exact bytes including escape sequences (strip them with e.g. sed -r 's/\x1b\[[0-9;]*[a-zA-Z]//g').

Detecting wtmux from Shell

wtmux sets environment variables that child processes can detect:

REM cmd.exe
if defined WTMUX echo Running in wtmux
# PowerShell
if ($env:WTMUX) { "Running in wtmux" }
# bash / zsh (macOS / Linux / WSL)
[ -n "$WTMUX" ] && echo "Running in wtmux"
Variable Meaning
WTMUX 1 when running inside wtmux
WTMUX_VERSION wtmux version
WTMUX_PID Process id of the wtmux instance (target for wtmux report-state)
WTMUX_PANE <window>.<pane> id of the pane the process runs in

Mouse Support

wtmux provides comprehensive mouse support:

Text Selection and Copy

You can select text with the mouse and copy it to the clipboard:

  1. Click and drag to select text
  2. Release the mouse button - selected text is automatically copied to clipboard
  3. Paste with Ctrl+V or right-click → Paste from the context menu

This works the same as standard terminal text selection.

Split Resize

Drag a pane split border with the left mouse button to resize adjacent panes. Boundary dragging is handled by wtmux even when the focused TUI application has enabled mouse tracking.

Mouse Passthrough for TUI Applications

When running TUI applications that use mouse input (e.g., htop, mc, vim with mouse, or apps using crossterm's EnableMouseCapture), mouse events are automatically passed through to the application.

How it works:

  • wtmux detects when a child application enables mouse tracking (DECSET 1000/1002/1003)
  • Mouse events within the pane are forwarded to the application
  • Supports SGR extended mouse mode (1006) for terminals larger than 223 columns/rows
  • Tab bar and status bar clicks still work as expected

Text selection in TUI apps:

  • Hold Shift while clicking/dragging to use wtmux's text selection instead of passing events to the child application
  • This is useful when you need to copy text from a mouse-enabled TUI app

Mouse Actions Summary

Action In normal shell In TUI app (mouse-enabled)
Left drag Select text App receives event
Shift + Left drag Select text Select text
Left drag on split border Resize panes Resize panes
Left click on tab bar Switch tab Switch tab
Left click [+] in tab bar Create new tab Create new tab
Right click Context menu (Paste, Zoom, Split, etc.) Context menu
Right click on tab bar Rename that window Rename that window
Scroll wheel Scroll buffer App receives event

While the message composer (Ctrl+B, m) is open, the mouse acts on the composer itself: click to place the cursor, drag to select, wheel to move through the text, and drag the right/bottom border to resize the popup.

Comparison with tmux

Feature tmux wtmux
Platform Unix/Linux/macOS Windows / macOS / Linux
Backend PTY ConPTY (Windows) / POSIX pty (macOS, Linux)
Windows/Panes ✓ ✓
Keybindings ✓ ✓ (compatible)
Configurable bindings ✓ (bind-key) ✓ ([bind] / [bind_root])
Copy mode ✓ ✓
Search ✓ ✓
Layout presets ✓ ✓
Config file ✓ ✓
Color schemes ✓ ✓
Mouse support ✓ ✓
Detach/Attach ✓ Planned
Session sharing ✓ Planned
Scripting ✓ ✓ (send-keys / capture-pane)

Project Structure

wtmux/
├── Cargo.toml
├── README.md
├── README.ja.md
├── LICENSE
├── CHANGELOG.md
├── config.example.toml
├── assets/
│   ├── wtmux-icon.svg       # Icon source artwork
│   └── generated/           # Generated .ico / preview PNG
├── installer/
│   ├── wtmux.iss          # Inno Setup script
│   ├── wtmux.wxs          # WiX script
│   ├── msix/Assets/       # MSIX icon assets
│   └── license.rtf
├── scripts/
│   ├── build-portable.ps1
│   ├── build-installer.ps1
│   ├── build-inno-installer.ps1
│   ├── build-msix.ps1
│   ├── generate-icons.ps1
│   ├── sign-and-notarize-macos.sh  # macOS signed/notarized .pkg build
│   └── build-linux-packages.sh     # Linux .deb / .rpm build
└── src/
    ├── main.rs            # Entry point
    ├── config.rs          # Configuration
    ├── copymode.rs        # Copy mode
    ├── history.rs         # Command history
    ├── core/
    │   ├── pty/
    │   │   ├── conpty.rs  # Windows ConPTY backend
    │   │   └── unix.rs    # POSIX pty backend (macOS / Linux)
    │   ├── session.rs     # Session management
    │   └── term/
    │       ├── state.rs   # Terminal state
    │       └── parser.rs  # VT parser
    ├── ui/
    │   ├── keymapper.rs   # Key mapping
    │   ├── renderer.rs    # Screen rendering
    │   └── wm_renderer.rs # Multi-pane rendering
    └── wm/
        ├── manager.rs     # Window manager
        ├── tab.rs         # Tab management
        ├── pane.rs        # Pane management
        └── layout.rs      # Layout calculation

Troubleshooting

Powerline / Nerd Font glyphs look wrong or misaligned

wtmux correctly renders prompts from oh-my-posh, Starship, and other Powerline-based themes. If glyphs still appear broken, follow these steps:

Step 1 — Install the full Nerd Font family

Download Regular, Bold, Italic, and BoldItalic variants from nerdfonts.com and install all four. Windows Terminal's font fallback will use a non-Nerd-Font bold face if the Bold variant is missing, breaking PUA glyphs.

Step 2 — Set the font in your host terminal

Windows Terminal example (macOS / Linux: set it in iTerm2, Ghostty, etc.):

"fontFace": "CaskaydiaCove Nerd Font"

Step 3 — If the problem persists, enable suppress_bold

# Windows: %LOCALAPPDATA%\wtmux\config.toml
# macOS / Linux: ~/.config/wtmux/config.toml
[font]
suppress_bold = true

This tells wtmux never to send SGR 1 (Bold) to the host terminal, keeping all text in the Regular face that contains the PUA glyphs.

Collecting a VT trace for bug reports

If a rendering problem is hard to reproduce, run wtmux with --vt-trace to record every raw byte from the PTY:

wtmux --vt-trace

The trace is written to vt_trace.log in the config directory (%LOCALAPPDATA%\wtmux on Windows, ~/.config/wtmux on macOS / Linux) in hex + UTF-8 format. Attach this file when filing a bug report.

Emoji, combining marks or flags shift text by a column (Windows)

Each wtmux pane runs inside a ConPTY. The inbox implementation reached through kernel32 (conhost.exe) counts text width per code point, so a combining mark, a VS16 emoji such as ❤️, a ZWJ sequence such as 👨‍👩‍👧 or a flag occupies more cells there than in any modern terminal, and positioned output after such text lands one or more columns off. Windows Terminal and WezTerm avoid this by shipping their own ConPTY (conpty.dll + OpenConsole.exe from microsoft/terminal, MIT), and wtmux uses the same pair.

The release packages (ZIP, Inno Setup, MSI, MSIX) install the two files next to wtmux.exe, so nothing needs to be done there. Check with:

wtmux --version
# ConPTY: bundled (C:\...\conpty.dll)      <- modern ConPTY in use
# ConPTY: system (kernel32 / conhost)       <- inbox conhost, no DLL found

If you built wtmux from source, copy conpty.dll and OpenConsole.exe next to wtmux.exe (both files must be in the same directory) or point WTMUX_CONPTY_DIR at a directory that contains them, for example the WezTerm install directory. Where to get the files is described in vendor/conpty/README.md. WTMUX_CONPTY=system forces the inbox implementation; when no DLL is found, wtmux silently falls back to it.

Known Limitations

  • Shell shortcuts (-c/-p/-7/-w) and Shift-JIS encoding are Windows-only
  • No detach/attach support yet (planned for future release)
  • No session sharing yet

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • tmux - The inspiration for this project
  • Windows ConPTY team for the pseudo-terminal API
  • microsoft/terminal - conpty.dll and OpenConsole.exe bundled with the Windows packages (MIT)
  • crossterm - Cross-platform terminal manipulation
  • unicode-width - Unicode character width calculation

About

A tmux-like terminal multiplexer for Windows, macOS and Linux written in Rust.

Topics

Resources

Stars

47 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages