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