@itc-steve/pi-wl-images

extensionmaintained

Flicker-free inline images for Pi on Wayland + Ghostty, using Kitty Unicode placeholders.

by · v1.0.1 · published 1w ago

$ pi install npm:@itc-steve/pi-wl-images
downloads/mo
0
stars
0
last push
1w ago
open issues
0

Signals

license: MITtestspi manifest: missinginstall size: —deps: 0peer deps: 0

Download trend

272 downloads · last 12 weeks (weekly)

README

pi-wl-images — flicker-free inline images for Pi on Wayland and Ghostty

npm version MIT license Node.js 22.6+ Wayland + Ghostty

Paste an image into Pi without the full-screen flicker.
Strict Wayland + Ghostty (+ Herdr). Kitty Unicode placeholders only.


Why this exists

Pi's built-in image paste reserves rows and forces a full redraw. That is the flicker.

This package puts images on the ordinary differential-redraw path with Kitty Unicode placeholders, so the image stays put while the rest of the TUI updates.

Built-in paste flickers; pi-wl-images uses placeholders and stays stable

No X11 fallback. No WSL/PowerShell path. No macOS pngpaste. No Sixel converter. No native clipboard addon.

Install

pi install npm:@itc-steve/pi-wl-images

Local checkout:

pi install /path/to/pi-wl-images

Required: free up ctrl+v

Pi binds ctrl+v to built-in app.clipboard.pasteImage. Leave it bound and both handlers fire — clean render and the full-screen redraw. Looks like the bug never got fixed.

Unbind it in ~/.pi/agent/keybindings.json:

{
  "app.clipboard.pasteImage": []
}

Empty array = no keys. Create the file if missing.

Prefer keeping pi's paste on ctrl+v? Skip the unbind and use /paste only. Do not run both on the same key.

First use

ActionResult
ctrl+vattach Wayland clipboard image (after unbind above)
/pastesame, explicit — works with or without unbind
/image <path>attach image file

Attaching inserts a [wl-image] marker in the draft. Send the message → marker becomes a real image for the model, plus an inline transcript render.

Delete the marker to cancel. Markers are counted, not just stripped — remove one and that image is dropped, not silently sent.

Paste, marker, send, flicker-free render

Requirements

  • Wayland session
  • wl-clipboard (wl-paste)
  • Kitty-graphics terminal with Unicode placeholder support (Ghostty qualifies)

Verify the terminal:

python3 -c '
import os,termios,tty,select
fd=os.open("/dev/tty",os.O_RDWR); old=termios.tcgetattr(fd); tty.setraw(fd)
os.write(fd,b"\x1b_Gi=99,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\\\")
print(os.read(fd,64) if select.select([fd],[],[],1)[0] else b"no reply")
termios.tcsetattr(fd,termios.TCSADRAIN,old)'

\x1b_Gi=99;OK\x1b\\ means good.

How it works

  1. Pastewl-paste only; bytes + dimensions stashed.
  2. Marker[wl-image] lands in the editor draft.
  3. Submit — marker swapped for a real image content block for the model.
  4. Render — Kitty virtual placement + Unicode placeholder cells via pi-tui Component.render(), not reserved rows.

Herdr is optional. Outside a Herdr pane the extension uses the terminal cell-size report and never touches the socket.

src/
  diacritics.ts       Unicode 6.0.0 row/column table (297 entries)
  placeholder.ts      virtual placement + placeholder cell rows
  image-component.ts  transmit out-of-band; cells via render()
  cell-size.ts        Herdr-aware cell geometry
  herdr-socket.ts     minimal herdr socket client
  clipboard.ts        wl-paste only
  dimensions.ts       PNG/JPEG/GIF/WebP header parsing
  index.ts            commands, shortcut, transform, entry renderer

Limits

LimitValue
Max image grid297×297 cells (diacritic table size)
Payload cap10MB
Animated GIFsfirst frame only
Terminal resizeretransmits (c/r fixed at transmit time)

Test

npm test        # 25 tests, no framework
npm run typecheck

Live flicker check:

node --experimental-strip-types test/render-demo.ts

Draws a red block, redraws the line beneath it 20 times. Block must not blink.

License

MIT © itc-steve