diff --git a/modules/home-manager/tmux.nix b/modules/home-manager/tmux.nix index 00e1716..8e402c1 100644 --- a/modules/home-manager/tmux.nix +++ b/modules/home-manager/tmux.nix @@ -1,60 +1,129 @@ { config, pkgs, inputs, ... }: +let + # Powerline separators, U+E0B0..U+E0B3. + # + # Written as escapes rather than literal glyphs. These are Private Use Area + # codepoints: they render only with a patched font, they survive copy/paste + # badly, and when they get stripped the loss is invisible -- the config still + # parses, the status bar just quietly loses its separators. Nix has no \u + # escape of its own, but fromJSON does. + sep = builtins.fromJSON '' + { + "right": "\ue0b0", + "rightThin": "\ue0b1", + "left": "\ue0b2", + "leftThin": "\ue0b3" + } + ''; +in { programs.tmux = { enable = true; prefix = "C-a"; keyMode = "vi"; mouse = true; - terminal = "screen-256color"; + + # tmux-256color gives truecolor, italics and undercurl in nvim. + # screen-256color has none of them. No terminfo gymnastics needed here -- + # unlike macOS, NixOS ships a current ncurses that has the entry. + terminal = "tmux-256color"; + + # Default is 500ms. That is the lag you feel leaving insert mode in nvim, + # because tmux is waiting to see whether Esc starts an escape sequence. + escapeTime = 10; + + # nvim autoread / :checktime react to focus. home-manager defaults to false. + focusEvents = true; + + # Default is 2000. + historyLimit = 50000; + + # Window numbers matching the number row. Behaviour change, so opt in. + # baseIndex = 1; + extraConfig = '' # Status keys set -g status-keys vi + + # Tell tmux the outer terminal can do 24-bit colour (tmux >= 3.2). + set -sa terminal-features ",*:RGB" + + # --- Shift+Enter / extended keys ----------------------------------- + # Still the open topic; uncommenting these makes tmux forward CSI-u + # sequences unconditionally. Needs tmux >= 3.5 for the format option, + # and a `tmux kill-server` afterwards -- a config reload is not enough. + #set -s extended-keys always + #set -s extended-keys-format csi-u + #set -as terminal-features '*:extkeys' + # Status bar options - set -g status-interval 1 - set -g status on + set -g status-interval 5 # was 1; the clock only has minute resolution set -g status-justify left + # Clock mode setw -g clock-mode-colour cyan + # Colors set -g status-bg black set -g status-fg white + # Key bindings - # Reload configuration - bind r source-file ~/.config/tmux/tmux.conf - # Panel split and selection + # Reload configuration. tmux.conf is a read-only symlink into the nix + # store, so this only picks up changes after `home-manager switch`. + bind r source-file ~/.config/tmux/tmux.conf \; display-message "tmux.conf reloaded" + + # Panel split and selection -- new panes/windows inherit the cwd unbind % unbind '"' - bind v split-window -v - bind c split-window -h - bind i new-window - bind b previous-window - bind n next-window + bind v split-window -v -c "#{pane_current_path}" + bind c split-window -h -c "#{pane_current_path}" + bind i new-window -c "#{pane_current_path}" + bind -r b previous-window + bind -r n next-window + # Move around panes with hjkl bind h select-pane -L bind j select-pane -D bind k select-pane -U bind l select-pane -R - # Resize panes - bind H resize-pane -L 5 - bind J resize-pane -D 5 - bind K resize-pane -U 5 - bind L resize-pane -R 5 + + # Resize panes -- -r makes them repeatable, hold instead of re-prefixing + bind -r H resize-pane -L 5 + bind -r J resize-pane -D 5 + bind -r K resize-pane -U 5 + bind -r L resize-pane -R 5 + # Wayland clipboard integration # Copy selection to both clipboard (Ctrl+V / Shift+Insert) and primary (middle-click) + # v/C-v added: you had no way to *start* a selection from the keyboard. + 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 y send-keys -X copy-pipe-and-cancel "wl-copy && wl-copy --primary" bind -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "wl-copy && wl-copy --primary" bind -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel "wl-copy && wl-copy --primary" + # Uncomment if you want yanks to also travel over SSH via OSC52. + # Locally redundant with wl-copy above. + #set -g set-clipboard on + # Kill commands - bind q kill-window - bind Q kill-session + # Drop the `confirm-before -p "..."` part for the old single keystroke. + bind q confirm-before -p "kill window #W? (y/n)" kill-window + bind Q confirm-before -p "kill session #S? (y/n)" kill-session + # Bars + # This is the nord-tmux status line. #{prefix_highlight} came from that + # theme's tmux-prefix-highlight integration -- a plugin you never loaded, + # so the token expanded to nothing. tmux can do it natively. Commas inside + # #{?...} are argument separators and have to be escaped as #, hence the + # look of the style spec. set -g status-left-length 24 - set -g status-left "#[fg=black,bg=blue,bold] #{=20:session_name} #[fg=blue,bg=black,nobold,noitalics,nounderscore]" - set -g status-right "#{prefix_highlight}#[fg=brightblack,bg=black,nobold,noitalics,nounderscore]#[fg=white,bg=brightblack] %Y-%m-%d #[fg=white,bg=brightblack,nobold,noitalics,nounderscore]#[fg=white,bg=brightblack] %H:%M #[fg=cyan,bg=brightblack,nobold,noitalics,nounderscore]#[fg=black,bg=cyan,bold] #H " + set -g status-left "#[fg=black,bg=blue,bold] #{=20:session_name} #[fg=blue,bg=black,nobold,noitalics,nounderscore]${sep.right}" + set -g status-right "#{?client_prefix,#[fg=black#,bg=yellow#,bold] ^A ,}#[fg=brightblack,bg=black,nobold,noitalics,nounderscore]${sep.left}#[fg=white,bg=brightblack] %Y-%m-%d #[fg=white,bg=brightblack,nobold,noitalics,nounderscore]${sep.leftThin}#[fg=white,bg=brightblack] %H:%M #[fg=cyan,bg=brightblack,nobold,noitalics,nounderscore]${sep.left}#[fg=black,bg=cyan,bold] #H " + # Windows - set -g window-status-format "#[fg=black,bg=brightblack,nobold,noitalics,nounderscore] #[fg=white,bg=brightblack]#I #[fg=white,bg=brightblack,nobold,noitalics,nounderscore] #[fg=white,bg=brightblack]#W #F #[fg=brightblack,bg=black,nobold,noitalics,nounderscore]" - set -g window-status-current-format "#[fg=black,bg=cyan,nobold,noitalics,nounderscore] #[fg=black,bg=cyan]#I #[fg=black,bg=cyan,nobold,noitalics,nounderscore] #[fg=black,bg=cyan]#W #F #[fg=cyan,bg=black,nobold,noitalics,nounderscore]" + set -g window-status-format "#[fg=black,bg=brightblack,nobold,noitalics,nounderscore]${sep.right} #[fg=white,bg=brightblack]#I #[fg=white,bg=brightblack,nobold,noitalics,nounderscore]${sep.rightThin} #[fg=white,bg=brightblack]#W #F #[fg=brightblack,bg=black,nobold,noitalics,nounderscore]${sep.right}" + set -g window-status-current-format "#[fg=black,bg=cyan,nobold,noitalics,nounderscore]${sep.right} #[fg=black,bg=cyan]#I #[fg=black,bg=cyan,nobold,noitalics,nounderscore]${sep.rightThin} #[fg=black,bg=cyan]#W #F #[fg=cyan,bg=black,nobold,noitalics,nounderscore]${sep.right}" set -g window-status-separator "" ''; };