on main help sign in
fix(ui): follow scroll when at bottom (#2336)

* fix(ui): follow scroll when at bottom This change attempt to completely fix the agent scroll issue when at bottom. It should follow the agent when the follow flag is set. * fix(ui): make sure we select the last item when at bottom

commit8762efc8fa0970883f807a5f564b5ef8b6042e4c authorAyman Bagabas <ayman.bagabas@gmail.com> dateMon 2 Mar 2026, 15:02 (8 months ago) parent4596b02
1 file changed+23−8
internal/ui/model/ui.go+23−8
internal/ui/model/ui.go+23−8
@@ -556,6 +556,11 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
556556 case tea.WindowSizeMsg:
557557 m.width, m.height = msg.Width, msg.Height
558558 m.updateLayoutAndSize()
559+ if m.state == uiChat && m.chat.Follow() {
560+ if cmd := m.chat.ScrollToBottomAndAnimate(); cmd != nil {
561+ cmds = append(cmds, cmd)
562+ }
563+ }
559564 case tea.KeyboardEnhancementsMsg:
560565 m.keyenh = msg
561566 if msg.SupportsKeyDisambiguation() {
@@ -680,7 +685,11 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
680685 cmds = append(cmds, cmd)
681686 }
682687 if !m.chat.SelectedItemInView() {
683− m.chat.SelectNext()
688+ if m.chat.AtBottom() {
689+ m.chat.SelectLast()
690+ } else {
691+ m.chat.SelectNext()
692+ }
684693 if cmd := m.chat.ScrollToSelectedAndAnimate(); cmd != nil {
685694 cmds = append(cmds, cmd)
686695 }
@@ -692,6 +701,11 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
692701 if cmd := m.chat.Animate(msg); cmd != nil {
693702 cmds = append(cmds, cmd)
694703 }
704+ if m.chat.Follow() {
705+ if cmd := m.chat.ScrollToBottomAndAnimate(); cmd != nil {
706+ cmds = append(cmds, cmd)
707+ }
708+ }
695709 }
696710 case spinner.TickMsg:
697711 if m.dialog.HasDialogs() {
@@ -822,7 +836,7 @@ func (m *UI) setSessionMessages(msgs []message.Message) tea.Cmd {
822836 cmds = append(cmds, cmd)
823837 }
824838 m.chat.SelectLast()
825− return tea.Batch(cmds...)
839+ return tea.Sequence(cmds...)
826840}
827841
828842// loadNestedToolCalls recursively loads nested tool calls for agent/agentic_fetch tools.
@@ -950,7 +964,7 @@ func (m *UI) appendSessionMessage(msg message.Message) tea.Cmd {
950964 }
951965 }
952966 }
953− return tea.Batch(cmds...)
967+ return tea.Sequence(cmds...)
954968}
955969
956970func (m *UI) handleClickFocus(msg tea.MouseClickMsg) (cmd tea.Cmd) {
@@ -1029,16 +1043,16 @@ func (m *UI) updateSessionMessage(msg message.Message) tea.Cmd {
10291043 if cmd := m.chat.ScrollToBottomAndAnimate(); cmd != nil {
10301044 cmds = append(cmds, cmd)
10311045 }
1046+ m.chat.SelectLast()
10321047 }
10331048
1034− return tea.Batch(cmds...)
1049+ return tea.Sequence(cmds...)
10351050}
10361051
10371052// handleChildSessionMessage handles messages from child sessions (agent tools).
10381053func (m *UI) handleChildSessionMessage(event pubsub.Event[message.Message]) tea.Cmd {
10391054 var cmds []tea.Cmd
10401055
1041− atBottom := m.chat.AtBottom()
10421056 // Only process messages with tool calls or results.
10431057 if len(event.Payload.ToolCalls()) == 0 && len(event.Payload.ToolResults()) == 0 {
10441058 return nil
@@ -1118,13 +1132,14 @@ func (m *UI) handleChildSessionMessage(event pubsub.Event[message.Message]) tea.
11181132 // Update the chat so it updates the index map for animations to work as expected
11191133 m.chat.UpdateNestedToolIDs(toolCallID)
11201134
1121− if atBottom {
1135+ if m.chat.Follow() {
11221136 if cmd := m.chat.ScrollToBottomAndAnimate(); cmd != nil {
11231137 cmds = append(cmds, cmd)
11241138 }
1139+ m.chat.SelectLast()
11251140 }
11261141
1127− return tea.Batch(cmds...)
1142+ return tea.Sequence(cmds...)
11281143}
11291144
11301145func (m *UI) handleDialogMsg(msg tea.Msg) tea.Cmd {
@@ -1804,7 +1819,7 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) tea.Cmd {
18041819 handleGlobalKeys(msg)
18051820 }
18061821
1807− return tea.Batch(cmds...)
1822+ return tea.Sequence(cmds...)
18081823}
18091824
18101825// drawHeader draws the header section of the UI.