Add tmux-fzf for session/window/pane navigation #4

Merged
branderson merged 1 commits from tmux-fzf-navigation into master 2026-08-02 23:30:31 -07:00
Owner

Summary generated by LLM agent

What

Adds sainnhe/tmux-fzf and three bindings, to solve two workflow problems:
seeing what is running across many sessions/windows/panes, and moving a pane
into another window without hand-typing a join-pane target.

Changes

  • config/tmux/plugins/tmux-fzf — new submodule
  • config/tmux.conf — @plugin line plus prefix+P / prefix+W /
    prefix+S bound directly to the pane, window and session panels

prefix+F (the plugin's own top-level menu) comes for free. prefix+w
(choose-tree) is deliberately untouched — it costs nothing and stays as a
fallback where the plugin isn't installed.

Reasoning

Why a submodule rather than letting TPM clone it. ~/.tmux symlinks to
config/tmux, so anything TPM installs lands inside this repo as untracked
files and dirties git status. A submodule pins a version and installs via
the git submodule update --init already in install.sh, matching how tpm
itself is handled. TPM still sources it from the @plugin line and can still
update it.

Why TMUX_FZF_CLIENT on each binding. A command run via run-shell has
no client context, so tmux falls back to the most recently active client.
With a second client attached to the same session — a browser terminal over
ttyd, say — the action lands in the wrong terminal. The plugin's own binding
sets this; the direct bindings need it too.

No env-var configuration. The plan called for setting TMUX_FZF_PREVIEW
and TMUX_FZF_ORDER, but reading the source showed both were unnecessary:
preview defaults to on, and the order default already leads with
session/window/pane. Worth noting the plugin's README suggests setting
TMUX_FZF_LAUNCH_KEY="C-f" in .tmux.conf — that is not valid tmux syntax
and would raise an unknown-command error. main.tmux reads these from the
environment only.

A planned install.sh change was dropped. The design assumed nothing
bootstrapped TPM. That was wrong: tpm is already a tracked submodule and
install.sh already runs git submodule update --init. No installer change
is needed, which also avoids adding a network clone to the install path.

Testing

Verified against tmux 3.5a and fzf 0.38.0:

  • Config parses with no errors; server starts cleanly
  • prefix+P, prefix+W, prefix+S all register with the expected commands
  • Existing bindings (T, a, y, r, R, C) are unaffected
  • TPM sources the plugin and creates the prefix+F binding
  • pane.sh, window.sh, session.sh pass bash -n
  • The plugin's fzf-version detection resolves to fzf-tmux with preview
    enabled and popup sizing (-p -w 62% -h 38% -m)

Not covered: the interactive fzf picker needs a real terminal, so driving the
pickers and performing an actual pane join is left to manual verification.

**Summary generated by LLM agent** ## What Adds `sainnhe/tmux-fzf` and three bindings, to solve two workflow problems: seeing what is running across many sessions/windows/panes, and moving a pane into another window without hand-typing a `join-pane` target. ## Changes - `config/tmux/plugins/tmux-fzf` — new submodule - `config/tmux.conf` — `@plugin` line plus `prefix+P` / `prefix+W` / `prefix+S` bound directly to the pane, window and session panels `prefix+F` (the plugin's own top-level menu) comes for free. `prefix+w` (`choose-tree`) is deliberately untouched — it costs nothing and stays as a fallback where the plugin isn't installed. ## Reasoning **Why a submodule rather than letting TPM clone it.** `~/.tmux` symlinks to `config/tmux`, so anything TPM installs lands inside this repo as untracked files and dirties `git status`. A submodule pins a version and installs via the `git submodule update --init` already in `install.sh`, matching how `tpm` itself is handled. TPM still sources it from the `@plugin` line and can still update it. **Why `TMUX_FZF_CLIENT` on each binding.** A command run via `run-shell` has no client context, so tmux falls back to the most recently active client. With a second client attached to the same session — a browser terminal over ttyd, say — the action lands in the wrong terminal. The plugin's own binding sets this; the direct bindings need it too. **No env-var configuration.** The plan called for setting `TMUX_FZF_PREVIEW` and `TMUX_FZF_ORDER`, but reading the source showed both were unnecessary: preview defaults to on, and the order default already leads with session/window/pane. Worth noting the plugin's README suggests setting `TMUX_FZF_LAUNCH_KEY="C-f"` in `.tmux.conf` — that is not valid tmux syntax and would raise an unknown-command error. `main.tmux` reads these from the environment only. **A planned `install.sh` change was dropped.** The design assumed nothing bootstrapped TPM. That was wrong: `tpm` is already a tracked submodule and `install.sh` already runs `git submodule update --init`. No installer change is needed, which also avoids adding a network clone to the install path. ## Testing Verified against tmux 3.5a and fzf 0.38.0: - Config parses with no errors; server starts cleanly - `prefix+P`, `prefix+W`, `prefix+S` all register with the expected commands - Existing bindings (`T`, `a`, `y`, `r`, `R`, `C`) are unaffected - TPM sources the plugin and creates the `prefix+F` binding - `pane.sh`, `window.sh`, `session.sh` pass `bash -n` - The plugin's fzf-version detection resolves to `fzf-tmux` with preview enabled and popup sizing (`-p -w 62% -h 38% -m`) Not covered: the interactive fzf picker needs a real terminal, so driving the pickers and performing an actual pane join is left to manual verification.
branderson added 1 commit 2026-08-02 23:07:19 -07:00
Many concurrent sessions with many panes each make it hard to track what is
running where, and moving a pane between windows means join-pane with a
hand-typed target, so it rarely gets done.

tmux-fzf gives a fuzzy-searchable list of every session, window and pane with
a live preview of each, and implements break/join/swap as pane actions --
which is the pane-movement gap. fzf is already in apt.global, arch.global and
brew.global, so no new packages are needed.

Checked out as a submodule rather than left for TPM to clone on first
prefix+I: ~/.tmux symlinks to config/tmux, so a TPM-installed plugin lands
inside this repo as untracked files. The submodule pins a version and
installs via the existing "git submodule update --init". TPM still sources it
from the @plugin line and can still update it.

prefix+F is the plugin's own top-level menu. prefix+P/W/S skip straight to
the pane, window and session panels. TMUX_FZF_CLIENT pins each action to the
client that opened it, as the plugin's own binding does -- run-shell has no
client context, so tmux otherwise targets the most recently active client,
which is wrong when a second client is attached to the same session.

No env-var configuration was needed: preview is already on by default, and
the TMUX_FZF_* variables are read from the environment only, so the README's
suggestion to set them in .tmux.conf is not valid tmux syntax.

Tested against tmux 3.5a and fzf 0.38.0: config parses with no errors, all
three bindings register, existing bindings are unaffected, TPM sources the
plugin and binds prefix+F, the three scripts pass bash -n, and the plugin's
fzf-version detection resolves to fzf-tmux with preview enabled. The
interactive picker itself needs a real terminal and is left to manual check.
branderson merged commit fe27e8178e into master 2026-08-02 23:30:31 -07:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: branderson/dotfiles#4