169 lines
4.7 KiB
Go
169 lines
4.7 KiB
Go
|
|
// 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
|
|||
|
|
}
|