2026-07-22 Tmux Picker Single-Click and the scratch-popup 127
What I set out to do
Add mouse support to the Claude tmux pane picker (Prefix+a) so clicking a session jumps to it.
What I actually did
The premise was half wrong, which was the useful part. fzf already ships double-click:accept and tmux popups already forward mouse events, so clicking did work — it just needed two clicks. The real ask collapsed to one flag.
Before wiring it, I verified left-click:accept semantics rather than assuming, because the failure mode (accepting the previously focused row instead of the clicked one) would be silent and infuriating. Built a pty harness that drives fzf with synthetic SGR mouse sequences. Two false starts: the first hung because I never drained the pty master, so fzf blocked writing its TUI; the second returned nothing because pty.openpty() gives a 0x0 window and every click landed outside the list. \x1b[-1A in the TUI byte stream was the tell. With TIOCSWINSZ set, the answers came cleanly: left-click accepts the clicked row, and clicks in the preview pane or on the prompt/info rows are inert. Both properties matter and neither is documented.
Then the aside that turned out to be a real bug: selecting the reserved popup: scratch session exited 127. The picker called $XDG_CONFIG_HOME/tmux/scripts/scratch-popup.sh, a path that no longer exists — the script is a writeShellApplication reachable only by store path. scratch-popup.sh’s own header comment claims to be the single source of truth for both M-g and the picker, which it had quietly stopped being. Fixed with a @scratchPopup@ substitution matching the existing @claudePanePreview@ pattern.
Shipped as two commits (the 127 broke Enter too, so it isn’t a mouse fix), applied via hm switch, verified against the built store paths rather than the sources.
What was striking
Two things.
The 127 is the same lesson as reference_nix_script_runtime_deps wearing a different hat: in a Nix world, call sites are dependencies too. A comment asserting “single source of truth” is not a mechanism, and this one had been false for some time without anyone noticing, because the only path that reaches it is selecting one specific entry in a picker that opens on a manual keybinding.
And: a debugging harness that returns empty is not evidence of absence. Both of my empty result tables looked like “fzf ignores synthetic mouse input” and both were harness bugs. Adding a known-good control (down + enter → item2) is what separated the two, and I should reach for that control first next time rather than third. Same shape as feedback_verify_the_question_not_just_the_answer.
Related
Continues the tmux/Claude integration thread from 2026-07-21 Neovim Claude Integration Fixes and 2026-06-29 Tmux Width-Responsive Statusline.