Kitty Graphics Protocol
Programs use the Kitty graphics protocol to show real images in a terminal: file previews, plots, diagrams, editor thumbnails. The program sends the image data in an escape sequence and tells the terminal where to draw it.
Nash supports the protocol. Since 0.8.1 it follows the protocol's placement rules closely, so images stack, scroll, erase, and delete the way the specification describes. They behave like text, not like stickers on top of it.
Before you start
- Install Nash 0.8.1 or newer. The behavior on this page, including Unicode placeholders and capability replies, arrived in 0.8.1.
- There is nothing to set up. Kitty graphics is on by default. To turn it off, see "Turn the protocol off" below.
- You rarely write these sequences yourself. The program that shows the image does. You only need this page to understand what you see, or to build a tool.
For nash imgcat and sixel images, see
Other Image Protocols.
If an image does not appear, start with
Kitty Graphics Troubleshooting.
How images behave
Placements
A placement is one display of an image at one position. A program can place the same image many times.
- A placement without a placement id (
p=) adds another placement of the image. A placement with an id replaces the placement that has the same image id and placement id. - Overlapping placements stack by z-index (
z=), then by image id. - Sending new data for an image id replaces the image and removes the placements of the old image.
- After a placement, the cursor moves to the cell after the last column of the
image, on its last row.
C=1keeps the cursor where it was. - The primary screen and the alternate screen each have their own images and placements, as in kitty. A full-screen program does not remove or change the images in your shell's screen.
Size and position
- Without
c=andr=, Nash draws the image at its pixel size, starting at the cursor cell. c=andr=set the number of cells. If you set only one of them, Nash computes the other so the image keeps its aspect ratio. If you set both, the image stretches to fill them.X=andY=move the image by a number of pixels inside the first cell. The offset is part of the area thatc=andr=describe. Nash limits it to one pixel less than the cell size, and each slice of the image stays inside its own cell, so a neighboring cell background cannot cover a piece of it.x=,y=,w=, andh=select a rectangle of the source image. Nash refuses an empty rectangle.
Layers
| z-index | Where Nash draws the image |
|---|---|
0 and above | Above the text |
Below 0, down to -1073741824 | Beneath the text, on top of cell backgrounds |
Below -1073741824 | Beneath the backgrounds of cells too, as in kitty |
Images beneath the text stay below underlines, the selection, and the cursor. A
cell with a non-default background color hides an image whose z-index is below
-1073741824.
Text, scrolling, and erasing
- Images move with their lines. They follow scrolling, scroll regions, and the scrollback. A program such as Neovim can scroll a window with a scroll region and its images follow.
- Erasing text with
EL,ECH, orED(below or above the cursor) leaves images whole. It does not cut holes into them. - Clearing the screen with
ED 2removes the placements on the screen. Erasing the scrollback withED 3removes the placements that lived in it. - Images keep their position and stacking order when wide-character edits, horizontal scroll margins, or a resize rewrap a line.
- An image under double-width text, such as CJK characters or emoji, does not turn the text into spaces or repeat a slice of the image. Double-width text written over an image leaves the image whole.
Deleting
Deleting one placement does not remove the other placements of its image. The full list of delete modes is in the reference below. A lowercase delete removes placements only. An uppercase delete also frees the image data, but only when no placement still shows the image.
Unicode placeholders
Some programs cannot position an image with cursor movement alone. An editor that draws windows and floating panels needs the image to move, scroll, clip, and disappear together with the text around it. Unicode placeholders solve that.
- The program creates a virtual placement with
a=p,U=1. It records how an image maps onto a grid of cells, but draws nothing. - The program prints the placeholder character
U+10EEEEin each cell of the grid. The foreground color carries the image id. The underline color carries an optional placement id. Combining characters after the placeholder carry the row, the column, and the high byte of the image id. - Nash draws the matching tile of the image in each placeholder cell.
Because the image position is ordinary text, the image follows every edit of that text. Nash also handles the details that make this robust:
- The order does not matter. The program can send the placement before the text, or the text before the placement, and it can resize the placement later without printing the text again.
- If a placeholder leaves out its row or column characters, Nash fills them in from the placeholder on its left, as kitty does.
- The image fits inside the grid with its aspect ratio kept and is centered. If
the grid size is
0, it follows from the size of the image. - Nash draws the image beneath the placeholder cell, so the cursor stays visible on top.
- The tiles also work in the scrollback, and each screen has its own virtual placements.
- A placeholder without a placement id shows the newest virtual placement of its image.
The snacks.nvim Neovim plugin uses this mechanism to show images inside
windows and floats when the terminal supports it. It is also how programs place
images inside tmux.
Capability queries and replies
A program that wants to know whether your terminal shows images sends a
query (a=q). Nash answers without loading or storing anything:
program sends: ESC _ G i=31,s=1,v=1,a=q,t=d,f=24;AAAA ESC \ Nash replies: ESC _ G i=31;OK ESC \
- Both ST (
ESC \) and BEL end a query. The reply always ends with ST. - Nash does not read the payload, open a file, decode pixels, assign an image id, or replace an existing image. An upload in chunks that is in progress is not interrupted by a query.
- A malformed or unsupported query gets
EINVALinstead of silence, so the program does not wait for a timeout. Examples are an unknownf=,t=, oro=value, a non-numeric number, bothi=andI=, a size of0, orm=1. - Some programs probe with a real upload instead of
a=q. Nash acknowledges a successful upload withOKfor an image id (i=) and for an image number (I=), unless the program asked for quiet replies withq=1orq=2. - If Kitty graphics is turned off, Nash does not answer at all.
Reference: what Nash accepts
Actions
| Action | Key | Support |
|---|---|---|
| Transmit | a=t | Supported |
| Transmit and display | a=T | Supported |
| Place | a=p | Supported, including U=1 for a virtual placement |
| Delete | a=d | Supported, except animation frames |
| Query | a=q | Supported |
| Animation frame | a=f | Accepted |
| Frame composition | a=c | Accepted |
| Animation control | a=a | Not supported |
Transmission
| Feature | Keys | Notes |
|---|---|---|
| Direct data | t=d (default) | Base64 in the sequence. Send large images in chunks with m=1. |
| File | t=f | The payload is the base64 path. S= and O= set size and offset. |
| Temporary file | t=t | Nash deletes the file after it reads it, but only inside /tmp/, /var/tmp/, /dev/shm/, or $TMPDIR. |
| Shared memory | t=s | On Unix and Windows. The payload is the base64 object name. |
| RGB, RGBA, PNG | f=24, f=32, f=100 | f=32 is the default. RGB and RGBA need s= and v=. |
| Compression | o=z | zlib deflate |
| Image id, number | i=, I= | Use one of them, not both in one request. |
Placement keys
x=, y=, w=, h= (source rectangle), X=, Y= (pixel offset), c=, r=
(cells), C= (cursor), p= (placement id), z= (z-index), and U= (virtual
placement) are supported.
Delete modes
Each mode has a lowercase form that removes placements and an uppercase form that also frees the image data when nothing refers to it.
| Mode | Deletes |
|---|---|
d=a, d=A | All placements on the visible screen. This is the default. |
d=i, d=I | Placements of image i=, or only placement p= when you give it. |
d=n, d=N | Placements of the newest image with number I=. |
d=c, d=C | Placements at the cursor position. |
d=p, d=P | Placements at cell x=, y=. Numbering starts at 1. |
d=q, d=Q | Placements at cell x=, y= with z-index z=. |
d=x, d=X | Placements that cross column x=, including the scrollback. |
d=y, d=Y | Placements that cross row y=. |
d=z, d=Z | Placements with z-index z=. |
d=r, d=R | Images with an id from x= to y=. |
d=f, d=F | Animation frames. Not supported. Nash ignores the request. |
Deletes by position and by z-index do not touch virtual placements, because a virtual placement has no position. Delete by image id to remove one.
Differences from kitty and limits
- A failed command gets no error reply. Nash writes the reason to its log
and carries on. The exceptions are a bad query (
EINVAL) and some frame commands (ENOENT). A program that waits for a reply to a failed upload waits for its own timeout. - No animation control. A program can send frames and compose them, but it
cannot start, stop, or loop an animation with
a=a. - Large images are refused. Nash ignores an image whose decoded RGBA data is larger than 100 MB, which is about 25 million pixels.
- Stored image data has a fixed budget of 320 MiB. When the stored data goes over it, Nash frees the images that no placement or placeholder uses. You cannot change this limit.
- Nash reads files and shared memory itself. The path or object name in a
t=f,t=t, ort=supload must be visible to the Nash process that reads the sequence. For a program on a remote machine, use direct transmission. - A pane with no pixel size refuses images. Nash cannot compute how many cells an image covers without the pixel size of a cell.
Try it
Replace the path with a PNG file on your machine:
img="$HOME/Pictures/example.png" printf '\033_Ga=T,f=100,t=f,c=20,q=2;%s\033\\' \ "$(printf '%s' "$img" | base64 | tr -d '\n')" printf '\n'
The command sends the file path (t=f), asks Nash to display it (a=T) 20
cells wide (c=20), and keeps the aspect ratio because it sets no r=. The
q=2 key stops Nash from sending a reply into your shell's input. Then print
more lines so the image scrolls, resize the window, or run clear to see it
removed. The image follows the rules above.
tmux, SSH, and the multiplexer
- tmux does not understand the protocol itself. Image sequences must pass through tmux to reach Nash. Programs that run in tmux place their images with Unicode placeholders, described above.
- SSH. The sequences travel through the connection like other output. Files and shared memory do not. See the note on file transmission above.
- Multiplexer sessions. The multiplexer server keeps your panes and sends
the images to the window. Nash 0.8.1 changed the multiplexer protocol to
version 48. A server that speaks an older protocol version must be
restarted.
nash mux statusshows the version of the server and the version of your build. See Multiplexer before you restart, because a restart closes every pane the server owns.
Turn the protocol off
Kitty graphics has no key in nash.toml. To turn it off, create custom.lua in
your Nash configuration directory (~/.config/nash by default):
return { enable_kitty_graphics = false }
Reload the configuration. Nash then ignores every Kitty graphics sequence, including capability queries, so programs fall back to other output.
Related
- Kitty Graphics Troubleshooting
- Other Image Protocols
- Images That Behave Like Text, the short story of the 0.8.1 changes