Inline previews
gifgrep ships GIF bytes into your terminal so you can see results without leaving the shell. There are two protocols at play.
| Terminal | Protocol | TUI animation | CLI --thumbs |
|---|---|---|---|
| Kitty | Kitty graphics | Native, hardware | Still frame |
| Ghostty | Kitty graphics | Software (frame ticks) | Still frame |
| iTerm2 | OSC 1337 (Inline Images) | Native (raw GIF) | Animated thumb |
| WezTerm | Kitty graphics (subset) | Best-effort | Still frame |
| Apple Terminal | Neither | Falls back to text | No thumbs |
The CLI never outputs image bytes when stdout is a pipe — only on a TTY.
#Kitty graphics
Used by Kitty and Ghostty. The escape envelope:
ESC _G <key=value;...> ; <base64 payload> ESC \
gifgrep uses these actions:
| Action | What it does |
|---|---|
a=T | Upload a base image (PNG bytes). |
a=f | Append an animation frame with per-frame delay. |
a=a | Set animation timing / start playback. |
a=p | Place an image into a cell rectangle. |
a=d | Delete an image by id (cleanup on scroll/quit). |
Each PNG payload is base64 encoded and chunked into 4096-character pieces to stay under terminal limits.
Kitty plays the animation natively once frames are uploaded.
Ghostty does not yet animate Kitty graphics natively, so gifgrep falls back to software playback: it ticks frames on a timer and re-uploads the current frame each time. That's the GIFGREP_SOFTWARE_ANIM knob:
GIFGREP_SOFTWARE_ANIM=1 gifgrep tui cats # always tick in software
GIFGREP_SOFTWARE_ANIM=0 gifgrep tui cats # always trust terminal animation
By default, software playback is auto-enabled when Ghostty is detected.
Cell aspect ratio for sizing previews:
GIFGREP_CELL_ASPECT=0.5 gifgrep tui cats # default
GIFGREP_CELL_ASPECT=0.45 gifgrep tui cats # tweak if previews look squashed
Reference: https://sw.kovidgoyal.net/kitty/graphics-protocol/.
#iTerm2 (OSC 1337)
iTerm2 has its own protocol — it accepts the original file bytes directly:
ESC ] 1337 ; File = <key=value;...> : <base64 file bytes> ESC \
Keys gifgrep sets:
name— base64-encoded filename (used for the iTerm2 download tray).size— bytes (drives the progress indicator).inline=1— render inline rather than queueing a download.width/height— sized in terminal cells, not pixels.preserveAspectRatio—1in the TUI;0when filling a fixed CLI thumbnail block.
iTerm2 plays animated GIFs natively, so the TUI sends the preview GIF bytes for each result and lets iTerm2 animate them.
Reference: https://iterm2.com/documentation-images.html.
#Detection
gifgrep decides which protocol to use from environment hints:
- iTerm2:
TERM_PROGRAM=iTerm.apporITERM_SESSION_ID. - Ghostty:
TERM_PROGRAM=ghosttyorGHOSTTY_RESOURCES_DIR. - Kitty:
TERM=xterm-kitty(or compatible). - Sixel:
TERMcontainssixel,TERM_PROGRAM=WezTerm, orWT_SESSION.
Truecolor ANSI fallback is opt-in:
GIFGREP_INLINE=ansi # render GIF frames as terminal color blocks
Override:
GIFGREP_INLINE=kitty # force Kitty graphics
GIFGREP_INLINE=iterm # force OSC 1337
GIFGREP_INLINE=sixel # force DEC Sixel
GIFGREP_INLINE=ansi # force truecolor ANSI fallback
GIFGREP_INLINE=off # disable inline previews entirely
#Diagnostics
If previews look wrong:
gifgrep --version
echo "$TERM $TERM_PROGRAM"
gifgrep tui cats -vv # very verbose stderr
Common gotchas:
- Pipes never get image bytes. Inline previews only render to a TTY.
- tmux can pass through Kitty graphics with the right config, but it's terminal-dependent.
set -ga terminal-features ',xterm-256color:hyperlinks'and friends help, but expect rough edges. - SSH works as long as the destination terminal speaks the protocol; the bytes flow through the pipe.
#Related docs
- TUI — keys, behaviour, when to reach for it.
- Search
--thumbs— still-frame inline thumbs in pipe-shaped CLI output.