Skip to content

seqizz/fold-er

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fold-er

A TUI tool that helps navigating terminal scrollback buffer by folding command outputs. Navigate between commands, view their outputs, yank full or partial results to clipboard via line selection.

Supports tmux, WezTerm, kitty, and GNU screen with auto-detection.

Warning: Mostly vibe coded in 30 minutes for fun. Expect rough edges and quirks. Contributions welcome!

Why

Terminal is mostly flat stream of text: prompt, output, another prompt, another output...

When you have a long session, finding a specific command's output means scrolling through everything. fold-er parses your scrollback into discrete command blocks and lets you browse them like a folding editor, one command at a time.

Usage

# Auto-detect multiplexer, capture scrollback, open viewer in new window/tab
fold-er

# Open in a horizontal split instead of new window
fold-er --split h

# Open in a vertical split
fold-er --split v

Running fold-er with no arguments detects your terminal multiplexer, captures scrollback from the current pane, and spawns the viewer in a new window (or split). No external scripts needed.

Direct mode

When an explicit input source is given, fold-er opens the TUI directly in the current terminal:

# Read from specific wezterm pane
fold-er --pane-id 3

# Read scrollback text from file
fold-er --text-file /tmp/scrollback.txt

# Pipe from stdin
some-command | fold-er --stdin

Keybindings

Normal mode

Key Action
j/k, Up/Down Navigate between commands
PgUp/PgDn Scroll output pane
Ctrl+u/d Scroll output pane (vim-style)
g/Home Jump to first command
G/End Jump to last command
y Yank full output to clipboard
v Enter line select mode
q, Esc Quit

Line select mode

Press v to enter line select mode. The output pane shows a cursor on a single line.

  1. Browse phase: Navigate to the start of your selection using j/k or arrow keys.
  2. Mark start: Press Enter or Space to mark the beginning of the selection.
  3. Extend: Move with j/k or arrow keys to extend the selection to the end.
  4. Yank: Press y to copy the selected lines to clipboard.
Key Action
j/k, Up/Down Browse (phase 1) / Extend (phase 2)
PgUp/PgDn Jump cursor by one page
Ctrl+u/d Jump cursor by one page (vim-style)
Enter, Space Mark start of selection
g/Home Jump cursor to first line
G/End Jump cursor to last line
y Yank selected lines (after marking)
v, Esc, q Cancel and return to normal mode

Prompt detection

fold-er uses three strategies to identify command boundaries, in priority order:

  1. OSC 133 semantic zones (via --zones-file): Pre-extracted JSON from wezterm's Lua API. Most reliable but currently broken with wezterm's mux mode (see known issues).
  2. OSC 133 markers in text: If the captured scrollback contains OSC 133 escape sequences (tmux and wezterm preserve these), fold-er parses them directly.
  3. Heuristic auto-detection: Looks at the last line of scrollback (your current prompt), extracts the hostname prefix and prompt ending (e.g. ~> , $ , # ), then matches all lines with the same pattern.

Heuristic detection works well in most cases but is not bulletproof. It can misfire when scrollback is dominated by log output (e.g. dmesg inside GNU screen with a small scrollback buffer). Use --prompt to override:

# Tell fold-er your prompt contains "~>"
fold-er --prompt "~>"

# Also works in direct mode
fold-er --text-file /tmp/sb.txt --prompt "myhost $"

The --prompt value is matched as a substring against each line. Any line containing it is treated as a command prompt.

Multiplexer support

fold-er auto-detects which multiplexer you're running in. Detection order:

Multiplexer Detection Capture method Split support
tmux $TMUX set tmux capture-pane -p -e Yes
WezTerm $WEZTERM_PANE set wezterm cli get-text --escapes Yes
kitty $KITTY_PID + $TERM=xterm-kitty kitty @ get-text --ansi Yes
GNU screen $STY + $TERM=screen* screen -X hardcopy -h No (new window only)

Detection ignores inherited env vars from parent terminals. For example, launching alacritty from within WezTerm won't falsely detect WezTerm as the active multiplexer.

Standalone terminals (alacritty, foot, ghostty, etc.) don't expose their scrollback buffer programmatically. If you use one of these, run tmux or screen inside it and fold-er will work through that. Alternatively, use --text-file or --stdin to feed scrollback manually.

GNU screen note: hardcopy -h can only capture what screen keeps in its scrollback buffer. The default is 100 lines, which is very small. Add defscrollback 20000 to your ~/.screenrc for useful results.

tmux keybind

# In .tmux.conf
bind-key f run-shell "fold-er"
bind-key F run-shell "fold-er --split h"

WezTerm keybind

Option A (type in terminal or bind to shell alias):

fold-er

Option B (keybind with callback, passes correct pane ID):

{
  key = 'f',
  mods = 'ALT',
  action = wezterm.action_callback(function(win, pane)
    wezterm.background_child_process({
      'fold-er', '--source-pane', tostring(pane:pane_id())
    })
  end),
},

Note: --source-pane is needed for WezTerm keybinds because $WEZTERM_PANE in a SpawnCommand context points to the new pane, not the source.

kitty keybind

Requires allow_remote_control yes (or allow_remote_control socket-only) in kitty.conf.

# In kitty.conf
map ctrl+shift+f launch --type=background fold-er

Clipboard (yank)

Pressing y pipes the selected command's output (ANSI-stripped) to a clipboard command via stdin. In line select mode (v), only the selected lines are yanked. The clipboard command is auto-detected from your environment:

Environment Command
Wayland ($WAYLAND_DISPLAY set) wl-copy
X11 ($DISPLAY set) xclip -selection clipboard, or xsel --clipboard --input
macOS pbcopy
SSH/tmux (fallback) oscyank

Override with --yank-cmd:

fold-er --text-file /tmp/sb.txt --yank-cmd 'xclip -selection clipboard'

Known issues

wezterm mux: get_semantic_zones() returns empty

When using wezterm with unix_domains (mux mode), panes in the GUI process are ClientPane instances that proxy to LocalPane in the mux server. The ClientPane type does not override get_semantic_zones(), so it falls through to the trait default which returns an empty vec. The shell integration hooks emit OSC 133 correctly and the mux server's LocalPane has the zones, but there is no RPC call to retrieve them from the client side.

This is a missing feature in wezterm's mux client, not a configuration issue. As a workaround, fold-er falls back to wezterm cli get-text with heuristic prompt detection, which works reliably.

Relevant code path: wezterm-client/src/pane/clientpane.rs has impl Pane for ClientPane but does not implement get_semantic_zones, inheriting the empty default from mux/src/pane.rs:303.

Relevant bug report: wezterm/wezterm#4229

Building

cargo build --release

Dependencies

  • ratatui for the TUI
  • crossterm for terminal handling
  • clap for CLI args
  • Your choice of clipboard backend

About

A TUI tool to help navigating/yanking scrollback buffer

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages