1package modes
2
3import (
4 "fmt"
5 "strings"
6
7 "github.com/mattn/go-runewidth"
8
9 "github.com/patriceckhart/zot/internal/tui"
10)
11
12type extPanelDialog struct {
13 active bool
14 ext string
15 id string
16 title string
17 lines []string
18 footer string
19}
20
21func newExtPanelDialog() *extPanelDialog { return &extPanelDialog{} }
22
23func (d *extPanelDialog) Active() bool { return d != nil && d.active }
24
25func (d *extPanelDialog) Open(ext, id, title string, lines []string, footer string) {
26 d.active = true
27 d.ext = ext
28 d.id = id
29 d.title = title
30 d.lines = append([]string(nil), lines...)
31 d.footer = footer
32}
33
34func (d *extPanelDialog) Update(title string, lines []string, footer string) {
35 if !d.active {
36 return
37 }
38 if title != "" {
39 d.title = title
40 }
41 d.lines = append(d.lines[:0], lines...)
42 d.footer = footer
43}
44
45func (d *extPanelDialog) Close() {
46 d.active = false
47 d.ext = ""
48 d.id = ""
49 d.title = ""
50 d.lines = nil
51 d.footer = ""
52}
53
54func (d *extPanelDialog) Render(th tui.Theme, width int) []string {
55 if !d.Active() {
56 return nil
57 }
58 title := d.title
59 if title == "" {
60 title = d.ext
61 }
62 out := []string{frameHeaderColor(th, title, width, th.Accent)}
63 for _, l := range d.lines {
64 plain := stripANSIBytes(l)
65 trimmed := strings.TrimLeft(plain, " ")
66 // Selection markers, in order of precedence:
67 // "▸ " / "● " visible glyph the user sees
68 // "\u200b" invisible zero-width-space sentinel for
69 // extensions that want the row highlight
70 // without rendering an arrow
71 selected := strings.HasPrefix(trimmed, "▸ ") ||
72 strings.HasPrefix(trimmed, "● ") ||
73 strings.HasPrefix(trimmed, "\u200b")
74 out = append(out, styleExtPanelLine(th, l, plain, width, selected))
75 }
76 if strings.TrimSpace(d.footer) != "" {
77 out = append(out, "")
78 out = append(out, th.FG256(th.Muted, d.footer))
79 }
80 out = append(out, frameRuleColor(th, width, th.Accent))
81 return out
82}
83
84func styleExtPanelLine(th tui.Theme, raw string, plain string, width int, selected bool) string {
85 if selected {
86 if visible := runewidth.StringWidth(plain); visible < width {
87 plain += strings.Repeat(" ", width-visible)
88 }
89 base := fmt.Sprintf("\x1b[38;5;%dm\x1b[48;5;%dm", th.SelectionFG, th.SelectionBG)
90 green := fmt.Sprintf("\x1b[38;5;%dm\x1b[48;5;%dm", th.Tool, th.SelectionBG)
91 return base + strings.ReplaceAll(plain, "✓", green+"✓"+base) + "\x1b[0m"
92 }
93 if raw != plain {
94 return raw + "\x1b[0m"
95 }
96 styled := th.FG256(th.Muted, plain)
97 styled = strings.ReplaceAll(styled, "✓", th.FG256(th.Tool, "✓"))
98 return styled
99}