Replaces lipgloss with ~100 lines of owned code. Supports bold, dim, italic, underline, RGB/hex colors. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
123 lines
2.3 KiB
Go
123 lines
2.3 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// ANSI escape codes
|
|
const (
|
|
ansiReset = "\033[0m"
|
|
ansiBold = "\033[1m"
|
|
ansiDim = "\033[2m"
|
|
ansiItalic = "\033[3m"
|
|
ansiUnderline = "\033[4m"
|
|
)
|
|
|
|
// AnsiStyle represents terminal text styling.
|
|
// Use NewStyle() to create, chain methods, call Render().
|
|
type AnsiStyle struct {
|
|
bold bool
|
|
dim bool
|
|
italic bool
|
|
underline bool
|
|
fg string
|
|
bg string
|
|
}
|
|
|
|
// NewStyle creates a new empty style.
|
|
func NewStyle() *AnsiStyle {
|
|
return &AnsiStyle{}
|
|
}
|
|
|
|
// Bold enables bold text.
|
|
func (s *AnsiStyle) Bold() *AnsiStyle {
|
|
s.bold = true
|
|
return s
|
|
}
|
|
|
|
// Dim enables dim text.
|
|
func (s *AnsiStyle) Dim() *AnsiStyle {
|
|
s.dim = true
|
|
return s
|
|
}
|
|
|
|
// Italic enables italic text.
|
|
func (s *AnsiStyle) Italic() *AnsiStyle {
|
|
s.italic = true
|
|
return s
|
|
}
|
|
|
|
// Underline enables underlined text.
|
|
func (s *AnsiStyle) Underline() *AnsiStyle {
|
|
s.underline = true
|
|
return s
|
|
}
|
|
|
|
// Foreground sets foreground color from hex string.
|
|
func (s *AnsiStyle) Foreground(hex string) *AnsiStyle {
|
|
s.fg = fgColorHex(hex)
|
|
return s
|
|
}
|
|
|
|
// Background sets background color from hex string.
|
|
func (s *AnsiStyle) Background(hex string) *AnsiStyle {
|
|
s.bg = bgColorHex(hex)
|
|
return s
|
|
}
|
|
|
|
// Render applies the style to text.
|
|
func (s *AnsiStyle) Render(text string) string {
|
|
if s == nil {
|
|
return text
|
|
}
|
|
|
|
var codes []string
|
|
if s.bold {
|
|
codes = append(codes, ansiBold)
|
|
}
|
|
if s.dim {
|
|
codes = append(codes, ansiDim)
|
|
}
|
|
if s.italic {
|
|
codes = append(codes, ansiItalic)
|
|
}
|
|
if s.underline {
|
|
codes = append(codes, ansiUnderline)
|
|
}
|
|
if s.fg != "" {
|
|
codes = append(codes, s.fg)
|
|
}
|
|
if s.bg != "" {
|
|
codes = append(codes, s.bg)
|
|
}
|
|
|
|
if len(codes) == 0 {
|
|
return text
|
|
}
|
|
|
|
return strings.Join(codes, "") + text + ansiReset
|
|
}
|
|
|
|
// Hex color support
|
|
func fgColorHex(hex string) string {
|
|
r, g, b := hexToRGB(hex)
|
|
return fmt.Sprintf("\033[38;2;%d;%d;%dm", r, g, b)
|
|
}
|
|
|
|
func bgColorHex(hex string) string {
|
|
r, g, b := hexToRGB(hex)
|
|
return fmt.Sprintf("\033[48;2;%d;%d;%dm", r, g, b)
|
|
}
|
|
|
|
func hexToRGB(hex string) (int, int, int) {
|
|
hex = strings.TrimPrefix(hex, "#")
|
|
if len(hex) != 6 {
|
|
return 255, 255, 255
|
|
}
|
|
r, _ := strconv.ParseInt(hex[0:2], 16, 64)
|
|
g, _ := strconv.ParseInt(hex[2:4], 16, 64)
|
|
b, _ := strconv.ParseInt(hex[4:6], 16, 64)
|
|
return int(r), int(g), int(b)
|
|
}
|