From 37eb752cb29f784f57e95f0e1afa20f10dc16ab9 Mon Sep 17 00:00:00 2001 From: patriceckhart Date: Mon, 27 Apr 2026 17:24:58 +0200 Subject: [PATCH] tui: drop redundant leading blanks in live tool overlay renderToolCall used to prepend a blank line at the top of each of its three branches (streaming-with-body, finished-no-result, finished-with-result) so the live box wouldn't sit flush against the prose / previous tool block above it. That made sense before each tool result owned its own complete box \u2014 back then the assistant message also injected a blank before its embedded top edge, and the two paths matched. After the box-ownership refactor the transcript path no longer emits a leading blank (Build()'s natural inter-message separator provides one), but renderToolCall still did, so a streaming tool sat two blank rows below the previous content while the same call, once finalised, sat only one. The visible "snap tighter" when streaming ended was the doubled gap collapsing. Drop the three leading blanks. Build()'s inter-message blank is the single source of vertical spacing now, so streaming and finalised forms have identical gaps. --- internal/tui/view.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/tui/view.go b/internal/tui/view.go index e6d8eac..5bcbae0 100644 --- a/internal/tui/view.go +++ b/internal/tui/view.go @@ -598,9 +598,12 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string { } label := tc.Name + " " + arg - // Every box is preceded by a blank line so it doesn't sit - // flush against the prose / previous tool block above it. - // Matches the transcript-side framing in renderMessage. + // No leading blank: Build()'s inter-message separator already + // places one blank row between the previous transcript content + // and the live overlay, matching the spacing the same call has + // once it finalises into a transcript box. Adding another blank + // here would double the gap during streaming and visibly tighten + // when the overlay disappears. // Streaming body (write/edit): top edge with the label, body // rows wrapped with vertical edges, bottom edge to close the @@ -608,7 +611,6 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string { // the same frame the transcript renders the closed box, so // there's no visible hop. if tc.Streaming && tc.Result == "" { - lines = append(lines, "") lines = append(lines, toolBoxTop(v.Theme, label, width)) lines = append(lines, toolBoxSide(v.Theme, "", width)) if body := v.renderLiveToolBody(tc, width); len(body) > 0 { @@ -623,7 +625,6 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string { // directly closed by the bottom. Avoids a blank interior row // for no-output tools. if tc.Result == "" { - lines = append(lines, "") lines = append(lines, toolBoxTop(v.Theme, label, width)) lines = append(lines, toolBoxBottom(v.Theme, width)) return lines @@ -634,7 +635,6 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string { // vertical edges; bottom edge closes the box. Blank interior // rows after the top and before the bottom give the body a bit // of breathing room from the corners. - lines = append(lines, "") lines = append(lines, toolBoxTop(v.Theme, label, width)) lines = append(lines, toolBoxSide(v.Theme, "", width)) color := v.Theme.ToolOut