package color import ( "fmt" "strings" ) const Reset = "\033[0m" var ansiFg = map[string]int{ "black": 30, "red": 31, "green": 32, "yellow": 33, "blue": 34, "magenta": 35, "cyan": 36, "white": 37, } var ansiBg = map[string]int{ "black": 40, "red": 41, "green": 42, "yellow": 43, "blue": 44, "magenta": 45, "cyan": 46, "white": 47, } var ansiBrightFg = map[string]int{ "bright_black": 90, "bright_red": 91, "bright_green": 92, "bright_yellow": 93, "bright_blue": 94, "bright_magenta": 95, "bright_cyan": 96, "bright_white": 97, } var ansiBrightBg = map[string]int{ "bright_black": 100, "bright_red": 101, "bright_green": 102, "bright_yellow": 103, "bright_blue": 104, "bright_magenta": 105, "bright_cyan": 106, "bright_white": 107, } var xtermFg = map[string]int{ "black": 0, "red": 1, "green": 2, "yellow": 3, "blue": 4, "magenta": 5, "cyan": 6, "white": 7, "bright_black": 8, "bright_red": 9, "bright_green": 10, "bright_yellow": 11, "bright_blue": 12, "bright_magenta": 13, "bright_cyan": 14, "bright_white": 15, } var ansiStyles = map[string]string{ "bold": "\033[1m", "dim": "\033[2m", "italic": "\033[3m", "underline": "\033[4m", } func Fg(mode, name string) string { if mode == "none" || mode == "" { return "" } if mode == "xterm256" { if c, ok := xtermFg[name]; ok { return fmt.Sprintf("\033[38;5;%dm", c) } return "" } if c, ok := ansiFg[name]; ok { return fmt.Sprintf("\033[%dm", c) } if c, ok := ansiBrightFg[name]; ok { return fmt.Sprintf("\033[%dm", c) } return "" } func Bg(mode, name string) string { if mode == "none" || mode == "" { return "" } if mode == "xterm256" { if c, ok := xtermFg[name]; ok { return fmt.Sprintf("\033[48;5;%dm", c) } return "" } if c, ok := ansiBg[name]; ok { return fmt.Sprintf("\033[%dm", c) } if c, ok := ansiBrightBg[name]; ok { return fmt.Sprintf("\033[%dm", c) } return "" } func Style(name string) string { if s, ok := ansiStyles[name]; ok { return s } return "" } type ColorSpec struct { Fg string Bg string Bold bool Dim bool Italic bool Underline bool } func Parse(input string) ColorSpec { var spec ColorSpec for _, token := range strings.Fields(input) { switch { case token == "bold": spec.Bold = true case token == "dim": spec.Dim = true case token == "italic": spec.Italic = true case token == "underline": spec.Underline = true case strings.HasPrefix(token, "fg="): spec.Fg = strings.TrimPrefix(token, "fg=") case strings.HasPrefix(token, "bg="): spec.Bg = strings.TrimPrefix(token, "bg=") } } return spec } func (s ColorSpec) Empty() bool { return s.Fg == "" && s.Bg == "" && !s.Bold && !s.Dim && !s.Italic && !s.Underline } func Render(mode string, spec ColorSpec, text string) string { if mode == "none" || mode == "" || spec.Empty() { return text } var codes []string if spec.Bold { codes = append(codes, Style("bold")) } if spec.Dim { codes = append(codes, Style("dim")) } if spec.Italic { codes = append(codes, Style("italic")) } if spec.Underline { codes = append(codes, Style("underline")) } if spec.Fg != "" { codes = append(codes, Fg(mode, spec.Fg)) } if spec.Bg != "" { codes = append(codes, Bg(mode, spec.Bg)) } if len(codes) == 0 { return text } return strings.Join(codes, "") + text + Reset }