// Package style provides a tiny, conservative ANSI-color helper for // banger's CLI. The contract: // // - Each helper takes the writer the styled string is going to and // returns either the wrapped string or the plain one. // - "Wrapped" only happens when the writer is a TTY AND the // NO_COLOR environment variable is unset. // - No 256-color or truecolor; no theme system; no external dep. // // Banger's CLI uses these for status (pass/fail/warn), error // prefixes, and dim secondary text. Anything richer belongs in a // dedicated TUI layer that this package isn't. package style import ( "io" "os" "strings" ) // ANSI escape sequences. Kept private — callers compose meaning via // the named helpers (Pass/Fail/Warn/...), not raw codes. const ( ansiReset = "\x1b[0m" ansiBold = "\x1b[1m" ansiDim = "\x1b[2m" ansiRed = "\x1b[31m" ansiGreen = "\x1b[32m" ansiYel = "\x1b[33m" ) // Pass wraps s in green when w is a TTY and NO_COLOR is unset. func Pass(w io.Writer, s string) string { return wrap(w, ansiGreen, s) } // Fail wraps s in red. func Fail(w io.Writer, s string) string { return wrap(w, ansiRed, s) } // Warn wraps s in yellow. func Warn(w io.Writer, s string) string { return wrap(w, ansiYel, s) } // Dim wraps s in dim. func Dim(w io.Writer, s string) string { return wrap(w, ansiDim, s) } // Bold wraps s in bold. func Bold(w io.Writer, s string) string { return wrap(w, ansiBold, s) } // SupportsColor reports whether colored output should be emitted to // w. Exposed so callers that build multi-segment strings can avoid // duplicating the gate per call. func SupportsColor(w io.Writer) bool { if strings.TrimSpace(os.Getenv("NO_COLOR")) != "" { return false } file, ok := w.(*os.File) if !ok { return false } info, err := file.Stat() if err != nil { return false } return info.Mode()&os.ModeCharDevice != 0 } func wrap(w io.Writer, code, s string) string { if !SupportsColor(w) { return s } return code + s + ansiReset }