|
| 1 | +import process from 'node:process' |
| 2 | +import { styleText } from 'node:util' |
| 3 | + |
| 4 | +export type TerminalBackground = 'dark' | 'light' | 'unknown' |
| 5 | + |
| 6 | +type Rgb = readonly [number, number, number] |
| 7 | + |
| 8 | +/** |
| 9 | + * Nuxt green, and the darker green it becomes on a light terminal. |
| 10 | + * |
| 11 | + * `#00DC82` is bright enough to all but disappear on white (a contrast ratio |
| 12 | + * of about 1.8:1), so the light variant trades some of the glow for a ratio |
| 13 | + * above 4:1. |
| 14 | + */ |
| 15 | +const BRAND_GREEN: Record<'dark' | 'light', Rgb> = { |
| 16 | + dark: [0, 220, 130], |
| 17 | + light: [0, 145, 92], |
| 18 | +} |
| 19 | + |
| 20 | +/** Palette indices a terminal reports as its background when it is a light one. */ |
| 21 | +const LIGHT_INDICES = new Set([7, 9, 10, 11, 12, 13, 14, 15]) |
| 22 | + |
| 23 | +/** |
| 24 | + * Whether the terminal is showing dark text on light or the other way round. |
| 25 | + * |
| 26 | + * `unknown` is the common answer: only some terminals set `COLORFGBG`, and |
| 27 | + * asking the terminal directly (OSC 11) means writing to stdout and waiting |
| 28 | + * for a reply that may never come. Callers are expected to have a choice that |
| 29 | + * is safe on either background rather than to guess. |
| 30 | + */ |
| 31 | +function resolveBackground(env: NodeJS.ProcessEnv = process.env): TerminalBackground { |
| 32 | + const override = env.NUXT_TERM_THEME?.trim().toLowerCase() |
| 33 | + if (override === 'dark' || override === 'light') { |
| 34 | + return override |
| 35 | + } |
| 36 | + |
| 37 | + const reported = env.COLORFGBG?.split(';').at(-1)?.trim() |
| 38 | + if (!reported || !/^\d+$/.test(reported)) { |
| 39 | + return 'unknown' |
| 40 | + } |
| 41 | + return LIGHT_INDICES.has(Number(reported)) ? 'light' : 'dark' |
| 42 | +} |
| 43 | + |
| 44 | +/** |
| 45 | + * Write `text` in Nuxt green, as close to it as the terminal can be trusted to |
| 46 | + * render legibly. |
| 47 | + * |
| 48 | + * Without a known background there is no safe exact colour, so the terminal's |
| 49 | + * own palette decides: the user chose it against the background they are |
| 50 | + * looking at. |
| 51 | + */ |
| 52 | +export function paintBrand(text: string, background = resolveBackground()): string { |
| 53 | + if (background === 'unknown' || (process.stdout.getColorDepth?.() ?? 1) < 24) { |
| 54 | + return styleText('green', text) |
| 55 | + } |
| 56 | + if (process.env.NO_COLOR || !process.stdout.hasColors?.()) { |
| 57 | + return text |
| 58 | + } |
| 59 | + const [r, g, b] = BRAND_GREEN[background] |
| 60 | + return `\u001B[38;2;${r};${g};${b}m${text}\u001B[39m` |
| 61 | +} |
0 commit comments