diff options
| author | Paul Buetow <paul@buetow.org> | 2026-06-22 14:07:54 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-06-22 14:07:54 +0300 |
| commit | 6417a632d65486f44eb51b48855eeafb2b03da31 (patch) | |
| tree | 26754d27265e1bad869836e9befe2664150dadfc /internal/ui/help/help.go | |
| parent | b83ee79cc9372f2fa51daf71757a1213328e1723 (diff) | |
tq0 refactor UI help rendering
Diffstat (limited to 'internal/ui/help/help.go')
| -rw-r--r-- | internal/ui/help/help.go | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/internal/ui/help/help.go b/internal/ui/help/help.go new file mode 100644 index 0000000..0d33279 --- /dev/null +++ b/internal/ui/help/help.go @@ -0,0 +1,116 @@ +package help + +import ( + "fmt" + "regexp" + "strings" + + "charm.land/lipgloss/v2" +) + +// Item is a single key binding and description in a help section. +type Item struct { + Key string + Desc string +} + +// Section groups related help items under a title. +type Section struct { + Title string + Items []Item +} + +// Palette contains the colors needed to render help content. +type Palette struct { + HeaderFG string + HeaderBG string + KeyFG string + DescFG string + SearchFG string + SearchBG string +} + +// Render converts help sections into styled terminal content. +func Render(sections []Section, palette Palette, search *regexp.Regexp) string { + headerStyle, keyStyle, descStyle := styles(palette) + lines := make([]string, 0, len(sections)*4) + for i, section := range sections { + lines = append(lines, headerStyle.Render(section.Title)) + for _, item := range section.Items { + lines = append(lines, formatLine(item.Key, item.Desc, keyStyle, descStyle)) + } + if i < len(sections)-1 { + lines = append(lines, "") + } + } + + if search != nil { + for i, line := range lines { + if search.MatchString(line) { + lines[i] = highlightLine(line, palette, search) + } + } + } + + return strings.Join(lines, "\n") +} + +// Lines returns plain text lines for searchable help content. +func Lines(sections []Section) []string { + lines := make([]string, 0, len(sections)*4) + for i, section := range sections { + lines = append(lines, section.Title) + for _, item := range section.Items { + lines = append(lines, fmt.Sprintf("%s: %s", item.Key, item.Desc)) + } + if i < len(sections)-1 { + lines = append(lines, "") + } + } + return lines +} + +func styles(palette Palette) (lipgloss.Style, lipgloss.Style, lipgloss.Style) { + headerStyle := lipgloss.NewStyle(). + Bold(true). + Foreground(lipgloss.Color(palette.HeaderFG)). + Background(lipgloss.Color(palette.HeaderBG)). + Padding(0, 1) + + keyStyle := lipgloss.NewStyle(). + Bold(true). + Foreground(lipgloss.Color(palette.KeyFG)) + + descStyle := lipgloss.NewStyle(). + Foreground(lipgloss.Color(palette.DescFG)) + + return headerStyle, keyStyle, descStyle +} + +func formatLine(key, desc string, keyStyle, descStyle lipgloss.Style) string { + paddedKey := fmt.Sprintf("%-12s", key) + return keyStyle.Render(paddedKey) + " " + descStyle.Render(desc) +} + +func highlightLine(line string, palette Palette, search *regexp.Regexp) string { + matches := search.FindAllStringIndex(line, -1) + if len(matches) == 0 { + return line + } + + highlightStyle := lipgloss.NewStyle(). + Background(lipgloss.Color(palette.SearchBG)). + Foreground(lipgloss.Color(palette.SearchFG)) + + highlighted := line + offset := 0 + for _, match := range matches { + start := match[0] + offset + end := match[1] + offset + rendered := highlightStyle.Render(highlighted[start:end]) + highlighted = highlighted[:start] + rendered + highlighted[end:] + offset += len(rendered) - (end - start) + } + + return highlighted +} |
