-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy path.tmux.conf
More file actions
285 lines (256 loc) · 15.5 KB
/
Copy path.tmux.conf
File metadata and controls
285 lines (256 loc) · 15.5 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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-sensible'
set -g @plugin 'tmux-plugins/tmux-copycat'
set -g @plugin 'tmux-plugins/tmux-open'
set -g @plugin 'tmux-plugins/tmux-pain-control'
set -g @plugin 'tmux-plugins/tmux-yank'
#set -g @plugin 'catppuccin/tmux'
set -g @plugin 'omerxx/tmux-floax'
set -g @floax-change-path 'false'
# Default configurations
set-option -g base-index 1 # Start window index count at 1 instead of 0
setw -g mouse on # Enable mouse mode: scrolling, pane selecting and window selecting
setw -g mode-keys vi # use vim keys while in copy mode
set -g allow-passthrough all
setw -g monitor-activity on # Monitor activity in windows
# set -g monitor-silence 0 # Don't monitor silence
# set -g activity-action other # Only trigger on substantial activity
setw -g monitor-bell on # Monitor bell in windows
set -g bell-action any # Ring bell for any window
set -g renumber-windows on # Automatically renumber windows when one is closed
set -g history-limit 500000 # Set very high to prevent claude code scrollback lag
# Name the terminal window/tab after the attached session. Ghostty labels a tab
# with the running command otherwise ("tmux -u attach"), which says nothing when
# several tabs are open — and tmux-focus (the Claude notification click action)
# matches on the "session:" prefix to raise the tab tmux is actually in.
set -g set-titles on
# The machine being sat at needs no prefix - nobody is looking in from outside,
# so it would be noise on every tab.
set -g set-titles-string "#S:#I #T"
# A box reached over ssh prefixes the name you reach it by, so a clicked
# notification can tell its tab apart from an identically named local session.
# The format is emitted whole by the script - tmux expands formats in a run-shell
# command before the shell sees them, so no # may appear on this line.
run-shell -b 'tmux set -g set-titles-string "$($HOME/bin/tmux-title-format)"'
set -g default-terminal "tmux-256color"
# True colour, plus the DECSCUSR sequences for the cursor shape.
#
# Written to explicit indices rather than appended with -a. These are array
# options, and -a adds a fresh entry each time instead of replacing one, so every
# source-file grew the list by another copy of all four lines — a server that had
# been reloaded through a day's editing was holding 82 of them. The index only
# has to clear tmux's own defaults, which start at 0.
set -s terminal-overrides[100] "xterm-256color:Tc"
set -s terminal-overrides[101] "xterm-ghostty:Tc"
set -s terminal-overrides[102] "*:Ss=\\E[%p1%d q:Se=\\E[2 q"
set -s terminal-features[100] "xterm-ghostty:hyperlinks"
set -g extended-keys on
set -g extended-keys-format csi-u
# Clear the ⊙ agent indicator once the pane has actually been looked at
set-hook -g pane-focus-in 'set-window-option -u @special_activity'
# Track zoom state in @zoomed for the status bar. Every hook below runs the same
# sync, so it lives in one command alias (index 100 stays clear of tmux's built-ins).
set -s command-alias[100] 'sync-zoom-flag=if-shell -F "#{window_zoomed_flag}" "set -s @zoomed 1" "set -s @zoomed 0"'
set-hook -g after-resize-pane sync-zoom-flag
set-hook -g window-linked sync-zoom-flag
set-hook -g session-window-changed sync-zoom-flag
set-hook -g client-session-changed sync-zoom-flag
# select-pane (vim-tmux-navigator's C-h/j/k/l) unzooms without firing after-resize-pane;
# window-layout-changed catches that. Guarded on window_active so a background window's
# layout change can't clobber the flag.
set-hook -g window-layout-changed 'if-shell -F "#{window_active}" sync-zoom-flag'
# Pane border colors set dynamically by ~/.tmux/theme-sync.sh (light/dark)
# Plugin configurations
set -g @open-S 'https://google.com/search?q=' # tmux-open Shift-S won't work w/i this
set -g @catppuccin_window_status_enable "yes" # Adds icons to each window (including if it's zoomed)
set -g @catppuccin_window_right_separator " " # Needed as icon was getting cut off
# Yank without leaving copy mode
set -g @yank_action 'copy-pipe'
# Simple status bar (colors set dynamically by ~/.tmux/theme-sync.sh)
set-option -g status 2 # Add top margin
# Top line is empty but embeds theme-sync so it re-runs on each status refresh
set-option -g status-format[0] "#(~/.tmux/theme-sync.sh)"
# Status format: session-name + zoom-icon + window-numbers + activity-indicators (⊙ = agent, • = bell) + spacing
set -g status-format[1] "\
#[range=left]#[fg=#{E:@time_fg}] #S#[fg=default]#[norange]\
#{W:\
#[range=window|#{window_index}]\
#{?window_start_flag, #{?#{@zoomed}, , },}\
#{?window_active,#[fg=#{E:@active_fg}],}\
#I\
#{?#{@special_activity},⊙,#{?#{window_bell_flag},•, }}\
#{?window_active,#[fg=default],}\
#[norange] \
}\
#[range=user|new-window]#[fg=default]+ #[norange]\
#[align=right]#[fg=#{E:@time_fg}]#[range=right]#(~/.tmux/gpu.sh)#(~/.tmux/loadavg.sh)#[norange] #[fg=#{E:@clock_fg}]#[range=user|clock]%H:%M CT#[norange]#[fg=default]"
# Click the session name (range=left) to cycle forward through sessions.
# Opt-` / Opt-~ do the same from the keyboard, in both directions.
bind -n MouseDown1StatusLeft run-shell '~/.tmux/session-jump.sh "#{session_name}" next'
bind -n 'M-`' run-shell '~/.tmux/session-jump.sh "#{session_name}" next'
bind -n 'M-~' run-shell '~/.tmux/session-jump.sh "#{session_name}" prev'
# Opt-Tab / Opt-Shift-Tab cycle through windows.
#
# Ctrl-h/l and Ctrl-j/k (at the bottom of this file) now cycle windows and
# sessions too, and more directly, so these four Opt bindings are redundant on
# this machine. They stay because Opt keys are plain escape sequences: they are
# the only way to cycle from a terminal that cannot send Ctrl-Shift-anything,
# i.e. Blink on iOS, which is also why the user-keys block below exists.
bind -n M-Tab next-window
bind -n M-BTab previous-window
# Click the GPU/load average (range=right) to open btop (already sorted by CPU).
# goto-window.sh reuses the window if one is already open in this session.
bind -n MouseDown1StatusRight run-shell '~/.tmux/goto-window.sh "#{session_name}" btop ~/.tmux/btop.sh'
# Clicking the clock (range=user|clock) opens today's agenda, and clicking the
# plus (range=user|new-window) opens a window in the current pane's directory.
# User ranges fire the plain Status key, so fall through to tmux's default
# select-window behavior for clicks on the window list.
bind -n MouseDown1Status if-shell -F '#{==:#{mouse_status_range},clock}' {
run-shell '~/.tmux/goto-window.sh "#{session_name}" agenda ~/.tmux/agenda.sh'
} {
if-shell -F '#{==:#{mouse_status_range},new-window}' {
new-window -c "#{pane_current_path}"
} {
switch-client -t =
}
}
# Keybindings
bind-key Escape copy-mode # Prefix + Escape to enter copy mode
bind-key -T copy-mode-vi v send-keys -X begin-selection # Make v work like you'd expect in copy mode
bind-key -T copy-mode-vi Escape send-keys -X cancel
# Space speaks the selection. kk is Kokoro-82M on MLX and so Apple Silicon only;
# ~/bin/kk is a symlink into a checkout that a Linux box does not have, leaving
# the binding piping every selection into a dangling symlink. Fall back to a
# plain copy there, which is what the key would have done anyway.
if-shell 'command -v kk >/dev/null 2>&1' {
bind-key -T copy-mode-vi Space send-keys -X copy-pipe "kk stop >/dev/null 2>&1; kk say"
} {
bind-key -T copy-mode-vi Space send-keys -X copy-selection-no-clear
}
# Override tmux-copycat's Ctrl-F to page forward instead of file search
bind-key -T copy-mode-vi C-f send-keys -X page-down
# Fuzzy pane finder: prefix+s as an expanded tree, filtered by pane title.
# Session headings stay put; only their windows/panes filter down.
bind F display-popup -E -w 90% -h 80% '~/.tmux/pane-find.sh'
# Make session chooser start from 1 instead of 0. See: https://unix.stackexchange.com/a/755474.
bind-key s choose-tree -ZsK '#{?#{e|<:#{line},9},#{e|+:1,#{line}},#{?#{e|<:#{line},35},M-#{a:#{e|+:97,#{e|-:#{line},9}}},}}'
# Clear screen + scrollback. Ctrl-l navigates panes/windows and so never reaches
# the shell, and Ctrl-Shift-l now moves the window right, leaving Opt-l free for
# the one thing Ctrl-l used to be for.
bind -n M-l send-keys C-l \; run-shell "sleep 0.2" \; clear-history
# Switch windows via Ctrl+number (needs a terminal that sends CSI-u keys)
bind -n C-1 select-window -t 1
bind -n C-2 select-window -t 2
bind -n C-3 select-window -t 3
bind -n C-4 select-window -t 4
bind -n C-5 select-window -t 5
bind -n C-6 select-window -t 6
bind -n C-7 select-window -t 7
bind -n C-8 select-window -t 8
bind -n C-9 select-window -t 9
# Switch windows via Ctrl+number for Blink/iOS (user-keys with custom escape sequences)
# Blink Custom Presses: set Ctrl-N to custom string \x1b[4N;9~ (e.g. Ctrl-1 = \x1b[41;9~)
set -s user-keys[0] "\e[41;9~"
set -s user-keys[1] "\e[42;9~"
set -s user-keys[2] "\e[43;9~"
set -s user-keys[3] "\e[44;9~"
set -s user-keys[4] "\e[45;9~"
set -s user-keys[5] "\e[46;9~"
set -s user-keys[6] "\e[47;9~"
set -s user-keys[7] "\e[48;9~"
set -s user-keys[8] "\e[49;9~"
bind -n User0 select-window -t 1
bind -n User1 select-window -t 2
bind -n User2 select-window -t 3
bind -n User3 select-window -t 4
bind -n User4 select-window -t 5
bind -n User5 select-window -t 6
bind -n User6 select-window -t 7
bind -n User7 select-window -t 8
bind -n User8 select-window -t 9
# Switch sessions via Ctrl-Shift+number (sorted by creation time to match Prefix+s).
# Ghostty emits these as modifyOtherKeys sequences of its own accord.
bind -n C-S-1 run-shell 'tmux switch-client -t $(tmux list-sessions -F "##{session_created}:##S" | sort -n | cut -d: -f2 | sed -n 1p)'
bind -n C-S-2 run-shell 'tmux switch-client -t $(tmux list-sessions -F "##{session_created}:##S" | sort -n | cut -d: -f2 | sed -n 2p)'
bind -n C-S-3 run-shell 'tmux switch-client -t $(tmux list-sessions -F "##{session_created}:##S" | sort -n | cut -d: -f2 | sed -n 3p)'
bind -n C-S-4 run-shell 'tmux switch-client -t $(tmux list-sessions -F "##{session_created}:##S" | sort -n | cut -d: -f2 | sed -n 4p)'
bind -n C-S-5 run-shell 'tmux switch-client -t $(tmux list-sessions -F "##{session_created}:##S" | sort -n | cut -d: -f2 | sed -n 5p)'
bind -n C-S-6 run-shell 'tmux switch-client -t $(tmux list-sessions -F "##{session_created}:##S" | sort -n | cut -d: -f2 | sed -n 6p)'
bind -n C-S-7 run-shell 'tmux switch-client -t $(tmux list-sessions -F "##{session_created}:##S" | sort -n | cut -d: -f2 | sed -n 7p)'
bind -n C-S-8 run-shell 'tmux switch-client -t $(tmux list-sessions -F "##{session_created}:##S" | sort -n | cut -d: -f2 | sed -n 8p)'
bind -n C-S-9 run-shell 'tmux switch-client -t $(tmux list-sessions -F "##{session_created}:##S" | sort -n | cut -d: -f2 | sed -n 9p)'
# Neovim :terminal bindings for fading inactive panes
# set-option -g default-command 'nvim +terminal +startinsert +"autocmd TermClose * ++once quit"'
# Emoji selector
bind e run-shell 'tmux new-window "ranger ~/Code/slack-emojis/; sleep 0.2; buffer=\$(tmux show-buffer); if [[ \$buffer != *$'\''\n'\''* ]]; then tmux send-keys -t #{pane_id} \"\$buffer\"; fi"'
bind g run-shell 'tmux new-window "ranger /Users/ericboehs/.local/share/weechat/xfer"'
bind C new-window -c "#{pane_current_path}" "zsh -ic 'pi; exec zsh'"
bind m set mouse \; display "Mouse #{?mouse,on,off}"
# Move the current pane to another session/window. Tab completes targets
# (popup list when ambiguous). e.g. type "be" Tab -> beta:1
bind M command-prompt -T target -p "Move pane to (session:window):" "break-pane -t %%"
# Apply theme based on macOS appearance (also refreshed via status-format[0])
run-shell '~/.tmux/theme-sync.sh'
# Allow custom configuration
if '[ -f ~/.tmux.conf.local ]' 'source ~/.tmux.conf.local'
# Initialize TMUX plugin manager (keep this line below all plugin declarations)
run '~/.tmux/plugins/tpm/tpm'
# --- Ctrl-hjkl navigation -----------------------------------------------------
#
# These stay below tpm. vim-tmux-navigator used to live here and bound
# Ctrl-h/j/k/l itself, and whoever binds last wins; it is gone now, but a plugin
# is still free to claim these keys, and being below the plugins settles that in
# advance. run-shell without -b blocks the config queue until tpm has finished,
# so this really is read last.
#
# Ctrl-h/j/k/l move between panes and keep going past the last one: h/l into the
# previous/next window, j/k into the previous/next session. The if-shell test is
# vim-tmux-navigator's, kept along with Ctrl-\ and the copy-mode bindings when
# the plugin was dropped, so that vim, nvim and fzf still see the keys first and
# get to move their own splits.
#
# Ctrl-Shift-h/j/k/l are the same directions with the window in tow: h/l reorder
# the window inside the session, j/k send it to another session and follow it.
#
# Ctrl-Shift-anything depends on the terminal sending a sequence that can be told
# apart from the plain Ctrl key. tmux only knows how to ask for that through
# xterm's modifyOtherKeys and never sends the request under TERM=xterm-ghostty,
# so nothing here is negotiated — Ghostty simply emits the sequences anyway.
# Were it not doing so, Ctrl-Shift-h would arrive as a bare 0x08 and silently run
# Ctrl-h instead, which is worth remembering on any other terminal. Note tmux 3.7
# treats C-H as its own (unreachable) key name, so these must be spelled C-S-h.
# tmux run-shell exports TMUX but not TMUX_PANE, so the pane has to be handed to
# the script explicitly as #{pane_id}.
%hidden IS_VIM="ps -o state= -o comm= -t '#{pane_tty}' | grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|l?n?vim?x?|fzf)(diff)?$'"
bind -n C-h if-shell "$IS_VIM" 'send-keys C-h' 'run-shell "~/.tmux/pane-nav.sh h #{pane_id}"'
bind -n C-j if-shell "$IS_VIM" 'send-keys C-j' 'run-shell "~/.tmux/pane-nav.sh j #{pane_id}"'
bind -n C-k if-shell "$IS_VIM" 'send-keys C-k' 'run-shell "~/.tmux/pane-nav.sh k #{pane_id}"'
bind -n C-l if-shell "$IS_VIM" 'send-keys C-l' 'run-shell "~/.tmux/pane-nav.sh l #{pane_id}"'
# Ctrl-\ jumps back to the pane you came from, inherited from vim-tmux-navigator
# along with the copy-mode bindings below, which are the only reason the plugin
# was still installed. Written out here it needs one backslash, not the four the
# plugin ended up sending: it built this line as a shell string inside an
# if-shell, and the extra escaping leaked a stray literal into the pane.
bind -n 'C-\' if-shell "$IS_VIM" 'send-keys C-\' 'select-pane -l'
# Copy mode has no vim to defer to, so these go straight to the script. Same
# behaviour as outside copy mode, including falling out of the window at the
# edge; the script leaves a pane alone while a selection is being made, since
# resizing one throws the selection away.
bind -T copy-mode-vi C-h run-shell "~/.tmux/pane-nav.sh h #{pane_id}"
bind -T copy-mode-vi C-j run-shell "~/.tmux/pane-nav.sh j #{pane_id}"
bind -T copy-mode-vi C-k run-shell "~/.tmux/pane-nav.sh k #{pane_id}"
bind -T copy-mode-vi C-l run-shell "~/.tmux/pane-nav.sh l #{pane_id}"
bind -T copy-mode-vi 'C-\' select-pane -l
# Escape hatch for the keys taken above: prefix Ctrl-h/j/k/l sends the literal
# control character to the pane, so readline still gets C-k (kill to end of
# line), C-l (clear) and so on. These also have to sit below tpm —
# tmux-pain-control binds prefix C-h/j/k/l to select-pane, which is what plain
# prefix h/j/k/l already do, so nothing is lost by claiming them here.
bind C-h send-keys 'C-h'
bind C-j send-keys 'C-j'
bind C-k send-keys 'C-k'
bind C-l send-keys 'C-l'
bind -n C-S-h run-shell '~/.tmux/move-window.sh "#{session_name}" left'
bind -n C-S-l run-shell '~/.tmux/move-window.sh "#{session_name}" right'
bind -n C-S-j run-shell '~/.tmux/session-jump.sh "#{session_name}" next move'
bind -n C-S-k run-shell '~/.tmux/session-jump.sh "#{session_name}" prev move'