| 1 | package cli |
| 2 | |
| 3 | import ( |
| 4 | "os" |
| 5 | |
| 6 | "github.com/charmbracelet/colorprofile" |
| 7 | ) |
| 8 | |
| 9 | // activeColorProfile is the single description of what the terminal can render. |
| 10 | // colorprofile owns the NO_COLOR, CLICOLOR_FORCE, TERM=dumb and isatty rules, |
| 11 | // so piped output stays plain. Colour fidelity is derived from it, never probed |
| 12 | // a second time. |
| 13 | var activeColorProfile = detectColorProfile(ansiConsoleReady()) |
| 14 | |
| 15 | func detectColorProfile(ansiReady bool) colorprofile.Profile { |
| 16 | if !ansiReady { |
| 17 | return colorprofile.ASCII |
| 18 | } |
| 19 | return colorprofile.Detect(os.Stdout, os.Environ()) |
| 20 | } |
| 21 | |
| 22 | func colorOn() bool { |
| 23 | return activeColorProfile > colorprofile.ASCII |
| 24 | } |
| 25 | |
| 26 | func trueColorTerminal() bool { |
| 27 | return activeColorProfile == colorprofile.TrueColor |
| 28 | } |
| 29 | |
| 30 | const ( |
| 31 | ansiReset = "\033[0m" |
| 32 | ansiBold = "\033[1m" |
| 33 | ansiReverse = "\033[7m" |
| 34 | // ansiAccent is the dark graphite accent as a literal escape, for tests that |
| 35 | // pin the concrete sequence instead of the active theme. |
| 36 | ansiAccent = "\033[38;5;173m" |
| 37 | ) |
| 38 | |
| 39 | func sgr(code, s string) string { |
| 40 | if !colorOn() { |
| 41 | return s |
| 42 | } |
| 43 | return code + s + ansiReset |
| 44 | } |
| 45 | |
| 46 | func bold(s string) string { return sgr(ansiBold, s) } |
| 47 | func dim(s string) string { return themeFg(activeCLITheme.faint, s) } |
| 48 | func green(s string) string { return themeFg(activeCLITheme.success, s) } |
| 49 | func red(s string) string { return themeFg(activeCLITheme.err, s) } |
| 50 | func yellow(s string) string { return themeFg(activeCLITheme.warn, s) } |
| 51 | func accent(s string) string { return themeFg(activeCLITheme.accent, s) } |
| 52 | func reverse(s string) string { return sgr(ansiReverse, s) } |
| 53 |