From a739ef9a8b83cead4c6a083259ccf761c4e6fa72 Mon Sep 17 00:00:00 2001 From: patriceckhart Date: Tue, 21 Apr 2026 21:29:08 +0200 Subject: [PATCH] fix(tui): repaint fully while scrolling chat --- internal/agent/modes/interactive.go | 10 ++++++++++ internal/tui/render.go | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/internal/agent/modes/interactive.go b/internal/agent/modes/interactive.go index 8eb45e7..6234223 100644 --- a/internal/agent/modes/interactive.go +++ b/internal/agent/modes/interactive.go @@ -530,6 +530,13 @@ func (i *Interactive) scrollBy(delta int) { i.parkedTurn = 0 i.parkedTotal = 0 } + if i.rend != nil { + // VS Code's terminal is especially prone to leaving stray + // wrapped-character fragments behind during scroll-driven + // viewport changes. Force a full repaint on scroll, but + // avoid a whole-screen clear because that visibly flickers. + i.rend.Invalidate() + } i.mu.Unlock() i.invalidate() } @@ -540,6 +547,9 @@ func (i *Interactive) scrollToBottom() { i.scrollOffset = 0 i.parkedTurn = 0 i.parkedTotal = 0 + if i.rend != nil { + i.rend.Invalidate() + } i.mu.Unlock() i.invalidate() } diff --git a/internal/tui/render.go b/internal/tui/render.go index 08a7a43..ff4aeeb 100644 --- a/internal/tui/render.go +++ b/internal/tui/render.go @@ -62,6 +62,13 @@ func (r *Renderer) Clear() { _, _ = io.WriteString(r.out, SeqClearScreen) } +// Invalidate forces a full repaint on the next Draw without clearing the +// whole terminal first. Useful when the cached diff is unreliable but a +// visible full-screen flash would be too distracting. +func (r *Renderer) Invalidate() { + r.prev = nil +} + // Draw updates the terminal so that the visible frame ends with the // given lines (bottom-aligned). cursorRow/cursorCol are offsets within // the lines slice indicating where to place the terminal cursor; use