Files
fush/pkg/ansi/colors.go
2026-05-22 00:26:27 +03:00

169 lines
4.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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
}