diff --git a/.gitignore b/.gitignore index b149de9..d4ab119 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ /docs/superpowers/ /docs/ideation/ /docs/plans/ +/docs/brainstorms/ # Sketchybar helper build artifacts. These are compiled by `make` when # `run_onchange_after_40-sketchybar-deps.sh.tmpl` runs on each machine — diff --git a/home/.chezmoiexternal.toml b/home/.chezmoiexternal.toml index 687b18a..cf1c7e8 100644 --- a/home/.chezmoiexternal.toml +++ b/home/.chezmoiexternal.toml @@ -2,3 +2,41 @@ type = "git-repo" url = "https://github.com/catppuccin/zsh-syntax-highlighting.git" refreshPeriod = "168h" + +# --- tmux plugins (vendored; sourced from dot_config/tmux/tmux.conf) --- +# Vendored to ~/.config/tmux/plugins// so one tmux.conf works under +# chezmoi here and a plain dotfiles repo on the VPS (no chezmoi-only paths +# in the config). vim-tmux-navigator is intentionally NOT vendored — its +# tmux side is a handful of stable bind-key lines written inline in tmux.conf. + +# catppuccin/tmux — PINNED to v2.3.0 (it shipped breaking v2 syntax, so a +# moving target would break the status line). The key below IS the clone +# destination dir; catppuccin.tmux sits at the repo root, so the full path +# `~/.config/tmux/plugins/catppuccin/tmux/catppuccin.tmux` resolves. +# Pinned entries omit refreshPeriod — the shallow tag clone is frozen. +[".config/tmux/plugins/catppuccin/tmux"] +type = "git-repo" +url = "https://github.com/catppuccin/tmux.git" + +[".config/tmux/plugins/catppuccin/tmux".clone] +args = ["--branch", "v2.3.0", "--depth", "1"] + +# tmux-menus — discoverability popup (pure POSIX/tmux script, no build step, +# no Python, no submodules). Unpinned: stable and self-contained. +[".config/tmux/plugins/tmux-menus"] +type = "git-repo" +url = "https://github.com/jaclu/tmux-menus.git" +refreshPeriod = "168h" + +# tmux-resurrect — save/restore sessions, windows, panes across reboots. +[".config/tmux/plugins/tmux-resurrect"] +type = "git-repo" +url = "https://github.com/tmux-plugins/tmux-resurrect.git" +refreshPeriod = "168h" + +# tmux-continuum — automatic save + restore-on-start. MUST be sourced last in +# tmux.conf (after catppuccin) so its status-right save hook is not overwritten. +[".config/tmux/plugins/tmux-continuum"] +type = "git-repo" +url = "https://github.com/tmux-plugins/tmux-continuum.git" +refreshPeriod = "168h" diff --git a/home/dot_config/nvim/lua/plugins/blink.lua b/home/dot_config/nvim/lua/plugins/blink.lua new file mode 100644 index 0000000..9eaea4b --- /dev/null +++ b/home/dot_config/nvim/lua/plugins/blink.lua @@ -0,0 +1,14 @@ +-- The tmux prefix is C-Space, and tmux intercepts the prefix key before the +-- focused app (Neovim) ever sees it. blink.cmp's default binding +-- (toggle completion) would therefore be a dead key inside tmux. Free it so it +-- isn't a phantom binding; LazyVim's blink shows the completion menu +-- automatically as you type, so no manual trigger is needed. If you ever want a +-- manual trigger reachable inside tmux, bind one to a non-prefix key here. +return { + "saghen/blink.cmp", + opts = { + keymap = { + [""] = false, + }, + }, +} diff --git a/home/dot_config/nvim/lua/plugins/editor.lua b/home/dot_config/nvim/lua/plugins/editor.lua new file mode 100644 index 0000000..dd58978 --- /dev/null +++ b/home/dot_config/nvim/lua/plugins/editor.lua @@ -0,0 +1,29 @@ +return { + -- Seamless C-h/j/k/l navigation across Neovim splits AND tmux panes. + -- The tmux half lives in ~/.config/tmux/tmux.conf (inline bind-key lines). + -- Safe to lazy-load: the tmux side does the "is this pane running vim?" check, + -- so Neovim only needs to register these commands/keys. + { + "christoomey/vim-tmux-navigator", + cmd = { + "TmuxNavigateLeft", + "TmuxNavigateDown", + "TmuxNavigateUp", + "TmuxNavigateRight", + "TmuxNavigatePrevious", + "TmuxNavigatorProcessList", + }, + keys = { + { "", "TmuxNavigateLeft" }, + { "", "TmuxNavigateDown" }, + { "", "TmuxNavigateUp" }, + { "", "TmuxNavigateRight" }, + { "", "TmuxNavigatePrevious" }, + }, + }, + + -- Keeps a live Session.vim in the cwd so tmux-resurrect's + -- @resurrect-strategy-nvim 'session' can restore your Neovim session + -- (open buffers/layout) after a reboot. No configuration needed. + { "tpope/vim-obsession" }, +} diff --git a/home/dot_config/tmux/tmux.conf b/home/dot_config/tmux/tmux.conf index e69de29..5376942 100644 --- a/home/dot_config/tmux/tmux.conf +++ b/home/dot_config/tmux/tmux.conf @@ -0,0 +1,101 @@ +# ~/.config/tmux/tmux.conf — LazyVim-cohesive, remote-first tmux +# +# Mental model: tmux is a durable, per-host workspace. Sessions live on the +# host (M5 / Mac Mini / VPS); your terminals (Ghostty, Termius) are viewports. +# Detach and the work keeps running; reattach from any device and it's there. +# +# Prefix is C-Space (Ctrl+Space) — the terminal-layer sibling of Neovim's +# Space leader. Forgot a binding? Press it, then Space, for the menu (tmux-menus, +# wired up below). hjkl moves panes; C-hjkl crosses into Neovim splits. + +# ── Prefix ─────────────────────────────────────────────────────────────────── +unbind C-b # drop the default prefix +set -g prefix C-Space # leader = Ctrl+Space +bind C-Space send-prefix # press it twice to reach a nested (e.g. SSH) tmux + +# ── Correctness / terminal fidelity ────────────────────────────────────────── +# tmux-256color advertises italics; truecolor + undercurl are NOT in the base +# terminfo on any platform, so we add them as capability overrides below. +set -g default-terminal "tmux-256color" +set -as terminal-features ",xterm-256color:RGB" # truecolor for Neovim +set -as terminal-features ",xterm-256color:clipboard" # OSC52 capability +# undercurl + colored underlines (LSP diagnostics in Neovim) +set -as terminal-overrides ',*:Smulx=\E[4::%p1%dm' +set -as terminal-overrides ',*:Setulc=\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m' + +set -s set-clipboard on # accept OSC52 from inner apps (Neovim 0.10+ yank) +set -g allow-passthrough on # let Neovim's DCS-wrapped OSC52 reach the terminal +set -g escape-time 0 # kill the ESC lag that plagues Neovim in insert mode +set -g focus-events on # forward focus to Neovim (autoread, etc.) + +# ── Ergonomics ─────────────────────────────────────────────────────────────── +set -g mouse on +set -g base-index 1 # windows start at 1 (0 is a long thumb reach) +setw -g pane-base-index 1 # panes too +set -g renumber-windows on # close a window → no gaps in the numbering + +# ── Copy mode (vim idioms) ─────────────────────────────────────────────────── +# v to start a selection, y to yank — yank rides OSC52 to your LOCAL clipboard, +# even over SSH. (Pasting INTO tmux uses the terminal's own paste; that's a tmux +# limitation, not a bug.) +setw -g mode-keys vi +bind -T copy-mode-vi v send-keys -X begin-selection +bind -T copy-mode-vi y send-keys -X copy-selection-and-cancel + +# ── Seamless nav: vim-tmux-navigator (tmux side, written inline) ───────────── +# C-h/j/k/l moves between tmux panes AND Neovim splits with the same keys and no +# prefix. The is_vim check asks "is the focused pane running Neovim?" — if so the +# keys pass through to Neovim; otherwise tmux moves the pane. (Neovim side is the +# christoomey/vim-tmux-navigator plugin spec.) +# [^[:space:]] (POSIX non-whitespace), not \S — \S is a GNU/PCRE extension that +# breaks under macOS/BSD grep -E, where `ps -o comm=` returns nvim's full path. +vim_pattern='([^[:space:]]+/)?g?\.?(view|l?n?vim?x?|fzf)(diff)?(-wrapped)?' +is_vim="ps -o state= -o comm= -t '#{pane_tty}' \ + | grep -iqE '^[^TXZ ]+ +${vim_pattern}$'" +bind -n 'C-h' if-shell "$is_vim" 'send-keys C-h' 'select-pane -L' +bind -n 'C-j' if-shell "$is_vim" 'send-keys C-j' 'select-pane -D' +bind -n 'C-k' if-shell "$is_vim" 'send-keys C-k' 'select-pane -U' +bind -n 'C-l' if-shell "$is_vim" 'send-keys C-l' 'select-pane -R' +bind -n 'C-\' if-shell "$is_vim" 'send-keys C-\\' 'select-pane -l' # last pane + +# ── Mnemonic bindings (small on purpose — the menu surfaces the rest) ──────── +# Splits open in the CURRENT pane's directory, so a new pane lands where you are. +bind | split-window -h -c "#{pane_current_path}" # | looks like a vertical wall +bind - split-window -v -c "#{pane_current_path}" # - looks like a horizontal one +bind c new-window -c "#{pane_current_path}" +bind r source-file ~/.config/tmux/tmux.conf \; display-message "tmux.conf reloaded" +# zoom a pane to fullscreen is prefix-z (built in); repeat to unzoom. + +# ═══ Plugins ═════════════════════════════════════════════════════════════════ +# Vendored to ~/.config/tmux/plugins/ by .chezmoiexternal.toml. The LOAD ORDER +# below is load-bearing: continuum extends status-right with its autosave hook, +# and catppuccin SETS status-right — so catppuccin must load before continuum or +# the autosave is silently lost. Order: menus → catppuccin → resurrect → continuum. + +# tmux-menus — press Space for a popup menu of tmux actions and their +# keys (the discoverability layer). Pure tmux script; nothing to build. +set -g @menus_trigger 'Space' +run-shell ~/.config/tmux/plugins/tmux-menus/menus.tmux + +# catppuccin (mocha) — theme + status line. Built-in modules only; cpu/ram/battery +# would need companion plugins (deferred). Sourced before resurrect/continuum. +set -g @catppuccin_flavor "mocha" +set -g @catppuccin_window_status_style "rounded" +run ~/.config/tmux/plugins/catppuccin/tmux/catppuccin.tmux +set -g status-right-length 100 +set -g status-left-length 100 +set -g status-left "" +set -g status-right "#{E:@catppuccin_status_application}" +set -agF status-right "#{E:@catppuccin_status_session}" +set -agF status-right "#{E:@catppuccin_status_date_time}" + +# tmux-resurrect — save/restore sessions. Restore Neovim sessions too (needs +# tpope/vim-obsession on the Neovim side, installed via lazy.nvim). +set -g @resurrect-strategy-nvim 'session' +run-shell ~/.config/tmux/plugins/tmux-resurrect/resurrect.tmux + +# tmux-continuum — auto-save every 15 min + restore on tmux start. MUST be the +# last plugin sourced (see the load-order note above). +set -g @continuum-restore 'on' +set -g @continuum-save-interval '15' +run-shell ~/.config/tmux/plugins/tmux-continuum/continuum.tmux diff --git a/home/dot_config/zsh/abbreviations b/home/dot_config/zsh/abbreviations index e858adb..9d60e47 100644 --- a/home/dot_config/zsh/abbreviations +++ b/home/dot_config/zsh/abbreviations @@ -74,3 +74,7 @@ abbr rgfzf="rg --line-number --no-heading . | fzf --ansi" abbr fdfzf="fd --type f | fzf --preview 'bat --color=always {}'" +# tmux — attach to (or create) the per-host "main" session. -d detaches any other +# viewport so it takes over full-size when you hop devices (no screen clamping). +abbr ta="tmux attach -d -t main || tmux new -s main" +