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.
- commit
- 37eb752cb29f784f57e95f0e1afa20f10dc16ab9
- author
- patriceckhart · · 2026-04-27 15:24 UTC
- parent
- 48d22c4
- signature
- unsigned
- refs
- tag: v0.1.30
1 file · +6 -6
M internal/tui/view.go +6 -6
| @@ -598,9 +598,12 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string { | |||
| 598 | 598 | } | |
| 599 | 599 | label := tc.Name + " " + arg | |
| 600 | 600 | ||
| 601 | - | // Every box is preceded by a blank line so it doesn't sit | |
| 602 | - | // flush against the prose / previous tool block above it. | |
| 603 | - | // Matches the transcript-side framing in renderMessage. | |
| 601 | + | // No leading blank: Build()'s inter-message separator already | |
| 602 | + | // places one blank row between the previous transcript content | |
| 603 | + | // and the live overlay, matching the spacing the same call has | |
| 604 | + | // once it finalises into a transcript box. Adding another blank | |
| 605 | + | // here would double the gap during streaming and visibly tighten | |
| 606 | + | // when the overlay disappears. | |
| 604 | 607 | ||
| 605 | 608 | // Streaming body (write/edit): top edge with the label, body | |
| 606 | 609 | // rows wrapped with vertical edges, bottom edge to close the | |
| @@ -608,7 +611,6 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string { | |||
| 608 | 611 | // the same frame the transcript renders the closed box, so | |
| 609 | 612 | // there's no visible hop. | |
| 610 | 613 | if tc.Streaming && tc.Result == "" { | |
| 611 | - | lines = append(lines, "") | |
| 612 | 614 | lines = append(lines, toolBoxTop(v.Theme, label, width)) | |
| 613 | 615 | lines = append(lines, toolBoxSide(v.Theme, "", width)) | |
| 614 | 616 | if body := v.renderLiveToolBody(tc, width); len(body) > 0 { | |
| @@ -623,7 +625,6 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string { | |||
| 623 | 625 | // directly closed by the bottom. Avoids a blank interior row | |
| 624 | 626 | // for no-output tools. | |
| 625 | 627 | if tc.Result == "" { | |
| 626 | - | lines = append(lines, "") | |
| 627 | 628 | lines = append(lines, toolBoxTop(v.Theme, label, width)) | |
| 628 | 629 | lines = append(lines, toolBoxBottom(v.Theme, width)) | |
| 629 | 630 | return lines | |
| @@ -634,7 +635,6 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string { | |||
| 634 | 635 | // vertical edges; bottom edge closes the box. Blank interior | |
| 635 | 636 | // rows after the top and before the bottom give the body a bit | |
| 636 | 637 | // of breathing room from the corners. | |
| 637 | - | lines = append(lines, "") | |
| 638 | 638 | lines = append(lines, toolBoxTop(v.Theme, label, width)) | |
| 639 | 639 | lines = append(lines, toolBoxSide(v.Theme, "", width)) | |
| 640 | 640 | color := v.Theme.ToolOut | |
| @@ -598,9 +598,12 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string { | |||||
| 598 | } | 598 | } | ||
| 599 | label := tc.Name + " " + arg | 599 | label := tc.Name + " " + arg | ||
| 600 | 600 | ||||
| 601 | - | // Every box is preceded by a blank line so it doesn't sit | 601 | + | // No leading blank: Build()'s inter-message separator already |
| 602 | - | // flush against the prose / previous tool block above it. | 602 | + | // places one blank row between the previous transcript content |
| 603 | - | // Matches the transcript-side framing in renderMessage. | 603 | + | // and the live overlay, matching the spacing the same call has |
| 604 | + | // once it finalises into a transcript box. Adding another blank | |||
| 605 | + | // here would double the gap during streaming and visibly tighten | |||
| 606 | + | // when the overlay disappears. | |||
| 604 | 607 | ||||
| 605 | // Streaming body (write/edit): top edge with the label, body | 608 | // Streaming body (write/edit): top edge with the label, body | ||
| 606 | // rows wrapped with vertical edges, bottom edge to close the | 609 | // rows wrapped with vertical edges, bottom edge to close the | ||
| @@ -608,7 +611,6 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string { | |||||
| 608 | // the same frame the transcript renders the closed box, so | 611 | // the same frame the transcript renders the closed box, so | ||
| 609 | // there's no visible hop. | 612 | // there's no visible hop. | ||
| 610 | if tc.Streaming && tc.Result == "" { | 613 | if tc.Streaming && tc.Result == "" { | ||
| 611 | - | lines = append(lines, "") | |||
| 612 | lines = append(lines, toolBoxTop(v.Theme, label, width)) | 614 | lines = append(lines, toolBoxTop(v.Theme, label, width)) | ||
| 613 | lines = append(lines, toolBoxSide(v.Theme, "", width)) | 615 | lines = append(lines, toolBoxSide(v.Theme, "", width)) | ||
| 614 | if body := v.renderLiveToolBody(tc, width); len(body) > 0 { | 616 | if body := v.renderLiveToolBody(tc, width); len(body) > 0 { | ||
| @@ -623,7 +625,6 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string { | |||||
| 623 | // directly closed by the bottom. Avoids a blank interior row | 625 | // directly closed by the bottom. Avoids a blank interior row | ||
| 624 | // for no-output tools. | 626 | // for no-output tools. | ||
| 625 | if tc.Result == "" { | 627 | if tc.Result == "" { | ||
| 626 | - | lines = append(lines, "") | |||
| 627 | lines = append(lines, toolBoxTop(v.Theme, label, width)) | 628 | lines = append(lines, toolBoxTop(v.Theme, label, width)) | ||
| 628 | lines = append(lines, toolBoxBottom(v.Theme, width)) | 629 | lines = append(lines, toolBoxBottom(v.Theme, width)) | ||
| 629 | return lines | 630 | return lines | ||
| @@ -634,7 +635,6 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string { | |||||
| 634 | // vertical edges; bottom edge closes the box. Blank interior | 635 | // vertical edges; bottom edge closes the box. Blank interior | ||
| 635 | // rows after the top and before the bottom give the body a bit | 636 | // rows after the top and before the bottom give the body a bit | ||
| 636 | // of breathing room from the corners. | 637 | // of breathing room from the corners. | ||
| 637 | - | lines = append(lines, "") | |||
| 638 | lines = append(lines, toolBoxTop(v.Theme, label, width)) | 638 | lines = append(lines, toolBoxTop(v.Theme, label, width)) | ||
| 639 | lines = append(lines, toolBoxSide(v.Theme, "", width)) | 639 | lines = append(lines, toolBoxSide(v.Theme, "", width)) | ||
| 640 | color := v.Theme.ToolOut | 640 | color := v.Theme.ToolOut | ||