first commit

This commit is contained in:
2026-05-22 00:26:27 +03:00
commit c9d6d38491
17 changed files with 2311 additions and 0 deletions

168
pkg/ansi/colors.go Normal file
View File

@@ -0,0 +1,168 @@
// 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
}