@@ -350,13 +350,12 @@ func (l *list[T]) selectionView(view string, textOnly bool) string {
350 scr := uv.NewScreenBuffer(area.Dx(), area.Dy())
350 scr := uv.NewScreenBuffer(area.Dx(), area.Dy())
351 uv.NewStyledString(view).Draw(scr, area)
351 uv.NewStyledString(view).Draw(scr, area)
353− selArea := uv.Rectangle{
353+ selArea := l.selectionArea(false)
354− Min: uv.Pos(l.selectionStartCol, l.selectionStartLine),
355− Max: uv.Pos(l.selectionEndCol, l.selectionEndLine),
357− selArea = selArea.Canon()
359 specialChars := getSpecialCharsMap()
354 specialChars := getSpecialCharsMap()
355+ selStyle := uv.Style{
356+ Bg: t.TextSelection.GetBackground(),
357+ Fg: t.TextSelection.GetForeground(),
361 isNonWhitespace := func(r rune) bool {
360 isNonWhitespace := func(r rune) bool {
362 return r != ' ' && r != '\t' && r != 0 && r != '\n' && r != '\r'
361 return r != ' ' && r != '\t' && r != 0 && r != '\n' && r != '\r'
@@ -371,9 +370,9 @@ func (l *list[T]) selectionView(view string, textOnly bool) string {
371 for y := range scr.Height() {
370 for y := range scr.Height() {
372 bounds := selectionBounds{startX: -1, endX: -1, inSelection: false}
371 bounds := selectionBounds{startX: -1, endX: -1, inSelection: false}
374− if y >= selArea.Min.Y && y <= selArea.Max.Y {
373+ if y >= selArea.Min.Y && y < selArea.Max.Y {
375 bounds.inSelection = true
374 bounds.inSelection = true
376− if selArea.Min.Y == selArea.Max.Y {
375+ if selArea.Min.Y == selArea.Max.Y-1 {
377 // Single line selection
376 // Single line selection
378 bounds.startX = selArea.Min.X
377 bounds.startX = selArea.Min.X
379 bounds.endX = selArea.Max.X
378 bounds.endX = selArea.Max.X
@@ -381,7 +380,7 @@ func (l *list[T]) selectionView(view string, textOnly bool) string {
381 // First line of multi-line selection
380 // First line of multi-line selection
382 bounds.startX = selArea.Min.X
381 bounds.startX = selArea.Min.X
383 bounds.endX = scr.Width()
382 bounds.endX = scr.Width()
384− } else if y == selArea.Max.Y {
383+ } else if y == selArea.Max.Y-1 {
385 // Last line of multi-line selection
384 // Last line of multi-line selection
386 bounds.startX = 0
385 bounds.startX = 0
387 bounds.endX = selArea.Max.X
386 bounds.endX = selArea.Max.X
@@ -470,13 +469,9 @@ func (l *list[T]) selectionView(view string, textOnly bool) string {
473− // Text selection styling, which is a Lip Gloss style. We must
474− // extract the values to use in a UV style, below.
475− ts := t.TextSelection
477 cell = cell.Clone()
472 cell = cell.Clone()
478− cell.Style.Bg = ts.GetBackground()
473+ cell.Style.Bg = selStyle.Bg
479− cell.Style.Fg = ts.GetForeground()
474+ cell.Style.Fg = selStyle.Fg
480 scr.SetCell(x, y, cell)
475 scr.SetCell(x, y, cell)
@@ -1706,11 +1701,75 @@ func (l *list[T]) HasSelection() bool {
1706 return l.hasSelection()
1701 return l.hasSelection()
1704+func (l *list[T]) selectionArea(absolute bool) uv.Rectangle {
1707+ startY, _ = l.viewPosition()
1709+ selArea := uv.Rectangle{
1710+ Min: uv.Pos(l.selectionStartCol, l.selectionStartLine+startY),
1711+ Max: uv.Pos(l.selectionEndCol, l.selectionEndLine+startY),
1713+ selArea = selArea.Canon()
1714+ selArea.Max.Y++ // make max Y exclusive
1709// GetSelectedText returns the currently selected text.
1718// GetSelectedText returns the currently selected text.
1710func (l *list[T]) GetSelectedText(paddingLeft int) string {
1719func (l *list[T]) GetSelectedText(paddingLeft int) string {
1711 if !l.hasSelection() {
1720 if !l.hasSelection() {
1712 return ""
1721 return ""
1715− return l.selectionView(l.View(), true)
1724+ selArea := l.selectionArea(true)
1725+ if selArea.Empty() {
1729+ selectionHeight := selArea.Dy()
1731+ tempBuf := uv.NewScreenBuffer(l.width, selectionHeight)
1732+ tempBufArea := tempBuf.Bounds()
1733+ renderedLines := l.getLines(selArea.Min.Y, selArea.Max.Y)
1734+ styled := uv.NewStyledString(renderedLines)
1735+ styled.Draw(tempBuf, tempBufArea)
1737+ // XXX: Left padding assumes the list component is rendered with absolute
1738+ // positioning. The chat component has a left margin of 1 and items in the
1739+ // list have a border of 1 plus a padding of 1. The paddingLeft parameter
1740+ // assumes this total left padding of 3 and we should fix that.
1741+ leftBorder := paddingLeft - 1
1743+ var b strings.Builder
1744+ for y := tempBufArea.Min.Y; y < tempBufArea.Max.Y; y++ {
1745+ var pending strings.Builder
1746+ for x := tempBufArea.Min.X + leftBorder; x < tempBufArea.Max.X; {
1747+ cell := tempBuf.CellAt(x, y)
1748+ if cell == nil || cell.IsZero() {
1752+ if y == 0 && x < selArea.Min.X {
1756+ if y == selectionHeight-1 && x > selArea.Max.X-1 {
1759+ if cell.Width == 1 && cell.Content == " " {
1760+ pending.WriteString(cell.Content)
1764+ b.WriteString(pending.String())
1766+ b.WriteString(cell.Content)
1769+ if y < tempBufArea.Max.Y-1 {