hexlogger 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +23 -0
- package/package.json +38 -0
- package/src/colors.js +79 -0
- package/src/config.js +62 -0
- package/src/core.js +49 -0
- package/src/formatting.js +84 -0
- package/src/gradient.js +56 -0
- package/src/progress.js +97 -0
- package/src/styles.js +74 -0
- package/src/themes.js +82 -0
- package/src/utils.js +15 -0
- package/src/widgets.js +116 -0
package/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const { Colors, rgb, bgrgb, hexclr, bghex } = require("./src/colors");
|
|
2
|
+
const { Theme, THEMES, LEVEL_ICONS } = require("./src/themes");
|
|
3
|
+
const { LogLevel, LoggerConfig, getConfig, settheme, setstyle, setlog, setlevel } = require("./src/config");
|
|
4
|
+
const { debug, info, success, warn, warning, error, critical, fatal, trace } = require("./src/core");
|
|
5
|
+
const { divider, section, blank, rule, banner, pair } = require("./src/formatting");
|
|
6
|
+
const { gtext, gprint, mgtext, mgprint, rbtext, rbprint } = require("./src/gradient");
|
|
7
|
+
const { panel, table, box } = require("./src/widgets");
|
|
8
|
+
const { Progress, Spinner, Timer } = require("./src/progress");
|
|
9
|
+
const { stripAnsi, clr, bold, italic, uline } = require("./src/utils");
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
__version__: "1.0.0",
|
|
13
|
+
__author__: "Montage",
|
|
14
|
+
Colors, rgb, bgrgb, hexclr, bghex,
|
|
15
|
+
Theme, THEMES, LEVEL_ICONS, LogLevel, LoggerConfig, getConfig,
|
|
16
|
+
settheme, setstyle, setlog, setlevel,
|
|
17
|
+
debug, info, success, warn, warning, error, critical, fatal, trace,
|
|
18
|
+
divider, section, blank, rule, banner, pair,
|
|
19
|
+
gtext, gprint, mgtext, mgprint, rbtext, rbprint,
|
|
20
|
+
panel, table, box,
|
|
21
|
+
Progress, Spinner, Timer,
|
|
22
|
+
stripAnsi, clr, bold, italic, uline,
|
|
23
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hexlogger",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Premium console logging library with themes, gradients, panels, tables, progress bars, and spinners.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"logger",
|
|
11
|
+
"logging",
|
|
12
|
+
"console",
|
|
13
|
+
"colors",
|
|
14
|
+
"terminal",
|
|
15
|
+
"ansi",
|
|
16
|
+
"themes",
|
|
17
|
+
"gradient",
|
|
18
|
+
"spinner",
|
|
19
|
+
"progress",
|
|
20
|
+
"table",
|
|
21
|
+
"panel",
|
|
22
|
+
"box",
|
|
23
|
+
"cli"
|
|
24
|
+
],
|
|
25
|
+
"author": "Montage",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": ""
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=14.0.0"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"index.js",
|
|
36
|
+
"src/"
|
|
37
|
+
]
|
|
38
|
+
}
|
package/src/colors.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const R = "\x1b[0m";
|
|
2
|
+
const BOLD = "\x1b[1m";
|
|
3
|
+
const DIM = "\x1b[2m";
|
|
4
|
+
const ITALIC = "\x1b[3m";
|
|
5
|
+
const UNDERLINE = "\x1b[4m";
|
|
6
|
+
const BLINK = "\x1b[5m";
|
|
7
|
+
const REVERSE = "\x1b[7m";
|
|
8
|
+
const STRIKE = "\x1b[9m";
|
|
9
|
+
const BLACK = "\x1b[30m";
|
|
10
|
+
const RED = "\x1b[31m";
|
|
11
|
+
const GREEN = "\x1b[32m";
|
|
12
|
+
const YELLOW = "\x1b[33m";
|
|
13
|
+
const BLUE = "\x1b[34m";
|
|
14
|
+
const MAGENTA = "\x1b[35m";
|
|
15
|
+
const CYAN = "\x1b[36m";
|
|
16
|
+
const WHITE = "\x1b[37m";
|
|
17
|
+
const GRAY = "\x1b[90m";
|
|
18
|
+
const BRIGHT_RED = "\x1b[91m";
|
|
19
|
+
const BRIGHT_GREEN = "\x1b[92m";
|
|
20
|
+
const BRIGHT_YELLOW = "\x1b[93m";
|
|
21
|
+
const BRIGHT_BLUE = "\x1b[94m";
|
|
22
|
+
const BRIGHT_MAGENTA = "\x1b[95m";
|
|
23
|
+
const BRIGHT_CYAN = "\x1b[96m";
|
|
24
|
+
const BRIGHT_WHITE = "\x1b[97m";
|
|
25
|
+
const BG_BLACK = "\x1b[40m";
|
|
26
|
+
const BG_RED = "\x1b[41m";
|
|
27
|
+
const BG_GREEN = "\x1b[42m";
|
|
28
|
+
const BG_YELLOW = "\x1b[43m";
|
|
29
|
+
const BG_BLUE = "\x1b[44m";
|
|
30
|
+
const BG_MAGENTA = "\x1b[45m";
|
|
31
|
+
const BG_CYAN = "\x1b[46m";
|
|
32
|
+
const BG_WHITE = "\x1b[47m";
|
|
33
|
+
|
|
34
|
+
const Colors = {
|
|
35
|
+
RESET: R, BOLD, DIM, ITALIC, UNDERLINE, BLINK, REVERSE, STRIKE,
|
|
36
|
+
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, GRAY,
|
|
37
|
+
BRIGHT_RED, BRIGHT_GREEN, BRIGHT_YELLOW, BRIGHT_BLUE,
|
|
38
|
+
BRIGHT_MAGENTA, BRIGHT_CYAN, BRIGHT_WHITE,
|
|
39
|
+
BG_BLACK, BG_RED, BG_GREEN, BG_YELLOW, BG_BLUE,
|
|
40
|
+
BG_MAGENTA, BG_CYAN, BG_WHITE,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
function _clamp(v) {
|
|
44
|
+
return Math.max(0, Math.min(255, Math.floor(Number(v) || 0)));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function rgb(r, g, b) {
|
|
48
|
+
return `\x1b[38;2;${_clamp(r)};${_clamp(g)};${_clamp(b)}m`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function bgrgb(r, g, b) {
|
|
52
|
+
return `\x1b[48;2;${_clamp(r)};${_clamp(g)};${_clamp(b)}m`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function hexclr(hex) {
|
|
56
|
+
try {
|
|
57
|
+
hex = String(hex).replace(/^#/, "");
|
|
58
|
+
if (hex.length < 6) hex = hex.padEnd(6, "0");
|
|
59
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
60
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
61
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
62
|
+
if (isNaN(r) || isNaN(g) || isNaN(b)) return R;
|
|
63
|
+
return rgb(r, g, b);
|
|
64
|
+
} catch { return R; }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function bghex(hex) {
|
|
68
|
+
try {
|
|
69
|
+
hex = String(hex).replace(/^#/, "");
|
|
70
|
+
if (hex.length < 6) hex = hex.padEnd(6, "0");
|
|
71
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
72
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
73
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
74
|
+
if (isNaN(r) || isNaN(g) || isNaN(b)) return R;
|
|
75
|
+
return bgrgb(r, g, b);
|
|
76
|
+
} catch { return R; }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = { Colors, rgb, bgrgb, hexclr, bghex, _clamp };
|
package/src/config.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const { Theme, THEMES } = require("./themes");
|
|
3
|
+
const { STYLES } = require("./styles");
|
|
4
|
+
|
|
5
|
+
const LogLevel = { TRACE: 0, DEBUG: 1, INFO: 2, SUCCESS: 3, WARNING: 4, ERROR: 5, CRITICAL: 6, FATAL: 7 };
|
|
6
|
+
|
|
7
|
+
class LoggerConfig {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.theme = THEMES.default || new Theme("default", {});
|
|
10
|
+
this.style = "default";
|
|
11
|
+
this.timeFormat = "HH:mm:ss";
|
|
12
|
+
this.showTime = true;
|
|
13
|
+
this.logFile = null;
|
|
14
|
+
this.minLevel = LogLevel.TRACE;
|
|
15
|
+
this._stream = null;
|
|
16
|
+
}
|
|
17
|
+
setTheme(name) {
|
|
18
|
+
if (THEMES[name]) this.theme = THEMES[name];
|
|
19
|
+
else throw new Error(`Unknown theme: ${name}. Available: ${Object.keys(THEMES).join(", ")}`);
|
|
20
|
+
}
|
|
21
|
+
setStyle(name) {
|
|
22
|
+
if (STYLES[name]) this.style = name;
|
|
23
|
+
else throw new Error(`Unknown style: ${name}. Available: ${Object.keys(STYLES).join(", ")}`);
|
|
24
|
+
}
|
|
25
|
+
addTheme(name, colors) {
|
|
26
|
+
try {
|
|
27
|
+
const t = new Theme(String(name), typeof colors === "object" ? colors : {});
|
|
28
|
+
THEMES[String(name)] = t;
|
|
29
|
+
return t;
|
|
30
|
+
} catch { throw new Error(`Failed to add theme: ${name}`); }
|
|
31
|
+
}
|
|
32
|
+
setLogFile(path) {
|
|
33
|
+
try {
|
|
34
|
+
this.logFile = String(path);
|
|
35
|
+
if (this._stream) { try { this._stream.end(); } catch { } }
|
|
36
|
+
this._stream = fs.createWriteStream(this.logFile, { flags: "a", encoding: "utf-8" });
|
|
37
|
+
} catch { this._stream = null; throw new Error(`Failed to open log file: ${path}`); }
|
|
38
|
+
}
|
|
39
|
+
close() {
|
|
40
|
+
if (this._stream) { try { this._stream.end(); } catch { } this._stream = null; }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const _config = new LoggerConfig();
|
|
45
|
+
|
|
46
|
+
function getConfig() { return _config; }
|
|
47
|
+
|
|
48
|
+
function settheme(name) { _config.setTheme(String(name)); }
|
|
49
|
+
function setstyle(name) { _config.setStyle(String(name)); }
|
|
50
|
+
function setlog(path) { _config.setLogFile(String(path)); }
|
|
51
|
+
function setlevel(level) {
|
|
52
|
+
const mapping = {
|
|
53
|
+
trace: LogLevel.TRACE, debug: LogLevel.DEBUG, info: LogLevel.INFO,
|
|
54
|
+
success: LogLevel.SUCCESS, warning: LogLevel.WARNING, error: LogLevel.ERROR,
|
|
55
|
+
critical: LogLevel.CRITICAL, fatal: LogLevel.FATAL,
|
|
56
|
+
};
|
|
57
|
+
const key = String(level).toLowerCase().trim();
|
|
58
|
+
if (mapping[key] !== undefined) _config.minLevel = mapping[key];
|
|
59
|
+
else throw new Error(`Unknown level: ${level}. Available: ${Object.keys(mapping).join(", ")}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = { LogLevel, LoggerConfig, getConfig, settheme, setstyle, setlog, setlevel };
|
package/src/core.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const { getConfig, LogLevel } = require("./config");
|
|
2
|
+
const { STYLES } = require("./styles");
|
|
3
|
+
const { stripAnsi } = require("./utils");
|
|
4
|
+
|
|
5
|
+
function _getTime() {
|
|
6
|
+
try {
|
|
7
|
+
const d = new Date();
|
|
8
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
9
|
+
return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
10
|
+
} catch { return ""; }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function _writeToFile(message) {
|
|
14
|
+
const cfg = getConfig();
|
|
15
|
+
if (cfg._stream) {
|
|
16
|
+
try { cfg._stream.write(stripAnsi(message) + "\n"); }
|
|
17
|
+
catch { }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function _log(level, message, levelValue) {
|
|
22
|
+
try {
|
|
23
|
+
const cfg = getConfig();
|
|
24
|
+
if (levelValue < cfg.minLevel) return;
|
|
25
|
+
const t = cfg.theme;
|
|
26
|
+
const now = _getTime();
|
|
27
|
+
const lc = t.get(level);
|
|
28
|
+
const ll = t.get(`label_${level}`);
|
|
29
|
+
const styleFn = STYLES[cfg.style] || STYLES.default;
|
|
30
|
+
const line = styleFn(now, level, lc, ll, String(message), t, cfg.showTime);
|
|
31
|
+
process.stdout.write(line + "\n");
|
|
32
|
+
_writeToFile(line);
|
|
33
|
+
} catch {
|
|
34
|
+
try { process.stdout.write(`[${level.toUpperCase()}] ${message}\n`); }
|
|
35
|
+
catch { }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function debug(msg) { _log("debug", msg, LogLevel.DEBUG); }
|
|
40
|
+
function info(msg) { _log("info", msg, LogLevel.INFO); }
|
|
41
|
+
function success(msg) { _log("success", msg, LogLevel.SUCCESS); }
|
|
42
|
+
function warn(msg) { _log("warning", msg, LogLevel.WARNING); }
|
|
43
|
+
function warning(msg) { _log("warning", msg, LogLevel.WARNING); }
|
|
44
|
+
function error(msg) { _log("error", msg, LogLevel.ERROR); }
|
|
45
|
+
function critical(msg) { _log("critical", msg, LogLevel.CRITICAL); }
|
|
46
|
+
function fatal(msg) { _log("fatal", msg, LogLevel.FATAL); }
|
|
47
|
+
function trace(msg) { _log("trace", msg, LogLevel.TRACE); }
|
|
48
|
+
|
|
49
|
+
module.exports = { debug, info, success, warn, warning, error, critical, fatal, trace };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const { Colors } = require("./colors");
|
|
2
|
+
const { getConfig } = require("./config");
|
|
3
|
+
const { stripAnsi } = require("./utils");
|
|
4
|
+
|
|
5
|
+
function _write(message) {
|
|
6
|
+
const cfg = getConfig();
|
|
7
|
+
if (cfg._stream) {
|
|
8
|
+
try { cfg._stream.write(stripAnsi(message) + "\n"); }
|
|
9
|
+
catch { }
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function divider(char = "─", length = 60, color = null) {
|
|
14
|
+
try {
|
|
15
|
+
const cfg = getConfig();
|
|
16
|
+
const c = color || cfg.theme.get("divider");
|
|
17
|
+
length = Math.max(1, Math.floor(length));
|
|
18
|
+
const ch = String(char)[0] || "─";
|
|
19
|
+
const line = `${c}${ch.repeat(length)}${Colors.RESET}`;
|
|
20
|
+
process.stdout.write(line + "\n");
|
|
21
|
+
_write(line);
|
|
22
|
+
} catch { process.stdout.write((String(char)[0] || "─").repeat(Math.max(1, length)) + "\n"); }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function section(title = "", char = "═", length = 60) {
|
|
26
|
+
try {
|
|
27
|
+
const cfg = getConfig();
|
|
28
|
+
const t = cfg.theme;
|
|
29
|
+
title = String(title);
|
|
30
|
+
length = Math.max(title.length + 4, Math.floor(length));
|
|
31
|
+
const pad = length - title.length - 2;
|
|
32
|
+
const left = Math.floor(pad / 2);
|
|
33
|
+
const right = pad - left;
|
|
34
|
+
const ch = String(char)[0] || "═";
|
|
35
|
+
const line = `${t.get("divider")}${ch.repeat(left)}${Colors.RESET} ${Colors.BOLD}${t.get("info")}${title}${Colors.RESET} ${t.get("divider")}${ch.repeat(right)}${Colors.RESET}`;
|
|
36
|
+
process.stdout.write(line + "\n");
|
|
37
|
+
_write(line);
|
|
38
|
+
} catch { process.stdout.write(`== ${title} ==\n`); }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function blank(count = 1) {
|
|
42
|
+
for (let i = 0; i < Math.max(1, Math.floor(count)); i++) process.stdout.write("\n");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function rule(text = "", char = "─", length = 60) {
|
|
46
|
+
if (text) section(text, char, length);
|
|
47
|
+
else divider(char, length);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function banner(text = "", char = "═", padding = 2) {
|
|
51
|
+
try {
|
|
52
|
+
const cfg = getConfig();
|
|
53
|
+
text = String(text);
|
|
54
|
+
padding = Math.max(0, Math.floor(padding));
|
|
55
|
+
const lines = text.split("\n");
|
|
56
|
+
const maxLen = Math.max(...lines.map(l => l.length), 0);
|
|
57
|
+
const width = maxLen + (padding * 2) + 2;
|
|
58
|
+
const t = cfg.theme;
|
|
59
|
+
const bc = t.get("info"), tc = t.get("success"), R = Colors.RESET;
|
|
60
|
+
const ch = String(char)[0] || "═";
|
|
61
|
+
process.stdout.write(`${bc}╔${ch.repeat(width - 2)}╗${R}\n`);
|
|
62
|
+
for (const l of lines) {
|
|
63
|
+
const pad2 = maxLen - l.length;
|
|
64
|
+
const lp = Math.floor(pad2 / 2);
|
|
65
|
+
const rp = pad2 - lp;
|
|
66
|
+
process.stdout.write(`${bc}║${R}${" ".repeat(padding)}${tc}${" ".repeat(lp)}${l}${" ".repeat(rp)}${R}${" ".repeat(padding)}${bc}║${R}\n`);
|
|
67
|
+
}
|
|
68
|
+
process.stdout.write(`${bc}╚${ch.repeat(width - 2)}╝${R}\n`);
|
|
69
|
+
} catch { process.stdout.write(text + "\n"); }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function pair(k, v, kClr = null, vClr = null) {
|
|
73
|
+
try {
|
|
74
|
+
const cfg = getConfig();
|
|
75
|
+
const kc = kClr || cfg.theme.get("info");
|
|
76
|
+
const vc = vClr || Colors.RESET;
|
|
77
|
+
k = String(k); v = String(v);
|
|
78
|
+
const line = ` ${kc}${Colors.BOLD}${k}${Colors.RESET}: ${vc}${v}${Colors.RESET}`;
|
|
79
|
+
process.stdout.write(line + "\n");
|
|
80
|
+
_write(` ${k}: ${v}`);
|
|
81
|
+
} catch { process.stdout.write(` ${k}: ${v}\n`); }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = { divider, section, blank, rule, banner, pair };
|
package/src/gradient.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const { Colors, rgb } = require("./colors");
|
|
2
|
+
|
|
3
|
+
const _RAINBOW = [[255, 0, 0], [255, 127, 0], [255, 255, 0], [0, 255, 0], [0, 127, 255], [75, 0, 130], [148, 0, 211]];
|
|
4
|
+
|
|
5
|
+
function _interp(c1, c2, t) {
|
|
6
|
+
t = Math.max(0, Math.min(1, t));
|
|
7
|
+
return [
|
|
8
|
+
Math.floor(c1[0] + (c2[0] - c1[0]) * t),
|
|
9
|
+
Math.floor(c1[1] + (c2[1] - c1[1]) * t),
|
|
10
|
+
Math.floor(c1[2] + (c2[2] - c1[2]) * t),
|
|
11
|
+
];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function gtext(text, start = [255, 0, 100], end = [100, 0, 255]) {
|
|
15
|
+
try {
|
|
16
|
+
text = String(text);
|
|
17
|
+
if (!text) return "";
|
|
18
|
+
const len = Math.max(text.length - 1, 1);
|
|
19
|
+
let res = "";
|
|
20
|
+
for (let i = 0; i < text.length; i++) {
|
|
21
|
+
const [r, g, b] = _interp(start, end, i / len);
|
|
22
|
+
res += `${rgb(r, g, b)}${text[i]}`;
|
|
23
|
+
}
|
|
24
|
+
return res + Colors.RESET;
|
|
25
|
+
} catch { return String(text); }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function gprint(text, start = [255, 0, 100], end = [100, 0, 255]) {
|
|
29
|
+
process.stdout.write(gtext(text, start, end) + "\n");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function mgtext(text, colors = null) {
|
|
33
|
+
try {
|
|
34
|
+
text = String(text);
|
|
35
|
+
if (!colors || colors.length < 2 || !text) return text;
|
|
36
|
+
const segments = colors.length - 1;
|
|
37
|
+
const cps = Math.max(text.length / segments, 1);
|
|
38
|
+
let res = "";
|
|
39
|
+
for (let i = 0; i < text.length; i++) {
|
|
40
|
+
const seg = Math.min(Math.floor(i / cps), segments - 1);
|
|
41
|
+
const t = (i - seg * cps) / cps;
|
|
42
|
+
const [r, g, b] = _interp(colors[seg], colors[seg + 1], t);
|
|
43
|
+
res += `${rgb(r, g, b)}${text[i]}`;
|
|
44
|
+
}
|
|
45
|
+
return res + Colors.RESET;
|
|
46
|
+
} catch { return String(text); }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function mgprint(text, colors = null) {
|
|
50
|
+
process.stdout.write(mgtext(text, colors) + "\n");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function rbtext(text) { return mgtext(text, _RAINBOW); }
|
|
54
|
+
function rbprint(text) { process.stdout.write(rbtext(text) + "\n"); }
|
|
55
|
+
|
|
56
|
+
module.exports = { gtext, gprint, mgtext, mgprint, rbtext, rbprint };
|
package/src/progress.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const { Colors } = require("./colors");
|
|
2
|
+
const { getConfig } = require("./config");
|
|
3
|
+
|
|
4
|
+
class Progress {
|
|
5
|
+
constructor(total, width = 30, label = "Progress", fill = "█", empty = "░", color = null) {
|
|
6
|
+
this.total = Math.max(1, Math.floor(total) || 1);
|
|
7
|
+
this.width = Math.max(5, Math.floor(width));
|
|
8
|
+
this.label = String(label);
|
|
9
|
+
this.fill = String(fill)[0] || "█";
|
|
10
|
+
this.empty = String(empty)[0] || "░";
|
|
11
|
+
try { this.color = color || getConfig().theme.get("success"); }
|
|
12
|
+
catch { this.color = ""; }
|
|
13
|
+
this.current = 0;
|
|
14
|
+
this._start = Date.now();
|
|
15
|
+
}
|
|
16
|
+
update(n = 1) {
|
|
17
|
+
this.current = Math.min(this.current + Math.max(0, Math.floor(n)), this.total);
|
|
18
|
+
this._render();
|
|
19
|
+
}
|
|
20
|
+
_render() {
|
|
21
|
+
try {
|
|
22
|
+
const pct = this.current / this.total;
|
|
23
|
+
const filled = Math.floor(this.width * pct);
|
|
24
|
+
const bar = this.fill.repeat(filled) + this.empty.repeat(this.width - filled);
|
|
25
|
+
const elapsed = (Date.now() - this._start) / 1000;
|
|
26
|
+
const eta = pct > 0 ? (elapsed / pct - elapsed) : 0;
|
|
27
|
+
process.stdout.write(
|
|
28
|
+
`\r${this.color}${this.label}${Colors.RESET} ` +
|
|
29
|
+
`│${this.color}${bar}${Colors.RESET}│ ` +
|
|
30
|
+
`${Colors.BOLD}${(pct * 100).toFixed(1).padStart(5)}%${Colors.RESET} ` +
|
|
31
|
+
`(${this.current}/${this.total}) ` +
|
|
32
|
+
`[${elapsed.toFixed(1)}s / ETA ${eta.toFixed(1)}s]`
|
|
33
|
+
);
|
|
34
|
+
if (this.current >= this.total) process.stdout.write("\n");
|
|
35
|
+
} catch { }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class Spinner {
|
|
40
|
+
constructor(msg = "Loading", style = "dots", color = null) {
|
|
41
|
+
this.msg = String(msg);
|
|
42
|
+
this.frames = ({
|
|
43
|
+
dots: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
|
44
|
+
line: ["-", "\\", "|", "/"],
|
|
45
|
+
arrow: ["←", "↖", "↑", "↗", "→", "↘", "↓", "↙"],
|
|
46
|
+
bounce: ["⠁", "⠂", "⠄", "⠂"],
|
|
47
|
+
box: ["▖", "▘", "▝", "▗"],
|
|
48
|
+
circle: ["◐", "◓", "◑", "◒"],
|
|
49
|
+
})[style] || ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
50
|
+
try { this.color = color || getConfig().theme.get("info"); }
|
|
51
|
+
catch { this.color = ""; }
|
|
52
|
+
this._run = false;
|
|
53
|
+
this._timer = null;
|
|
54
|
+
this._i = 0;
|
|
55
|
+
}
|
|
56
|
+
start() {
|
|
57
|
+
this._run = true;
|
|
58
|
+
this._i = 0;
|
|
59
|
+
this._timer = setInterval(() => {
|
|
60
|
+
try {
|
|
61
|
+
const frame = this.frames[this._i % this.frames.length];
|
|
62
|
+
process.stdout.write(`\r${this.color}${frame}${Colors.RESET} ${this.msg}`);
|
|
63
|
+
this._i++;
|
|
64
|
+
} catch { }
|
|
65
|
+
}, 80);
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
stop(final = "") {
|
|
69
|
+
this._run = false;
|
|
70
|
+
if (this._timer) { clearInterval(this._timer); this._timer = null; }
|
|
71
|
+
try { process.stdout.write("\r" + " ".repeat(this.msg.length + 10) + "\r"); }
|
|
72
|
+
catch { }
|
|
73
|
+
if (final) {
|
|
74
|
+
try { const { success } = require("./core"); success(final); }
|
|
75
|
+
catch { process.stdout.write(final + "\n"); }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
class Timer {
|
|
81
|
+
constructor(label = "Elapsed") {
|
|
82
|
+
this.label = String(label);
|
|
83
|
+
this._start = null;
|
|
84
|
+
}
|
|
85
|
+
start() { this._start = Date.now(); return this; }
|
|
86
|
+
stop() {
|
|
87
|
+
try {
|
|
88
|
+
if (this._start === null) return 0;
|
|
89
|
+
const elapsed = (Date.now() - this._start) / 1000;
|
|
90
|
+
const { info } = require("./core");
|
|
91
|
+
info(`${this.label}: ${elapsed.toFixed(3)}s`);
|
|
92
|
+
return elapsed;
|
|
93
|
+
} catch { return 0; }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { Progress, Spinner, Timer };
|
package/src/styles.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const { Colors, bgrgb } = require("./colors");
|
|
2
|
+
const { LEVEL_ICONS } = require("./themes");
|
|
3
|
+
|
|
4
|
+
const _TAG_BG = {
|
|
5
|
+
debug: bgrgb(50, 80, 120), info: bgrgb(40, 70, 130),
|
|
6
|
+
success: bgrgb(30, 100, 50), warning: bgrgb(130, 100, 0),
|
|
7
|
+
error: bgrgb(130, 30, 30), critical: bgrgb(150, 20, 20),
|
|
8
|
+
fatal: bgrgb(120, 0, 0), trace: bgrgb(60, 60, 60),
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function _ljust(s, w) { s = String(s); return s.length >= w ? s : s + " ".repeat(w - s.length); }
|
|
12
|
+
|
|
13
|
+
const R = Colors.RESET, B = Colors.BOLD;
|
|
14
|
+
|
|
15
|
+
function _default(now, level, lc, ll, msg, t, st) {
|
|
16
|
+
const br = t.get("bracket"), ts = t.get("timestamp"), dt = t.get("dot"), sep = t.get("separator");
|
|
17
|
+
if (st) return `${br}[-(${ts}${now}${R}${br})-]${R} ${br}[${R} ${dt}●${R} ${br}]${R} ${B}${lc}${ll}${R} ${sep}»${R} ${lc}${msg}${R}`;
|
|
18
|
+
return `${br}[${R} ${dt}●${R} ${br}]${R} ${B}${lc}${ll}${R} ${sep}»${R} ${lc}${msg}${R}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function _box(now, level, lc, ll, msg, t, st) {
|
|
22
|
+
const br = t.get("bracket"), ts = t.get("timestamp"), icon = LEVEL_ICONS[level] || "●", lbl = _ljust(ll, 8);
|
|
23
|
+
if (st) return `${br}┃${R} ${ts}${now}${R} ${br}│${R} ${lc}${icon}${R} ${B}${lc}${lbl}${R} ${br}│${R} ${lc}${msg}${R}`;
|
|
24
|
+
return `${br}┃${R} ${lc}${icon}${R} ${B}${lc}${lbl}${R} ${br}│${R} ${lc}${msg}${R}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function _modern(now, level, lc, ll, msg, t, st) {
|
|
28
|
+
const ts = t.get("timestamp"), icon = LEVEL_ICONS[level] || "●";
|
|
29
|
+
if (st) return `${ts}${now}${R} ${lc}${icon} ${B}${ll}${R} ${lc}${msg}${R}`;
|
|
30
|
+
return `${lc}${icon} ${B}${ll}${R} ${lc}${msg}${R}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function _bracket(now, level, lc, ll, msg, t, st) {
|
|
34
|
+
const br = t.get("bracket"), ts = t.get("timestamp");
|
|
35
|
+
if (st) return `${br}[${ts}${now}${R}${br}]${R} ${br}[${B}${lc}${ll}${R}${br}]${R} ${lc}${msg}${R}`;
|
|
36
|
+
return `${br}[${B}${lc}${ll}${R}${br}]${R} ${lc}${msg}${R}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function _arrow(now, level, lc, ll, msg, t, st) {
|
|
40
|
+
const ts = t.get("timestamp"), sep = t.get("separator");
|
|
41
|
+
if (st) return `${ts}${now}${R} ${sep}▸${R} ${B}${lc}${ll}${R} ${sep}▸${R} ${lc}${msg}${R}`;
|
|
42
|
+
return `${sep}▸${R} ${B}${lc}${ll}${R} ${sep}▸${R} ${lc}${msg}${R}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function _pipe(now, level, lc, ll, msg, t, st) {
|
|
46
|
+
const ts = t.get("timestamp"), br = t.get("bracket"), lbl = _ljust(ll, 8);
|
|
47
|
+
if (st) return `${lc}▌${R} ${ts}${now}${R} ${br}|${R} ${B}${lc}${lbl}${R} ${br}|${R} ${lc}${msg}${R}`;
|
|
48
|
+
return `${lc}▌${R} ${B}${lc}${lbl}${R} ${br}|${R} ${lc}${msg}${R}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function _tag(now, level, lc, ll, msg, t, st) {
|
|
52
|
+
const ts = t.get("timestamp"), bg = _TAG_BG[level] || "";
|
|
53
|
+
if (st) return `${ts}${now}${R} ${bg}${B} ${ll} ${R} ${lc}${msg}${R}`;
|
|
54
|
+
return `${bg}${B} ${ll} ${R} ${lc}${msg}${R}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function _dots(now, level, lc, ll, msg, t, st) {
|
|
58
|
+
const ts = t.get("timestamp"), sep = t.get("separator"), icon = LEVEL_ICONS[level] || "●";
|
|
59
|
+
if (st) return `${lc}${icon}${R} ${ts}${now}${R} ${sep}·${R} ${B}${lc}${ll}${R} ${sep}·${R} ${lc}${msg}${R}`;
|
|
60
|
+
return `${lc}${icon} ${B}${lc}${ll}${R} ${sep}·${R} ${lc}${msg}${R}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function _clean(now, level, lc, ll, msg, t, st) {
|
|
64
|
+
const ts = t.get("timestamp"), lbl = _ljust(ll, 8);
|
|
65
|
+
if (st) return ` ${ts}${now}${R} ${B}${lc}${lbl}${R} ${lc}${msg}${R}`;
|
|
66
|
+
return ` ${B}${lc}${lbl}${R} ${lc}${msg}${R}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const STYLES = {
|
|
70
|
+
default: _default, box: _box, modern: _modern, bracket: _bracket,
|
|
71
|
+
arrow: _arrow, pipe: _pipe, tag: _tag, dots: _dots, clean: _clean,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
module.exports = { STYLES };
|
package/src/themes.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const { Colors, rgb } = require("./colors");
|
|
2
|
+
|
|
3
|
+
class Theme {
|
|
4
|
+
constructor(name, colors) {
|
|
5
|
+
this.name = String(name);
|
|
6
|
+
this.colors = (colors && typeof colors === "object") ? colors : {};
|
|
7
|
+
}
|
|
8
|
+
get(key) {
|
|
9
|
+
try { return this.colors[key] !== undefined ? this.colors[key] : Colors.RESET; }
|
|
10
|
+
catch { return Colors.RESET; }
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const THEMES = {
|
|
15
|
+
default: new Theme("default", {
|
|
16
|
+
timestamp: rgb(100, 100, 100), bracket: rgb(70, 70, 70), dot: rgb(255, 255, 255),
|
|
17
|
+
separator: rgb(100, 100, 100), debug: rgb(138, 180, 248), info: rgb(130, 170, 255),
|
|
18
|
+
success: rgb(80, 230, 120), warning: rgb(255, 200, 60), error: rgb(255, 85, 85),
|
|
19
|
+
critical: rgb(255, 50, 50), fatal: rgb(200, 0, 0), trace: rgb(160, 160, 160),
|
|
20
|
+
divider: rgb(60, 60, 60),
|
|
21
|
+
label_debug: "DEBUG", label_info: "INFO", label_success: "SUCCESS",
|
|
22
|
+
label_warning: "WARNING", label_error: "ERROR", label_critical: "CRITICAL",
|
|
23
|
+
label_fatal: "FATAL", label_trace: "TRACE",
|
|
24
|
+
}),
|
|
25
|
+
neon: new Theme("neon", {
|
|
26
|
+
timestamp: rgb(0, 255, 200), bracket: rgb(0, 200, 150), dot: rgb(0, 255, 255),
|
|
27
|
+
separator: rgb(0, 180, 180), debug: rgb(0, 200, 255), info: rgb(0, 255, 200),
|
|
28
|
+
success: rgb(0, 255, 100), warning: rgb(255, 255, 0), error: rgb(255, 50, 80),
|
|
29
|
+
critical: rgb(255, 0, 100), fatal: rgb(200, 0, 50), trace: rgb(100, 200, 200),
|
|
30
|
+
divider: rgb(0, 100, 100),
|
|
31
|
+
label_debug: "DEBUG", label_info: "INFO", label_success: "SUCCESS",
|
|
32
|
+
label_warning: "WARNING", label_error: "ERROR", label_critical: "CRITICAL",
|
|
33
|
+
label_fatal: "FATAL", label_trace: "TRACE",
|
|
34
|
+
}),
|
|
35
|
+
minimal: new Theme("minimal", {
|
|
36
|
+
timestamp: rgb(120, 120, 120), bracket: rgb(80, 80, 80), dot: rgb(180, 180, 180),
|
|
37
|
+
separator: rgb(100, 100, 100), debug: rgb(150, 150, 150), info: rgb(200, 200, 200),
|
|
38
|
+
success: rgb(100, 200, 100), warning: rgb(200, 180, 80), error: rgb(200, 80, 80),
|
|
39
|
+
critical: rgb(220, 50, 50), fatal: rgb(180, 0, 0), trace: rgb(120, 120, 120),
|
|
40
|
+
divider: rgb(60, 60, 60),
|
|
41
|
+
label_debug: "DBG", label_info: "INF", label_success: "OK",
|
|
42
|
+
label_warning: "WRN", label_error: "ERR", label_critical: "CRT",
|
|
43
|
+
label_fatal: "FTL", label_trace: "TRC",
|
|
44
|
+
}),
|
|
45
|
+
hacker: new Theme("hacker", {
|
|
46
|
+
timestamp: rgb(0, 180, 0), bracket: rgb(0, 120, 0), dot: rgb(0, 255, 0),
|
|
47
|
+
separator: rgb(0, 150, 0), debug: rgb(0, 200, 0), info: rgb(0, 255, 0),
|
|
48
|
+
success: rgb(50, 255, 50), warning: rgb(200, 255, 0), error: rgb(255, 100, 0),
|
|
49
|
+
critical: rgb(255, 50, 0), fatal: rgb(255, 0, 0), trace: rgb(0, 150, 0),
|
|
50
|
+
divider: rgb(0, 80, 0),
|
|
51
|
+
label_debug: "DEBUG", label_info: "INFO", label_success: "SUCCESS",
|
|
52
|
+
label_warning: "WARNING", label_error: "ERROR", label_critical: "CRITICAL",
|
|
53
|
+
label_fatal: "FATAL", label_trace: "TRACE",
|
|
54
|
+
}),
|
|
55
|
+
sunset: new Theme("sunset", {
|
|
56
|
+
timestamp: rgb(255, 150, 50), bracket: rgb(200, 100, 50), dot: rgb(255, 200, 100),
|
|
57
|
+
separator: rgb(200, 120, 60), debug: rgb(255, 180, 100), info: rgb(255, 160, 80),
|
|
58
|
+
success: rgb(255, 220, 50), warning: rgb(255, 120, 30), error: rgb(255, 60, 60),
|
|
59
|
+
critical: rgb(200, 30, 30), fatal: rgb(150, 0, 0), trace: rgb(200, 150, 100),
|
|
60
|
+
divider: rgb(150, 80, 40),
|
|
61
|
+
label_debug: "DEBUG", label_info: "INFO", label_success: "SUCCESS",
|
|
62
|
+
label_warning: "WARNING", label_error: "ERROR", label_critical: "CRITICAL",
|
|
63
|
+
label_fatal: "FATAL", label_trace: "TRACE",
|
|
64
|
+
}),
|
|
65
|
+
ocean: new Theme("ocean", {
|
|
66
|
+
timestamp: rgb(0, 150, 200), bracket: rgb(0, 100, 160), dot: rgb(0, 200, 255),
|
|
67
|
+
separator: rgb(0, 130, 180), debug: rgb(100, 180, 255), info: rgb(0, 200, 255),
|
|
68
|
+
success: rgb(0, 255, 200), warning: rgb(255, 220, 80), error: rgb(255, 80, 100),
|
|
69
|
+
critical: rgb(255, 40, 80), fatal: rgb(200, 0, 50), trace: rgb(80, 150, 200),
|
|
70
|
+
divider: rgb(0, 80, 120),
|
|
71
|
+
label_debug: "DEBUG", label_info: "INFO", label_success: "SUCCESS",
|
|
72
|
+
label_warning: "WARNING", label_error: "ERROR", label_critical: "CRITICAL",
|
|
73
|
+
label_fatal: "FATAL", label_trace: "TRACE",
|
|
74
|
+
}),
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const LEVEL_ICONS = {
|
|
78
|
+
debug: "●", info: "ℹ", success: "✓", warning: "⚠",
|
|
79
|
+
error: "✗", critical: "☠", fatal: "💀", trace: "→",
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
module.exports = { Theme, THEMES, LEVEL_ICONS };
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const { Colors } = require("./colors");
|
|
2
|
+
|
|
3
|
+
const _ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
4
|
+
|
|
5
|
+
function stripAnsi(text) {
|
|
6
|
+
try { return String(text).replace(_ANSI_RE, ""); }
|
|
7
|
+
catch { return String(text); }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function clr(text, color) { return `${color}${text}${Colors.RESET}`; }
|
|
11
|
+
function bold(text) { return `${Colors.BOLD}${text}${Colors.RESET}`; }
|
|
12
|
+
function italic(text) { return `${Colors.ITALIC}${text}${Colors.RESET}`; }
|
|
13
|
+
function uline(text) { return `${Colors.UNDERLINE}${text}${Colors.RESET}`; }
|
|
14
|
+
|
|
15
|
+
module.exports = { stripAnsi, clr, bold, italic, uline };
|
package/src/widgets.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
const { Colors } = require("./colors");
|
|
2
|
+
const { getConfig } = require("./config");
|
|
3
|
+
const { stripAnsi } = require("./utils");
|
|
4
|
+
|
|
5
|
+
const _PANEL = {
|
|
6
|
+
rounded: ["╭", "╮", "╰", "╯", "─", "│"], sharp: ["┌", "┐", "└", "┘", "─", "│"],
|
|
7
|
+
double: ["╔", "╗", "╚", "╝", "═", "║"], heavy: ["┏", "┓", "┗", "┛", "━", "┃"],
|
|
8
|
+
ascii: ["+", "+", "+", "+", "-", "|"],
|
|
9
|
+
};
|
|
10
|
+
const _TABLE = {
|
|
11
|
+
rounded: ["╭", "╮", "╰", "╯", "─", "│", "┬", "┴", "├", "┤", "┼"],
|
|
12
|
+
sharp: ["┌", "┐", "└", "┘", "─", "│", "┬", "┴", "├", "┤", "┼"],
|
|
13
|
+
double: ["╔", "╗", "╚", "╝", "═", "║", "╦", "╩", "╠", "╣", "╬"],
|
|
14
|
+
heavy: ["┏", "┓", "┗", "┛", "━", "┃", "┳", "┻", "┣", "┫", "╋"],
|
|
15
|
+
ascii: ["+", "+", "+", "+", "-", "|", "+", "+", "+", "+", "+"],
|
|
16
|
+
};
|
|
17
|
+
const _BOX = {
|
|
18
|
+
..._PANEL,
|
|
19
|
+
stars: ["*", "*", "*", "*", "*", "*"], dashes: ["+", "+", "+", "+", "~", ":"],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function _ljust(s, w) { s = String(s); return s.length >= w ? s : s + " ".repeat(w - s.length); }
|
|
23
|
+
|
|
24
|
+
function panel(content = "", title = "", width = 50, color = null, style = "rounded") {
|
|
25
|
+
try {
|
|
26
|
+
const cfg = getConfig();
|
|
27
|
+
const bc = color || cfg.theme.get("info"), R = Colors.RESET;
|
|
28
|
+
const [tl, tr, bl, br, h, v] = _PANEL[style] || _PANEL.rounded;
|
|
29
|
+
content = String(content);
|
|
30
|
+
const lines = content.split("\n");
|
|
31
|
+
width = Math.max(width, 8);
|
|
32
|
+
const innerW = width - 4;
|
|
33
|
+
if (title) {
|
|
34
|
+
title = String(title);
|
|
35
|
+
const hp = Math.max(width - stripAnsi(title).length - 5, 1);
|
|
36
|
+
process.stdout.write(`${bc}${tl}${h} ${Colors.BOLD}${title}${R}${bc} ${h.repeat(hp)}${tr}${R}\n`);
|
|
37
|
+
} else {
|
|
38
|
+
process.stdout.write(`${bc}${tl}${h.repeat(width - 2)}${tr}${R}\n`);
|
|
39
|
+
}
|
|
40
|
+
for (const l of lines) {
|
|
41
|
+
const pad = Math.max(innerW - stripAnsi(l).length, 0);
|
|
42
|
+
process.stdout.write(`${bc}${v}${R} ${l}${" ".repeat(pad)} ${bc}${v}${R}\n`);
|
|
43
|
+
}
|
|
44
|
+
process.stdout.write(`${bc}${bl}${h.repeat(width - 2)}${br}${R}\n`);
|
|
45
|
+
} catch { process.stdout.write(content + "\n"); }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function table(headers = null, rows = null, color = null, style = "rounded") {
|
|
49
|
+
try {
|
|
50
|
+
if (!headers || !headers.length) return;
|
|
51
|
+
headers = headers.map(h => String(h));
|
|
52
|
+
rows = (rows || []).map(r => r.map(c => String(c)));
|
|
53
|
+
const cfg = getConfig();
|
|
54
|
+
const bc = color || cfg.theme.get("info");
|
|
55
|
+
const hc = cfg.theme.get("success");
|
|
56
|
+
const R = Colors.RESET, B = Colors.BOLD;
|
|
57
|
+
const [tl, tr, bl, br, h, v, tm, bm, ml, mr, mm] = _TABLE[style] || _TABLE.rounded;
|
|
58
|
+
const colW = headers.map(hdr => hdr.length);
|
|
59
|
+
for (const r of rows) {
|
|
60
|
+
for (let i = 0; i < r.length; i++) {
|
|
61
|
+
if (i < colW.length) colW[i] = Math.max(colW[i], r[i].length);
|
|
62
|
+
else colW.push(r[i].length);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const sep = (l, m, r2, f) => `${bc}${l}${colW.map(w => f.repeat(w + 2)).join(`${m}`)}${r2}${R}`;
|
|
66
|
+
const row = (cells, c = "") => `${bc}${v}${R}` + cells.map((cell, i) => {
|
|
67
|
+
const w = i < colW.length ? colW[i] : cell.length;
|
|
68
|
+
return ` ${c}${_ljust(cell, w)}${R} `;
|
|
69
|
+
}).join(`${bc}${v}${R}`) + `${bc}${v}${R}`;
|
|
70
|
+
process.stdout.write(sep(tl, tm, tr, h) + "\n");
|
|
71
|
+
process.stdout.write(row(headers, B + hc) + "\n");
|
|
72
|
+
process.stdout.write(sep(ml, mm, mr, h) + "\n");
|
|
73
|
+
for (const r of rows) process.stdout.write(row(r) + "\n");
|
|
74
|
+
process.stdout.write(sep(bl, bm, br, h) + "\n");
|
|
75
|
+
} catch {
|
|
76
|
+
if (headers) {
|
|
77
|
+
process.stdout.write(headers.join(" | ") + "\n");
|
|
78
|
+
for (const r of (rows || [])) process.stdout.write(r.join(" | ") + "\n");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function box(text = "", width = null, style = "rounded", color = null, textClr = null, align = "left", padding = 1) {
|
|
84
|
+
try {
|
|
85
|
+
const cfg = getConfig();
|
|
86
|
+
const bc = color || cfg.theme.get("info");
|
|
87
|
+
const tc = textClr || Colors.RESET;
|
|
88
|
+
const R = Colors.RESET;
|
|
89
|
+
const [tl, tr, bl, br, h, v] = _BOX[style] || _BOX.rounded;
|
|
90
|
+
text = String(text);
|
|
91
|
+
padding = Math.max(0, Math.floor(padding));
|
|
92
|
+
const lines = text.split("\n");
|
|
93
|
+
const maxLine = Math.max(...lines.map(l => stripAnsi(l).length), 0);
|
|
94
|
+
let innerW = width ? (width - 4) : maxLine + (padding * 2);
|
|
95
|
+
innerW = Math.max(innerW, 1);
|
|
96
|
+
const totalW = innerW + 4;
|
|
97
|
+
process.stdout.write(`${bc}${tl}${h.repeat(totalW - 2)}${tr}${R}\n`);
|
|
98
|
+
for (const l of lines) {
|
|
99
|
+
const sLen = stripAnsi(l).length;
|
|
100
|
+
const space = Math.max(innerW - sLen, 0);
|
|
101
|
+
let padded;
|
|
102
|
+
if (align === "center") {
|
|
103
|
+
const lp = Math.floor(space / 2), rp = space - lp;
|
|
104
|
+
padded = `${" ".repeat(lp)}${tc}${l}${R}${" ".repeat(rp)}`;
|
|
105
|
+
} else if (align === "right") {
|
|
106
|
+
padded = `${" ".repeat(space)}${tc}${l}${R}`;
|
|
107
|
+
} else {
|
|
108
|
+
padded = `${tc}${l}${R}${" ".repeat(space)}`;
|
|
109
|
+
}
|
|
110
|
+
process.stdout.write(`${bc}${v}${R} ${padded} ${bc}${v}${R}\n`);
|
|
111
|
+
}
|
|
112
|
+
process.stdout.write(`${bc}${bl}${h.repeat(totalW - 2)}${br}${R}\n`);
|
|
113
|
+
} catch { process.stdout.write(text + "\n"); }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = { panel, table, box };
|