-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.tmux.conf
More file actions
210 lines (192 loc) · 10.1 KB
/
.tmux.conf
File metadata and controls
210 lines (192 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# plugins
set -g @plugin 'tmux-plugins/tpm'
# persist environments (tmux-resurrect + tmux-continuum)
# Saves: session/window/pane layout, working directories, and running commands.
# Does NOT save: scrollback, env vars, or process internal state.
# Identical state is deduplicated (no new file if nothing changed).
#
# Auto-save: every 3 minutes via continuum (hooked into status-right)
# Auto-restore: on tmux server start (@continuum-restore on)
# Manual save: prefix + C-s
# Manual restore: prefix + C-r
# Saved files: ~/.local/share/tmux/resurrect/
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'
set -g @continuum-restore 'on'
set -g @continuum-save-interval '15'
# extract URLs from pane (uses capture-pane -J to join wrapped lines)
# prefix + b : pick a URL with fzf and open it (browse)
# ctrl-y in fzf : copy URL to clipboard instead of opening
set -g @plugin 'wfxr/tmux-fzf-url'
set -g @fzf-url-bind 'b'
set -g @fzf-url-history-limit '2000'
# set prefix key
set -g prefix C-q
unbind C-b
# send prefix to nested tmux (e.g. over SSH) with C-q C-q
bind C-q send-prefix
set-option -g history-limit 500000
# enable mouse
set-option -g mouse on
# Work around a tmux 3.6a crash seen when mouse drag enters copy mode.
# In alternate screen apps, keep forwarding drag events instead of cloning
# the pane into copy mode.
bind-key -n MouseDrag1Pane if-shell -F "#{||:#{alternate_on},#{pane_in_mode},#{mouse_any_flag}}" { send-keys -M } { copy-mode -M }
# OSC 52 clipboard forwarding
# Goal: Vim yank on SSH remote → host clipboard via OSC 52
# Chain: Neovim (OSC 52) → remote tmux → SSH → local tmux → WezTerm → macOS clipboard
#
# set-clipboard on : accept OSC 52 from apps and forward to outer terminal
# terminal-overrides : add Ms capability so tmux knows HOW to forward OSC 52
# terminal-features : declare clipboard support so tmux actually forwards
#
# Pattern matches xterm-256color (WezTerm's $TERM, set by wezterm.lua `term`)
# for the outer terminal, and tmux-256color for nested tmux sessions (e.g. over SSH).
#
# RGB : tells tmux the outer terminal supports 24-bit true color,
# so it passes through Neovim's RGB escape sequences as-is.
# Ms=\E]52;... : OSC 52 template for clipboard forwarding.
# clipboard feature : enables tmux to accept and relay OSC 52 from inner apps.
# Allow DCS passthrough so OSC 7 (CWD notification) from the shell
# reaches WezTerm through tmux. Used by WezTerm Ctrl-T to spawn
# new tabs in the current directory.
set -g allow-passthrough on
set -s set-clipboard on
set-option -s terminal-overrides 'xterm-256color:RGB:Ms=\E]52;%p1%s;%p2%s\007'
set-option -as terminal-overrides ',tmux-256color:RGB:Ms=\E]52;%p1%s;%p2%s\007'
set-option -s terminal-features 'xterm-256color:clipboard:sixel'
set-option -as terminal-features ',tmux-256color:clipboard:sixel'
# default-terminal: the $TERM value seen by apps running inside tmux.
# tmux-256color lets Neovim (and other apps) detect true color support
# and safely enable termguicolors / 24-bit color output.
set -g default-terminal "tmux-256color"
# reduce delay
set -sg escape-time 1
set-option -g repeat-time 500
# index number
set -g base-index 1
set -g pane-base-index 1
setw -g monitor-activity on
# used for autoreading file changes at vim
set -g focus-events on
# session
# create new session
bind N new-session -c '#{pane_current_path}'
# window
# disable tmux window hotkeys (managed by WezTerm tabs)
unbind c
unbind n
unbind p
# close current window (for WezTerm Cmd+W)
# Defer kill-window via run-shell -b to avoid a use-after-free crash
# in tty_keys_next (tmux 3.6a). Running kill-window synchronously
# inside the key-binding handler frees the window while tty_keys_next
# still holds references on the call stack.
bind-key -n F12 run-shell -b "tmux kill-window -t '#{window_id}'"
# split window
bind v split-window -h -c '#{pane_current_path}'
bind s split-window -v -c '#{pane_current_path}'
# pane
# select pane
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
# resize pane
bind -r H resize-pane -L 2
bind -r J resize-pane -D 2
bind -r K resize-pane -U 2
bind -r L resize-pane -R 2
# remove pane
bind q confirm-before -p "kill-pane? (y/n)" kill-pane
# Pane options set by the ssh() / myssh() wrappers in zsh/alias.zsh:
# @ssh_host — set on any ssh pane; value is the target host. Drives the
# SSH indicator in the status bar and the purple pane border.
# @ssh_my_machine — set only when the user opened the pane with `myssh`, i.e.
# declared it to be one of their own machines (tmux +
# tmux-track-session expected on the remote). Used below so
# that prefix + p/t/o/u passes the chord through to the
# nested remote tmux; plain `ssh` panes and local panes
# fall back to the local popup / copy-mode.
bind p if-shell -F '#{@ssh_my_machine}' 'send-keys C-q p' 'display-popup -E -d "#{pane_current_path}" -w 80% -h 80% "tmux new-session -A -s _popup_#{pane_id} -c \"#{pane_current_path}\""'
bind t if-shell -F '#{@ssh_my_machine}' 'send-keys C-q t' 'display-popup -E -d "#{pane_current_path}" -w 80% -h 80% "tmux new-session -A -s _tig_#{pane_id} -c \"#{pane_current_path}\" tig"'
bind o if-shell -F '#{@ssh_my_machine}' 'send-keys C-q o' 'display-popup -E -d "#{pane_current_path}" -w 80% -h 80% "tmux new-session -A -s _fzf_nvim_#{pane_id} -c \"#{pane_current_path}\" \"file=\\\$(fzf --preview \\\"bat --color=always --style=numbers --line-range=:500 {-1}\\\") || true; if [ -n \\\"\\\$file\\\" ]; then nvim -- \\\"\\\$file\\\"; else nvim; fi\""'
bind g display-popup -E -d "#{pane_current_path}" -w 30 -h 3 'echo " Opening GitHub..." && (gh pr view --web 2>/dev/null || gh repo view --web) >/dev/null 2>&1; sleep 1'
# copy
bind u if-shell -F '#{@ssh_my_machine}' 'send-keys C-q u' 'copy-mode'
setw -g mode-keys vi
# Setup 'v' to begin selection as in Vim
bind-key -T copy-mode-vi v send-keys -X begin-selection
# Write to OS clipboard directly: pbcopy on macOS, clip.exe on WSL.
# clip.exe bypasses OSC 52 (which is unreliable through ConPTY on Windows)
# and writes via Win32 API. When neither command exists (e.g. SSH on a
# headless server), set-clipboard on still forwards via OSC 52.
#
# WSL detection uses a filesystem check on the absolute clip.exe path
# instead of `command -v clip.exe`. The PATH-based check is fragile —
# if the tmux server was started in a context where Windows interop
# PATH had not yet been appended, the binding was never registered and
# survived for the lifetime of the server. The absolute path is stable
# across WSL distros and PATH-independent.
if-shell '[ -x /usr/bin/pbcopy ]' {
bind-key -T copy-mode-vi y send-keys -X copy-pipe "pbcopy"
bind-key -T copy-mode-vi M-c send-keys -X copy-pipe "pbcopy"
} {
if-shell '[ -x /mnt/c/Windows/System32/clip.exe ]' {
bind-key -T copy-mode-vi y send-keys -X copy-pipe "/mnt/c/Windows/System32/clip.exe"
bind-key -T copy-mode-vi M-c send-keys -X copy-pipe "/mnt/c/Windows/System32/clip.exe"
}
}
# Exit copy mode with Escape or Ctrl-C
bind-key -T copy-mode-vi Escape send-keys -X cancel
bind-key -T copy-mode-vi C-c send-keys -X cancel
# status bar — Everforest dark hard: bg_dim (#1e2326), fg (#d3c6aa)
set -g status-style 'bg=#1e2326 fg=#d3c6aa'
set-option -g status-position top
set-option -g status-left-length 300
set-option -g status-right-length 300
# SSH indicator uses Everforest dark hard palette: bg_purple (#463f48), purple (#d699b6)
set-option -g status-left '#{?@ssh_host,#[bg=#463f48 fg=#d699b6] SSH: #{@ssh_host} #[default],#{pane_current_path}}'
# The #() runs continuum_save.sh on each status refresh (every status-interval seconds).
# Embedded here instead of relying on continuum's auto-injection, which breaks when
# other tmux servers (e.g. Claude Code) trigger its multi-server detection guard.
set-option -g status-right '#(~/.tmux/plugins/tmux-continuum/scripts/continuum_save.sh)%Y-%m-%d(%a) %H:%M'
set-option -g status-interval 30 # refresh interval (lower fork frequency for #() commands)
set-option -g status-justify centre
set-option -g automatic-rename off
set-option -g allow-rename off
# propagate window title to terminal (WezTerm tab name)
set -g set-titles on
# SSH pane: show host + remote pane title (propagated by remote tmux's
# set-titles or by the shell's OSC 2 escape). Local pane: show window
# name set by tmux-pane-titles.
set -g set-titles-string '#{?@ssh_host,#{@ssh_host}:#{pane_title},#{window_name}}'
# Window title updates moved to zsh precmd/chpwd hooks (directory.zsh).
# tmux server-side run-shell -b caused SIGSEGV in tty_keys_next due to
# fork racing with tty input processing (tmux 3.6a, macOS ARM64).
# Clean up old hooks on config reload.
set-hook -gu after-new-window
set-hook -gu after-new-session
set-hook -gu after-split-window
set-hook -gu window-layout-changed
# Track remote session switches for SSH auto-reconnect.
# tmux-track-session uses a per-connection polling monitor (started via
# run-shell in the attach command) instead of global hooks. This avoids
# race conditions from stale tty mappings when PTY numbers are reused
# on reconnect. Unset old hooks so a config reload cleans them up.
set-hook -gu client-session-changed
set-hook -gu client-detached
# pane borders
set -g pane-border-lines heavy # thick Unicode box-drawing characters
set -g pane-border-indicators both # colour + arrows to indicate active pane
# Everforest dark hard: bg3 (#414b50)
set -g pane-border-style 'fg=#414b50'
# Everforest palette: SSH → purple (#d699b6), local → green (#a7c080)
set -g pane-active-border-style '#{?@ssh_host,fg=#d699b6,fg=#a7c080}'
# Initialize tmux plugin manager
run '~/.tmux/plugins/tpm/tpm'
# Patch tmux-fzf-url: drop -e from capture-pane.
# ANSI escapes embedded in scrollback can split a URL string and prevent
# xre's regex from matching it whole. Plain-text capture (-J alone) is
# sufficient because xre already uses --strip-ansi.
run-shell 'f="$HOME/.tmux/plugins/tmux-fzf-url/fzf-url.sh"; if [ -f "$f" ]; then sed -i.bak "s/capture-pane -J -p -e/capture-pane -J -p/g" "$f" && rm -f "$f.bak"; fi'