interactive: blank line above status block
Insert one blank row between the chat area and the status bar (spinner / model / cwd). Previously the status block sat flush against the chat content above; the blank gives it air whether the agent is busy (spinner first) or idle (model line first). Cursor row math bumped to skip both the new top blank and the existing blank between statusLines and edLines so the input cursor still lands on the right cell.
- commit
- 0a670c01e257b298a4c91fc70504ef852f066ed8
- author
- patriceckhart · · 2026-04-28 07:08 UTC
- parent
- cead9fd
- signature
- unsigned
- refs
- tag: v0.1.35
1 file · +14 -10
M internal/agent/modes/interactive.go +14 -10
| @@ -816,15 +816,17 @@ func (i *Interactive) redraw() { | |||
| 816 | 816 | queue = append(queue, "") | |
| 817 | 817 | } | |
| 818 | 818 | ||
| 819 | - | // Bottom-sticky sections (always visible, never scroll). A blank | |
| 820 | - | // row is inserted between the status bar and the editor, and a | |
| 821 | - | // trailing blank row is added at the very bottom, so the input | |
| 822 | - | // has breathing room from the surrounding chrome instead of | |
| 823 | - | // sitting flush against the status line and the terminal edge. | |
| 824 | - | bottom := make([]string, 0, len(dialog)+len(suggest)+len(queue)+len(edLines)+3) | |
| 819 | + | // Bottom-sticky sections (always visible, never scroll). One | |
| 820 | + | // blank row above statusLines so the spinner / model / cwd | |
| 821 | + | // block doesn't sit flush against the chat content above it, | |
| 822 | + | // one blank between status bar and editor for input breathing | |
| 823 | + | // room, and one trailing blank at the very bottom so the | |
| 824 | + | // editor isn't flush against the terminal edge. | |
| 825 | + | bottom := make([]string, 0, len(dialog)+len(suggest)+len(queue)+len(edLines)+4) | |
| 825 | 826 | bottom = append(bottom, dialog...) | |
| 826 | 827 | bottom = append(bottom, suggest...) | |
| 827 | 828 | bottom = append(bottom, queue...) | |
| 829 | + | bottom = append(bottom, "") | |
| 828 | 830 | bottom = append(bottom, statusLines...) | |
| 829 | 831 | bottom = append(bottom, "") | |
| 830 | 832 | bottom = append(bottom, edLines...) | |
| @@ -941,10 +943,12 @@ func (i *Interactive) redraw() { | |||
| 941 | 943 | // the blinking cursor shows where the user is actually typing. | |
| 942 | 944 | // Dialogs without a cursor (model picker, /help, /login, etc.) | |
| 943 | 945 | // return -1 and the main editor keeps the cursor. | |
| 944 | - | // +1 accounts for the blank row inserted between statusLines | |
| 945 | - | // and edLines above. Without it the rendered cursor would land | |
| 946 | - | // on the blank instead of inside the editor row. | |
| 947 | - | cursorRow := len(visibleChat) + len(dialog) + len(suggest) + len(queue) + len(statusLines) + 1 + curR | |
| 946 | + | // +2 accounts for the blank row above statusLines (so the | |
| 947 | + | // status block has air above it) and the blank row between | |
| 948 | + | // statusLines and edLines (input breathing room). Without | |
| 949 | + | // these the rendered cursor would land on a blank instead of | |
| 950 | + | // inside the editor row. | |
| 951 | + | cursorRow := len(visibleChat) + len(dialog) + len(suggest) + len(queue) + 1 + len(statusLines) + 1 + curR | |
| 948 | 952 | cursorCol := curC | |
| 949 | 953 | if i.btwDialog.Active() { | |
| 950 | 954 | if r, c := i.btwDialog.CursorPos(cols); r >= 0 { | |
| @@ -816,15 +816,17 @@ func (i *Interactive) redraw() { | |||||
| 816 | queue = append(queue, "") | 816 | queue = append(queue, "") | ||
| 817 | } | 817 | } | ||
| 818 | 818 | ||||
| 819 | - | // Bottom-sticky sections (always visible, never scroll). A blank | 819 | + | // Bottom-sticky sections (always visible, never scroll). One |
| 820 | - | // row is inserted between the status bar and the editor, and a | 820 | + | // blank row above statusLines so the spinner / model / cwd |
| 821 | - | // trailing blank row is added at the very bottom, so the input | 821 | + | // block doesn't sit flush against the chat content above it, |
| 822 | - | // has breathing room from the surrounding chrome instead of | 822 | + | // one blank between status bar and editor for input breathing |
| 823 | - | // sitting flush against the status line and the terminal edge. | 823 | + | // room, and one trailing blank at the very bottom so the |
| 824 | - | bottom := make([]string, 0, len(dialog)+len(suggest)+len(queue)+len(edLines)+3) | 824 | + | // editor isn't flush against the terminal edge. |
| 825 | + | bottom := make([]string, 0, len(dialog)+len(suggest)+len(queue)+len(edLines)+4) | |||
| 825 | bottom = append(bottom, dialog...) | 826 | bottom = append(bottom, dialog...) | ||
| 826 | bottom = append(bottom, suggest...) | 827 | bottom = append(bottom, suggest...) | ||
| 827 | bottom = append(bottom, queue...) | 828 | bottom = append(bottom, queue...) | ||
| 829 | + | bottom = append(bottom, "") | |||
| 828 | bottom = append(bottom, statusLines...) | 830 | bottom = append(bottom, statusLines...) | ||
| 829 | bottom = append(bottom, "") | 831 | bottom = append(bottom, "") | ||
| 830 | bottom = append(bottom, edLines...) | 832 | bottom = append(bottom, edLines...) | ||
| @@ -941,10 +943,12 @@ func (i *Interactive) redraw() { | |||||
| 941 | // the blinking cursor shows where the user is actually typing. | 943 | // the blinking cursor shows where the user is actually typing. | ||
| 942 | // Dialogs without a cursor (model picker, /help, /login, etc.) | 944 | // Dialogs without a cursor (model picker, /help, /login, etc.) | ||
| 943 | // return -1 and the main editor keeps the cursor. | 945 | // return -1 and the main editor keeps the cursor. | ||
| 944 | - | // +1 accounts for the blank row inserted between statusLines | 946 | + | // +2 accounts for the blank row above statusLines (so the |
| 945 | - | // and edLines above. Without it the rendered cursor would land | 947 | + | // status block has air above it) and the blank row between |
| 946 | - | // on the blank instead of inside the editor row. | 948 | + | // statusLines and edLines (input breathing room). Without |
| 947 | - | cursorRow := len(visibleChat) + len(dialog) + len(suggest) + len(queue) + len(statusLines) + 1 + curR | 949 | + | // these the rendered cursor would land on a blank instead of |
| 950 | + | // inside the editor row. | |||
| 951 | + | cursorRow := len(visibleChat) + len(dialog) + len(suggest) + len(queue) + 1 + len(statusLines) + 1 + curR | |||
| 948 | cursorCol := curC | 952 | cursorCol := curC | ||
| 949 | if i.btwDialog.Active() { | 953 | if i.btwDialog.Active() { | ||
| 950 | if r, c := i.btwDialog.CursorPos(cols); r >= 0 { | 954 | if r, c := i.btwDialog.CursorPos(cols); r >= 0 { | ||