nix/scripts/mux
2026-02-13 20:53:39 -05:00

232 lines
6.1 KiB
Bash
Executable file

#!/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"
if tmux list-windows -F '#{window_name}' | grep -Fx "$name" >/dev/null; then
tmux select-window -t "${name}"
else
if [ -n "$cmd" ]; then
tmux new-window -c '#{pane_current_path}' -n "${name}" "$cmd"
else
tmux new-window -c '#{pane_current_path}' -n "${name}"
fi
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
left=' #[align=left list=on]#{W:#[range=window|#{window_index}]#{window_index}:#{window_name}#{window_flags}#[norange]#{?window_end_flag,, │ }}#[nolist]'
right="#[align=right]$indicator $bar_content "
tmux set -g 'status-format[0]' "$left$right"
;;
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]])"'
;;
term)
spawn_or_focus term
;;
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