// colors.go - система цветного вывода для fush shell // Преобразует hex цвета в ANSI escape последовательности // Поддерживает 256-цветную палитру для совместимости с терминалами // Автоматически отключает цвета при выводе не в терминал package ansi import ( "fmt" "os" "strconv" "strings" "github.com/mattn/go-isatty" ) // Color представляет ANSI цвет type Color struct { code string } // Основные цвета var ( Reset = Color{"\033[0m"} Bold = Color{"\033[1m"} Dim = Color{"\033[2m"} Italic = Color{"\033[3m"} Underline = Color{"\033[4m"} Black = Color{"\033[30m"} Red = Color{"\033[31m"} Green = Color{"\033[32m"} Yellow = Color{"\033[33m"} Blue = Color{"\033[34m"} Magenta = Color{"\033[35m"} Cyan = Color{"\033[36m"} White = Color{"\033[37m"} BrightBlack = Color{"\033[90m"} BrightRed = Color{"\033[91m"} BrightGreen = Color{"\033[92m"} BrightYellow = Color{"\033[93m"} BrightBlue = Color{"\033[94m"} BrightMagenta = Color{"\033[95m"} BrightCyan = Color{"\033[96m"} BrightWhite = Color{"\033[97m"} ) // HexToANSI преобразует hex цвет в ANSI код func HexToANSI(hex string) (Color, error) { // Удаляем # если есть hex = strings.TrimPrefix(hex, "#") if len(hex) != 6 && len(hex) != 3 { return Color{}, fmt.Errorf("неверный формат hex цвета: %s", hex) } // Обработка 3-символьных hex кодов (например, #0FF) if len(hex) == 3 { hex = string([]byte{hex[0], hex[0], hex[1], hex[1], hex[2], hex[2]}) } // Парсим RGB компоненты r, err := strconv.ParseInt(hex[0:2], 16, 64) if err != nil { return Color{}, err } g, err := strconv.ParseInt(hex[2:4], 16, 64) if err != nil { return Color{}, err } b, err := strconv.ParseInt(hex[4:6], 16, 64) if err != nil { return Color{}, err } // Определяем, является ли цвет одним из стандартных if r == 0 && g == 255 && b == 255 { return BrightCyan, nil // #00ffff -> BrightCyan } if r == 255 && g == 255 && b == 0 { return BrightYellow, nil // #ffff00 -> BrightYellow } if r == 0 && g == 255 && b == 0 { return BrightGreen, nil // #00ff00 -> BrightGreen } if r == 255 && g == 0 && b == 0 { return BrightRed, nil // #ff0000 -> BrightRed } if r == 0 && g == 0 && b == 255 { return BrightBlue, nil // #0000ff -> BrightBlue } if r == 255 && g == 0 && b == 255 { return BrightMagenta, nil // #ff00ff -> BrightMagenta } if r == 255 && g == 255 && b == 255 { return BrightWhite, nil // #ffffff -> BrightWhite } if r == 0 && g == 0 && b == 0 { return Black, nil // #000000 -> Black } // Используем 256-цветную палитру для других цветов // Формула: 16 + 36*r + 6*g + b, где r,g,b в диапазоне 0-5 rIdx := int(r * 5 / 255) gIdx := int(g * 5 / 255) bIdx := int(b * 5 / 255) colorCode := 16 + (36 * rIdx) + (6 * gIdx) + bIdx return Color{fmt.Sprintf("\033[38;5;%dm", colorCode)}, nil } // Colorize окрашивает строку в указанный цвет func Colorize(s string, color Color) string { if !IsTerminalSupported() { return s } return color.code + s + Reset.code } // Sprintf форматирует строку с цветом func Sprintf(color Color, format string, args ...interface{}) string { if !IsTerminalSupported() { return fmt.Sprintf(format, args...) } return color.code + fmt.Sprintf(format, args...) + Reset.code } // Println выводит строку с цветом func Println(color Color, args ...interface{}) { if !IsTerminalSupported() { fmt.Println(args...) return } fmt.Print(color.code) fmt.Println(args...) fmt.Print(Reset.code) } // Printf выводит форматированную строку с цветом func Printf(color Color, format string, args ...interface{}) { if !IsTerminalSupported() { fmt.Printf(format, args...) return } fmt.Print(color.code) fmt.Printf(format, args...) fmt.Print(Reset.code) } // IsTerminalSupported проверяет поддержку цвета в терминале func IsTerminalSupported() bool { // Проверяем переменную окружения TERM term := strings.ToLower(os.Getenv("TERM")) if term == "dumb" || term == "" { return false } // Проверяем, что вывод идет в терминал if !isatty.IsTerminal(os.Stdout.Fd()) { return false } return true }