From 2cf21d7bda6c2f9ecc590443ea52c09e2c9304a2 Mon Sep 17 00:00:00 2001 From: patriceckhart Date: Tue, 21 Apr 2026 21:32:41 +0200 Subject: [PATCH] fix(tui): suppress clipped images near status bar --- internal/agent/modes/interactive.go | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/internal/agent/modes/interactive.go b/internal/agent/modes/interactive.go index 6234223..d4570ff 100644 --- a/internal/agent/modes/interactive.go +++ b/internal/agent/modes/interactive.go @@ -822,6 +822,7 @@ func (i *Interactive) redraw() { start = alignSliceStartToImageBlock(chat, start, end) visibleChat = chat[start:end] } + visibleChat = clipBottomClippedImages(visibleChat) // A tiny "scrolled up" indicator in the top-right of the chat pane // so you know you're not at the bottom. When the viewport was @@ -895,6 +896,37 @@ func alignSliceStartToImageBlock(chat []string, start, end int) int { return start } +func clipBottomClippedImages(lines []string) []string { + if len(lines) == 0 { + return lines + } + out := append([]string(nil), lines...) + for i, line := range out { + if !strings.Contains(line, "\x1b]1337;File=") && !strings.Contains(line, "\x1b_G") { + continue + } + // Image blocks render as: image escape, zero or more blank + // reservation rows, then the muted "image - ..." info line, + // then one trailing blank. If the info line isn't visible in + // the current chat slice, the image would paint down into the + // fixed status bar area. Suppress that image for this frame. + foundInfo := false + for j := i + 1; j < len(out); j++ { + if strings.Contains(out[j], "image - ") { + foundInfo = true + break + } + if strings.TrimSpace(out[j]) != "" { + break + } + } + if !foundInfo { + out[i] = "" + } + } + return out +} + // truncateLine shortens s so it fits within n display cells, with an // ellipsis if trimmed. Used by the "sliding in" chips so a pasted // novel doesn't blow past the status line.