dialog_frame.go RawHistoryFolder
1package modes
2
3import (
4 "strings"
5
6 "github.com/mattn/go-runewidth"
7
8 "github.com/bnema/zut/packages/tui"
9)
10
11// frameHeader returns a full-width rule with a small title at the left.
12// Matches the thin rule style used for code blocks and tool results so
13// every dialog in the TUI looks the same.
14//
15// ─── title ────────────────────────────────
16func frameHeader(th tui.Theme, title string, width int) string {
17 label := "── " + title + " "
18 if width <= 0 {
19 return th.FGColor(th.Muted, label)
20 }
21 padLen := width - runewidth.StringWidth(label)
22 if padLen < 0 {
23 padLen = 0
24 }
25 return th.FGColor(th.Muted, label+strings.Repeat("─", padLen))
26}
27
28// frameRule returns a full-width horizontal rule in the muted color.
29func frameRule(th tui.Theme, width int) string {
30 if width <= 0 {
31 width = 1
32 }
33 return th.FGColor(th.Muted, strings.Repeat("─", width))
34}
35
36// padDialogFrame inserts breathing room between the shared dialog frame
37// chrome and its body while keeping frameHeader/frameRule as single-row
38// primitives for callers that need exact row accounting.
39func padDialogFrame(lines []string) []string {
40 if len(lines) == 0 {
41 return lines
42 }
43
44 out := append([]string(nil), lines...)
45 if isFrameHeaderLine(out[0]) && (len(out) == 1 || strings.TrimSpace(stripANSIBytes(out[1])) != "") {
46 out = append(out[:1], append([]string{""}, out[1:]...)...)
47 }
48
49 last := len(out) - 1
50 if last > 0 && isFrameRuleLine(out[last]) && strings.TrimSpace(stripANSIBytes(out[last-1])) != "" {
51 out = append(out[:last], append([]string{""}, out[last:]...)...)
52 }
53 return out
54}
55
56func isFrameHeaderLine(line string) bool {
57 return strings.HasPrefix(stripANSIBytes(line), "── ")
58}
59
60func isFrameRuleLine(line string) bool {
61 plain := stripANSIBytes(line)
62 if plain == "" {
63 return false
64 }
65 for _, r := range plain {
66 if r != '─' {
67 return false
68 }
69 }
70 return true
71}
72
73// frameHeaderColor is like frameHeader but renders in a caller-supplied
74// theme color. Used by the update-available banner which wants a yellow
75// accent on the rules and title.
76func frameHeaderColor(th tui.Theme, title string, width int, color tui.TerminalColor) string {
77 label := "── " + title + " "
78 if width <= 0 {
79 return th.FGColor(color, label)
80 }
81 padLen := width - runewidth.StringWidth(label)
82 if padLen < 0 {
83 padLen = 0
84 }
85 return th.FGColor(color, label+strings.Repeat("─", padLen))
86}
87
88// frameRuleColor is like frameRule in an explicit color.
89func frameRuleColor(th tui.Theme, width int, color tui.TerminalColor) string {
90 if width <= 0 {
91 width = 1
92 }
93 return th.FGColor(color, strings.Repeat("─", width))
94}