SIXEL encodes in 6-pixel vertical bands. When targetHeightPx was not a
multiple of 6 (common for cell heights like 16, 19, 20, 21, 22), the
encoder padded the last band. The terminal allocated cell rows for the
padded height, which could cross into one more row than the TUI reserved.
The next line of content then overwrote the bottom of the image, carving
a visible slice out of it.
Fix: round targetHeightPx DOWN to the largest multiple of 6 within the
requested row budget (fit.rows * cellHeight). This eliminates padding
without growing the reserved row count past the caller's maxHeightCells
cap. The width is scaled by the same ratio so resize_exact preserves the
image aspect ratio instead of squashing it vertically.
- Wrapped every Kitty graphics APC for tmux passthrough, preserved quiet mode across chunks, and enabled placeholder placement when Kitty is explicitly forced.
- Routed Agent Hub transcript images through the shared image budget and terminal image setting.
Fixes#5381
- Adjusted image budget IDs to use a shared 24-bit seed and wrap safely so new budgets no longer collide.
- Added transmit-state reset and changed image output ordering so full paints can replay image data after terminal clears.
- Updated image and protocol tests to match the new wrapped-id behavior and cursor-save/restore-wrapped transmit sequences.
- Added Matplotlib figure PNG rendering and display tracking in Python runner to emit PNG output immediately when figures are displayed via display(fig).
- Extended session persistence to externalize oversized image payloads in both content and details.images, enabling tool result images to survive session reload.
- Enhanced session loader to resolve image data payloads and blob references across content and details.images during session reconstruction.
- Added image cache invalidation in TUI image component when image protocol, cell dimensions, or Kitty Unicode placeholder mode changes.
- Added comprehensive test coverage for Matplotlib display, image persistence across reload, and TUI image rendering with protocol and dimension changes.
Added C=1 to Kitty display commands so direct inline-image cursor restoration is the sole row accounting path. Updated focused image rendering and placement encoding coverage.\n\nFixes #2863
Restored the cursor below direct inline-image placements after emitting the graphics sequence so following chat rows cannot overwrite the reserved image area. Added coverage for the placement row terminator.\n\nFixes #2863
- Changed transcript commit tracking from a volatile boolean to a cooldown counter that decays over clean frames.
- Normalized row equality checks to ignore ANSI and trailing-space noise so equivalent renders do not trigger rewrites.
- Removed the Bun test-runtime exception from Ghostty image paint deferral so normal delay logic always runs.
- Added kitty-graphics module with Unicode placeholder and temp-file APIs.
- Refined terminal image handling to use rendered placeholder lines and detect placeholder output.
- Raised the max inline-image default from 3 to 8 in schema and image constants.
- Refined terminal capability flow for Kitty support, including screen-to-scrollback and text-sizing handling.
- Added integration tests for kitty placeholders, virtual placement, and temp-file rendering.
Image previews were sized from max width first and then height-limited, which could stretch renders when maxHeightCells was the actual limiting bound. Compute a proportional fit within both bounds and forward maxHeightCells through the Image component.