Files
lolcathost/internal/tui/styles.go
T
lukaszraczylo 22f8f7a008 refactor(tui): move to Bubble Tea, Lip Gloss and Bubbles v2
Switch imports to the charm.land v2 modules. Return tea.View from Model.View,
set alt screen and window title there, use KeyPressMsg and the "space" key
name, and call SetWidth on text inputs.
2026-10-04 10:12:33 +01:00

182 lines
4.0 KiB
Go

// Package tui provides the terminal user interface.
package tui
import (
"strings"
"charm.land/lipgloss/v2"
)
// Colors - matching kportal style, optimized for dark terminals
var (
colorPrimary = lipgloss.Color("205") // Pink/Magenta
colorSuccess = lipgloss.Color("42") // Green
colorWarning = lipgloss.Color("220") // Yellow
colorError = lipgloss.Color("196") // Red
colorMuted = lipgloss.Color("245") // Gray (brighter for dark terminals)
colorAccent = lipgloss.Color("141") // Light purple (brighter for dark terminals)
colorHeader = lipgloss.Color("220") // Yellow for headers
colorSelectedBg = lipgloss.Color("236") // Gray background for selection
colorSelectedFg = lipgloss.Color("255") // White foreground for selection
colorGroupHeader = lipgloss.Color("213") // Light pink for group headers
)
// Title and header styles
var (
titleStyle = lipgloss.NewStyle().
Bold(true).
Foreground(colorHeader).
Padding(0, 1)
)
// Status indicators
var (
enabledStyle = lipgloss.NewStyle().
Foreground(colorSuccess).
Bold(true)
disabledStyle = lipgloss.NewStyle().
Foreground(colorMuted)
)
// Status bar and help
var (
statusBarStyle = lipgloss.NewStyle().
Foreground(colorMuted)
connectedStyle = lipgloss.NewStyle().
Foreground(colorSuccess).
SetString("Connected")
disconnectedStyle = lipgloss.NewStyle().
Foreground(colorError).
SetString("Disconnected")
helpBarStyle = lipgloss.NewStyle().
Foreground(colorMuted)
helpKeyStyle = lipgloss.NewStyle().
Foreground(colorHeader).
Bold(true)
helpDescStyle = lipgloss.NewStyle().
Foreground(colorMuted)
)
// Message styles
var (
errorMsgStyle = lipgloss.NewStyle().
Foreground(colorError).
Bold(true).
MarginTop(1)
successMsgStyle = lipgloss.NewStyle().
Foreground(colorSuccess).
MarginTop(1)
updateStyle = lipgloss.NewStyle().
Foreground(colorSuccess).
Bold(true)
)
// Form styles
var (
inputLabelStyle = lipgloss.NewStyle().
Foreground(colorPrimary).
Bold(true)
inputStyle = lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(colorMuted).
Padding(0, 1)
inputFocusStyle = lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(colorPrimary).
Padding(0, 1)
)
// Dialog/modal styles
var (
dialogStyle = lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(colorAccent).
Padding(1, 2)
presetItemStyle = lipgloss.NewStyle().
Padding(0, 1)
presetSelectedStyle = lipgloss.NewStyle().
Background(colorSelectedBg).
Foreground(colorSelectedFg).
Padding(0, 1)
)
// WrapHelpText wraps help text to fit within maxWidth, splitting on bullet separators.
// If maxWidth is 0 or negative, returns the original text.
func WrapHelpText(text string, maxWidth int) string {
if maxWidth <= 0 {
return helpDescStyle.Render(text)
}
separator := " • "
parts := splitOnSeparator(text, separator)
var lines []string
var currentLine string
var currentWidth int
for i, part := range parts {
partWidth := len(part)
sepWidth := 3 // len(" • ")
newWidth := currentWidth + partWidth
if currentWidth > 0 {
newWidth += sepWidth
}
if newWidth > maxWidth && currentWidth > 0 {
lines = append(lines, currentLine)
currentLine = part
currentWidth = partWidth
} else {
if currentWidth > 0 {
currentLine += separator
}
currentLine += part
if i == 0 {
currentWidth = partWidth
} else {
currentWidth = newWidth
}
}
}
if currentLine != "" {
lines = append(lines, currentLine)
}
// Apply style to each line and join
var result []string
for _, line := range lines {
result = append(result, helpDescStyle.Render(line))
}
return strings.Join(result, "\n")
}
// splitOnSeparator splits a string on the given separator.
func splitOnSeparator(s, sep string) []string {
var parts []string
for {
idx := strings.Index(s, sep)
if idx == -1 {
parts = append(parts, s)
break
}
parts = append(parts, s[:idx])
s = s[idx+len(sep):]
}
return parts
}