@reliverse/relico 1.3.3 → 1.3.5
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/dist-npm/bin/mod.d.mts +47 -0
- package/dist-npm/bin/mod.mjs +356 -0
- package/package.json +10 -5
- package/dist-npm/bin/mod.js +0 -324
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//#region src/mod.d.ts
|
|
2
|
+
type ColorLevel = 0 | 1 | 2 | 3;
|
|
3
|
+
interface Rgb {
|
|
4
|
+
r: number;
|
|
5
|
+
g: number;
|
|
6
|
+
b: number;
|
|
7
|
+
}
|
|
8
|
+
type SgrOp = {
|
|
9
|
+
kind: "style";
|
|
10
|
+
open: number[];
|
|
11
|
+
} | {
|
|
12
|
+
kind: "fg-basic";
|
|
13
|
+
idx: number;
|
|
14
|
+
bright: boolean;
|
|
15
|
+
} | {
|
|
16
|
+
kind: "bg-basic";
|
|
17
|
+
idx: number;
|
|
18
|
+
bright: boolean;
|
|
19
|
+
} | {
|
|
20
|
+
kind: "fg-256";
|
|
21
|
+
code: number;
|
|
22
|
+
} | {
|
|
23
|
+
kind: "bg-256";
|
|
24
|
+
code: number;
|
|
25
|
+
} | {
|
|
26
|
+
kind: "fg-true";
|
|
27
|
+
rgb: Rgb;
|
|
28
|
+
} | {
|
|
29
|
+
kind: "bg-true";
|
|
30
|
+
rgb: Rgb;
|
|
31
|
+
};
|
|
32
|
+
type ApplyInput = string | number;
|
|
33
|
+
type FormatCallable = ((input: ApplyInput) => string) & {
|
|
34
|
+
readonly [OP_SYMBOL]: SgrOp[];
|
|
35
|
+
};
|
|
36
|
+
type BaseColorName = "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "gray" | "orange" | "pink" | "purple" | "teal" | "lime" | "brown" | "navy" | "maroon" | "olive" | "silver";
|
|
37
|
+
type ColorName = BaseColorName | BrightColorName | BgColorName;
|
|
38
|
+
type BrightColorName = "blackBright" | "redBright" | "greenBright" | "yellowBright" | "blueBright" | "magentaBright" | "cyanBright" | "whiteBright" | "orangeBright" | "pinkBright" | "purpleBright" | "tealBright" | "limeBright" | "brownBright" | "navyBright" | "maroonBright" | "oliveBright" | "silverBright";
|
|
39
|
+
type BgColorName = `bg${Capitalize<BaseColorName>}` | `bg${Capitalize<BrightColorName>}`;
|
|
40
|
+
type ReStyleKey = "reset" | "bold" | "dim" | "italic" | "underline" | "inverse" | "hidden" | "strikethrough";
|
|
41
|
+
type Re = FormatCallable & { readonly [K in ReStyleKey]: Re } & { readonly [K in ColorName]: Re } & { readonly [K in BgColorName]: Re };
|
|
42
|
+
declare const OP_SYMBOL: unique symbol;
|
|
43
|
+
declare const setColorLevel: (level: ColorLevel) => void;
|
|
44
|
+
declare const re: Re;
|
|
45
|
+
declare const chain: (...parts: FormatCallable[]) => Re;
|
|
46
|
+
//#endregion
|
|
47
|
+
export { BaseColorName, BgColorName, BrightColorName, ColorName, Re, ReStyleKey, chain, re, setColorLevel };
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
//#region src/mod.ts
|
|
2
|
+
const ESC = "\x1B[";
|
|
3
|
+
const RESET = `${ESC}0m`;
|
|
4
|
+
const OP_SYMBOL = Symbol("re.ops");
|
|
5
|
+
const COLOR_LEVEL_OFF = 0;
|
|
6
|
+
const COLOR_LEVEL_BASIC = 1;
|
|
7
|
+
const COLOR_LEVEL_256 = 2;
|
|
8
|
+
const COLOR_LEVEL_TRUECOLOR = 3;
|
|
9
|
+
const MIN_BYTE = 0;
|
|
10
|
+
const MAX_BYTE = 255;
|
|
11
|
+
const WHITE_RGB = 255;
|
|
12
|
+
const ANSI_256_GRAYSCALE_MIN = 8;
|
|
13
|
+
const ANSI_256_GRAYSCALE_MAX = 248;
|
|
14
|
+
const ANSI_256_BASE_OFFSET = 16;
|
|
15
|
+
const ANSI_256_GRAYSCALE_BASE = 232;
|
|
16
|
+
const ANSI_256_GRAYSCALE_RANGE = 247;
|
|
17
|
+
const ANSI_256_GRAYSCALE_STEPS = 24;
|
|
18
|
+
const ANSI_256_BRIGHT_THRESHOLD = 231;
|
|
19
|
+
const ANSI_256_RGB_LEVELS = 5;
|
|
20
|
+
const ANSI_256_RGB_RED_MULTIPLIER = 36;
|
|
21
|
+
const ANSI_256_RGB_GREEN_MULTIPLIER = 6;
|
|
22
|
+
const SGR_FG_BASE = 30;
|
|
23
|
+
const SGR_BG_BASE = 40;
|
|
24
|
+
const SGR_FG_BRIGHT_BASE = 90;
|
|
25
|
+
const SGR_BG_BRIGHT_BASE = 100;
|
|
26
|
+
const SGR_RESET = 0;
|
|
27
|
+
const SGR_BOLD = 1;
|
|
28
|
+
const SGR_DIM = 2;
|
|
29
|
+
const SGR_ITALIC = 3;
|
|
30
|
+
const SGR_UNDERLINE = 4;
|
|
31
|
+
const SGR_INVERSE = 7;
|
|
32
|
+
const SGR_HIDDEN = 8;
|
|
33
|
+
const SGR_STRIKETHROUGH = 9;
|
|
34
|
+
const HEX_BYTE_LENGTH = 2;
|
|
35
|
+
const HEX_RED_START = 0;
|
|
36
|
+
const HEX_GREEN_START = 2;
|
|
37
|
+
const HEX_BLUE_START = 4;
|
|
38
|
+
const HEX_BLUE_END = 6;
|
|
39
|
+
const HEX_RADIX = 16;
|
|
40
|
+
const BRIGHT_SUFFIX_LENGTH = 6;
|
|
41
|
+
const BG_PREFIX_LENGTH = 2;
|
|
42
|
+
const BG_COLOR_START = 3;
|
|
43
|
+
const BRIGHT_MIX_FACTOR = .25;
|
|
44
|
+
const BRIGHT_SUFFIX_REGEX = /Bright$/u;
|
|
45
|
+
let CURRENT_LEVEL = COLOR_LEVEL_TRUECOLOR;
|
|
46
|
+
const setColorLevel = (level) => {
|
|
47
|
+
if (level !== COLOR_LEVEL_OFF && level !== COLOR_LEVEL_BASIC && level !== COLOR_LEVEL_256 && level !== COLOR_LEVEL_TRUECOLOR) throw new Error("Invalid color level");
|
|
48
|
+
CURRENT_LEVEL = level;
|
|
49
|
+
};
|
|
50
|
+
const clampByte = (n) => {
|
|
51
|
+
if (!Number.isFinite(n)) return MIN_BYTE;
|
|
52
|
+
if (n < MIN_BYTE) return MIN_BYTE;
|
|
53
|
+
if (n > MAX_BYTE) return MAX_BYTE;
|
|
54
|
+
return Math.round(n);
|
|
55
|
+
};
|
|
56
|
+
const BASIC8 = [
|
|
57
|
+
{
|
|
58
|
+
r: 0,
|
|
59
|
+
g: 0,
|
|
60
|
+
b: 0
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
r: 205,
|
|
64
|
+
g: 0,
|
|
65
|
+
b: 0
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
r: 0,
|
|
69
|
+
g: 205,
|
|
70
|
+
b: 0
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
r: 205,
|
|
74
|
+
g: 205,
|
|
75
|
+
b: 0
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
r: 0,
|
|
79
|
+
g: 0,
|
|
80
|
+
b: 238
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
r: 205,
|
|
84
|
+
g: 0,
|
|
85
|
+
b: 205
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
r: 0,
|
|
89
|
+
g: 205,
|
|
90
|
+
b: 205
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
r: 229,
|
|
94
|
+
g: 229,
|
|
95
|
+
b: 229
|
|
96
|
+
}
|
|
97
|
+
];
|
|
98
|
+
const sgr = (codes) => `${ESC}${codes.join(";")}m`;
|
|
99
|
+
const nearestBasicIndex = (rgb) => {
|
|
100
|
+
let best = 0;
|
|
101
|
+
let bestDist = Number.POSITIVE_INFINITY;
|
|
102
|
+
for (let i = 0; i < BASIC8.length; i++) {
|
|
103
|
+
const c = BASIC8[i];
|
|
104
|
+
const dr = c.r - rgb.r;
|
|
105
|
+
const dg = c.g - rgb.g;
|
|
106
|
+
const db = c.b - rgb.b;
|
|
107
|
+
const d = dr * dr + dg * dg + db * db;
|
|
108
|
+
if (d < bestDist) {
|
|
109
|
+
bestDist = d;
|
|
110
|
+
best = i;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return best;
|
|
114
|
+
};
|
|
115
|
+
const rgbToAnsi256 = (rgb) => {
|
|
116
|
+
if (rgb.r === rgb.g && rgb.g === rgb.b) {
|
|
117
|
+
if (rgb.r < ANSI_256_GRAYSCALE_MIN) return ANSI_256_BASE_OFFSET;
|
|
118
|
+
if (rgb.r > ANSI_256_GRAYSCALE_MAX) return ANSI_256_BRIGHT_THRESHOLD;
|
|
119
|
+
const step = Math.round((rgb.r - ANSI_256_GRAYSCALE_MIN) / ANSI_256_GRAYSCALE_RANGE * ANSI_256_GRAYSCALE_STEPS);
|
|
120
|
+
return ANSI_256_GRAYSCALE_BASE + step;
|
|
121
|
+
}
|
|
122
|
+
const r = Math.round(rgb.r / MAX_BYTE * ANSI_256_RGB_LEVELS);
|
|
123
|
+
const g = Math.round(rgb.g / MAX_BYTE * ANSI_256_RGB_LEVELS);
|
|
124
|
+
const b = Math.round(rgb.b / MAX_BYTE * ANSI_256_RGB_LEVELS);
|
|
125
|
+
return ANSI_256_BASE_OFFSET + ANSI_256_RGB_RED_MULTIPLIER * r + ANSI_256_RGB_GREEN_MULTIPLIER * g + b;
|
|
126
|
+
};
|
|
127
|
+
const NAMED_COLORS = {
|
|
128
|
+
black: "#000000",
|
|
129
|
+
red: "#ff0000",
|
|
130
|
+
green: "#00ff00",
|
|
131
|
+
yellow: "#ffff00",
|
|
132
|
+
blue: "#0000ff",
|
|
133
|
+
magenta: "#ff00ff",
|
|
134
|
+
cyan: "#00ffff",
|
|
135
|
+
white: "#ffffff",
|
|
136
|
+
gray: "#808080",
|
|
137
|
+
orange: "#ffa500",
|
|
138
|
+
pink: "#ffc0cb",
|
|
139
|
+
purple: "#800080",
|
|
140
|
+
teal: "#008080",
|
|
141
|
+
lime: "#00ff00",
|
|
142
|
+
brown: "#a52a2a",
|
|
143
|
+
navy: "#000080",
|
|
144
|
+
maroon: "#800000",
|
|
145
|
+
olive: "#808000",
|
|
146
|
+
silver: "#c0c0c0"
|
|
147
|
+
};
|
|
148
|
+
const mixWithWhite = (rgb, factor) => {
|
|
149
|
+
const t = factor;
|
|
150
|
+
return {
|
|
151
|
+
r: clampByte(rgb.r * (1 - t) + WHITE_RGB * t),
|
|
152
|
+
g: clampByte(rgb.g * (1 - t) + WHITE_RGB * t),
|
|
153
|
+
b: clampByte(rgb.b * (1 - t) + WHITE_RGB * t)
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
const fromNamed = (name) => {
|
|
157
|
+
const hex = NAMED_COLORS[name];
|
|
158
|
+
const clean = hex.startsWith("#") ? hex.slice(1) : hex;
|
|
159
|
+
const r = Number.parseInt(clean.slice(HEX_RED_START, HEX_BYTE_LENGTH), HEX_RADIX);
|
|
160
|
+
const g = Number.parseInt(clean.slice(HEX_GREEN_START, HEX_BLUE_START), HEX_RADIX);
|
|
161
|
+
const b = Number.parseInt(clean.slice(HEX_BLUE_START, HEX_BLUE_END), HEX_RADIX);
|
|
162
|
+
return {
|
|
163
|
+
r,
|
|
164
|
+
g,
|
|
165
|
+
b
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
const toBaseName = (compound) => {
|
|
169
|
+
const base = compound.replace(BRIGHT_SUFFIX_REGEX, "");
|
|
170
|
+
const key = base.charAt(0).toLowerCase() + base.slice(1);
|
|
171
|
+
return key;
|
|
172
|
+
};
|
|
173
|
+
const parseColorName = (name) => {
|
|
174
|
+
if (name.endsWith("Bright")) {
|
|
175
|
+
const base = toBaseName(name);
|
|
176
|
+
const rgb = fromNamed(base);
|
|
177
|
+
const rgbAdj = mixWithWhite(rgb, BRIGHT_MIX_FACTOR);
|
|
178
|
+
return {
|
|
179
|
+
rgb: rgbAdj,
|
|
180
|
+
wantBright: true
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
rgb: fromNamed(name),
|
|
185
|
+
wantBright: false
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
const openForOp = (op) => {
|
|
189
|
+
if (CURRENT_LEVEL === COLOR_LEVEL_OFF) return "";
|
|
190
|
+
switch (op.kind) {
|
|
191
|
+
case "style": return sgr(op.open);
|
|
192
|
+
case "fg-basic": return sgr([(op.bright ? SGR_FG_BRIGHT_BASE : SGR_FG_BASE) + op.idx]);
|
|
193
|
+
case "bg-basic": return sgr([(op.bright ? SGR_BG_BRIGHT_BASE : SGR_BG_BASE) + op.idx]);
|
|
194
|
+
case "fg-256": return `${ESC}38;5;${op.code}m`;
|
|
195
|
+
case "bg-256": return `${ESC}48;5;${op.code}m`;
|
|
196
|
+
case "fg-true": return `${ESC}38;2;${op.rgb.r};${op.rgb.g};${op.rgb.b}m`;
|
|
197
|
+
case "bg-true": return `${ESC}48;2;${op.rgb.r};${op.rgb.g};${op.rgb.b}m`;
|
|
198
|
+
default: return "";
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
const opsToOpen = (ops) => {
|
|
202
|
+
if (CURRENT_LEVEL === COLOR_LEVEL_OFF) return "";
|
|
203
|
+
let out = "";
|
|
204
|
+
for (const op of ops) out += openForOp(op);
|
|
205
|
+
return out;
|
|
206
|
+
};
|
|
207
|
+
const applyOpsToText = (ops, input) => {
|
|
208
|
+
const text = String(input);
|
|
209
|
+
if (CURRENT_LEVEL === COLOR_LEVEL_OFF || ops.length === 0 || text.length === 0) return text;
|
|
210
|
+
const open = opsToOpen(ops);
|
|
211
|
+
if (!text.includes("\n")) return `${open}${text}${RESET}`;
|
|
212
|
+
const lines = text.split("\n");
|
|
213
|
+
const result = new Array(lines.length);
|
|
214
|
+
for (let i = 0; i < lines.length; i++) {
|
|
215
|
+
const line = lines[i];
|
|
216
|
+
if (line.endsWith("\r")) result[i] = `${open}${line.slice(0, -1)}\r${RESET}`;
|
|
217
|
+
else result[i] = `${open}${line}${RESET}`;
|
|
218
|
+
}
|
|
219
|
+
return result.join("\n");
|
|
220
|
+
};
|
|
221
|
+
const mkFgOpsFromRgb = (rgb, wantBright = false) => {
|
|
222
|
+
if (CURRENT_LEVEL === COLOR_LEVEL_BASIC) {
|
|
223
|
+
const idx = nearestBasicIndex(rgb);
|
|
224
|
+
return [{
|
|
225
|
+
kind: "fg-basic",
|
|
226
|
+
idx,
|
|
227
|
+
bright: wantBright
|
|
228
|
+
}];
|
|
229
|
+
}
|
|
230
|
+
if (CURRENT_LEVEL === COLOR_LEVEL_256) return [{
|
|
231
|
+
kind: "fg-256",
|
|
232
|
+
code: rgbToAnsi256(rgb)
|
|
233
|
+
}];
|
|
234
|
+
return [{
|
|
235
|
+
kind: "fg-true",
|
|
236
|
+
rgb
|
|
237
|
+
}];
|
|
238
|
+
};
|
|
239
|
+
const mkBgOpsFromRgb = (rgb, wantBright = false) => {
|
|
240
|
+
if (CURRENT_LEVEL === COLOR_LEVEL_BASIC) {
|
|
241
|
+
const idx = nearestBasicIndex(rgb);
|
|
242
|
+
return [{
|
|
243
|
+
kind: "bg-basic",
|
|
244
|
+
idx,
|
|
245
|
+
bright: wantBright
|
|
246
|
+
}];
|
|
247
|
+
}
|
|
248
|
+
if (CURRENT_LEVEL === COLOR_LEVEL_256) return [{
|
|
249
|
+
kind: "bg-256",
|
|
250
|
+
code: rgbToAnsi256(rgb)
|
|
251
|
+
}];
|
|
252
|
+
return [{
|
|
253
|
+
kind: "bg-true",
|
|
254
|
+
rgb
|
|
255
|
+
}];
|
|
256
|
+
};
|
|
257
|
+
const STYLE_TABLE = {
|
|
258
|
+
reset: {
|
|
259
|
+
kind: "style",
|
|
260
|
+
open: [SGR_RESET]
|
|
261
|
+
},
|
|
262
|
+
bold: {
|
|
263
|
+
kind: "style",
|
|
264
|
+
open: [SGR_BOLD]
|
|
265
|
+
},
|
|
266
|
+
dim: {
|
|
267
|
+
kind: "style",
|
|
268
|
+
open: [SGR_DIM]
|
|
269
|
+
},
|
|
270
|
+
italic: {
|
|
271
|
+
kind: "style",
|
|
272
|
+
open: [SGR_ITALIC]
|
|
273
|
+
},
|
|
274
|
+
underline: {
|
|
275
|
+
kind: "style",
|
|
276
|
+
open: [SGR_UNDERLINE]
|
|
277
|
+
},
|
|
278
|
+
inverse: {
|
|
279
|
+
kind: "style",
|
|
280
|
+
open: [SGR_INVERSE]
|
|
281
|
+
},
|
|
282
|
+
hidden: {
|
|
283
|
+
kind: "style",
|
|
284
|
+
open: [SGR_HIDDEN]
|
|
285
|
+
},
|
|
286
|
+
strikethrough: {
|
|
287
|
+
kind: "style",
|
|
288
|
+
open: [SGR_STRIKETHROUGH]
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
const STYLE_KEYS = new Set([
|
|
292
|
+
"reset",
|
|
293
|
+
"bold",
|
|
294
|
+
"dim",
|
|
295
|
+
"italic",
|
|
296
|
+
"underline",
|
|
297
|
+
"inverse",
|
|
298
|
+
"hidden",
|
|
299
|
+
"strikethrough"
|
|
300
|
+
]);
|
|
301
|
+
const isColorKey = (key) => {
|
|
302
|
+
if (key in NAMED_COLORS) return true;
|
|
303
|
+
if (key.endsWith("Bright") && isColorKey(key.slice(0, -BRIGHT_SUFFIX_LENGTH))) return true;
|
|
304
|
+
return false;
|
|
305
|
+
};
|
|
306
|
+
const isBgKey = (key) => {
|
|
307
|
+
if (!key.startsWith("bg") || key.length <= BG_PREFIX_LENGTH) return false;
|
|
308
|
+
const colorPart = key.charAt(BG_PREFIX_LENGTH).toLowerCase() + key.slice(BG_COLOR_START);
|
|
309
|
+
return isColorKey(colorPart);
|
|
310
|
+
};
|
|
311
|
+
const callableProxy = (ops) => {
|
|
312
|
+
const base = (input) => applyOpsToText(ops, input);
|
|
313
|
+
Object.defineProperty(base, OP_SYMBOL, {
|
|
314
|
+
value: ops,
|
|
315
|
+
enumerable: false,
|
|
316
|
+
configurable: false,
|
|
317
|
+
writable: false
|
|
318
|
+
});
|
|
319
|
+
return new Proxy(base, {
|
|
320
|
+
apply(_target, _thisArg, argArray) {
|
|
321
|
+
const [input] = argArray;
|
|
322
|
+
return applyOpsToText(ops, input);
|
|
323
|
+
},
|
|
324
|
+
get(_target, prop) {
|
|
325
|
+
const key = String(prop);
|
|
326
|
+
if (prop === OP_SYMBOL) return ops;
|
|
327
|
+
if (STYLE_KEYS.has(key)) {
|
|
328
|
+
const op = STYLE_TABLE[key];
|
|
329
|
+
return callableProxy([...ops, op]);
|
|
330
|
+
}
|
|
331
|
+
if (isBgKey(key)) {
|
|
332
|
+
const raw = key.slice(BG_PREFIX_LENGTH);
|
|
333
|
+
const colorName = raw.charAt(0).toLowerCase() + raw.slice(1);
|
|
334
|
+
const { rgb, wantBright } = parseColorName(colorName);
|
|
335
|
+
return callableProxy([...ops, ...mkBgOpsFromRgb(rgb, wantBright)]);
|
|
336
|
+
}
|
|
337
|
+
if (isColorKey(key)) {
|
|
338
|
+
const { rgb, wantBright } = parseColorName(key);
|
|
339
|
+
return callableProxy([...ops, ...mkFgOpsFromRgb(rgb, wantBright)]);
|
|
340
|
+
}
|
|
341
|
+
return callableProxy(ops);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
};
|
|
345
|
+
const re = callableProxy([]);
|
|
346
|
+
const chain = (...parts) => {
|
|
347
|
+
const collected = [];
|
|
348
|
+
for (const p of parts) {
|
|
349
|
+
const ops = p[OP_SYMBOL];
|
|
350
|
+
if (ops && ops.length > 0) for (const op of ops) collected.push(op);
|
|
351
|
+
}
|
|
352
|
+
return callableProxy(collected);
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
//#endregion
|
|
356
|
+
export { chain, re, setColorLevel };
|
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reliverse/relico",
|
|
3
3
|
"author": "reliverse",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "@reliverse/relico is a themeable, chainable, typed, truecolor-powered, modern terminal styling toolkit. Designed to make your CLI output colorful, accessible, and human-friendly. It gives you a flexible way to apply colors and styles — with reliability and a smart developer experience baked in. Built for humans, not just terminals.",
|
|
7
7
|
"exports": {
|
|
8
|
-
".":
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist-npm/bin/mod.d.mts",
|
|
10
|
+
"import": "./dist-npm/bin/mod.mjs",
|
|
11
|
+
"default": "./dist-npm/bin/mod.mjs"
|
|
12
|
+
}
|
|
9
13
|
},
|
|
10
14
|
"publishConfig": {
|
|
11
15
|
"access": "public"
|
|
@@ -18,7 +22,7 @@
|
|
|
18
22
|
],
|
|
19
23
|
"scripts": {
|
|
20
24
|
"pub": "bun run build && bun publish",
|
|
21
|
-
"build": "bun
|
|
25
|
+
"build": "bun obuild",
|
|
22
26
|
"dev": "bun examples/core.ts",
|
|
23
27
|
"latest": "bun update --latest && bun check",
|
|
24
28
|
"check": "tsc --noEmit && biome check --fix --unsafe",
|
|
@@ -28,12 +32,13 @@
|
|
|
28
32
|
},
|
|
29
33
|
"devDependencies": {
|
|
30
34
|
"@biomejs/biome": "2.2.0",
|
|
31
|
-
"@reliverse/dler": "^1.7.
|
|
35
|
+
"@reliverse/dler": "^1.7.94",
|
|
32
36
|
"@reliverse/rse-cfg": "^1.7.12",
|
|
33
37
|
"@total-typescript/ts-reset": "^0.6.1",
|
|
34
38
|
"@types/bun": "^1.2.20",
|
|
35
39
|
"@types/node": "^24.3.0",
|
|
40
|
+
"obuild": "^0.2.1",
|
|
36
41
|
"typescript": "^5.9.2",
|
|
37
|
-
"ultracite": "^5.
|
|
42
|
+
"ultracite": "^5.2.5"
|
|
38
43
|
}
|
|
39
44
|
}
|
package/dist-npm/bin/mod.js
DELETED
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
// src/mod.ts
|
|
2
|
-
var ESC = "\x1B[";
|
|
3
|
-
var RESET = `${ESC}0m`;
|
|
4
|
-
var OP_SYMBOL = Symbol("re.ops");
|
|
5
|
-
var COLOR_LEVEL_OFF = 0;
|
|
6
|
-
var COLOR_LEVEL_BASIC = 1;
|
|
7
|
-
var COLOR_LEVEL_256 = 2;
|
|
8
|
-
var COLOR_LEVEL_TRUECOLOR = 3;
|
|
9
|
-
var MIN_BYTE = 0;
|
|
10
|
-
var MAX_BYTE = 255;
|
|
11
|
-
var WHITE_RGB = 255;
|
|
12
|
-
var ANSI_256_GRAYSCALE_MIN = 8;
|
|
13
|
-
var ANSI_256_GRAYSCALE_MAX = 248;
|
|
14
|
-
var ANSI_256_BASE_OFFSET = 16;
|
|
15
|
-
var ANSI_256_GRAYSCALE_BASE = 232;
|
|
16
|
-
var ANSI_256_GRAYSCALE_RANGE = 247;
|
|
17
|
-
var ANSI_256_GRAYSCALE_STEPS = 24;
|
|
18
|
-
var ANSI_256_BRIGHT_THRESHOLD = 231;
|
|
19
|
-
var ANSI_256_RGB_LEVELS = 5;
|
|
20
|
-
var ANSI_256_RGB_RED_MULTIPLIER = 36;
|
|
21
|
-
var ANSI_256_RGB_GREEN_MULTIPLIER = 6;
|
|
22
|
-
var SGR_FG_BASE = 30;
|
|
23
|
-
var SGR_BG_BASE = 40;
|
|
24
|
-
var SGR_FG_BRIGHT_BASE = 90;
|
|
25
|
-
var SGR_BG_BRIGHT_BASE = 100;
|
|
26
|
-
var SGR_RESET = 0;
|
|
27
|
-
var SGR_BOLD = 1;
|
|
28
|
-
var SGR_DIM = 2;
|
|
29
|
-
var SGR_ITALIC = 3;
|
|
30
|
-
var SGR_UNDERLINE = 4;
|
|
31
|
-
var SGR_INVERSE = 7;
|
|
32
|
-
var SGR_HIDDEN = 8;
|
|
33
|
-
var SGR_STRIKETHROUGH = 9;
|
|
34
|
-
var HEX_BYTE_LENGTH = 2;
|
|
35
|
-
var HEX_RED_START = 0;
|
|
36
|
-
var HEX_GREEN_START = 2;
|
|
37
|
-
var HEX_BLUE_START = 4;
|
|
38
|
-
var HEX_BLUE_END = 6;
|
|
39
|
-
var HEX_RADIX = 16;
|
|
40
|
-
var BRIGHT_SUFFIX_LENGTH = 6;
|
|
41
|
-
var BG_PREFIX_LENGTH = 2;
|
|
42
|
-
var BG_COLOR_START = 3;
|
|
43
|
-
var BRIGHT_MIX_FACTOR = 0.25;
|
|
44
|
-
var BRIGHT_SUFFIX_REGEX = /Bright$/u;
|
|
45
|
-
var CURRENT_LEVEL = COLOR_LEVEL_TRUECOLOR;
|
|
46
|
-
var setColorLevel = (level) => {
|
|
47
|
-
if (level !== COLOR_LEVEL_OFF && level !== COLOR_LEVEL_BASIC && level !== COLOR_LEVEL_256 && level !== COLOR_LEVEL_TRUECOLOR) {
|
|
48
|
-
throw new Error("Invalid color level");
|
|
49
|
-
}
|
|
50
|
-
CURRENT_LEVEL = level;
|
|
51
|
-
};
|
|
52
|
-
var clampByte = (n) => {
|
|
53
|
-
if (!Number.isFinite(n)) {
|
|
54
|
-
return MIN_BYTE;
|
|
55
|
-
}
|
|
56
|
-
if (n < MIN_BYTE) {
|
|
57
|
-
return MIN_BYTE;
|
|
58
|
-
}
|
|
59
|
-
if (n > MAX_BYTE) {
|
|
60
|
-
return MAX_BYTE;
|
|
61
|
-
}
|
|
62
|
-
return Math.round(n);
|
|
63
|
-
};
|
|
64
|
-
var BASIC8 = [
|
|
65
|
-
{ r: 0, g: 0, b: 0 },
|
|
66
|
-
{ r: 205, g: 0, b: 0 },
|
|
67
|
-
{ r: 0, g: 205, b: 0 },
|
|
68
|
-
{ r: 205, g: 205, b: 0 },
|
|
69
|
-
{ r: 0, g: 0, b: 238 },
|
|
70
|
-
{ r: 205, g: 0, b: 205 },
|
|
71
|
-
{ r: 0, g: 205, b: 205 },
|
|
72
|
-
{ r: 229, g: 229, b: 229 }
|
|
73
|
-
];
|
|
74
|
-
var sgr = (codes) => `${ESC}${codes.join(";")}m`;
|
|
75
|
-
var nearestBasicIndex = (rgb) => {
|
|
76
|
-
let best = 0;
|
|
77
|
-
let bestDist = Number.POSITIVE_INFINITY;
|
|
78
|
-
for (let i = 0;i < BASIC8.length; i++) {
|
|
79
|
-
const c = BASIC8[i];
|
|
80
|
-
const dr = c.r - rgb.r;
|
|
81
|
-
const dg = c.g - rgb.g;
|
|
82
|
-
const db = c.b - rgb.b;
|
|
83
|
-
const d = dr * dr + dg * dg + db * db;
|
|
84
|
-
if (d < bestDist) {
|
|
85
|
-
bestDist = d;
|
|
86
|
-
best = i;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return best;
|
|
90
|
-
};
|
|
91
|
-
var rgbToAnsi256 = (rgb) => {
|
|
92
|
-
if (rgb.r === rgb.g && rgb.g === rgb.b) {
|
|
93
|
-
if (rgb.r < ANSI_256_GRAYSCALE_MIN) {
|
|
94
|
-
return ANSI_256_BASE_OFFSET;
|
|
95
|
-
}
|
|
96
|
-
if (rgb.r > ANSI_256_GRAYSCALE_MAX) {
|
|
97
|
-
return ANSI_256_BRIGHT_THRESHOLD;
|
|
98
|
-
}
|
|
99
|
-
const step = Math.round((rgb.r - ANSI_256_GRAYSCALE_MIN) / ANSI_256_GRAYSCALE_RANGE * ANSI_256_GRAYSCALE_STEPS);
|
|
100
|
-
return ANSI_256_GRAYSCALE_BASE + step;
|
|
101
|
-
}
|
|
102
|
-
const r = Math.round(rgb.r / MAX_BYTE * ANSI_256_RGB_LEVELS);
|
|
103
|
-
const g = Math.round(rgb.g / MAX_BYTE * ANSI_256_RGB_LEVELS);
|
|
104
|
-
const b = Math.round(rgb.b / MAX_BYTE * ANSI_256_RGB_LEVELS);
|
|
105
|
-
return ANSI_256_BASE_OFFSET + ANSI_256_RGB_RED_MULTIPLIER * r + ANSI_256_RGB_GREEN_MULTIPLIER * g + b;
|
|
106
|
-
};
|
|
107
|
-
var NAMED_COLORS = {
|
|
108
|
-
black: "#000000",
|
|
109
|
-
red: "#ff0000",
|
|
110
|
-
green: "#00ff00",
|
|
111
|
-
yellow: "#ffff00",
|
|
112
|
-
blue: "#0000ff",
|
|
113
|
-
magenta: "#ff00ff",
|
|
114
|
-
cyan: "#00ffff",
|
|
115
|
-
white: "#ffffff",
|
|
116
|
-
gray: "#808080",
|
|
117
|
-
orange: "#ffa500",
|
|
118
|
-
pink: "#ffc0cb",
|
|
119
|
-
purple: "#800080",
|
|
120
|
-
teal: "#008080",
|
|
121
|
-
lime: "#00ff00",
|
|
122
|
-
brown: "#a52a2a",
|
|
123
|
-
navy: "#000080",
|
|
124
|
-
maroon: "#800000",
|
|
125
|
-
olive: "#808000",
|
|
126
|
-
silver: "#c0c0c0"
|
|
127
|
-
};
|
|
128
|
-
var mixWithWhite = (rgb, factor) => {
|
|
129
|
-
const t = factor;
|
|
130
|
-
return {
|
|
131
|
-
r: clampByte(rgb.r * (1 - t) + WHITE_RGB * t),
|
|
132
|
-
g: clampByte(rgb.g * (1 - t) + WHITE_RGB * t),
|
|
133
|
-
b: clampByte(rgb.b * (1 - t) + WHITE_RGB * t)
|
|
134
|
-
};
|
|
135
|
-
};
|
|
136
|
-
var fromNamed = (name) => {
|
|
137
|
-
const hex = NAMED_COLORS[name];
|
|
138
|
-
const clean = hex.startsWith("#") ? hex.slice(1) : hex;
|
|
139
|
-
const r = Number.parseInt(clean.slice(HEX_RED_START, HEX_BYTE_LENGTH), HEX_RADIX);
|
|
140
|
-
const g = Number.parseInt(clean.slice(HEX_GREEN_START, HEX_BLUE_START), HEX_RADIX);
|
|
141
|
-
const b = Number.parseInt(clean.slice(HEX_BLUE_START, HEX_BLUE_END), HEX_RADIX);
|
|
142
|
-
return { r, g, b };
|
|
143
|
-
};
|
|
144
|
-
var toBaseName = (compound) => {
|
|
145
|
-
const base = compound.replace(BRIGHT_SUFFIX_REGEX, "");
|
|
146
|
-
const key = base.charAt(0).toLowerCase() + base.slice(1);
|
|
147
|
-
return key;
|
|
148
|
-
};
|
|
149
|
-
var parseColorName = (name) => {
|
|
150
|
-
if (name.endsWith("Bright")) {
|
|
151
|
-
const base = toBaseName(name);
|
|
152
|
-
const rgb = fromNamed(base);
|
|
153
|
-
const rgbAdj = mixWithWhite(rgb, BRIGHT_MIX_FACTOR);
|
|
154
|
-
return { rgb: rgbAdj, wantBright: true };
|
|
155
|
-
}
|
|
156
|
-
return { rgb: fromNamed(name), wantBright: false };
|
|
157
|
-
};
|
|
158
|
-
var openForOp = (op) => {
|
|
159
|
-
if (CURRENT_LEVEL === COLOR_LEVEL_OFF) {
|
|
160
|
-
return "";
|
|
161
|
-
}
|
|
162
|
-
switch (op.kind) {
|
|
163
|
-
case "style":
|
|
164
|
-
return sgr(op.open);
|
|
165
|
-
case "fg-basic":
|
|
166
|
-
return sgr([(op.bright ? SGR_FG_BRIGHT_BASE : SGR_FG_BASE) + op.idx]);
|
|
167
|
-
case "bg-basic":
|
|
168
|
-
return sgr([(op.bright ? SGR_BG_BRIGHT_BASE : SGR_BG_BASE) + op.idx]);
|
|
169
|
-
case "fg-256":
|
|
170
|
-
return `${ESC}38;5;${op.code}m`;
|
|
171
|
-
case "bg-256":
|
|
172
|
-
return `${ESC}48;5;${op.code}m`;
|
|
173
|
-
case "fg-true":
|
|
174
|
-
return `${ESC}38;2;${op.rgb.r};${op.rgb.g};${op.rgb.b}m`;
|
|
175
|
-
case "bg-true":
|
|
176
|
-
return `${ESC}48;2;${op.rgb.r};${op.rgb.g};${op.rgb.b}m`;
|
|
177
|
-
default:
|
|
178
|
-
return "";
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
var opsToOpen = (ops) => {
|
|
182
|
-
if (CURRENT_LEVEL === COLOR_LEVEL_OFF) {
|
|
183
|
-
return "";
|
|
184
|
-
}
|
|
185
|
-
let out = "";
|
|
186
|
-
for (const op of ops) {
|
|
187
|
-
out += openForOp(op);
|
|
188
|
-
}
|
|
189
|
-
return out;
|
|
190
|
-
};
|
|
191
|
-
var applyOpsToText = (ops, input) => {
|
|
192
|
-
const text = String(input);
|
|
193
|
-
if (CURRENT_LEVEL === COLOR_LEVEL_OFF || ops.length === 0 || text.length === 0) {
|
|
194
|
-
return text;
|
|
195
|
-
}
|
|
196
|
-
const open = opsToOpen(ops);
|
|
197
|
-
if (!text.includes(`
|
|
198
|
-
`)) {
|
|
199
|
-
return `${open}${text}${RESET}`;
|
|
200
|
-
}
|
|
201
|
-
const lines = text.split(`
|
|
202
|
-
`);
|
|
203
|
-
const result = new Array(lines.length);
|
|
204
|
-
for (let i = 0;i < lines.length; i++) {
|
|
205
|
-
const line = lines[i];
|
|
206
|
-
if (line.endsWith("\r")) {
|
|
207
|
-
result[i] = `${open}${line.slice(0, -1)}\r${RESET}`;
|
|
208
|
-
} else {
|
|
209
|
-
result[i] = `${open}${line}${RESET}`;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
return result.join(`
|
|
213
|
-
`);
|
|
214
|
-
};
|
|
215
|
-
var mkFgOpsFromRgb = (rgb, wantBright = false) => {
|
|
216
|
-
if (CURRENT_LEVEL === COLOR_LEVEL_BASIC) {
|
|
217
|
-
const idx = nearestBasicIndex(rgb);
|
|
218
|
-
return [{ kind: "fg-basic", idx, bright: wantBright }];
|
|
219
|
-
}
|
|
220
|
-
if (CURRENT_LEVEL === COLOR_LEVEL_256) {
|
|
221
|
-
return [{ kind: "fg-256", code: rgbToAnsi256(rgb) }];
|
|
222
|
-
}
|
|
223
|
-
return [{ kind: "fg-true", rgb }];
|
|
224
|
-
};
|
|
225
|
-
var mkBgOpsFromRgb = (rgb, wantBright = false) => {
|
|
226
|
-
if (CURRENT_LEVEL === COLOR_LEVEL_BASIC) {
|
|
227
|
-
const idx = nearestBasicIndex(rgb);
|
|
228
|
-
return [{ kind: "bg-basic", idx, bright: wantBright }];
|
|
229
|
-
}
|
|
230
|
-
if (CURRENT_LEVEL === COLOR_LEVEL_256) {
|
|
231
|
-
return [{ kind: "bg-256", code: rgbToAnsi256(rgb) }];
|
|
232
|
-
}
|
|
233
|
-
return [{ kind: "bg-true", rgb }];
|
|
234
|
-
};
|
|
235
|
-
var STYLE_TABLE = {
|
|
236
|
-
reset: { kind: "style", open: [SGR_RESET] },
|
|
237
|
-
bold: { kind: "style", open: [SGR_BOLD] },
|
|
238
|
-
dim: { kind: "style", open: [SGR_DIM] },
|
|
239
|
-
italic: { kind: "style", open: [SGR_ITALIC] },
|
|
240
|
-
underline: { kind: "style", open: [SGR_UNDERLINE] },
|
|
241
|
-
inverse: { kind: "style", open: [SGR_INVERSE] },
|
|
242
|
-
hidden: { kind: "style", open: [SGR_HIDDEN] },
|
|
243
|
-
strikethrough: { kind: "style", open: [SGR_STRIKETHROUGH] }
|
|
244
|
-
};
|
|
245
|
-
var STYLE_KEYS = new Set([
|
|
246
|
-
"reset",
|
|
247
|
-
"bold",
|
|
248
|
-
"dim",
|
|
249
|
-
"italic",
|
|
250
|
-
"underline",
|
|
251
|
-
"inverse",
|
|
252
|
-
"hidden",
|
|
253
|
-
"strikethrough"
|
|
254
|
-
]);
|
|
255
|
-
var isColorKey = (key) => {
|
|
256
|
-
if (key in NAMED_COLORS) {
|
|
257
|
-
return true;
|
|
258
|
-
}
|
|
259
|
-
if (key.endsWith("Bright") && isColorKey(key.slice(0, -BRIGHT_SUFFIX_LENGTH))) {
|
|
260
|
-
return true;
|
|
261
|
-
}
|
|
262
|
-
return false;
|
|
263
|
-
};
|
|
264
|
-
var isBgKey = (key) => {
|
|
265
|
-
if (!key.startsWith("bg") || key.length <= BG_PREFIX_LENGTH) {
|
|
266
|
-
return false;
|
|
267
|
-
}
|
|
268
|
-
const colorPart = key.charAt(BG_PREFIX_LENGTH).toLowerCase() + key.slice(BG_COLOR_START);
|
|
269
|
-
return isColorKey(colorPart);
|
|
270
|
-
};
|
|
271
|
-
var callableProxy = (ops) => {
|
|
272
|
-
const base = (input) => applyOpsToText(ops, input);
|
|
273
|
-
Object.defineProperty(base, OP_SYMBOL, {
|
|
274
|
-
value: ops,
|
|
275
|
-
enumerable: false,
|
|
276
|
-
configurable: false,
|
|
277
|
-
writable: false
|
|
278
|
-
});
|
|
279
|
-
return new Proxy(base, {
|
|
280
|
-
apply(_target, _thisArg, argArray) {
|
|
281
|
-
const [input] = argArray;
|
|
282
|
-
return applyOpsToText(ops, input);
|
|
283
|
-
},
|
|
284
|
-
get(_target, prop) {
|
|
285
|
-
const key = String(prop);
|
|
286
|
-
if (prop === OP_SYMBOL) {
|
|
287
|
-
return ops;
|
|
288
|
-
}
|
|
289
|
-
if (STYLE_KEYS.has(key)) {
|
|
290
|
-
const op = STYLE_TABLE[key];
|
|
291
|
-
return callableProxy([...ops, op]);
|
|
292
|
-
}
|
|
293
|
-
if (isBgKey(key)) {
|
|
294
|
-
const raw = key.slice(BG_PREFIX_LENGTH);
|
|
295
|
-
const colorName = raw.charAt(0).toLowerCase() + raw.slice(1);
|
|
296
|
-
const { rgb, wantBright } = parseColorName(colorName);
|
|
297
|
-
return callableProxy([...ops, ...mkBgOpsFromRgb(rgb, wantBright)]);
|
|
298
|
-
}
|
|
299
|
-
if (isColorKey(key)) {
|
|
300
|
-
const { rgb, wantBright } = parseColorName(key);
|
|
301
|
-
return callableProxy([...ops, ...mkFgOpsFromRgb(rgb, wantBright)]);
|
|
302
|
-
}
|
|
303
|
-
return callableProxy(ops);
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
};
|
|
307
|
-
var re = callableProxy([]);
|
|
308
|
-
var chain = (...parts) => {
|
|
309
|
-
const collected = [];
|
|
310
|
-
for (const p of parts) {
|
|
311
|
-
const ops = p[OP_SYMBOL];
|
|
312
|
-
if (ops && ops.length > 0) {
|
|
313
|
-
for (const op of ops) {
|
|
314
|
-
collected.push(op);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
return callableProxy(collected);
|
|
319
|
-
};
|
|
320
|
-
export {
|
|
321
|
-
setColorLevel,
|
|
322
|
-
re,
|
|
323
|
-
chain
|
|
324
|
-
};
|