Files
CyberStrikeAI/internal/termout/style.go
T
2026-07-11 11:41:00 +08:00

109 lines
2.7 KiB
Go

package termout
import (
"fmt"
"io"
"os"
"strings"
)
const (
codeReset = "\033[0m"
codeBold = "\033[1m"
codeDim = "\033[2m"
codeRed = "\033[31m"
codeGreen = "\033[32m"
codeYellow = "\033[33m"
codeBlue = "\033[34m"
codeCyan = "\033[36m"
codeWhite = "\033[97m"
)
// Style wraps ANSI styling with TTY / NO_COLOR awareness.
type Style struct {
out io.Writer
enabled bool
}
// New creates a Style writing to out (typically os.Stdout).
func New(out io.Writer) *Style {
return &Style{out: out, enabled: colorEnabled(out)}
}
func colorEnabled(w io.Writer) bool {
if strings.TrimSpace(os.Getenv("NO_COLOR")) != "" {
return false
}
force := strings.TrimSpace(os.Getenv("FORCE_COLOR"))
if force == "1" || strings.EqualFold(force, "true") || strings.EqualFold(force, "yes") {
return true
}
f, ok := w.(*os.File)
if !ok {
return false
}
stat, err := f.Stat()
if err != nil {
return false
}
return stat.Mode()&os.ModeCharDevice != 0
}
func (s *Style) paint(code, text string) string {
if !s.enabled || text == "" {
return text
}
return code + text + codeReset
}
func (s *Style) Bold(text string) string { return s.paint(codeBold, text) }
func (s *Style) Dim(text string) string { return s.paint(codeDim, text) }
func (s *Style) Red(text string) string { return s.paint(codeRed, text) }
func (s *Style) Green(text string) string { return s.paint(codeGreen, text) }
func (s *Style) Yellow(text string) string { return s.paint(codeYellow, text) }
func (s *Style) Blue(text string) string { return s.paint(codeBlue, text) }
func (s *Style) Cyan(text string) string { return s.paint(codeCyan, text) }
func (s *Style) White(text string) string { return s.paint(codeWhite, text) }
func (s *Style) Println(text string) {
_, _ = fmt.Fprintln(s.out, text)
}
func (s *Style) Printf(format string, args ...interface{}) {
_, _ = fmt.Fprintf(s.out, format, args...)
}
func (s *Style) BlankLine() {
s.Println("")
}
func (s *Style) boxTop(innerWidth int) string {
return s.Cyan("╭" + strings.Repeat("─", innerWidth+2) + "╮")
}
func (s *Style) boxBottom(innerWidth int) string {
return s.Cyan("╰" + strings.Repeat("─", innerWidth+2) + "╯")
}
func (s *Style) boxRow(innerWidth int, content string) string {
return s.Cyan("│ ") + padRightDisplay(content, innerWidth) + s.Cyan(" │")
}
func (s *Style) printBox(rows []string, minInner, maxInner int) {
inner := maxDisplayWidth(rows...)
if inner < minInner {
inner = minInner
}
if maxInner > 0 && inner > maxInner {
inner = maxInner
}
s.BlankLine()
s.Println(s.boxTop(inner))
for _, row := range rows {
s.Println(s.boxRow(inner, row))
}
s.Println(s.boxBottom(inner))
s.BlankLine()
}