Skip to content

Instantly share code, notes, and snippets.

@sverrejoh
Last active March 9, 2026 08:55
Show Gist options
  • Select an option

  • Save sverrejoh/42742b2522c9ce07900ac7e5a7a5f1ac to your computer and use it in GitHub Desktop.

Select an option

Save sverrejoh/42742b2522c9ce07900ac7e5a7a5f1ac to your computer and use it in GitHub Desktop.
tmux.conf — Tokyo Night theme, top status bar, Emacs copy mode, fzf session switcher

tmux.conf

My tmux config. Tokyo Night color scheme, top status bar with system stats, Emacs-style copy mode, and fzf session switching.

tmux status bar preview

Prerequisites

  • tmux 3.3+
  • TPM (Tmux Plugin Manager)
  • fzf (for session switcher popup)
  • A Nerd Font (for status bar icons)

Install

# Install TPM
git clone https://github.com/tmux-plugins/tpm ~/.config/tmux/plugins/tpm

# Copy config and scripts
mkdir -p ~/.config/tmux
cp tmux.conf ~/.config/tmux/tmux.conf
cp *.sh ~/.config/tmux/
chmod +x ~/.config/tmux/*.sh

# Start tmux and install plugins
tmux
# Press C-s I (capital I) to install plugins via TPM

Features

  • Tokyo Night color scheme with rounded pill-shaped status tabs
  • Rainbow window tabs — each window index gets a unique color
  • Top status bar with CPU, RAM, disk usage, hostname, and clock
  • fzf session/window tree (C-s s) — expand/collapse sessions, fuzzy filter, Alt+0-9/a-z jump keys
  • Emacs copy mode with C-space to select, Enter to copy
  • Mouse toggle (C-s M) for text selection on iPad/mobile

Helper scripts

All scripts go in ~/.config/tmux/ and need chmod +x:

Script Purpose
session-tree.sh fzf session/window tree picker
mouse-indicator.sh Shows ON/OFF mouse mode in status bar
ram-usage.sh Displays RAM usage (used/total)
disk-usage.sh Displays disk usage for root partition
session-prev.sh Cycle to previous session by creation order
session-next.sh Cycle to next session by creation order
toggle-status.sh Toggle between full and minimal status bar

The tmux-cpu plugin handles CPU percentage automatically.

Key bindings

Key Action
C-s Prefix (replaces default C-b)
C-s C-s Send literal C-s to terminal
C-s r Reload config
C-s h/j/k/l Navigate panes (vim-style)
C-s | Split horizontally
C-s - Split vertically
C-s n/p Next/previous window (repeatable)
C-s (/) Previous/next session (repeatable)
C-s s fzf session/window tree picker
C-s S Built-in choose-tree
C-s B Toggle status bar
C-s M Toggle mouse mode
C-s [ Enter copy mode (Emacs keys)
#!/bin/bash
# Get disk space for root: used/total in TB
df -BG / | awk 'NR==2 {gsub("G","",$2); gsub("G","",$3); printf "%.1fTB/%.1fTB", $3/1024, $2/1024}'
#!/bin/sh
# Called by tmux status-left via #() - outputs mouse state icon
mouse=$(tmux show -gv mouse)
if [ "$mouse" = "on" ]; then
printf '\U000f037d'
else
printf '\U000f037e'
fi
#!/bin/bash
# Get memory in GB: used/total
read -r total used <<< $(free -g | awk '/^Mem:/ {print $2, $3}')
printf "%dGB/%dGB" "$used" "$total"
#!/bin/bash
sessions=($(tmux list-sessions -F '#{session_created}:#{session_name}' | sort -n | cut -d: -f2))
current=$(tmux display-message -p '#{session_name}')
count=${#sessions[@]}
for i in "${!sessions[@]}"; do
if [[ "${sessions[$i]}" == "$current" ]]; then
next=$(( (i + 1) % count ))
tmux switch-client -t "${sessions[$next]}"
exit
fi
done
#!/bin/bash
sessions=($(tmux list-sessions -F '#{session_created}:#{session_name}' | sort -n | cut -d: -f2))
current=$(tmux display-message -p '#{session_name}')
count=${#sessions[@]}
for i in "${!sessions[@]}"; do
if [[ "${sessions[$i]}" == "$current" ]]; then
prev=$(( (i - 1 + count) % count ))
tmux switch-client -t "${sessions[$prev]}"
exit
fi
done
#!/usr/bin/env bash
# fzf-based session/window tree with expand/collapse
# Tab: expand/collapse, Enter: switch, Alt+0-9/a-z: jump to item
STATE="/tmp/tmux-tree-state"
SELF="$HOME/.config/tmux/session-tree.sh"
IC=$'\uf07b'
IE=$'\uf07c'
LABELS=(0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z)
JUMPS='alt-0:pos(1)+accept,alt-1:pos(2)+accept,alt-2:pos(3)+accept,alt-3:pos(4)+accept,alt-4:pos(5)+accept,alt-5:pos(6)+accept,alt-6:pos(7)+accept,alt-7:pos(8)+accept,alt-8:pos(9)+accept,alt-9:pos(10)+accept,alt-a:pos(11)+accept,alt-b:pos(12)+accept,alt-c:pos(13)+accept,alt-d:pos(14)+accept,alt-e:pos(15)+accept,alt-f:pos(16)+accept,alt-g:pos(17)+accept,alt-h:pos(18)+accept,alt-i:pos(19)+accept,alt-j:pos(20)+accept,alt-k:pos(21)+accept,alt-l:pos(22)+accept,alt-m:pos(23)+accept,alt-n:pos(24)+accept,alt-o:pos(25)+accept,alt-p:pos(26)+accept,alt-q:pos(27)+accept,alt-r:pos(28)+accept,alt-s:pos(29)+accept,alt-t:pos(30)+accept,alt-u:pos(31)+accept,alt-v:pos(32)+accept,alt-w:pos(33)+accept,alt-x:pos(34)+accept,alt-y:pos(35)+accept,alt-z:pos(36)+accept'
gen_list() {
local query="$1" cur="$2" idx=0
local exp=""
[[ -s "$STATE" ]] && exp=$'\n'$(<"$STATE")$'\n'
local prev="" show=false is_exp=false
while IFS='|' read -r _ sname wcount widx wname wflag; do
if [[ "$sname" != "$prev" ]]; then
prev="$sname"
show=true
if [[ -n "$query" ]]; then
local lower="${sname,,}"
for word in $query; do
[[ "$lower" == *"${word,,}"* ]] || { show=false; break; }
done
fi
is_exp=false
[[ "$exp" == *$'\n'"$sname"$'\n'* ]] && is_exp=true
if $show; then
local icon lbl="${LABELS[$idx]:- }" style='' rstyle='' star=''
$is_exp && icon="$IE" || icon="$IC"
if [[ "$sname" = "$cur" ]]; then
style=$'\033[1;34m' rstyle=$'\033[0m' star=' *'
fi
printf '%s\t\033[2m%s\033[0m %s%s %s (%d windows)%s%s\n' \
"$sname" "$lbl" "$style" "$icon" "$sname" "$wcount" "$star" "$rstyle"
idx=$((idx + 1))
fi
fi
if $show && $is_exp; then
printf '%s:%s\t\033[2m%s\033[0m %s: %s %s\n' \
"$sname" "$widx" "${LABELS[$idx]:- }" "$widx" "$wname" "$wflag"
idx=$((idx + 1))
fi
done < <(tmux list-windows -a \
-F '#{session_id}|#S|#{session_windows}|#{window_index}|#{window_name}|#{?window_active,*,}' \
| sort -t'$' -k2 -n)
}
case "${1:-}" in
toggle)
session="${2%%:*}"
if [[ -s "$STATE" ]]; then
found=false new=""
while IFS= read -r line; do
if [[ "$line" = "$session" ]]; then
found=true
else
new+="$line"$'\n'
fi
done < "$STATE"
if $found; then
printf '%s' "$new" > "$STATE"
else
echo "$session" >> "$STATE"
fi
else
echo "$session" > "$STATE"
fi
gen_list "$3" "$4"
;;
list)
gen_list "$2" "$3"
;;
*)
current=$(tmux display-message -p '#S')
: > "$STATE"
current_pos=$(tmux list-sessions -F '#{session_id}|#S' | \
sort -t'$' -k2 -n | \
awk -F'|' -v cur="$current" '{if ($2 == cur) {print NR; exit}}')
result=$(gen_list "" "$current" | fzf --reverse --ansi --disabled \
--cycle --no-separator --no-scrollbar \
--delimiter=$'\t' --with-nth=2 \
--header='tab: expand/collapse | alt+0-9/a-z: jump' \
--bind "load:pos(${current_pos:-1})+unbind(load)" \
--bind "change:reload($SELF list {q} $current)" \
--bind "tab:reload($SELF toggle {1} {q} $current)" \
--bind "$JUMPS")
[[ -n "$result" ]] && tmux switch-client -t "${result%%$'\t'*}"
rm -f "$STATE"
;;
esac
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
unbind r
bind r source-file ~/.config/tmux/tmux.conf
set -g prefix C-s
set -g prefix2 None
unbind C-b
set -g mouse on
set -gq allow-passthrough on
set -g bell-action any
set -g monitor-bell on
set -g default-terminal "tmux-256color"
set -as terminal-features ',*:RGB'
set -g history-limit 100000
set -sg escape-time 0
set -g focus-events on
set -g set-clipboard on
set -g display-time 2000
bind-key h select-pane -L
bind-key j select-pane -D
bind-key k select-pane -U
bind-key l select-pane -R
# Easier splits (preserves working directory)
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
set-option -g status-position top
set -g base-index 0
setw -g pane-base-index 0
set -g renumber-windows on
# Emacs-style copy mode
set -g mode-keys emacs
bind -T copy-mode Enter send -X copy-selection-and-cancel
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-sensible'
set -g @plugin 'omerxx/tmux-sessionx'
set -g @plugin 'tmux-plugins/tmux-cpu'
# Minimal theme
set -g status-style bg='#1a1b26',fg='#565f89'
set -g status-interval 2
set -g status-left-length 50
set -g status-right-length 400
# Status left/right
set -g status-left '#[fg=#1a1b26,bg=#7aa2f7,bold]  #S #[fg=#7aa2f7,bg=default,nobold] #(~/.config/tmux/mouse-indicator.sh) '
set -g status-right '#[fg=#9ece6a]󰻠 #{cpu_percentage} #[fg=#bb9af7]󰘚 #(~/.config/tmux/ram-usage.sh) #[fg=#ff9e64]󰋊 #(~/.config/tmux/disk-usage.sh) #[fg=#c0caf5]󰌢 #H #[fg=#7aa2f7]#[fg=#1a1b26,bg=#7aa2f7,bold] 󰥔 %H:%M '
# Window list
set -g window-status-format '#[fg=#3b4261]#[bg=default]#[fg=#565f89]#[bg=#3b4261]#{?#{==:#I,0},󰼎,}#{?#{==:#I,1},󰼏,}#{?#{==:#I,2},󰼐,}#{?#{==:#I,3},󰼑,}#{?#{==:#I,4},󰼒,}#{?#{==:#I,5},󰼓,}#{?#{==:#I,6},󰼔,}#{?#{==:#I,7},󰼕,}#{?#{==:#I,8},󰼖,}#{?#{==:#I,9},󰼗,} #W#[fg=#3b4261]#[bg=default]'
set -g window-status-current-format '#[fg=#7aa2f7]#{?#{==:#I,0},#[fg=#7aa2f7],}#{?#{==:#I,1},#[fg=#9ece6a],}#{?#{==:#I,2},#[fg=#bb9af7],}#{?#{==:#I,3},#[fg=#ff9e64],}#{?#{==:#I,4},#[fg=#73daca],}#{?#{==:#I,5},#[fg=#f7768e],}#{?#{==:#I,6},#[fg=#2ac3de],}#{?#{==:#I,7},#[fg=#e0af68],}#{?#{==:#I,8},#[fg=#a9b1d6],}#{?#{==:#I,9},#[fg=#d4bfff],}#[bg=default]#[fg=#1a1b26]#[bg=#7aa2f7]#{?#{==:#I,0},#[bg=#7aa2f7],}#{?#{==:#I,1},#[bg=#9ece6a],}#{?#{==:#I,2},#[bg=#bb9af7],}#{?#{==:#I,3},#[bg=#ff9e64],}#{?#{==:#I,4},#[bg=#73daca],}#{?#{==:#I,5},#[bg=#f7768e],}#{?#{==:#I,6},#[bg=#2ac3de],}#{?#{==:#I,7},#[bg=#e0af68],}#{?#{==:#I,8},#[bg=#a9b1d6],}#{?#{==:#I,9},#[bg=#d4bfff],}#[bold]#{?#{==:#I,0},󰼎,}#{?#{==:#I,1},󰼏,}#{?#{==:#I,2},󰼐,}#{?#{==:#I,3},󰼑,}#{?#{==:#I,4},󰼒,}#{?#{==:#I,5},󰼓,}#{?#{==:#I,6},󰼔,}#{?#{==:#I,7},󰼕,}#{?#{==:#I,8},󰼖,}#{?#{==:#I,9},󰼗,} #W#[nobold]#[fg=#7aa2f7]#{?#{==:#I,0},#[fg=#7aa2f7],}#{?#{==:#I,1},#[fg=#9ece6a],}#{?#{==:#I,2},#[fg=#bb9af7],}#{?#{==:#I,3},#[fg=#ff9e64],}#{?#{==:#I,4},#[fg=#73daca],}#{?#{==:#I,5},#[fg=#f7768e],}#{?#{==:#I,6},#[fg=#2ac3de],}#{?#{==:#I,7},#[fg=#e0af68],}#{?#{==:#I,8},#[fg=#a9b1d6],}#{?#{==:#I,9},#[fg=#d4bfff],}#[bg=default]'
set -g window-status-separator ' '
# Pane borders
set -g pane-border-style fg='#3b4261'
set -g pane-active-border-style fg='#7aa2f7'
# Message style
set -g message-style bg='#7aa2f7',fg='#1a1b26'
set -g message-command-style bg='#292e42',fg='#7aa2f7'
# Tree/choose mode styling
run '~/.config/tmux/plugins/tpm/tpm'
bind-key -T root MouseDown1StatusLeft choose-tree -Zs
# Repeatable session cycling (C-s ( ( ( or C-s ) ) ))
# Longer repeat time for key sequences
set -g repeat-time 1000
# Repeatable window cycling (C-s n n n or C-s p p p)
bind-key -r n next-window
bind-key -r p previous-window
# Session cycling by creation order
bind-key -r '(' run-shell "~/.config/tmux/session-prev.sh"
bind-key -r ')' run-shell "~/.config/tmux/session-next.sh"
# Toggle status bar (C-s B for "bar")
bind-key B run-shell "~/.config/tmux/toggle-status.sh"
# Session/window tree picker popup (C-s s) — like choose-tree but with fzf
bind-key s display-popup -w 90% -h 75% -E "~/.config/tmux/session-tree.sh"
# Built-in choose-tree (C-s S)
bind-key S choose-tree -Zs
# Toggle mouse mode (C-s M) - turn off to select/copy text on iPad
bind M set -g mouse
# Agent dashboard (C-s D) — split pane at top
bind-key D split-window -vb -l 40% \
"~/.claude/scripts/agent-dashboard.sh"
# Kill session and switch away (C-s X)
bind-key X confirm-before -p "Kill session #S? (y/n)" \
"run-shell '~/.config/tmux/kill-session.sh #S'"
# C-s C-s sends C-s to terminal (override plugin bindings)
bind C-s send-prefix
#!/bin/bash
# Toggle between full and minimal status bar
FULL='#[fg=#9ece6a]󰻠 #(~/.config/tmux/plugins/tmux-cpu/scripts/cpu_percentage.sh) #[fg=#bb9af7]󰘚 #(~/.config/tmux/ram-usage.sh) #[fg=#ff9e64]󰋊 #(~/.config/tmux/disk-usage.sh) #[fg=#c0caf5]󰌢 #H #[fg=#7aa2f7]󰥔 %H:%M '
MINI='#[fg=#565f89]󰁌 '
current=$(tmux show-option -gqv @status-mode)
if [ "$current" = "mini" ]; then
tmux set -g status-right "$FULL"
tmux set -g @status-mode "full"
else
tmux set -g status-right "$MINI"
tmux set -g @status-mode "mini"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment