Files
YunShu/pkg/style/style.go
titor d2b9b2c4bb refactor: 项目结构重组,src/ 扁平化为根目录,提取 pkg/ 子包
- 模块名重命名 yunshu -> hub.gaomia.site/titor/YunShu
- Go 版本升级 1.21 -> 1.25
- src/ 目录删除,所有文件移至根目录
- 新增 pkg/mdprint/: Markdown AST 解析+ANSI 渲染
- 新增 pkg/style/: 终端颜色样式(8色 ANSI + 24位真彩色)
- 新增 pkg/termui/: 终端输入组件(交互式输入/密码/确认)
- 更新文档:AGENTS.md、architecture.md、changelog.md、taolun.md
- gitignore 通配符修复 yunshu.exe -> yunshu.exe*
2026-05-09 03:55:56 +08:00

133 lines
2.4 KiB
Go

package style
import (
"fmt"
"os"
"strings"
)
var noColor = os.Getenv("NO_COLOR") != "" || os.Getenv("TERM") == "dumb"
type Style struct {
codes []string
}
func New() *Style {
return &Style{}
}
func (s *Style) clone() *Style {
c := &Style{codes: make([]string, len(s.codes))}
copy(c.codes, s.codes)
return c
}
func (s *Style) Bold() *Style {
c := s.clone()
c.codes = append(c.codes, "1")
return c
}
func (s *Style) Dim() *Style {
c := s.clone()
c.codes = append(c.codes, "2")
return c
}
func (s *Style) Italic() *Style {
c := s.clone()
c.codes = append(c.codes, "3")
return c
}
func (s *Style) Underline() *Style {
c := s.clone()
c.codes = append(c.codes, "4")
return c
}
func (s *Style) Fg(c Color) *Style {
n := s.clone()
n.codes = append(n.codes, fmt.Sprintf("%d", c))
return n
}
func (s *Style) Bg(c Color) *Style {
n := s.clone()
n.codes = append(n.codes, fmt.Sprintf("%d", c+10))
return n
}
func (s *Style) FgHex(hex string) *Style {
n := s.clone()
r, g, b := hexToRGB(hex)
n.codes = append(n.codes, fmt.Sprintf("38;2;%d;%d;%d", r, g, b))
return n
}
func (s *Style) BgHex(hex string) *Style {
n := s.clone()
r, g, b := hexToRGB(hex)
n.codes = append(n.codes, fmt.Sprintf("48;2;%d;%d;%d", r, g, b))
return n
}
func hexToRGB(hex string) (r, g, b int) {
hex = strings.TrimPrefix(hex, "#")
if len(hex) != 6 {
return 255, 255, 255
}
r = parseHex(hex[0:2])
g = parseHex(hex[2:4])
b = parseHex(hex[4:6])
return
}
func parseHex(s string) int {
v := 0
for _, c := range s {
v *= 16
switch {
case c >= '0' && c <= '9':
v += int(c - '0')
case c >= 'a' && c <= 'f':
v += int(c - 'a' + 10)
case c >= 'A' && c <= 'F':
v += int(c - 'A' + 10)
}
}
return v
}
func (s *Style) Render(text string) string {
if noColor || len(s.codes) == 0 {
return text
}
return "\033[" + strings.Join(s.codes, ";") + "m" + text + "\033[0m"
}
type Color int
const (
ColorBlack Color = 30
ColorRed Color = 31
ColorGreen Color = 32
ColorYellow Color = 33
ColorBlue Color = 34
ColorMagenta Color = 35
ColorCyan Color = 36
ColorWhite Color = 37
)
var (
Red = New().Fg(ColorRed)
Green = New().Fg(ColorGreen)
Yellow = New().Fg(ColorYellow)
Cyan = New().Fg(ColorCyan)
Blue = New().Fg(ColorBlue)
Magenta = New().Fg(ColorMagenta)
White = New().Fg(ColorWhite)
Bold = New().Bold()
Dim = New().Dim()
)