@@ -72,6 +72,7 @@ type selectionState struct {
7272 endLine int
7373 endCol int
7474 mouseButtonDown bool
75+ mouseY int // Screen Y coordinate for autoscroll
7576}
7677
7778// start initializes a new selection at the given position
@@ -156,13 +157,15 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
156157 if msg .Button == tea .MouseLeft {
157158 line , col := m .mouseToLineCol (msg .X , msg .Y )
158159 m .selection .start (line , col )
160+ m .selection .mouseY = msg .Y // Store screen Y for autoscroll
159161 }
160162 return m , nil
161163
162164 case tea.MouseMotionMsg :
163165 if m .selection .mouseButtonDown && m .selection .active {
164166 line , col := m .mouseToLineCol (msg .X , msg .Y )
165167 m .selection .update (line , col )
168+ m .selection .mouseY = msg .Y // Store screen Y for autoscroll
166169
167170 cmd := m .autoScroll ()
168171 return m , cmd
@@ -945,15 +948,17 @@ func (m *model) applySelectionHighlight(lines []string, viewportStartLine int) [
945948 case absoluteLine == startLine :
946949 // Start of multi-line selection
947950 plainLine := ansi .Strip (line )
948- lineWidth := runewidth .StringWidth (plainLine )
951+ trimmedLine := strings .TrimRight (plainLine , " \t " )
952+ lineWidth := runewidth .StringWidth (trimmedLine )
949953 highlighted [i ] = m .highlightLine (line , startCol , lineWidth )
950954 case absoluteLine == endLine :
951955 // End of multi-line selection
952956 highlighted [i ] = m .highlightLine (line , 0 , endCol )
953957 default :
954958 // Middle of multi-line selection
955959 plainLine := ansi .Strip (line )
956- lineWidth := runewidth .StringWidth (plainLine )
960+ trimmedLine := strings .TrimRight (plainLine , " \t " )
961+ lineWidth := runewidth .StringWidth (trimmedLine )
957962 highlighted [i ] = m .highlightLine (line , 0 , lineWidth )
958963 }
959964 }
@@ -1007,23 +1012,31 @@ func (m *model) autoScroll() tea.Cmd {
10071012 const scrollThreshold = 2
10081013 direction := 0
10091014
1010- if m .selection .endCol < scrollThreshold && m .scrollOffset > 0 {
1011- // Scroll up
1015+ // Use stored screen Y coordinate to check if mouse is in autoscroll region
1016+ // mouseToLineCol subtracts 2 for header, so viewport-relative Y is mouseY - 2
1017+ viewportY := m .selection .mouseY - 2
1018+
1019+ // Ensure viewportY is valid (can't be negative or beyond viewport)
1020+ if viewportY < 0 {
1021+ viewportY = 0
1022+ }
1023+
1024+ if viewportY < scrollThreshold && m .scrollOffset > 0 {
1025+ // Scroll up - mouse is near top of viewport
10121026 direction = - 1
10131027 m .scrollUp ()
1014- if m . selection . endLine > 0 {
1015- m . selection . endLine --
1016- }
1017- } else if m . selection . endCol >= m .height - scrollThreshold {
1018- // Scroll down
1028+ // Update endLine to reflect new scroll position
1029+ // When scrolling up, content moves up, so mouse points to a line that's 1 less in absolute terms
1030+ m . selection . endLine = max ( 0 , m . selection . endLine - 1 )
1031+ } else if viewportY >= m .height - scrollThreshold && viewportY < m . height {
1032+ // Scroll down - mouse is near bottom of viewport
10191033 maxScrollOffset := max (0 , m .totalHeight - m .height )
10201034 if m .scrollOffset < maxScrollOffset {
10211035 direction = 1
10221036 m .scrollDown ()
1023- lines := strings .Split (m .rendered , "\n " )
1024- if m .selection .endLine < len (lines )- 1 {
1025- m .selection .endLine ++
1026- }
1037+ // Update endLine to reflect new scroll position
1038+ // When scrolling down, content moves down, so mouse points to a line that's 1 more in absolute terms
1039+ m .selection .endLine ++
10271040 }
10281041 }
10291042
0 commit comments