Replace vaxis NewImage/Resize (which spawns goroutines that panic in
sixel encoder) with direct halfblock character rendering. Each terminal
cell uses upper-half-block with RGB fg/bg colors for 2x vertical
resolution. Pre-renders in background goroutine, Draw() just paints
pre-computed cells. Works in any 24-bit color terminal.
The go-sixel library panics on certain image dimensions during Resize().
Added panic recovery in drawImage() that falls back to alt text display.
Cleaned up all debug logging from the investigation.
- Decode images in background goroutine, not in Draw()
- Show [loading image...] placeholder until decode completes
- Skip pv.term.Draw() when composite is active (pager got no input)
- Thread-safe access to decoded image cache via sync.Mutex
- Invalidate callback triggers redraw when images finish decoding
Composite mode now handles j/k/g/G/PgUp/PgDown/mouse wheel for scrolling.
Text uses the configured default style. Images render inline via Vaxis.
Emails without images still use the standard pager path.
The composite renderer bypassed the pager (less) for any email with images,
losing colors, scrolling, and search. Now always forward to the pager with
[image: alt] text placeholders. Image refs are still extracted and stored
for future inline rendering.
Implements ExtractImages() which scans filter stdout line-by-line,
extracts valid OSC 9 image markers into ImageRef structs, and replaces
marker lines with null-delimited placeholders (\x00IMG:N\x00). Invalid
markers (missing path) pass through unchanged. Also clarifies the Index
field comment in ImageRef to indicate it is set by ExtractImages, not
ParseImageOSC.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces ImageRef struct and ParseImageOSC() to parse OSC 9 image
markers (\033]9;image:path=...;alt=...\007) emitted by filters. Supports
BEL and ESC-backslash terminators, tolerates markers embedded in larger
lines, and rejects malformed or path-less markers.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>