Skip to content

Inline images

Shotty speaks two inline-image protocols, in both directions:

  • iTerm2's OSC 1337 — also accepted by WezTerm.
  • Kitty graphics protocol — also accepted by Ghostty.

Receiving images from child commands

When the child writes an inline-image escape sequence to its stdout or stderr, Shotty decodes the embedded PNG, places it on the cell grid at the cursor's current position, and advances the cursor below the image (matching iTerm2's documented behaviour).

Auto-handled details:

  • Chunked Kitty payloads are accumulated across m=1 continuation packets until the final m=0 arrives.
  • Whitespace inside the base64 payload is tolerated.
  • Fully-transparent edges are trimmed off the received image before placement, so PNGs with their own shadow margin (a child shotty is the prime example) sit flush against the outer grid instead of floating with a transparent halo.
  • Image scaling uses 2 image-pixels per logical point (Retina convention), so a 200×150 px image takes the same visual space as it would in iTerm2.

What's supported

Protocol Format Status
OSC 1337 base64-encoded PNG/JPEG (anything ImageIO decodes)
Kitty f=100 (PNG) base64-encoded PNG, single or chunked
Kitty f=24 / f=32 (raw RGB/RGBA) not yet

Emitting the resulting PNG inline

When stdout is a TTY and the terminal advertises support, Shotty also emits the freshly-written PNG inline after writing it. So you see your screenshot in your terminal as soon as it appears on disk.

Detection uses environment variables:

Env signal Protocol used
KITTY_WINDOW_ID set Kitty graphics
TERM contains kitty or ghostty Kitty graphics
TERM_PROGRAM=iTerm.app OSC 1337
TERM_PROGRAM=WezTerm OSC 1337

The --inline flag overrides detection (auto / on / off).

The meta shotty trick

When Shotty runs a child command under PTY, it sets KITTY_WINDOW_ID=1 in the child's environment. So a child shotty — or any Kitty-aware tool — sees a Kitty-capable terminal, auto-detects, and emits its render as Kitty graphics, which the outer composites into its frame:

shotty 'shotty -o /tmp/inner.png "ls -la | head -8"'

nested screenshot

The "📸 Wrote /tmp/inner.png …" line is the inner shotty's own status output, captured below the embedded image just like it would appear in your real terminal.

The same composition works for any tool that emits OSC 1337 or Kitty graphics:

Tool What you get
Another shotty Inner shotty's screenshot inside the outer's frame.
imgcat <file> A real image inside your screenshot.
wezterm imgcat <file> Same.
kitten icat <file> Same.
chafa --format=kitty <file> Same.

See also