Core Principle

tmux capture-pane -ep gives you only what the running program actually wrote: typically foreground colors and trimmed trailing whitespace. It does not synthesize a background, and it does not pad cells. If you want a rectangular block with a consistent background (for example, matching Neovim’s Normal highlight), you have to wrap and pad every line yourself with explicit truecolor bg sequences and re-apply the bg after any embedded \e[0m or \e[49m.

Why This Matters

The intuition “capture-pane gives me what I see” is wrong in two directions. First: programs like Claude Code set foreground color but leave background to whatever the terminal’s default is, so empty cells arrive with no ANSI at all and render as whichever surrounding tool’s default bg (fzf, another terminal) which looks washed-out and dull. Second: trailing whitespace is stripped, so a “full-width” pane capture arrives as variable-length short lines. Any downstream renderer that expects a rectangular block has to reconstruct both the width and the background.

Evidence/Examples

  • No background in capture: Claude Code’s output captured via tmux capture-pane -ep contains \e[38;2;R;G;Bm foreground escapes and no \e[48;...m background escapes. Rendered inside an fzf popup, past-content cells show fzf’s default background instead of the source terminal’s.
  • Trailing whitespace stripped: A 210-column pane with a 40-column status line is captured as a 40-character string for that row. Anything downstream expecting 210 has to pad.
  • Fix pattern: For each captured line, emit \e[48;2;R;G;Bm + content + space-padding to target width + \e[0m, and after any embedded \e[0m or \e[49m inside the content, re-apply the bg prefix so the rest of the line keeps painting.
  • $TMUX_PANE is inherited by children: A Claude Code process running inside a Neovim terminal buffer inherits the outer tmux pane’s $TMUX_PANE. Hooks that need the outer pane don’t need to walk a process tree.
  • pane-focus-in hook: Fires with #{pane_id} of the newly-focused pane available for expansion inside the hook command. Run with -b so the hook command never blocks focus changes.

Implications

  • Any tool that embeds tmux-captured content inside another ANSI renderer (fzf, a second tmux pane, an external pager) needs to own the background and width, not delegate them to the renderer.
  • When the source is Neovim, query Neovim for its Normal bg (not the terminal’s default) so the capture visually integrates with what the user actually sees.
  • Environment-variable-driven pane identification is trustworthy across nested shells, editors, and REPLs. Prefer it over process-tree walking.

Questions

  • Is there a tmux option to force capture to be a full rectangular block (padded to pane width)? capture-pane -J joins wrapped lines but doesn’t pad.
  • Can capture-pane be told to synthesize a background for cells that have none, or is that always the consumer’s job?