detached ea58887

Fix login dialog cursor alignment

commit
ea58887bfa85dfb864b2fa5c2515a703c61a8e31
author
patriceckhart · · 2026-05-31 11:51 UTC
parent
917da8c
signature
unsigned
refs
tag: v0.2.7
2 files · +30 -1
M packages/agent/modes/login_dialog.go +5 -1
@@ -490,7 +490,11 @@ func (d *loginDialog) CursorPos(width int) (row, col int) {
490490 wrapW = 20
491491 }
492492 urlLines := len(tui.WrapANSILine(d.url, wrapW))
493- baseOffset := 1 /*frameHeader*/ + 1 /*hint*/ + urlLines + 1 /*blank*/ + 1 /*prompt*/
493+ // interactive.redraw wraps dialog output with padDialogFrame, which
494+ // injects a blank row after the frame header. Count that row here so
495+ // the real terminal cursor lands on the editor input instead of the
496+ // prompt above it.
497+ baseOffset := 1 /*frameHeader*/ + 1 /*padDialogFrame blank*/ + 1 /*hint*/ + urlLines + 1 /*blank*/ + 1 /*prompt*/
494498 return baseOffset + eRow, eCol
495499 }
496500
@@ -490,7 +490,11 @@ func (d *loginDialog) CursorPos(width int) (row, col int) {
490 wrapW = 20490 wrapW = 20
491 }491 }
492 urlLines := len(tui.WrapANSILine(d.url, wrapW))492 urlLines := len(tui.WrapANSILine(d.url, wrapW))
493- baseOffset := 1 /*frameHeader*/ + 1 /*hint*/ + urlLines + 1 /*blank*/ + 1 /*prompt*/493+ // interactive.redraw wraps dialog output with padDialogFrame, which
494+ // injects a blank row after the frame header. Count that row here so
495+ // the real terminal cursor lands on the editor input instead of the
496+ // prompt above it.
497+ baseOffset := 1 /*frameHeader*/ + 1 /*padDialogFrame blank*/ + 1 /*hint*/ + urlLines + 1 /*blank*/ + 1 /*prompt*/
494 return baseOffset + eRow, eCol498 return baseOffset + eRow, eCol
495 }499 }
496 500
A packages/agent/modes/login_dialog_test.go +25 -0
@@ -0,0 +1,25 @@
1+package modes
2+
3+import (
4+ "strings"
5+ "testing"
6+
7+ "github.com/patriceckhart/zot/packages/tui"
8+)
9+
10+func TestLoginDialogCursorPosMatchesPaddedInputRow(t *testing.T) {
11+ d := newLoginDialog()
12+ d.Open(t.TempDir())
13+ d.method = "oauth"
14+ d.provider = "anthropic"
15+ d.ShowWaiting("https://example.com/oauth/authorize?code_challenge=abc&state=xyz")
16+
17+ lines := padDialogFrame(d.Render(tui.Theme{}, 80))
18+ row, _ := d.CursorPos(80)
19+ if row < 0 || row >= len(lines) {
20+ t.Fatalf("CursorPos row = %d outside rendered lines %d", row, len(lines))
21+ }
22+ if got := stripANSIBytes(lines[row]); !strings.Contains(got, "▌") {
23+ t.Fatalf("CursorPos row %d = %q; want editor input row", row, got)
24+ }
25+}
@@ -0,0 +1,25 @@
1+package modes
2+
3+import (
4+ "strings"
5+ "testing"
6+
7+ "github.com/patriceckhart/zot/packages/tui"
8+)
9+
10+func TestLoginDialogCursorPosMatchesPaddedInputRow(t *testing.T) {
11+ d := newLoginDialog()
12+ d.Open(t.TempDir())
13+ d.method = "oauth"
14+ d.provider = "anthropic"
15+ d.ShowWaiting("https://example.com/oauth/authorize?code_challenge=abc&state=xyz")
16+
17+ lines := padDialogFrame(d.Render(tui.Theme{}, 80))
18+ row, _ := d.CursorPos(80)
19+ if row < 0 || row >= len(lines) {
20+ t.Fatalf("CursorPos row = %d outside rendered lines %d", row, len(lines))
21+ }
22+ if got := stripANSIBytes(lines[row]); !strings.Contains(got, "▌") {
23+ t.Fatalf("CursorPos row %d = %q; want editor input row", row, got)
24+ }
25+}

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