package modes import ( "strings" "github.com/mattn/go-runewidth" "github.com/bnema/zut/packages/tui" ) // frameHeader returns a full-width rule with a small title at the left. // Matches the thin rule style used for code blocks and tool results so // every dialog in the TUI looks the same. // // ─── title ──────────────────────────────── func frameHeader(th tui.Theme, title string, width int) string { label := "── " + title + " " if width <= 0 { return th.FGColor(th.Muted, label) } padLen := width - runewidth.StringWidth(label) if padLen < 0 { padLen = 0 } return th.FGColor(th.Muted, label+strings.Repeat("─", padLen)) } // frameRule returns a full-width horizontal rule in the muted color. func frameRule(th tui.Theme, width int) string { if width <= 0 { width = 1 } return th.FGColor(th.Muted, strings.Repeat("─", width)) } // padDialogFrame inserts breathing room between the shared dialog frame // chrome and its body while keeping frameHeader/frameRule as single-row // primitives for callers that need exact row accounting. func padDialogFrame(lines []string) []string { if len(lines) == 0 { return lines } out := append([]string(nil), lines...) if isFrameHeaderLine(out[0]) && (len(out) == 1 || strings.TrimSpace(stripANSIBytes(out[1])) != "") { out = append(out[:1], append([]string{""}, out[1:]...)...) } last := len(out) - 1 if last > 0 && isFrameRuleLine(out[last]) && strings.TrimSpace(stripANSIBytes(out[last-1])) != "" { out = append(out[:last], append([]string{""}, out[last:]...)...) } return out } func isFrameHeaderLine(line string) bool { return strings.HasPrefix(stripANSIBytes(line), "── ") } func isFrameRuleLine(line string) bool { plain := stripANSIBytes(line) if plain == "" { return false } for _, r := range plain { if r != '─' { return false } } return true } // frameHeaderColor is like frameHeader but renders in a caller-supplied // theme color. Used by the update-available banner which wants a yellow // accent on the rules and title. func frameHeaderColor(th tui.Theme, title string, width int, color tui.TerminalColor) string { label := "── " + title + " " if width <= 0 { return th.FGColor(color, label) } padLen := width - runewidth.StringWidth(label) if padLen < 0 { padLen = 0 } return th.FGColor(color, label+strings.Repeat("─", padLen)) } // frameRuleColor is like frameRule in an explicit color. func frameRuleColor(th tui.Theme, width int, color tui.TerminalColor) string { if width <= 0 { width = 1 } return th.FGColor(color, strings.Repeat("─", width)) }