shw 0.2.5

Blazing fast terminal viewer for images, videos and GitHub-flavored Markdown using the kitty graphics protocol
shw-0.2.5 is not a library.

shw

CI crates.io license

shw (show) is a very simple terminal viewer for images, videos and Markdown based on Rust. It renders with pixel precision through the kitty graphics protocol.

shw photo.jpg          # zoomable, pannable image viewer
shw clip.mp4           # video player with audio, seeking and zoom
shw README.md          # GitHub-style markdown with images, tables, alerts, emoji…
shw ~/Pictures         # browse a folder (n / p)

Features

  • Images: PNG, JPEG, GIF, WebP, BMP, TIFF, ICO, QOI, TGA, DDS, HDR, PNM and SVG (rendered as vectors, so they stay sharp at any zoom). HEIC, AVIF, JXL and RAW work through ffmpeg if it is installed.
    • Images are drawn 1:1 at native pixel resolution with Lanczos3 SIMD resampling and EXIF orientation.
    • Zoom with the mouse wheel (anchored at the pointer) or + / -. Pan by dragging or with hjkl. 0 fits the window, 1 shows actual pixels. Above 400 % pixels are drawn crisp (nearest neighbour).
    • Transparent images can be shown over a white or black background (b toggles transparent / white / black).
    • Animated GIF, WebP and APNG playback. Neighbouring files are decoded in the background, so n / p feel instant.
  • Videos (anything ffmpeg can decode). ffmpeg scales each frame to the exact pixel size of the terminal. Frames are sent through shared memory locally, or PNG-compressed over ssh.
    • Play/pause, seeking (arrow keys or a click on the progress bar), frame stepping, loop, mute, zoom and pan inside the video, and saving a frame as PNG.
    • Audio via ffplay; it is turned off automatically over ssh.
  • Markdown, rendered close to the way github.com renders it:
    • large semibold headings like GitHub's (H1 2×, H2 1.5×, H3 1.25×), typeset with a system font and drawn as images so they work in every kitty-graphics terminal; plus bold, italic and strikethrough
    • syntax-highlighted code blocks, tables with alignment and zebra striping, task lists, numbered footnotes, and > [!NOTE] / [!TIP] / [!IMPORTANT] / [!WARNING] / [!CAUTION] alerts
    • :emoji: shortcodes and clickable links (OSC 8), including bare URLs
    • inline images (local files, remote URLs, SVG badges from shields.io), HTML <img width=…> and <p align="center">
    • diagrams in fenced code blocks, as on GitHub, drawn in a few milliseconds without external tools: mermaid (flowcharts, sequence, class, state, ER, gantt, pie, …), geojson / topojson maps on a base map of country outlines, and ASCII stl 3D models
    • the document appears immediately; remote images are downloaded in the background and slot in as they arrive
    • search with /, n, N; select text with the mouse to copy it (the terminal's native selection), and click links
  • Code / text files with syntax highlighting and line numbers, and a hex view for binaries.
  • Works everywhere kitty graphics work: kitty, Ghostty, WezTerm and Konsole, over ssh, and inside tmux (Unicode placeholders + passthrough).
  • Scriptable: -i/--inline prints straight into the terminal, like icat, which suits fzf previews, file managers and scripts. It reads from stdin and URLs, and opens GitHub links (shw https://github.com/user/repo shows the README).

Install

Prebuilt binaries (Linux x86_64/aarch64 static musl, macOS Intel/Apple Silicon) go into ~/.local/bin:

curl -fsSL https://raw.githubusercontent.com/TillBeemelmanns/shw/main/install.sh | bash

From crates.io:

cargo install shw

From source:

git clone https://github.com/TillBeemelmanns/shw && cd shw
cargo install --path .

Optional runtime tools: ffmpeg/ffprobe for videos and HEIC/AVIF, ffplay for audio, and mmdc as a fallback for mermaid diagrams the built-in renderer cannot draw.

Dotfiles

Release assets follow the same naming as bat: shw-v<version>-<arch>-<target>.tar.gz, with unknown-linux-musl or apple-darwin as the target. Each archive holds the binary and shell completions. A dotfiles install script can therefore be a copy of install.sh or simply:

curl -fsSL https://raw.githubusercontent.com/TillBeemelmanns/shw/main/install.sh | bash

Usage

shw [OPTIONS] [FILE]...

  -i, --inline           print inline instead of opening the interactive viewer
  -t, --type <TYPE>      force the type: image, video, markdown, code, binary
  -l, --language <LANG>  syntax for code / stdin (rust, py, json, …)
  -W, --width <COLS>     inline width in cells (default: terminal or fzf preview)
  -H, --height <ROWS>    inline height in cells
      --upscale          enlarge small images to fill the inline area
      --theme <THEME>    auto | dark | light
      --transfer <MODE>  auto | direct | file | shm
      --placeholders     use kitty Unicode placeholders (automatic in tmux / fzf)
  -s, --start <TIME>     video start position (90, 1:30, 1:02:03)
  -m, --mute             play videos without sound
      --loop             loop videos
      --offline          do not download remote images in markdown
  -f, --force            skip terminal detection
      --doctor           print detected terminal capabilities
      --completions <SH> print a completion script (bash, zsh, fish, elvish, powershell)

With no arguments, shw reads stdin if it is piped, otherwise opens README.md in the current directory.

Keys

Image viewer Video player Markdown / code
wheel, + - zoom space play / pause j k wheel scroll
drag, hjkl pan ← → ∓ 5 s space b page
0 / f fit ↓ ↑ ∓ 30 s d u half page
1 100 % click bar seek g G top / bottom
r R rotate wheel, + - 0 zoom / n N search
n p next / prev file m l . s mute, loop, step, save frame Tab next file
t dark / bright theme
? help ? help ? help

q or Esc quits everywhere. The bottom line of every viewer lists its keys.

Integrations

fzf file preview (fzf ≥ 0.44 supports kitty images):

fzf --preview 'shw -i {}'
# or as an alias
alias fzfp="fzf --preview 'shw -i {}' --preview-window=right:60%"

yazi: open files with shw (~/.config/yazi/yazi.toml):

[opener]
shw = [{ run = 'shw "$@"', block = true, desc = "shw" }]

[open]
prepend_rules = [
  { mime = "image/*", use = ["shw", "open"] },
  { mime = "video/*", use = ["shw", "open"] },
  { name = "*.md",    use = ["shw", "edit"] },
]

lf / ranger: use shw -i "$1" as the previewer, or map a key to shw "$f".

git: render the README of any repo with shw https://github.com/user/repo.

Shell completions:

shw --completions zsh  > ~/.local/share/zsh/site-functions/_shw
shw --completions bash > ~/.local/share/bash-completion/completions/shw
shw --completions fish > ~/.config/fish/completions/shw.fish

Terminals, ssh and tmux

Terminal Status
kitty ✅ full support, including text sizing for headings
Ghostty ✅
WezTerm ✅ (no Unicode placeholders, so no tmux)
Konsole ✅ basic
others ❌ shw reports that the protocol is missing
  • ssh: works out of the box. Data is sent inline and PNG-compressed. When you zoom or pan an image, shw reuses the frame already on screen and lets the terminal scale and move it (a few bytes per step, also inside tmux); one sharp frame follows when you stop, and is cancelled if you move again. Video frames are capped at 640×360 and 30 fps for smooth playback over a network, and the terminal scales them up.
  • tmux: enable passthrough with set -g allow-passthrough on. shw then uses Unicode placeholders automatically.
  • Run shw --doctor to see what was detected.

How it works

On startup shw sends a handful of queries, terminated by a DA1 sentinel, so detection never hangs. They check for kitty graphics support, which transfer modes work (direct, temp file, shared memory), the background colour (for the light or dark theme), the cell size in pixels and text-sizing support. Every frame is resampled to the exact number of device pixels the terminal will show, so the terminal never rescales the image. Updates are wrapped in synchronized-output mode and swap between two image ids, so they don't flicker.

Security

shw is designed to be safe on untrusted files: no terminal escape injection, no script execution, only safe links are clickable, and remote images only come from public hosts (--offline disables downloads). Release archives ship with checksums and signed build provenance. See SECURITY.md.

Development

cargo test
cargo clippy --all-targets -- -D warnings
cargo run -- README.md

Startup timing: SHW_TRACE=1 shw README.md prints how long each phase took (terminal query, fonts, images, layout, first frame) after you quit.

Releasing

  1. Bump version in Cargo.toml and run cargo check to update Cargo.lock.
  2. Commit and tag: git commit -am "release v0.2.0" && git tag v0.2.0 && git push --follow-tags.
  3. The release workflow builds binaries for all targets, creates the GitHub release with checksums and completions, and publishes to crates.io. It needs a CARGO_REGISTRY_TOKEN repository secret.

License

Licensed under MIT