From 624ac003e8141b6a020396a1a0362146b82daf9a Mon Sep 17 00:00:00 2001 From: martian56 Date: Fri, 24 Jul 2026 13:28:15 +0400 Subject: [PATCH] fix(ui): keep green tool bullets away from the thinking indicator The pulsing glyph shared its column with the transcript's tool bullets (green in every theme) on the row directly above, so during tool-heavy turns the corner read as the animation flashing green. A permanent blank spacer row now separates the transcript from the thinking cluster, and the indicator is indented past the bullet column, so nothing green ever sits in or beside it. Co-Authored-By: Rook <308697149+rook-agent-code@users.noreply.github.com> --- src/app/view.rv | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/app/view.rv b/src/app/view.rv index f88124f..8921599 100644 --- a/src/app/view.rv +++ b/src/app/view.rv @@ -35,7 +35,10 @@ fun chat_view(m: Chat, f: Frame) { if strip > 6 { strip = 6 } - let cs: List = [Constraint.Fill(1)] + // A permanent blank spacer sits between the transcript and the strip or + // thinking line, so the transcript's last row (often a green tool + // bullet) is never directly adjacent to the pulsing indicator. + let cs: List = [Constraint.Fill(1), Constraint.Len(1)] if strip > 0 { cs.push(Constraint.Len(strip)) } @@ -47,7 +50,7 @@ fun chat_view(m: Chat, f: Frame) { if strip > 0 { off = 1 } - let input_area = rows[2 + off] + let input_area = rows[3 + off] let body = Rect.new(rows[0].x + 1, rows[0].y, rows[0].w - 2, rows[0].h) if m.entries.len() == 0 { @@ -56,15 +59,15 @@ fun chat_view(m: Chat, f: Frame) { _transcript(m, body, f) } if strip > 0 { - _todo_strip(m, todos, rows[1], f) + _todo_strip(m, todos, rows[2], f) } - _thinking_line(m, rows[1 + off], f) + _thinking_line(m, rows[2 + off], f) let box = Block.new().rounded().style(m.theme.border) box.render(input_area, f.buf) let ia = box.inner(input_area) f.buf.set_string(ia.x + 1, ia.y, "> ", m.theme.dim) m.entry.render(Rect.new(ia.x + 3, ia.y, ia.w - 4, 1), f.buf) - _status(m, rows[3 + off], f) + _status(m, rows[4 + off], f) let idle = !m.active && m.pending == "" && !m.picking && !m.theme_picking && !m.entering_key && !m.help_open && !m.permissions_open && !m.shells_open && !m.sessions_open if idle && !m.mention_dismissed && m.mention_matches.len() > 0 && current_mention(m.entry.value).active { _mention_menu(m, input_area, f) @@ -1038,10 +1041,12 @@ fun _thinking_line(m: Chat, area: Rect, f: Frame) { return } if m.pending != "" { - f.buf.set_string(area.x + 1, area.y, "awaiting approval…", m.theme.warn) + f.buf.set_string(area.x + 3, area.y, "awaiting approval…", m.theme.warn) return } - let x = f.buf.set_string(area.x + 1, area.y, "${pulse(m.tick)} ${m.word}… ", m.theme.spinner) + // Indented past the transcript's bullet column, so the pulsing glyph + // never shares a column with the green tool bullets above it. + let x = f.buf.set_string(area.x + 3, area.y, "${pulse(m.tick)} ${m.word}… ", m.theme.spinner) // Elapsed seconds, so a long tool-heavy turn visibly advances instead of // sitting on a bare spinner word. Ticks arrive every 45ms. let secs = m.elapsed * 45 / 1000