detached a8648e7

fix(tui): refine floating pane interactions

commit
a8648e7e99f2a87329a740bdab32bee5546c2a32
author
brice · · 2026-08-14 11:42 UTC
parent
4eba8ac
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +28 -3
M packages/agent/modes/interactive_input_routing.go +10 -2
@@ -281,9 +281,17 @@ func (i *Interactive) handleKey(ctx context.Context, k tui.Key) (done bool) {
281281 residentSession.Scroll(10)
282282 case tui.KeyPageDown:
283283 residentSession.Scroll(-10)
284- case tui.KeyUp, tui.KeyMouseWheelUp:
284+ case tui.KeyUp:
285+ if !residentSession.HandleNavigation(k) {
286+ residentSession.Scroll(1)
287+ }
288+ case tui.KeyDown:
289+ if !residentSession.HandleNavigation(k) {
290+ residentSession.Scroll(-1)
291+ }
292+ case tui.KeyMouseWheelUp:
285293 residentSession.Scroll(1)
286- case tui.KeyDown, tui.KeyMouseWheelDown:
294+ case tui.KeyMouseWheelDown:
287295 residentSession.Scroll(-1)
288296 default:
289297 session := residentSession
@@ -281,9 +281,17 @@ func (i *Interactive) handleKey(ctx context.Context, k tui.Key) (done bool) {
281 residentSession.Scroll(10)281 residentSession.Scroll(10)
282 case tui.KeyPageDown:282 case tui.KeyPageDown:
283 residentSession.Scroll(-10)283 residentSession.Scroll(-10)
284- case tui.KeyUp, tui.KeyMouseWheelUp:284+ case tui.KeyUp:
285+ if !residentSession.HandleNavigation(k) {
286+ residentSession.Scroll(1)
287+ }
288+ case tui.KeyDown:
289+ if !residentSession.HandleNavigation(k) {
290+ residentSession.Scroll(-1)
291+ }
292+ case tui.KeyMouseWheelUp:
285 residentSession.Scroll(1)293 residentSession.Scroll(1)
286- case tui.KeyDown, tui.KeyMouseWheelDown:294+ case tui.KeyMouseWheelDown:
287 residentSession.Scroll(-1)295 residentSession.Scroll(-1)
288 default:296 default:
289 session := residentSession297 session := residentSession
M packages/agent/modes/subagent_session.go +9 -0
@@ -297,6 +297,15 @@ func (s *residentChildSession) HandleKey(k tui.Key) (string, bool) {
297297 return prompt, prompt != ""
298298 }
299299
300+func (s *residentChildSession) HandleNavigation(k tui.Key) bool {
301+ if s == nil || s.composer == nil {
302+ return false
303+ }
304+ s.mu.Lock()
305+ defer s.mu.Unlock()
306+ return s.composer.HandleKey(k)
307+}
308+
300309 func (s *residentChildSession) FinishSubmission(err error) {
301310 if s == nil {
302311 return
@@ -297,6 +297,15 @@ func (s *residentChildSession) HandleKey(k tui.Key) (string, bool) {
297 return prompt, prompt != ""297 return prompt, prompt != ""
298 }298 }
299 299
300+func (s *residentChildSession) HandleNavigation(k tui.Key) bool {
301+ if s == nil || s.composer == nil {
302+ return false
303+ }
304+ s.mu.Lock()
305+ defer s.mu.Unlock()
306+ return s.composer.HandleKey(k)
307+}
308+
300 func (s *residentChildSession) FinishSubmission(err error) {309 func (s *residentChildSession) FinishSubmission(err error) {
301 if s == nil {310 if s == nil {
302 return311 return
M packages/agent/modes/subagents_slash.go +4 -0
@@ -98,11 +98,15 @@ func residentResultStatus(id string, result subagents.ResidentResult) string {
9898 func (i *Interactive) openResidentChildSession(childID string) {
9999 session := newResidentChildSession(i.cfg.ResidentManager, childID, i.cfg.Theme)
100100 i.mu.Lock()
101+ previous := i.residentChildSession
101102 if i.residentSubagentsDialog != nil {
102103 i.residentSubagentsDialog.Close()
103104 }
104105 i.residentChildSession = session
105106 i.mu.Unlock()
107+ if previous != nil {
108+ previous.Close()
109+ }
106110 i.setResidentChildMouseReporting(true)
107111 i.invalidate()
108112 if session.BeginLoad() {
@@ -98,11 +98,15 @@ func residentResultStatus(id string, result subagents.ResidentResult) string {
98 func (i *Interactive) openResidentChildSession(childID string) {98 func (i *Interactive) openResidentChildSession(childID string) {
99 session := newResidentChildSession(i.cfg.ResidentManager, childID, i.cfg.Theme)99 session := newResidentChildSession(i.cfg.ResidentManager, childID, i.cfg.Theme)
100 i.mu.Lock()100 i.mu.Lock()
101+ previous := i.residentChildSession
101 if i.residentSubagentsDialog != nil {102 if i.residentSubagentsDialog != nil {
102 i.residentSubagentsDialog.Close()103 i.residentSubagentsDialog.Close()
103 }104 }
104 i.residentChildSession = session105 i.residentChildSession = session
105 i.mu.Unlock()106 i.mu.Unlock()
107+ if previous != nil {
108+ previous.Close()
109+ }
106 i.setResidentChildMouseReporting(true)110 i.setResidentChildMouseReporting(true)
107 i.invalidate()111 i.invalidate()
108 if session.BeginLoad() {112 if session.BeginLoad() {
M packages/tui/render.go +5 -1
@@ -571,9 +571,13 @@ func (r *Renderer) DrawFloating(pane FloatingPaneFrame, cursorRow, cursorCol int
571571 if y < 0 || y >= r.rows {
572572 continue
573573 }
574+ if pane.Rect.X >= r.cols {
575+ continue
576+ }
577+ width := min(pane.Rect.Width, r.cols-pane.Rect.X)
574578 w.WriteString(MoveTo(y+1, pane.Rect.X+1))
575579 w.WriteString("\x1b[0m")
576- w.WriteString(paintBackgroundRow(truncateToWidth(line, pane.Rect.Width), pane.Rect.Width, r.backgroundStyle))
580+ w.WriteString(paintBackgroundRow(truncateToWidth(line, width), width, r.backgroundStyle))
577581 }
578582 if cursorRow >= 0 && cursorRow < r.rows && cursorCol >= 0 && cursorCol < r.cols {
579583 w.WriteString(MoveTo(cursorRow+1, cursorCol+1))
@@ -571,9 +571,13 @@ func (r *Renderer) DrawFloating(pane FloatingPaneFrame, cursorRow, cursorCol int
571 if y < 0 || y >= r.rows {571 if y < 0 || y >= r.rows {
572 continue572 continue
573 }573 }
574+ if pane.Rect.X >= r.cols {
575+ continue
576+ }
577+ width := min(pane.Rect.Width, r.cols-pane.Rect.X)
574 w.WriteString(MoveTo(y+1, pane.Rect.X+1))578 w.WriteString(MoveTo(y+1, pane.Rect.X+1))
575 w.WriteString("\x1b[0m")579 w.WriteString("\x1b[0m")
576- w.WriteString(paintBackgroundRow(truncateToWidth(line, pane.Rect.Width), pane.Rect.Width, r.backgroundStyle))580+ w.WriteString(paintBackgroundRow(truncateToWidth(line, width), width, r.backgroundStyle))
577 }581 }
578 if cursorRow >= 0 && cursorRow < r.rows && cursorCol >= 0 && cursorCol < r.cols {582 if cursorRow >= 0 && cursorRow < r.rows && cursorCol >= 0 && cursorCol < r.cols {
579 w.WriteString(MoveTo(cursorRow+1, cursorCol+1))583 w.WriteString(MoveTo(cursorRow+1, cursorCol+1))

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