Repository navigation
Support recording and rendering Kitty graphics protocol output (Kitty, Ghostty) #87
Description
Summary
The recording stack (tuirec + the tig/avt and tig/agg forks) supports Sixel graphics (#70, #72, #73, #77, #78) but does not support the Kitty graphics protocol (Kitty, Ghostty). Recording a Kitty-graphics app captures only the text cells / cell-rendered fallback — the inline images are dropped from the cast/GIF.
This is the Kitty counterpart of the Sixel umbrella #70, and the work splits across the same three layers.
Why this matters now
- Terminal.Gui PR gui-cs/Terminal.Gui#5493 adds Kitty graphics protocol support and makes Kitty the preferred protocol, with Sixel as the fallback for non-Kitty terminals (Windows Terminal, etc). The new protocol-agnostic API is
ImageView.UseRasterGraphics/IsUsingRasterGraphics. - Apps built on Terminal.Gui therefore now emit Kitty graphics on Kitty/Ghostty terminals. Concretely, winprint's TUI page-preview (
PreviewPane→ImageView) emits Kitty graphics when run under Ghostty. - Ghostty is Kitty-only (no Sixel) and is a primary daily-driver terminal. On Ghostty the recording stack currently captures only the colored-cell fallback, so a recorded GIF/PNG cannot show — or prove — the real inline raster preview. Full-fidelity capture + visual regression for raster UIs is unavailable on Kitty terminals until the stack speaks the protocol.
Expected
tuirec should capture and render Kitty graphics protocol sequences (ESC _ G <control> ; <base64> ESC \) so terminal images are represented in the generated cast/GIF — the same way it now does for Sixel — or clearly report that Kitty capture is unsupported.
Actual
Kitty APC graphics sequences are not answered (so the app may not enable Kitty output), not modeled by avt, and not rendered by agg. The resulting cast/GIF contains the text UI but no Kitty image payloads.
Protocol notes (how Kitty differs from Sixel)
- Transport is APC, not DCS: images arrive as
ESC _ G <control data> ; <base64 payload> ESC \rather than Sixel'sESC P ... ESC \. Large images are chunked across multiple APCs (m=1on every chunk but the last, which sendsm=0); the payload must be reassembled before base64-decode. - Capability handshake is a graphics query, not DA1: apps detect support by sending a query action (
a=q, e.g.ESC _ G i=<id>,a=q,s=1,v=1,t=d,f=24 ; <base64 1px> ESC \) and reading the response (ESC _ G i=<id> ; OK ESC \). The recorder must answerOKduring the handshake window so the app turns Kitty output on — this is the Kitty analogue of Answer sixel handshake queries so apps emit and size sixel correctly #77 (which answers DA1 for Sixel). Cell/pixel-size reporting (CSI 14/16/18 t) is already in place from the Sixel work and is shared. - Minimum useful subset: direct RGB/RGBA transmit-and-display at the cursor cell (
a=T,f=24/f=32,t=d). Unicode-placeholder placement, z-index, anda=ddeletion can follow.
Proposed work (mirrors the Sixel breakdown)
-
tig/avt— parse and model Kitty APC graphics: reassemble chunked payloads, base64-decodef=24/f=32direct images, and place them in the virtual-terminal grid anchored to their cell (parallels where Sixel DCS is modeled). cf. Bundle agg v1.10.1-sixel (reads sixel from reviewed avt) #80 ("reads sixel from reviewed avt"). -
tig/agg— composite placed Kitty images into the rendered GIF frames at their cell positions, honoring clipping / overlays / z-order (the occlusion concerns from Bundle agg v1.10.2-sixel (occludes sixel under overlays) #83). Ship a Kitty-capableaggfork and pin/auto-download it likeagg v1.10.2-sixel(Ship the sixel-capable agg fork (tig/agg v1.9.0-sixel) #78). -
tuirec— answer the Kitty graphics capability query (a=q→OK) so apps emit Kitty output (Kitty analogue of Answer sixel handshake queries so apps emit and size sixel correctly #77). -
tuirec— preserve Kitty APC sequences in the.castand don't drop them during trim (analogue of Preserve sixel-only casts during trim #73). - docs /
agent-guide— Kitty capture notes; carry over the Linux/macOS-only caveat (Windows ConPTY strips APC/DCS from the output stream, same as Sixel). - integration test — record a Kitty-graphics view (Terminal.Gui UICatalog raster scenario, or
wp --viewpage preview) under a forced-Kitty harness and assert the GIF actually contains the image.
Repro context
- App: any Terminal.Gui v2 app emitting raster graphics with
ImageView.UseRasterGraphicson a Kitty/Ghostty terminal (e.g. winprintwppage preview), or the UICatalog Mandelbrot scenario once it advertises Kitty. - Record with
tuirec recordon macOS/Linux; inspect the.castfor Kitty APC starts (\e_G) — currently none are preserved/rendered.
References
- Sixel umbrella: Support recording and rendering sixel output #70 (and Support recording sixel output #72 record, Preserve sixel-only casts during trim #73 preserve-through-trim, Answer sixel handshake queries so apps emit and size sixel correctly #77 answer handshake, Ship the sixel-capable agg fork (tig/agg v1.9.0-sixel) #78 ship agg fork, Bundle agg v1.10.2-sixel (occludes sixel under overlays) #83/Advertised sixel cell resolution doesn't match agg's rendered font cell — sixels render ~4% undersized #84/Calibrate sixel cell size to agg's actual rendering (#84) #86 rendering/calibration).
- Terminal.Gui Kitty support: Fixes #5489. Add Kitty graphics protocol support (Kitty, Ghostty) with Kitty-first priority Terminal.Gui#5493 (fixes Add support for Kitty graphics protocol in addition to Sixel Terminal.Gui#5489).
Activity