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 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 };
@@ -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 };
@@ -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 };