#!/bin/sh require() { for cmd in "$@"; do command -v "$cmd" >/dev/null 2>&1 || { echo "mux: missing dependency: $cmd" >&2 exit 1 } done } require tmux get_scope() { _path=$(tmux display-message -p '#{pane_current_path}') case "$_path" in "$HOME") printf '~' ;; *) basename "$_path" ;; esac } spawn_or_focus() { scope=$(get_scope) name="${1}@${scope}" cmd="$2" idx=$(tmux list-windows -F '#{window_name} #{window_index}' | awk -F'\t' -v n="$name" '$1 == n { print $2; exit }') if [ -n "$idx" ]; then tmux select-window -t ":${idx}" elif [ -n "$cmd" ]; then tmux new-window -c '#{pane_current_path}' -n "${name}" "$cmd" else tmux new-window -c '#{pane_current_path}' -n "${name}" fi } pick_session() { require fzf column sel=$({ printf 'name\twindows\tstatus\n' tmux list-sessions -F '#{session_name} #{session_windows}w #{?session_attached,*,}' } | column -t -s "$(printf '\t')" | fzf --reverse --header-lines 1 --prompt 'select-session> ') [ -n "$sel" ] && tmux switch-client -t "${sel%% *}" } pick_window() { require fzf column sel=$({ printf 'target\tname\tcommand\tpanes\n' tmux list-windows -a -F '#{window_index}:#{session_name} #{window_name} #{pane_current_command} #{window_panes}p' } | column -t -s "$(printf '\t')" | fzf --reverse --header-lines 1 --prompt 'select-window> ') sel="${sel%% *}" [ -n "$sel" ] && tmux switch-client -t "${sel#*:}:${sel%%:*}" } pick_pane() { require fzf column sel=$({ printf 'target\tcommand\tpath\n' tmux list-panes -a -F '#{window_index}.#{pane_index}:#{session_name} #{pane_current_command} #{pane_current_path}' | sed "s|$HOME|~|g" } | column -t -s "$(printf '\t')" | fzf --reverse --header-lines 1 --prompt 'select-pane> ') sel="${sel%% *}" [ -n "$sel" ] && tmux switch-client -t "${sel#*:}:${sel%%:*}" } target_pane() { require fzf column sel=$({ printf 'target\tcommand\tpath\n' tmux list-panes -a -F '#{window_index}.#{pane_index}:#{session_name} #{pane_current_command} #{pane_current_path}' | sed "s|$HOME|~|g" } | column -t -s "$(printf '\t')" | fzf --reverse --header-lines 1 --prompt "$1> ") sel="${sel%% *}" [ -n "$sel" ] && printf '%s' "${sel#*:}:${sel%%:*}" } confirm() { printf '%s ' "$1" old=$(stty -g) stty raw -echo c=$(dd bs=1 count=1 2>/dev/null) stty "$old" printf '\n' [ "$c" = "y" ] } case "$1" in bar) if [ "$(tmux show-options -gv mouse)" = "on" ]; then indicator='#{?pane_in_mode,[mouse#{@c}copy],[mouse]}' else indicator='#{?pane_in_mode,[copy],}' fi set -f keys="H J K L" bar_content="" i=0 total=$(tmux ls -F x | wc -l) for line in $(tmux ls -F '#{session_id}:#{session_name}'); do sid="${line%%:*}" sname="${line#*:}" if [ $i -lt 4 ]; then key=$(echo "$keys" | cut -d' ' -f $((i + 1))) elif [ $i -eq $((total - 1)) ]; then key='$' else key='?' fi star="#{?#{==:#S,$sname},*,}" [ -n "$bar_content" ] && bar_content="$bar_content │ " bar_content="$bar_content#[range=session|${sid}]$key:$sname$star#[norange]" i=$((i + 1)) done set +f tmux set -g status-left ' ' tmux set -g window-status-format '#[range=window|#{window_index}]#{window_index}:#{window_name}#{window_flags}#[norange]' tmux set -g window-status-current-format '#[range=window|#{window_index}]#{window_index}:#{window_name}#{window_flags}#[norange]' tmux set -g window-status-separator ' │ ' tmux set -g status-right "$indicator $bar_content " ;; switch) session="$(tmux ls -F '#S' | tail -n "+$(($2 + 1))" | head -1)" tmux switch -t "$session" ;; pick-session) pick_session ;; pick-window) pick_window ;; pick-pane) pick_pane ;; ai) require claude spawn_or_focus ai 'claude --dangerously-skip-permissions' ;; code) require nvim spawn_or_focus code 'nvim -c "lua require([[config.tmux]]).run([[nvim]])"' ;; git) require nvim git pane_path=$(tmux display-message -p '#{pane_current_path}') if ! git -C "$pane_path" rev-parse --is-inside-work-tree >/dev/null 2>&1; then tmux display-message "Not a git repository" else spawn_or_focus git 'nvim -c "lua require([[config.tmux]]).run([[git]])"' fi ;; run) require nvim spawn_or_focus run 'nvim -c "lua require([[config.tmux]]).run([[run]])"' ;; shell) spawn_or_focus shell ;; misc) spawn_or_focus misc ;; cmd) require fzf result=$(tmux list-commands | sed 's/ / /' | fzf --reverse --prompt ':' --print-query \ --delimiter '\t' --with-nth '1,2' --accept-nth '1' \ --bind 'ctrl-y:transform-query(echo {1})+disable-search') rc=$? query=$(printf '%s' "$result" | head -1) action=$(printf '%s' "$result" | sed -n '2p') [ $rc -eq 130 ] && exit if [ -n "$query" ] && [ "$query" != "$action" ]; then tmux $query elif [ -n "$action" ]; then case "$action" in switch-client) pick_session ;; select-window) pick_window ;; select-pane) pick_pane ;; rename-window) cur=$(tmux display-message -p '#{window_name}') printf 'name [%s]: ' "$cur" read -r name [ -n "$name" ] && tmux rename-window "$name" ;; rename-session) cur=$(tmux display-message -p '#S') printf 'name [%s]: ' "$cur" read -r name [ -n "$name" ] && tmux rename-session "$name" ;; join-pane) target=$(target_pane 'join-pane') [ -n "$target" ] && tmux join-pane -s "$target" ;; swap-pane) target=$(target_pane 'swap-pane') [ -n "$target" ] && tmux swap-pane -t "$target" ;; select-layout) layout=$(printf '%s\n' \ 'even-horizontal' \ 'even-vertical' \ 'main-horizontal' \ 'main-vertical' \ 'tiled' | fzf --reverse --prompt 'layout> ') [ -n "$layout" ] && tmux select-layout "$layout" ;; kill-pane) confirm 'kill-pane? [y/N]:' && tmux kill-pane ;; kill-window) confirm "kill-window \"$(tmux display-message -p '#{window_name}')\"? [y/N]:" && tmux kill-window ;; kill-session) confirm "kill-session \"$(tmux display-message -p '#S')\"? [y/N]:" && tmux kill-session ;; kill-server) confirm 'kill-server? [y/N]:' && tmux kill-server ;; *) tmux "$action" ;; esac fi ;; *) tmux attach 2>/dev/null || tmux new-session ;; esac