package display import ( "charm.land/lipgloss/v2" "github.com/charmbracelet/x/ansi" "strings" ) // Line clips trusted, possibly application-styled text to display cells. func Line(s string, width int) string { if width <= 0 { return "" } return ansi.Truncate(s, width, "…") } // Row spends width on identity before optional metadata. Its prefix has the // same width in both states, so moving the cursor never shifts the label. func Row(label, detail string, width int, current bool) string { if width <= 0 { return "" } prefix := " " if current { prefix = "> " } if width <= 2 { return Line(prefix, width) } room := width - 2 metadata := "" if width >= 48 && detail != "" { metadata = Line(SingleLine(detail), 18) room -= ansi.StringWidth(metadata) + 2 } name := Line(SingleLine(label), room) body := prefix + name if metadata != "" { body += strings.Repeat(" ", max(0, room-ansi.StringWidth(name))) + " " + metadata } return lipgloss.NewStyle().Bold(current).Render(body) } // Panel's argument is the OUTER width with zero margins. Padding and borders // are already inside Width in Lip Gloss v2. No double frame subtraction. func Panel(content string, outerWidth int) string { if outerWidth <= 0 { return "" } box := lipgloss.NewStyle().Border(lipgloss.NormalBorder()).Padding(0, 1) frame := box.GetHorizontalFrameSize() if outerWidth <= frame { return Line(strings.ReplaceAll(content, "\n", " "), outerWidth) } lines := strings.Split(content, "\n") for i := range lines { lines[i] = Line(lines[i], outerWidth-frame) } return box.Width(outerWidth).Render(strings.Join(lines, "\n")) } // FillRows fixes a one-line row region's height without rendering hidden items. func FillRows(lines []string, width, height int) string { if height <= 0 || width <= 0 { return "" } out := make([]string, height) for i := 0; i < min(len(lines), height); i++ { out[i] = Line(lines[i], width) } return strings.Join(out, "\n") }