Core Principle
fzf has two distinct window concepts with different resize semantics: the popup (set once at launch via --tmux "center,W,H", cannot be resized after) and the preview window (can be resized per-entry via the focus:transform bind emitting a change-preview-window(...) action). To make a picker feel “native” across heterogeneous entries, size the popup up-front to the widest entry and then re-size the preview on every focus change using a hidden per-row width field.
Why This Matters
Almost every intuitive “dynamic fzf window” idea hits the popup wall: the popup dimensions are fixed for the session. The focus:transform escape hatch is easy to miss because it’s listed under “transform” events rather than preview controls, and the common wrong turn is reaching for a nonexistent transform-preview-window action. Understanding the split saves hours of iteration. A second common miss is COLORTERM: fzf in a tmux popup inherits a reduced TERM and will silently downgrade truecolor content unless COLORTERM=truecolor is set in its environment.
Evidence/Examples
focus:transform:CMD: on every focus change, fzf runsCMD, which must print a fzf action string on stdout. fzf then executes that action. This is how you get per-entry preview behavior. Example:focus:transform:printf 'change-preview-window(right,%s,noinfo,follow)' {3}where{3}is a hidden width field on each row.transform-preview-windowdoes not exist. There is no such action. Onlychange-preview-window(...)exists; the dynamism comes from generating it viafocus:transform.--with-nth=N..: hides fields1..N-1from the visible list but keeps them accessible in binds via{1},{2}, etc. This is how you attach per-row metadata (pane_id, socket, width, height) without showing it to the user.COLORTERM=truecolor: fzf detects 24-bit color support from this env var. Tmux popups commonly inheritTERM=screen-256color, and fzf will strip truecolor bg sequences to 256-color approximations withoutCOLORTERM=truecolorset. Prefix:COLORTERM=truecolor fzf ....--ansi: required for fzf to pass through any ANSI escapes from preview output. Without it, fzf strips escape sequences and the preview is plain text.
Implications
- Compute popup dimensions up-front from all entries’ dimensions (
max(widths) + list_column + padding), then letfocus:transformshrink the preview to match each entry. Don’t try to resize the popup. - Any fzf invocation that renders captured terminal content needs
--ansiplusCOLORTERM=truecolorin its env; otherwise truecolor silently degrades. - Store per-row metadata as leading hidden fields and expose them to binds via
{N}placeholders. Don’t try to parse visible content to recover metadata.
Related Ideas
- Tmux capture-pane Color Semantics — the typical producer of the bg-wrapped truecolor content fzf is rendering.
- Neovim Window Geometry and Read-Only RPC — provides the geometry that drives per-entry preview width.
Questions
- Does fzf expose any event for “popup size changed” that would let you rebuild the list layout if the parent tmux window resizes mid-session?
- Is there a way to set
COLORTERMvia fzf config rather than at invocation, so callers don’t have to remember the prefix?