detached 48d22c4

tui: add blank interior rows at top and bottom of tool boxes

Body content used to start on the row immediately under the top
edge and end on the row immediately above the bottom edge, leaving
the box visually cramped against its corners.

Insert a blank box-side row (a \u2502 \u2026 \u2502 with no content) after the
top edge and before the bottom edge in both the transcript path
(renderMessage RoleTool branch) and the live overlay path
(renderToolCall, both streaming and finished-with-body). Empty-
bodied boxes (finished call with no result) stay compact \u2014 the top
and bottom edges sit directly adjacent so a no-output tool doesn't
render as a tall hollow frame.
commit
48d22c481505901e8d084313732dfe332dd58eef
author
patriceckhart · · 2026-04-27 14:08 UTC
parent
955a9b7
signature
unsigned
refs
tag: v0.1.29
1 file · +9 -1
M internal/tui/view.go +9 -1
@@ -561,6 +561,7 @@ func (v *View) renderMessage(m provider.Message, width int, turnOpen bool) []str
561561 // in one assistant message render as N adjacent boxes
562562 // instead of stacking unclosed top edges.
563563 lines = append(lines, toolBoxTop(v.Theme, label, width))
564+ lines = append(lines, toolBoxSide(v.Theme, "", width))
564565 if tr.IsError {
565566 lines = append(lines, toolBoxSide(v.Theme, v.Theme.FG256(color, " error"), width))
566567 }
@@ -576,6 +577,7 @@ func (v *View) renderMessage(m provider.Message, width int, turnOpen bool) []str
576577 }
577578 lines = append(lines, toolBoxSide(v.Theme, body, width))
578579 }
580+ lines = append(lines, toolBoxSide(v.Theme, "", width))
579581 lines = append(lines, toolBoxBottom(v.Theme, width))
580582 }
581583 }
@@ -608,9 +610,11 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string {
608610 if tc.Streaming && tc.Result == "" {
609611 lines = append(lines, "")
610612 lines = append(lines, toolBoxTop(v.Theme, label, width))
613+ lines = append(lines, toolBoxSide(v.Theme, "", width))
611614 if body := v.renderLiveToolBody(tc, width); len(body) > 0 {
612615 lines = append(lines, body...)
613616 }
617+ lines = append(lines, toolBoxSide(v.Theme, "", width))
614618 lines = append(lines, toolBoxBottom(v.Theme, width))
615619 return lines
616620 }
@@ -627,9 +631,12 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string {
627631
628632 // Finished tool call with a result body. Top edge embeds the
629633 // label; body lines go through toolBoxSide so each row gets
630- // vertical edges; bottom edge closes the box.
634+ // vertical edges; bottom edge closes the box. Blank interior
635+ // rows after the top and before the bottom give the body a bit
636+ // of breathing room from the corners.
631637 lines = append(lines, "")
632638 lines = append(lines, toolBoxTop(v.Theme, label, width))
639+ lines = append(lines, toolBoxSide(v.Theme, "", width))
633640 color := v.Theme.ToolOut
634641 if tc.Error {
635642 color = v.Theme.Error
@@ -642,6 +649,7 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string {
642649 }
643650 lines = append(lines, toolBoxSide(v.Theme, l, width))
644651 }
652+ lines = append(lines, toolBoxSide(v.Theme, "", width))
645653 lines = append(lines, toolBoxBottom(v.Theme, width))
646654 return lines
647655 }
@@ -561,6 +561,7 @@ func (v *View) renderMessage(m provider.Message, width int, turnOpen bool) []str
561 // in one assistant message render as N adjacent boxes561 // in one assistant message render as N adjacent boxes
562 // instead of stacking unclosed top edges.562 // instead of stacking unclosed top edges.
563 lines = append(lines, toolBoxTop(v.Theme, label, width))563 lines = append(lines, toolBoxTop(v.Theme, label, width))
564+ lines = append(lines, toolBoxSide(v.Theme, "", width))
564 if tr.IsError {565 if tr.IsError {
565 lines = append(lines, toolBoxSide(v.Theme, v.Theme.FG256(color, " error"), width))566 lines = append(lines, toolBoxSide(v.Theme, v.Theme.FG256(color, " error"), width))
566 }567 }
@@ -576,6 +577,7 @@ func (v *View) renderMessage(m provider.Message, width int, turnOpen bool) []str
576 }577 }
577 lines = append(lines, toolBoxSide(v.Theme, body, width))578 lines = append(lines, toolBoxSide(v.Theme, body, width))
578 }579 }
580+ lines = append(lines, toolBoxSide(v.Theme, "", width))
579 lines = append(lines, toolBoxBottom(v.Theme, width))581 lines = append(lines, toolBoxBottom(v.Theme, width))
580 }582 }
581 }583 }
@@ -608,9 +610,11 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string {
608 if tc.Streaming && tc.Result == "" {610 if tc.Streaming && tc.Result == "" {
609 lines = append(lines, "")611 lines = append(lines, "")
610 lines = append(lines, toolBoxTop(v.Theme, label, width))612 lines = append(lines, toolBoxTop(v.Theme, label, width))
613+ lines = append(lines, toolBoxSide(v.Theme, "", width))
611 if body := v.renderLiveToolBody(tc, width); len(body) > 0 {614 if body := v.renderLiveToolBody(tc, width); len(body) > 0 {
612 lines = append(lines, body...)615 lines = append(lines, body...)
613 }616 }
617+ lines = append(lines, toolBoxSide(v.Theme, "", width))
614 lines = append(lines, toolBoxBottom(v.Theme, width))618 lines = append(lines, toolBoxBottom(v.Theme, width))
615 return lines619 return lines
616 }620 }
@@ -627,9 +631,12 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string {
627 631
628 // Finished tool call with a result body. Top edge embeds the632 // Finished tool call with a result body. Top edge embeds the
629 // label; body lines go through toolBoxSide so each row gets633 // label; body lines go through toolBoxSide so each row gets
630- // vertical edges; bottom edge closes the box.634+ // vertical edges; bottom edge closes the box. Blank interior
635+ // rows after the top and before the bottom give the body a bit
636+ // of breathing room from the corners.
631 lines = append(lines, "")637 lines = append(lines, "")
632 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))
633 color := v.Theme.ToolOut640 color := v.Theme.ToolOut
634 if tc.Error {641 if tc.Error {
635 color = v.Theme.Error642 color = v.Theme.Error
@@ -642,6 +649,7 @@ func (v *View) renderToolCall(tc ToolCallView, width int) []string {
642 }649 }
643 lines = append(lines, toolBoxSide(v.Theme, l, width))650 lines = append(lines, toolBoxSide(v.Theme, l, width))
644 }651 }
652+ lines = append(lines, toolBoxSide(v.Theme, "", width))
645 lines = append(lines, toolBoxBottom(v.Theme, width))653 lines = append(lines, toolBoxBottom(v.Theme, width))
646 return lines654 return lines
647 }655 }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close