289 lines
8.0 KiB
JavaScript
289 lines
8.0 KiB
JavaScript
import * as tty from 'node:tty';
|
|
|
|
const {
|
|
env = {},
|
|
argv = [],
|
|
platform = ""
|
|
} = typeof process === "undefined" ? {} : process;
|
|
const isDisabled = "NO_COLOR" in env || argv.includes("--no-color");
|
|
const isForced = "FORCE_COLOR" in env || argv.includes("--color");
|
|
const isWindows = platform === "win32";
|
|
const isDumbTerminal = env.TERM === "dumb";
|
|
const isCompatibleTerminal = tty && tty.isatty && tty.isatty(1) && env.TERM && !isDumbTerminal;
|
|
const isCI = "CI" in env && ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env);
|
|
const isColorSupported = !isDisabled && (isForced || isWindows && !isDumbTerminal || isCompatibleTerminal || isCI);
|
|
function replaceClose(index, string, close, replace, head = string.slice(0, Math.max(0, index)) + replace, tail = string.slice(Math.max(0, index + close.length)), next = tail.indexOf(close)) {
|
|
return head + (next < 0 ? tail : replaceClose(next, tail, close, replace));
|
|
}
|
|
function clearBleed(index, string, open, close, replace) {
|
|
return index < 0 ? open + string + close : open + replaceClose(index, string, close, replace) + close;
|
|
}
|
|
function filterEmpty(open, close, replace = open, at = open.length + 1) {
|
|
return (string) => string || !(string === "" || string === void 0) ? clearBleed(
|
|
("" + string).indexOf(close, at),
|
|
string,
|
|
open,
|
|
close,
|
|
replace
|
|
) : "";
|
|
}
|
|
function init(open, close, replace) {
|
|
return filterEmpty(`\x1B[${open}m`, `\x1B[${close}m`, replace);
|
|
}
|
|
const colorDefs = {
|
|
reset: init(0, 0),
|
|
bold: init(1, 22, "\x1B[22m\x1B[1m"),
|
|
dim: init(2, 22, "\x1B[22m\x1B[2m"),
|
|
italic: init(3, 23),
|
|
underline: init(4, 24),
|
|
inverse: init(7, 27),
|
|
hidden: init(8, 28),
|
|
strikethrough: init(9, 29),
|
|
black: init(30, 39),
|
|
red: init(31, 39),
|
|
green: init(32, 39),
|
|
yellow: init(33, 39),
|
|
blue: init(34, 39),
|
|
magenta: init(35, 39),
|
|
cyan: init(36, 39),
|
|
white: init(37, 39),
|
|
gray: init(90, 39),
|
|
bgBlack: init(40, 49),
|
|
bgRed: init(41, 49),
|
|
bgGreen: init(42, 49),
|
|
bgYellow: init(43, 49),
|
|
bgBlue: init(44, 49),
|
|
bgMagenta: init(45, 49),
|
|
bgCyan: init(46, 49),
|
|
bgWhite: init(47, 49),
|
|
blackBright: init(90, 39),
|
|
redBright: init(91, 39),
|
|
greenBright: init(92, 39),
|
|
yellowBright: init(93, 39),
|
|
blueBright: init(94, 39),
|
|
magentaBright: init(95, 39),
|
|
cyanBright: init(96, 39),
|
|
whiteBright: init(97, 39),
|
|
bgBlackBright: init(100, 49),
|
|
bgRedBright: init(101, 49),
|
|
bgGreenBright: init(102, 49),
|
|
bgYellowBright: init(103, 49),
|
|
bgBlueBright: init(104, 49),
|
|
bgMagentaBright: init(105, 49),
|
|
bgCyanBright: init(106, 49),
|
|
bgWhiteBright: init(107, 49)
|
|
};
|
|
function createColors(useColor = isColorSupported) {
|
|
return useColor ? colorDefs : Object.fromEntries(Object.keys(colorDefs).map((key) => [key, String]));
|
|
}
|
|
const colors = createColors();
|
|
function getColor(color, fallback = "reset") {
|
|
return colors[color] || colors[fallback];
|
|
}
|
|
function colorize(color, text) {
|
|
return getColor(color)(text);
|
|
}
|
|
|
|
const ansiRegex = [
|
|
String.raw`[\u001B\u009B][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d\/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d\/#&.:=?%@~_]*)*)?\u0007)`,
|
|
String.raw`(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))`
|
|
].join("|");
|
|
function stripAnsi(text) {
|
|
return text.replace(new RegExp(ansiRegex, "g"), "");
|
|
}
|
|
function centerAlign(str, len, space = " ") {
|
|
const free = len - str.length;
|
|
if (free <= 0) {
|
|
return str;
|
|
}
|
|
const freeLeft = Math.floor(free / 2);
|
|
let _str = "";
|
|
for (let i = 0; i < len; i++) {
|
|
_str += i < freeLeft || i >= freeLeft + str.length ? space : str[i - freeLeft];
|
|
}
|
|
return _str;
|
|
}
|
|
function rightAlign(str, len, space = " ") {
|
|
const free = len - str.length;
|
|
if (free <= 0) {
|
|
return str;
|
|
}
|
|
let _str = "";
|
|
for (let i = 0; i < len; i++) {
|
|
_str += i < free ? space : str[i - free];
|
|
}
|
|
return _str;
|
|
}
|
|
function leftAlign(str, len, space = " ") {
|
|
let _str = "";
|
|
for (let i = 0; i < len; i++) {
|
|
_str += i < str.length ? str[i] : space;
|
|
}
|
|
return _str;
|
|
}
|
|
function align(alignment, str, len, space = " ") {
|
|
switch (alignment) {
|
|
case "left": {
|
|
return leftAlign(str, len, space);
|
|
}
|
|
case "right": {
|
|
return rightAlign(str, len, space);
|
|
}
|
|
case "center": {
|
|
return centerAlign(str, len, space);
|
|
}
|
|
default: {
|
|
return str;
|
|
}
|
|
}
|
|
}
|
|
|
|
const boxStylePresets = {
|
|
solid: {
|
|
tl: "\u250C",
|
|
tr: "\u2510",
|
|
bl: "\u2514",
|
|
br: "\u2518",
|
|
h: "\u2500",
|
|
v: "\u2502"
|
|
},
|
|
double: {
|
|
tl: "\u2554",
|
|
tr: "\u2557",
|
|
bl: "\u255A",
|
|
br: "\u255D",
|
|
h: "\u2550",
|
|
v: "\u2551"
|
|
},
|
|
doubleSingle: {
|
|
tl: "\u2553",
|
|
tr: "\u2556",
|
|
bl: "\u2559",
|
|
br: "\u255C",
|
|
h: "\u2500",
|
|
v: "\u2551"
|
|
},
|
|
doubleSingleRounded: {
|
|
tl: "\u256D",
|
|
tr: "\u256E",
|
|
bl: "\u2570",
|
|
br: "\u256F",
|
|
h: "\u2500",
|
|
v: "\u2551"
|
|
},
|
|
singleThick: {
|
|
tl: "\u250F",
|
|
tr: "\u2513",
|
|
bl: "\u2517",
|
|
br: "\u251B",
|
|
h: "\u2501",
|
|
v: "\u2503"
|
|
},
|
|
singleDouble: {
|
|
tl: "\u2552",
|
|
tr: "\u2555",
|
|
bl: "\u2558",
|
|
br: "\u255B",
|
|
h: "\u2550",
|
|
v: "\u2502"
|
|
},
|
|
singleDoubleRounded: {
|
|
tl: "\u256D",
|
|
tr: "\u256E",
|
|
bl: "\u2570",
|
|
br: "\u256F",
|
|
h: "\u2550",
|
|
v: "\u2502"
|
|
},
|
|
rounded: {
|
|
tl: "\u256D",
|
|
tr: "\u256E",
|
|
bl: "\u2570",
|
|
br: "\u256F",
|
|
h: "\u2500",
|
|
v: "\u2502"
|
|
}
|
|
};
|
|
const defaultStyle = {
|
|
borderColor: "white",
|
|
borderStyle: "rounded",
|
|
valign: "center",
|
|
padding: 2,
|
|
marginLeft: 1,
|
|
marginTop: 1,
|
|
marginBottom: 1
|
|
};
|
|
function box(text, _opts = {}) {
|
|
const opts = {
|
|
..._opts,
|
|
style: {
|
|
...defaultStyle,
|
|
..._opts.style
|
|
}
|
|
};
|
|
const textLines = text.split("\n");
|
|
const boxLines = [];
|
|
const _color = getColor(opts.style.borderColor);
|
|
const borderStyle = {
|
|
...typeof opts.style.borderStyle === "string" ? boxStylePresets[opts.style.borderStyle] || boxStylePresets.solid : opts.style.borderStyle
|
|
};
|
|
if (_color) {
|
|
for (const key in borderStyle) {
|
|
borderStyle[key] = _color(
|
|
borderStyle[key]
|
|
);
|
|
}
|
|
}
|
|
const paddingOffset = opts.style.padding % 2 === 0 ? opts.style.padding : opts.style.padding + 1;
|
|
const height = textLines.length + paddingOffset;
|
|
const width = Math.max(
|
|
...textLines.map((line) => stripAnsi(line).length),
|
|
opts.title ? stripAnsi(opts.title).length : 0
|
|
) + paddingOffset;
|
|
const widthOffset = width + paddingOffset;
|
|
const leftSpace = opts.style.marginLeft > 0 ? " ".repeat(opts.style.marginLeft) : "";
|
|
if (opts.style.marginTop > 0) {
|
|
boxLines.push("".repeat(opts.style.marginTop));
|
|
}
|
|
if (opts.title) {
|
|
const title = _color ? _color(opts.title) : opts.title;
|
|
const left = borderStyle.h.repeat(
|
|
Math.floor((width - stripAnsi(opts.title).length) / 2)
|
|
);
|
|
const right = borderStyle.h.repeat(
|
|
width - stripAnsi(opts.title).length - stripAnsi(left).length + paddingOffset
|
|
);
|
|
boxLines.push(
|
|
`${leftSpace}${borderStyle.tl}${left}${title}${right}${borderStyle.tr}`
|
|
);
|
|
} else {
|
|
boxLines.push(
|
|
`${leftSpace}${borderStyle.tl}${borderStyle.h.repeat(widthOffset)}${borderStyle.tr}`
|
|
);
|
|
}
|
|
const valignOffset = opts.style.valign === "center" ? Math.floor((height - textLines.length) / 2) : opts.style.valign === "top" ? height - textLines.length - paddingOffset : height - textLines.length;
|
|
for (let i = 0; i < height; i++) {
|
|
if (i < valignOffset || i >= valignOffset + textLines.length) {
|
|
boxLines.push(
|
|
`${leftSpace}${borderStyle.v}${" ".repeat(widthOffset)}${borderStyle.v}`
|
|
);
|
|
} else {
|
|
const line = textLines[i - valignOffset];
|
|
const left = " ".repeat(paddingOffset);
|
|
const right = " ".repeat(width - stripAnsi(line).length);
|
|
boxLines.push(
|
|
`${leftSpace}${borderStyle.v}${left}${line}${right}${borderStyle.v}`
|
|
);
|
|
}
|
|
}
|
|
boxLines.push(
|
|
`${leftSpace}${borderStyle.bl}${borderStyle.h.repeat(widthOffset)}${borderStyle.br}`
|
|
);
|
|
if (opts.style.marginBottom > 0) {
|
|
boxLines.push("".repeat(opts.style.marginBottom));
|
|
}
|
|
return boxLines.join("\n");
|
|
}
|
|
|
|
export { centerAlign as a, align as b, colorize as c, box as d, colors as e, getColor as g, leftAlign as l, rightAlign as r, stripAnsi as s };
|