@@ -257,6 +257,11 @@ func ImageProtocolSupported() bool {
257257 return imageProtocolSupported ()
258258}
259259
260+ // SixelSupported returns true if the terminal uses the Sixel graphics protocol.
261+ func SixelSupported () bool {
262+ return sixelSupported ()
263+ }
264+
260265// imageProtocolSupported checks if any supported image protocol terminal is detected.
261266func imageProtocolSupported () bool {
262267 return sixelSupported () || kittySupported () || ghosttySupported () || iterm2Supported () ||
@@ -581,16 +586,20 @@ func RenderImageToStdout(placement *ImagePlacement, screenRow int, screenCol ...
581586 // Priority: Sixel in multiplexers
582587 if sixelSupported () {
583588 debugImageProtocol ("Sixel: RenderImageToStdout row=%d col=%d base64len=%d" , screenRow , col , len (placement .Base64 ))
584- seq := sixelImageEscapeOnly (placement .Base64 )
585- if seq == "" {
586- debugImageProtocol ("Sixel: sixelImageEscapeOnly returned empty" )
587- return
589+
590+ // Encode once, reuse cached Sixel on subsequent renders (like Kitty's upload-once pattern)
591+ if placement .SixelEncoded == "" {
592+ placement .SixelEncoded = sixelImageEscapeOnly (placement .Base64 )
593+ if placement .SixelEncoded == "" {
594+ debugImageProtocol ("Sixel: sixelImageEscapeOnly returned empty" )
595+ return
596+ }
588597 }
589598
590- debugImageProtocol ("Sixel: rendering %d bytes at row=%d col=%d" , len (seq ), screenRow + 1 , col )
599+ debugImageProtocol ("Sixel: rendering %d bytes at row=%d col=%d" , len (placement . SixelEncoded ), screenRow + 1 , col )
591600 // Position cursor + render Sixel
592601 fmt .Fprintf (os .Stdout , "\x1b [s\x1b [%d;%dH%s\x1b [u" ,
593- screenRow + 1 , col , seq )
602+ screenRow + 1 , col , placement . SixelEncoded )
594603 os .Stdout .Sync ()
595604 return
596605 }
@@ -640,11 +649,12 @@ type InlineImage struct {
640649// line in the email body. Images are rendered directly to stdout (bypassing
641650// bubbletea's cell-based renderer which cannot handle graphics protocols).
642651type ImagePlacement struct {
643- Line int // Line number in the processed body text where the image starts
644- Base64 string // Base64-encoded image data (PNG)
645- Rows int // Number of terminal rows the image occupies
646- Uploaded bool // Whether the image has been uploaded to the terminal via Kitty ID
647- ID uint32 // Kitty image ID for display-by-reference
652+ Line int // Line number in the processed body text where the image starts
653+ Base64 string // Base64-encoded image data (PNG)
654+ Rows int // Number of terminal rows the image occupies
655+ Uploaded bool // Whether the image has been uploaded to the terminal via Kitty ID
656+ ID uint32 // Kitty image ID for display-by-reference
657+ SixelEncoded string // Cached Sixel escape sequence (encode once, reuse on scroll)
648658}
649659
650660// ProcessBodyWithInline renders the body and resolves CID inline images when provided.
0 commit comments