1package tui
2
3import (
4 "strings"
5
6 "github.com/charmbracelet/x/ansi"
7)
8
9// overlayBlock paints the lines of block on top of base starting at the
10// (row, col) cell position. Lines that extend past the bottom of base are
11// appended. The result preserves existing ANSI styling around the
12// overlaid region.
13func overlayBlock(base string, block []string, row, col int) string {
14 if len(block) == 0 {
15 return base
16 }
17 lines := strings.Split(base, "\n")
18 for i, overlay := range block {
19 r := row + i
20 for r >= len(lines) {
21 lines = append(lines, "")
22 }
23 lines[r] = overlayLine(lines[r], overlay, col)
24 }
25 return strings.Join(lines, "\n")
26}
27
28// overlayLine returns base with overlay painted starting at column col.
29// Existing cells under the overlay are removed; cells to the left and
30// right of the overlay are preserved with their ANSI styling intact.
31// When col exceeds the visible width of base the gap is padded with
32// spaces.
33func overlayLine(base, overlay string, col int) string {
34 if overlay == "" {
35 return base
36 }
37 overlayWidth := ansi.StringWidth(overlay)
38 baseWidth := ansi.StringWidth(base)
39
40 left := ansi.Truncate(base, col, "")
41 leftWidth := ansi.StringWidth(left)
42
43 var pad string
44 if leftWidth < col {
45 pad = strings.Repeat(" ", col-leftWidth)
46 }
47
48 var right string
49 rightStart := col + overlayWidth
50 if rightStart < baseWidth {
51 right = ansi.Cut(base, rightStart, baseWidth)
52 }
53
54 // Reset SGR after the overlay so the overlay's styles don't bleed
55 // into the surrounding cells (the rest of the row may inherit ANSI
56 // from earlier in the string).
57 return left + pad + overlay + "\x1b[0m" + right
58}