# tmux.conf
# By Henrik Lissner <henrik@lissner.net>
# https://github.com/hlissner/dotfiles
########################################

# Ensure key env vars exist even when tmux is launched outside our wrapper.
if-shell '[ -z "${TMUX_HOME:-}" ]' 'set-environment -g TMUX_HOME "$HOME/.config/tmux"'
if-shell '[ -z "${DOTFILES:-}" ]' 'set-environment -g DOTFILES "$HOME/.config/dotfiles"'
if-shell '[ -z "${DOTFILES_BIN:-}" ]' 'set-environment -g DOTFILES_BIN "$HOME/.config/dotfiles/bin"'

# Keep tmux from turning local shells into "remote" shells after an SSH/mosh
# attach. The tmux default includes SSH_CONNECTION, which leaks into new panes
# and makes tools like nix-darwin/Home Manager think activation is over SSH.
# Preserve useful GUI/auth vars and explicitly add Moshi's local client signal.
set-option -g update-environment "DISPLAY KRB5CCNAME MSYSTEM SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID WINDOWID XAUTHORITY MOSHI_CLIENT"

# Pass zoxide's DB location to all popup shells (Nix sets _ZO_DATA_DIR=$XDG_CACHE_HOME)
# run-shell ensures $HOME is expanded by the shell, not tmux's config parser
run-shell 'tmux set-environment -g _ZO_DATA_DIR "$HOME/.cache"'

# Use ghostty terminfo when available, fall back to xterm-256color
set  -g default-terminal "ghostty"
set -ga terminal-overrides ",ghostty:Tc"

# Enable extended keys for proper modifier key support (Shift+Enter, etc.)
set -s extended-keys on
set -g extended-keys-format csi-u
set -as terminal-features ',ghostty:extkeys'
# Allow passthrough for Kitty keyboard protocol queries
set -g allow-passthrough on

# Spawn a session only when none exist (safe to re-source).
if-shell 'tmux list-sessions >/dev/null 2>&1' '' 'new-session'

setw -g automatic-rename off      # rename window after current program
set  -g renumber-windows on      # renumber windows when one is closed
# Push session + smart subtitle to the terminal title via OSC 2.
# tmux-smart-name owns @smart_title_context and keeps titles boring:
#   <session>
#   <session> · <pi session / filename / other subtitle>
# This lets Ghostty keep a stable landing session while ADE project sessions
# still surface their canonical tmux session name clearly.
set  -g set-titles on
set  -g set-titles-string "#{?#{@smart_title_context},#{session_name} · #{@smart_title_context},#{session_name}}"
# Zero-based indexing is fine in programming languages, but not so much in a
# multiplexer when zero is on the other side of the keyboard.
set  -g base-index 1
setw -g pane-base-index 1
# display tmux messages longer
set  -g display-time 1500
set  -g display-panes-time 800
# Address vim-mode switching delay (http://superuser.com/a/252717/65504)
set  -s escape-time   0
set -sg repeat-time   600
set  -g history-limit 100000
# Rather than constraining window size to the maximum size of any client
# connected to the *session*, constrain window size to the maximum size of any
# client connected to *that window*. Much more reasonable.
setw -g aggressive-resize off
set  -g detach-on-destroy off    # Stay in tmux when session closes
# For terminals that support them, propagate these events to programs that
# understand them.
set  -s focus-events on
# Enable mouse + mouse wheel
set  -g mouse on

########################################
# Keybinds                             #
########################################

# Rebind prefix to C-c. Press twice to send literal C-c.
unbind C-b
set -g prefix C-c
bind C-c send-prefix

# Vi-style keybinds
set -g status-keys vi
set -g mode-keys   vi

bind c new-window      -c "#{pane_current_path}"
unbind s
bind s split-window -v -c "#{pane_current_path}"
bind v split-window -h -c "#{pane_current_path}"

# Session menu popup
bind a run-shell '$TMUX_HOME/smart-name.sh --menu'

# Command palette
# Use the Nix profile path because tmux server environments can keep a stale PATH.
# C-Space is a nice Raycast-style binding when terminals pass it through.
bind-key -n C-Space run-shell "/etc/profiles/per-user/emiller/bin/tmux-palette"
bind m run-shell "/etc/profiles/per-user/emiller/bin/tmux-palette"

# Fast current-session Pi entry
bind A run '$TMUX_HOME/open-pi-here.sh'

# Open current project note in Neovide/Obsidian workflow.
bind V run-shell 'TMUX_PANE_CURRENT_PATH="#{pane_current_path}" "${DOTFILES_BIN:-$HOME/.config/dotfiles/bin}/obsidian-neovide" "#{pane_current_path}"'

# Toggle tml layout (AI + lazygit + shell)
bind T run '$TMUX_HOME/toggle-tml.sh'

# Cross-runtime Agent + Hunk session picker
bind Z run-shell "bash $TMUX_HOME/worktree-agent-hunk-picker.sh"

# Git/review popups
bind g display-popup -E -w 80% -h 80% -d "#{pane_current_path}" "$TMUX_HOME/open-git-tui.sh"
bind G display-popup -E -w 90% -h 90% -d "#{pane_current_path}" "$TMUX_HOME/open-critique.sh"
bind R display-popup -E -w 90% -h 90% -d "#{pane_current_path}" "$TMUX_HOME/open-critique.sh --staged"
# Persistent right-pane Hunk review (non-popup) without stealing h/j/k pane-nav keys.
# Direct bindings:
#   prefix + ]  -> worktree diff
#   prefix + }  -> staged diff
#   prefix + {  -> branch-committed diff
bind-key ']' split-window -h -p 50 -c "#{pane_current_path}" "$TMUX_HOME/open-hunk.sh"
bind-key '}' split-window -h -p 50 -c "#{pane_current_path}" "$TMUX_HOME/open-hunk.sh --staged"
bind-key '{' split-window -h -p 50 -c "#{pane_current_path}" "$TMUX_HOME/open-hunk.sh --branch-committed"

# Generic command in new split
bind C command-prompt -p "Command:" "split-window -v -c '#{pane_current_path}' '%%'"

bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
unbind /
unbind .
bind / choose-session
bind . choose-window

# Secondary sesh switch/discovery picker; tmproj/tp is the primary project entry.
# ^a: all | ^t: tmux | ^c: configs | ^x: zoxide | ^d: kill session
bind-key t run-shell "$TMUX_HOME/sesh-picker.sh"

# bind = select-layout tiled
bind | select-layout even-horizontal
bind _ select-layout even-vertical

# Keep direct kill bindings without confirmation.
unbind x
unbind &
bind x kill-pane
bind X kill-window

# Smart pane switching with awareness of vim splits and AI agents
# See: https://github.com/christoomey/vim-tmux-navigator
vim_pattern='(\S+/)?g?\.?(view|l?n?vim?x?)(diff)?$'
# pi needs C-j passed through for shift+enter (sends \n)
agent_pattern='pi$'
is_vim="ps -o state= -o comm= -t '#{pane_tty}' | grep -iqE '^[^TXZ ]+ +${vim_pattern}$'"
is_vim_or_agent="ps -o state= -o comm= -t '#{pane_tty}' | grep -iqE '^[^TXZ ]+ +(${vim_pattern}|${agent_pattern})$'"
bind-key -n 'C-h' if-shell "$is_vim" 'send-keys C-h'  'select-pane -L'
bind-key -n 'C-j' if-shell "$is_vim_or_agent" 'send-keys C-j'  'select-pane -D'
bind-key -n 'C-k' if-shell "$is_vim" 'send-keys C-k'  'select-pane -U'
bind-key -n 'C-l' if-shell "$is_vim" 'send-keys C-l'  'select-pane -R'
bind-key -n 'C-\' if-shell "$is_vim" 'send-keys C-\\' 'select-pane -l'

bind-key -T copy-mode-vi 'C-h' select-pane -L
bind-key -T copy-mode-vi 'C-j' select-pane -D
bind-key -T copy-mode-vi 'C-k' select-pane -U
bind-key -T copy-mode-vi 'C-l' select-pane -R
bind-key -T copy-mode-vi 'C-\' select-pane -l
bind C-w last-pane
bind n next-window
bind p previous-window
bind C-n next-window
bind C-p previous-window

# reload config without killing server
bind r source-file $DOTFILES/config/tmux/config \; display-message "  Config reloaded..".
bind ^r refresh-client


########################################
# Copy mode                            #
########################################

bind Enter copy-mode # enter copy mode

bind P run "xclip -selection clipboard -o | tmux load-buffer - ; tmux paste-buffer"

bind -T copy-mode-vi v send-keys -X begin-selection
bind -T copy-mode-vi C-v send-keys -X rectangle-toggle
bind -T copy-mode-vi Escape send-keys -X cancel
bind -T copy-mode-vi C-g send-keys -X cancel
bind -T copy-mode-vi H send-keys -X start-of-line
bind -T copy-mode-vi L send-keys -X end-of-line


########################################
# Theme                                #
########################################
# Theme configuration is in theme.conf (Catppuccin + auto-hide status bar)
# Loaded via nix module rcFiles


########################################
# Local config                         #
########################################
set -g @open-editor 'C-e'
set -g @open-S 'https://www.duckduckgo.com/'

# Optional local overrides in XDG path (instead of ~/.tmux.conf).
if-shell '[ -f "$TMUX_HOME/local.conf" ]' 'source-file "$TMUX_HOME/local.conf"'
# Load generated module init last so declarative plugin/key settings win.
if-shell '[ -f "$TMUX_HOME/extraInit" ]' 'source-file "$TMUX_HOME/extraInit"'

# Re-assert a few direct bindings after plugins load.
unbind /
unbind .
bind / choose-session
bind . choose-window

# Which-key submenu launchers for top-level namespaces.
unbind g
unbind f
bind g run-shell "$DOTFILES/config/tmux/show-which-key-menu.sh git"
bind f run-shell "$DOTFILES/config/tmux/show-which-key-menu.sh find"
unbind w
bind w choose-tree -Zw

# Reserve prefix+M for jmux's new session/worktree interceptor.
# tmux's default prefix+M (`select-pane -M`) is not part of this setup.
unbind M

# Keep next/previous-window on muscle-memory keys.
bind n next-window
bind p previous-window

# If experimental tmux-agent-status is enabled, it may bind n/p; reclaim them.
unbind n
unbind p
bind n next-window
bind p previous-window

# prefix+b should hard-toggle the agent sidebar (open if absent, close if present).
unbind b
bind b run-shell 'cmd=$(tmux show -gv @agent-sidebar-toggle-cmd 2>/dev/null); [ -z "$cmd" ] && cmd="$HOME/.config/tmux/plugins/tmux-agent-status/scripts/sidebar-toggle.sh"; pane=$(tmux list-panes -F "#{pane_id} #{pane_title}" | awk "\$2==\"agent-sidebar\"{print \$1; exit}"); if [ -n "$pane" ]; then tmux kill-pane -t "$pane"; else "$cmd"; fi'

# Keep Moshi swipe window detection reliable without changing local themes.
if-shell '[ -n "$MOSHI_CLIENT" ]' {
  set -g status-left ''
  set -g status-right ''
}
