Zero-dependency image rendering for Neovim via the kitty graphics protocol. No ImageMagick. No luarocks. No external binaries. Pure Lua on Neovim >= 0.10.
Status: v0.2.0 released.
show()/clear()/clear_all()render and remove PNG placements via the kitty graphics protocol. A placement scrolled partway past a window edge shows a cropped slice of the image rather than disappearing; it's hidden only once it has no overlap with the window at all.width/heightinshow()are optional — an omitted dimension is derived from the PNG's native aspect ratio. Seedocs/spec/renderer-placement.mdfor the design.
- Neovim >= 0.10
- Terminal: kitty, WezTerm, or Ghostty
- tmux is explicitly unsupported in v0.x (blit no-ops under tmux)
- GUI frontends /
--embed(e.g. Neovide) are unsupported (blit no-ops)
Run :checkhealth blit to see detection results for your environment.
-- lazy.nvim
{ "optiflowic/blit.nvim", opts = {} }- Zero external dependencies — never requires ImageMagick, luarocks, Python, Node, or any external binary.
- PNG only (v0.x). Other formats are out of scope until v2.
- Only kitty, WezTerm, and Ghostty are supported; every other terminal
(or an unsupported environment such as tmux or a GUI frontend) is a
silent no-op, reported via
:checkhealth.
See AGENTS.md for the full set of project constraints and architecture.
- WezTerm: process crash under sustained fast scrolling (#31). Scrolling quickly and continuously with an image anchored away from the top of the buffer can crash the WezTerm process itself (not just the pane), taking the Neovim session down with it. Root cause is confirmed WezTerm-side (an unbounded render-quad count under placement reposition + scroll, filed upstream as wezterm/wezterm#7953) with a verified fix, but that fix hasn't shipped in a WezTerm release yet. Nothing to change on blit's side — this will be re-verified and closed once the fix lands upstream. Until then, avoid sustained fast scrolling on WezTerm while an image is anchored far down a long buffer.
make # format-check + lint + test (same as CI)
make test # run unit tests (mini.test, headless nvim)
make format-check # stylua --check .
make lint # selene .See CONTRIBUTING.md. Bug reports and feature requests use the issue templates; general questions go in Discussions.
- Renderer: a placement scrolled partway past a window edge shows a cropped
slice of the image instead of being hidden; it's hidden only once it has no
overlap with the window at all (
lua/blit/renderer.lua). show(): an omittedwidthorheightis derived from the PNG's native aspect ratio instead of being required.- Fixed a wrapped anchor-line row miscalculation in
virt_linesplacement, and a Ghostty resize-retransmit retry that could get stuck after a failed attempt.
- Renderer:
show()/clear()/clear_all()place and remove PNG images via extmark-reservedvirt_lines, with scroll/resize redraw (debounced), transmission caching by(path, mtime), and full lifecycle cleanup (lua/blit/renderer.lua). Images are shown only when fully visible; no partial/cropped placement in v0.x. - Protocol plumbing: kitty graphics escape-sequence construction, terminal
capability detection, and tty transport (
lua/blit/terminal.lua).