- 模块名重命名 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*
133 lines
2.4 KiB
Go
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()
|
|
)
|