@@ -3,10 +3,20 @@ package messages
33import (
44 "strings"
55
6+ "charm.land/lipgloss/v2"
67 "github.com/charmbracelet/x/ansi"
78 "github.com/mattn/go-runewidth"
89)
910
11+ var underlineStyle = lipgloss .NewStyle ().Underline (true )
12+
13+ // hoveredURL tracks the URL currently under the mouse cursor.
14+ type hoveredURL struct {
15+ line int // global rendered line
16+ startCol int // display column where URL starts
17+ endCol int // display column where URL ends (exclusive)
18+ }
19+
1020// urlAtPosition extracts a URL from the rendered line at the given display column.
1121// Returns the URL string if found, or empty string if the click position is not on a URL.
1222func urlAtPosition (renderedLine string , col int ) string {
@@ -128,3 +138,44 @@ func (m *model) urlAt(line, col int) string {
128138 }
129139 return urlAtPosition (m .renderedLines [line ], col )
130140}
141+
142+ // updateHoveredURL updates the hovered URL state based on mouse position.
143+ func (m * model ) updateHoveredURL (line , col int ) {
144+ m .ensureAllItemsRendered ()
145+
146+ if line >= 0 && line < len (m .renderedLines ) {
147+ plainLine := ansi .Strip (m .renderedLines [line ])
148+ for _ , span := range findURLSpans (plainLine ) {
149+ if col >= span .startCol && col < span .endCol {
150+ newHover := & hoveredURL {line : line , startCol : span .startCol , endCol : span .endCol }
151+ if m .hoveredURL == nil || * m .hoveredURL != * newHover {
152+ m .hoveredURL = newHover
153+ m .renderDirty = true
154+ }
155+ return
156+ }
157+ }
158+ }
159+
160+ if m .hoveredURL != nil {
161+ m .hoveredURL = nil
162+ m .renderDirty = true
163+ }
164+ }
165+
166+ // applyURLUnderline underlines the hovered URL in the visible lines.
167+ func (m * model ) applyURLUnderline (lines []string , viewportStartLine int ) []string {
168+ if m .hoveredURL == nil {
169+ return lines
170+ }
171+
172+ viewIdx := m .hoveredURL .line - viewportStartLine
173+ if viewIdx < 0 || viewIdx >= len (lines ) {
174+ return lines
175+ }
176+
177+ result := make ([]string , len (lines ))
178+ copy (result , lines )
179+ result [viewIdx ] = styleLineSegment (lines [viewIdx ], m .hoveredURL .startCol , m .hoveredURL .endCol , underlineStyle )
180+ return result
181+ }
0 commit comments