@reliverse/relico 1.4.1 → 1.4.2
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/mod.d.ts +51 -0
- package/{src/mod.ts → dist/mod.js} +69 -246
- package/package.json +15 -19
- package/.vscode/extensions.json +0 -8
- package/.vscode/settings.json +0 -45
- package/biome.json +0 -86
- package/examples/benchmarks/bundle-size.ts +0 -143
- package/examples/benchmarks/config.example.ts +0 -55
- package/examples/benchmarks/config.ts +0 -93
- package/examples/benchmarks/performance.ts +0 -172
- package/examples/core.ts +0 -62
- package/reliverse.ts +0 -399
- package/reltypes.ts +0 -1490
- package/tsconfig.json +0 -33
package/dist/mod.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
type ColorLevel = 0 | 1 | 2 | 3;
|
|
2
|
+
interface Rgb {
|
|
3
|
+
r: number;
|
|
4
|
+
g: number;
|
|
5
|
+
b: number;
|
|
6
|
+
}
|
|
7
|
+
type SgrOp = {
|
|
8
|
+
kind: "style";
|
|
9
|
+
open: number[];
|
|
10
|
+
} | {
|
|
11
|
+
kind: "fg-basic";
|
|
12
|
+
idx: number;
|
|
13
|
+
bright: boolean;
|
|
14
|
+
} | {
|
|
15
|
+
kind: "bg-basic";
|
|
16
|
+
idx: number;
|
|
17
|
+
bright: boolean;
|
|
18
|
+
} | {
|
|
19
|
+
kind: "fg-256";
|
|
20
|
+
code: number;
|
|
21
|
+
} | {
|
|
22
|
+
kind: "bg-256";
|
|
23
|
+
code: number;
|
|
24
|
+
} | {
|
|
25
|
+
kind: "fg-true";
|
|
26
|
+
rgb: Rgb;
|
|
27
|
+
} | {
|
|
28
|
+
kind: "bg-true";
|
|
29
|
+
rgb: Rgb;
|
|
30
|
+
};
|
|
31
|
+
type ApplyInput = string | number;
|
|
32
|
+
type FormatCallable = ((input: ApplyInput) => string) & {
|
|
33
|
+
readonly [OP_SYMBOL]: SgrOp[];
|
|
34
|
+
};
|
|
35
|
+
export type BaseColorName = "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "gray" | "orange" | "pink" | "purple" | "teal" | "lime" | "brown" | "navy" | "maroon" | "olive" | "silver";
|
|
36
|
+
export type ColorName = BaseColorName | BrightColorName | BgColorName;
|
|
37
|
+
export type BrightColorName = "blackBright" | "redBright" | "greenBright" | "yellowBright" | "blueBright" | "magentaBright" | "cyanBright" | "whiteBright" | "orangeBright" | "pinkBright" | "purpleBright" | "tealBright" | "limeBright" | "brownBright" | "navyBright" | "maroonBright" | "oliveBright" | "silverBright";
|
|
38
|
+
export type BgColorName = `bg${Capitalize<BaseColorName>}` | `bg${Capitalize<BrightColorName>}`;
|
|
39
|
+
export type ReStyleKey = "reset" | "bold" | "dim" | "italic" | "underline" | "inverse" | "hidden" | "strikethrough";
|
|
40
|
+
export type Re = FormatCallable & {
|
|
41
|
+
readonly [K in ReStyleKey]: Re;
|
|
42
|
+
} & {
|
|
43
|
+
readonly [K in ColorName]: Re;
|
|
44
|
+
} & {
|
|
45
|
+
readonly [K in BgColorName]: Re;
|
|
46
|
+
};
|
|
47
|
+
declare const OP_SYMBOL: unique symbol;
|
|
48
|
+
export declare const setColorLevel: (level: ColorLevel) => void;
|
|
49
|
+
export declare const re: Re;
|
|
50
|
+
export declare const chain: (...parts: FormatCallable[]) => Re;
|
|
51
|
+
export {};
|
|
@@ -1,110 +1,13 @@
|
|
|
1
|
-
/* @reliverse/relico - Tiny, type-safe terminal color library with chainable API
|
|
2
|
-
- Levels: 0 (off), 1 (ANSI 8/bright), 2 (ANSI 256), 3 (Truecolor)
|
|
3
|
-
- Named palettes (std, web, grayscale), Bright & Pastel variants, bg-variants
|
|
4
|
-
- Chainable: re.bold.red.underline("text"), chain(re.bold, re.red)("text")
|
|
5
|
-
- Multiline-safe: styles applied per line with reset to prevent bleed
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
type ColorLevel = 0 | 1 | 2 | 3;
|
|
9
|
-
|
|
10
|
-
interface Rgb {
|
|
11
|
-
r: number;
|
|
12
|
-
g: number;
|
|
13
|
-
b: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
type SgrOp =
|
|
17
|
-
| { kind: "style"; open: number[] } // closed by global reset at line end
|
|
18
|
-
| { kind: "fg-basic"; idx: number; bright: boolean }
|
|
19
|
-
| { kind: "bg-basic"; idx: number; bright: boolean }
|
|
20
|
-
| { kind: "fg-256"; code: number }
|
|
21
|
-
| { kind: "bg-256"; code: number }
|
|
22
|
-
| { kind: "fg-true"; rgb: Rgb }
|
|
23
|
-
| { kind: "bg-true"; rgb: Rgb };
|
|
24
|
-
|
|
25
|
-
type ApplyInput = string | number;
|
|
26
|
-
|
|
27
|
-
type FormatCallable = ((input: ApplyInput) => string) & { readonly [OP_SYMBOL]: SgrOp[] };
|
|
28
|
-
|
|
29
|
-
export type BaseColorName =
|
|
30
|
-
| "black"
|
|
31
|
-
| "red"
|
|
32
|
-
| "green"
|
|
33
|
-
| "yellow"
|
|
34
|
-
| "blue"
|
|
35
|
-
| "magenta"
|
|
36
|
-
| "cyan"
|
|
37
|
-
| "white"
|
|
38
|
-
| "gray"
|
|
39
|
-
| "orange"
|
|
40
|
-
| "pink"
|
|
41
|
-
| "purple"
|
|
42
|
-
| "teal"
|
|
43
|
-
| "lime"
|
|
44
|
-
| "brown"
|
|
45
|
-
| "navy"
|
|
46
|
-
| "maroon"
|
|
47
|
-
| "olive"
|
|
48
|
-
| "silver";
|
|
49
|
-
|
|
50
|
-
export type ColorName = BaseColorName | BrightColorName | BgColorName;
|
|
51
|
-
|
|
52
|
-
export type BrightColorName =
|
|
53
|
-
| "blackBright"
|
|
54
|
-
| "redBright"
|
|
55
|
-
| "greenBright"
|
|
56
|
-
| "yellowBright"
|
|
57
|
-
| "blueBright"
|
|
58
|
-
| "magentaBright"
|
|
59
|
-
| "cyanBright"
|
|
60
|
-
| "whiteBright"
|
|
61
|
-
| "orangeBright"
|
|
62
|
-
| "pinkBright"
|
|
63
|
-
| "purpleBright"
|
|
64
|
-
| "tealBright"
|
|
65
|
-
| "limeBright"
|
|
66
|
-
| "brownBright"
|
|
67
|
-
| "navyBright"
|
|
68
|
-
| "maroonBright"
|
|
69
|
-
| "oliveBright"
|
|
70
|
-
| "silverBright";
|
|
71
|
-
|
|
72
|
-
export type BgColorName = `bg${Capitalize<BaseColorName>}` | `bg${Capitalize<BrightColorName>}`;
|
|
73
|
-
|
|
74
|
-
export type ReStyleKey =
|
|
75
|
-
| "reset"
|
|
76
|
-
| "bold"
|
|
77
|
-
| "dim"
|
|
78
|
-
| "italic"
|
|
79
|
-
| "underline"
|
|
80
|
-
| "inverse"
|
|
81
|
-
| "hidden"
|
|
82
|
-
| "strikethrough";
|
|
83
|
-
|
|
84
|
-
export type Re = FormatCallable & {
|
|
85
|
-
readonly [K in ReStyleKey]: Re;
|
|
86
|
-
} & {
|
|
87
|
-
readonly [K in ColorName]: Re;
|
|
88
|
-
} & {
|
|
89
|
-
readonly [K in BgColorName]: Re;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
1
|
const ESC = "\x1B[";
|
|
93
2
|
const RESET = `${ESC}0m`;
|
|
94
|
-
const OP_SYMBOL
|
|
95
|
-
|
|
96
|
-
// Color level constants
|
|
3
|
+
const OP_SYMBOL = Symbol("re.ops");
|
|
97
4
|
const COLOR_LEVEL_OFF = 0;
|
|
98
5
|
const COLOR_LEVEL_BASIC = 1;
|
|
99
6
|
const COLOR_LEVEL_256 = 2;
|
|
100
7
|
const COLOR_LEVEL_TRUECOLOR = 3;
|
|
101
|
-
|
|
102
|
-
// RGB and byte constants
|
|
103
8
|
const MIN_BYTE = 0;
|
|
104
9
|
const MAX_BYTE = 255;
|
|
105
10
|
const WHITE_RGB = 255;
|
|
106
|
-
|
|
107
|
-
// ANSI 256 color constants
|
|
108
11
|
const ANSI_256_GRAYSCALE_MIN = 8;
|
|
109
12
|
const ANSI_256_GRAYSCALE_MAX = 248;
|
|
110
13
|
const ANSI_256_BASE_OFFSET = 16;
|
|
@@ -115,14 +18,10 @@ const ANSI_256_BRIGHT_THRESHOLD = 231;
|
|
|
115
18
|
const ANSI_256_RGB_LEVELS = 5;
|
|
116
19
|
const ANSI_256_RGB_RED_MULTIPLIER = 36;
|
|
117
20
|
const ANSI_256_RGB_GREEN_MULTIPLIER = 6;
|
|
118
|
-
|
|
119
|
-
// SGR code constants
|
|
120
21
|
const SGR_FG_BASE = 30;
|
|
121
22
|
const SGR_BG_BASE = 40;
|
|
122
23
|
const SGR_FG_BRIGHT_BASE = 90;
|
|
123
24
|
const SGR_BG_BRIGHT_BASE = 100;
|
|
124
|
-
|
|
125
|
-
// Style SGR codes
|
|
126
25
|
const SGR_RESET = 0;
|
|
127
26
|
const SGR_BOLD = 1;
|
|
128
27
|
const SGR_DIM = 2;
|
|
@@ -131,41 +30,25 @@ const SGR_UNDERLINE = 4;
|
|
|
131
30
|
const SGR_INVERSE = 7;
|
|
132
31
|
const SGR_HIDDEN = 8;
|
|
133
32
|
const SGR_STRIKETHROUGH = 9;
|
|
134
|
-
|
|
135
|
-
// Hex parsing constants
|
|
136
33
|
const HEX_BYTE_LENGTH = 2;
|
|
137
34
|
const HEX_RED_START = 0;
|
|
138
35
|
const HEX_GREEN_START = 2;
|
|
139
36
|
const HEX_BLUE_START = 4;
|
|
140
37
|
const HEX_BLUE_END = 6;
|
|
141
38
|
const HEX_RADIX = 16;
|
|
142
|
-
|
|
143
|
-
// String processing constants
|
|
144
39
|
const BRIGHT_SUFFIX_LENGTH = 6;
|
|
145
40
|
const BG_PREFIX_LENGTH = 2;
|
|
146
41
|
const BG_COLOR_START = 3;
|
|
147
|
-
|
|
148
|
-
// Color mixing constants
|
|
149
42
|
const BRIGHT_MIX_FACTOR = 0.25;
|
|
150
|
-
|
|
151
|
-
// Regex constants
|
|
152
43
|
const BRIGHT_SUFFIX_REGEX = /Bright$/u;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
export const setColorLevel = (level: ColorLevel): void => {
|
|
157
|
-
if (
|
|
158
|
-
level !== COLOR_LEVEL_OFF &&
|
|
159
|
-
level !== COLOR_LEVEL_BASIC &&
|
|
160
|
-
level !== COLOR_LEVEL_256 &&
|
|
161
|
-
level !== COLOR_LEVEL_TRUECOLOR
|
|
162
|
-
) {
|
|
44
|
+
let CURRENT_LEVEL = COLOR_LEVEL_TRUECOLOR;
|
|
45
|
+
export const setColorLevel = (level) => {
|
|
46
|
+
if (level !== COLOR_LEVEL_OFF && level !== COLOR_LEVEL_BASIC && level !== COLOR_LEVEL_256 && level !== COLOR_LEVEL_TRUECOLOR) {
|
|
163
47
|
throw new Error("Invalid color level");
|
|
164
48
|
}
|
|
165
49
|
CURRENT_LEVEL = level;
|
|
166
50
|
};
|
|
167
|
-
|
|
168
|
-
const clampByte = (n: number): number => {
|
|
51
|
+
const clampByte = (n) => {
|
|
169
52
|
if (!Number.isFinite(n)) {
|
|
170
53
|
return MIN_BYTE;
|
|
171
54
|
}
|
|
@@ -177,24 +60,26 @@ const clampByte = (n: number): number => {
|
|
|
177
60
|
}
|
|
178
61
|
return Math.round(n);
|
|
179
62
|
};
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
{ r:
|
|
184
|
-
|
|
185
|
-
{ r: 0, g: 205, b: 0 },
|
|
186
|
-
|
|
187
|
-
{ r:
|
|
188
|
-
|
|
189
|
-
{ r: 0, g:
|
|
190
|
-
|
|
63
|
+
const BASIC8 = [
|
|
64
|
+
{ r: 0, g: 0, b: 0 },
|
|
65
|
+
// black
|
|
66
|
+
{ r: 205, g: 0, b: 0 },
|
|
67
|
+
// red
|
|
68
|
+
{ r: 0, g: 205, b: 0 },
|
|
69
|
+
// green
|
|
70
|
+
{ r: 205, g: 205, b: 0 },
|
|
71
|
+
// yellow
|
|
72
|
+
{ r: 0, g: 0, b: 238 },
|
|
73
|
+
// blue
|
|
74
|
+
{ r: 205, g: 0, b: 205 },
|
|
75
|
+
// magenta
|
|
76
|
+
{ r: 0, g: 205, b: 205 },
|
|
77
|
+
// cyan
|
|
78
|
+
{ r: 229, g: 229, b: 229 }
|
|
79
|
+
// white (light gray)
|
|
191
80
|
];
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const sgr = (codes: number[]): string => `${ESC}${codes.join(";")}m`;
|
|
195
|
-
|
|
196
|
-
// RGB → closest of BASIC8 index (0..7)
|
|
197
|
-
const nearestBasicIndex = (rgb: Rgb): number => {
|
|
81
|
+
const sgr = (codes) => `${ESC}${codes.join(";")}m`;
|
|
82
|
+
const nearestBasicIndex = (rgb) => {
|
|
198
83
|
let best = 0;
|
|
199
84
|
let bestDist = Number.POSITIVE_INFINITY;
|
|
200
85
|
for (let i = 0; i < BASIC8.length; i++) {
|
|
@@ -210,10 +95,7 @@ const nearestBasicIndex = (rgb: Rgb): number => {
|
|
|
210
95
|
}
|
|
211
96
|
return best;
|
|
212
97
|
};
|
|
213
|
-
|
|
214
|
-
// RGB → ANSI 256 index
|
|
215
|
-
const rgbToAnsi256 = (rgb: Rgb): number => {
|
|
216
|
-
// Try grayscale if r≈g≈b
|
|
98
|
+
const rgbToAnsi256 = (rgb) => {
|
|
217
99
|
if (rgb.r === rgb.g && rgb.g === rgb.b) {
|
|
218
100
|
if (rgb.r < ANSI_256_GRAYSCALE_MIN) {
|
|
219
101
|
return ANSI_256_BASE_OFFSET;
|
|
@@ -222,20 +104,16 @@ const rgbToAnsi256 = (rgb: Rgb): number => {
|
|
|
222
104
|
return ANSI_256_BRIGHT_THRESHOLD;
|
|
223
105
|
}
|
|
224
106
|
const step = Math.round(
|
|
225
|
-
(
|
|
107
|
+
(rgb.r - ANSI_256_GRAYSCALE_MIN) / ANSI_256_GRAYSCALE_RANGE * ANSI_256_GRAYSCALE_STEPS
|
|
226
108
|
);
|
|
227
109
|
return ANSI_256_GRAYSCALE_BASE + step;
|
|
228
110
|
}
|
|
229
|
-
const r = Math.round(
|
|
230
|
-
const g = Math.round(
|
|
231
|
-
const b = Math.round(
|
|
232
|
-
return
|
|
233
|
-
ANSI_256_BASE_OFFSET + ANSI_256_RGB_RED_MULTIPLIER * r + ANSI_256_RGB_GREEN_MULTIPLIER * g + b
|
|
234
|
-
);
|
|
111
|
+
const r = Math.round(rgb.r / MAX_BYTE * ANSI_256_RGB_LEVELS);
|
|
112
|
+
const g = Math.round(rgb.g / MAX_BYTE * ANSI_256_RGB_LEVELS);
|
|
113
|
+
const b = Math.round(rgb.b / MAX_BYTE * ANSI_256_RGB_LEVELS);
|
|
114
|
+
return ANSI_256_BASE_OFFSET + ANSI_256_RGB_RED_MULTIPLIER * r + ANSI_256_RGB_GREEN_MULTIPLIER * g + b;
|
|
235
115
|
};
|
|
236
|
-
|
|
237
|
-
// Color data
|
|
238
|
-
const NAMED_COLORS: Record<BaseColorName, string> = {
|
|
116
|
+
const NAMED_COLORS = {
|
|
239
117
|
black: "#000000",
|
|
240
118
|
red: "#ff0000",
|
|
241
119
|
green: "#00ff00",
|
|
@@ -254,34 +132,27 @@ const NAMED_COLORS: Record<BaseColorName, string> = {
|
|
|
254
132
|
navy: "#000080",
|
|
255
133
|
maroon: "#800000",
|
|
256
134
|
olive: "#808000",
|
|
257
|
-
silver: "#c0c0c0"
|
|
135
|
+
silver: "#c0c0c0"
|
|
258
136
|
};
|
|
259
|
-
|
|
260
|
-
const mixWithWhite = (rgb: Rgb, factor: number): Rgb => {
|
|
137
|
+
const mixWithWhite = (rgb, factor) => {
|
|
261
138
|
const t = factor;
|
|
262
139
|
return {
|
|
263
140
|
r: clampByte(rgb.r * (1 - t) + WHITE_RGB * t),
|
|
264
141
|
g: clampByte(rgb.g * (1 - t) + WHITE_RGB * t),
|
|
265
|
-
b: clampByte(rgb.b * (1 - t) + WHITE_RGB * t)
|
|
142
|
+
b: clampByte(rgb.b * (1 - t) + WHITE_RGB * t)
|
|
266
143
|
};
|
|
267
144
|
};
|
|
268
|
-
|
|
269
|
-
const fromNamed = (name: BaseColorName): Rgb => {
|
|
145
|
+
const fromNamed = (name) => {
|
|
270
146
|
const hex = NAMED_COLORS[name];
|
|
271
147
|
if (!hex || typeof hex !== "string") {
|
|
272
|
-
// Return black as fallback for invalid color names
|
|
273
148
|
return { r: 0, g: 0, b: 0 };
|
|
274
149
|
}
|
|
275
|
-
// Simple hex to RGB conversion for named colors only
|
|
276
150
|
const clean = hex.startsWith("#") ? hex.slice(1) : hex;
|
|
277
151
|
if (clean.length !== HEX_BLUE_END && clean.length !== 3) {
|
|
278
|
-
// Return black as fallback for invalid hex format
|
|
279
152
|
return { r: 0, g: 0, b: 0 };
|
|
280
153
|
}
|
|
281
|
-
|
|
282
|
-
let rHex: string, gHex: string, bHex: string;
|
|
154
|
+
let rHex, gHex, bHex;
|
|
283
155
|
if (clean.length === 3) {
|
|
284
|
-
// Expand short hex format (e.g., "abc" -> "aabbcc")
|
|
285
156
|
rHex = clean[0].repeat(2);
|
|
286
157
|
gHex = clean[1].repeat(2);
|
|
287
158
|
bHex = clean[2].repeat(2);
|
|
@@ -290,48 +161,38 @@ const fromNamed = (name: BaseColorName): Rgb => {
|
|
|
290
161
|
gHex = clean.slice(HEX_GREEN_START, HEX_BLUE_START);
|
|
291
162
|
bHex = clean.slice(HEX_BLUE_START, HEX_BLUE_END);
|
|
292
163
|
}
|
|
293
|
-
|
|
294
164
|
const r = Number.parseInt(rHex, HEX_RADIX);
|
|
295
165
|
const g = Number.parseInt(gHex, HEX_RADIX);
|
|
296
166
|
const b = Number.parseInt(bHex, HEX_RADIX);
|
|
297
|
-
|
|
298
|
-
// Validate parsed RGB values
|
|
299
167
|
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {
|
|
300
168
|
return { r: 0, g: 0, b: 0 };
|
|
301
169
|
}
|
|
302
|
-
|
|
303
170
|
return { r, g, b };
|
|
304
171
|
};
|
|
305
|
-
|
|
306
|
-
const toBaseName = (compound: BrightColorName): BaseColorName => {
|
|
172
|
+
const toBaseName = (compound) => {
|
|
307
173
|
if (!compound || typeof compound !== "string") {
|
|
308
|
-
return "black";
|
|
174
|
+
return "black";
|
|
309
175
|
}
|
|
310
176
|
const base = compound.replace(BRIGHT_SUFFIX_REGEX, "");
|
|
311
177
|
if (!base) {
|
|
312
|
-
return "black";
|
|
178
|
+
return "black";
|
|
313
179
|
}
|
|
314
180
|
const key = base.charAt(0).toLowerCase() + base.slice(1);
|
|
315
|
-
return key
|
|
181
|
+
return key;
|
|
316
182
|
};
|
|
317
|
-
|
|
318
|
-
const parseColorName = (name: ColorName): { rgb: Rgb; wantBright: boolean } => {
|
|
183
|
+
const parseColorName = (name) => {
|
|
319
184
|
if (!name || typeof name !== "string") {
|
|
320
|
-
// Return black as fallback for invalid input
|
|
321
185
|
return { rgb: { r: 0, g: 0, b: 0 }, wantBright: false };
|
|
322
186
|
}
|
|
323
|
-
|
|
324
187
|
if (name.endsWith("Bright")) {
|
|
325
|
-
const base = toBaseName(name
|
|
188
|
+
const base = toBaseName(name);
|
|
326
189
|
const rgb = fromNamed(base);
|
|
327
|
-
// Lighten a bit in high levels; level 1 will use bright SGR
|
|
328
190
|
const rgbAdj = mixWithWhite(rgb, BRIGHT_MIX_FACTOR);
|
|
329
191
|
return { rgb: rgbAdj, wantBright: true };
|
|
330
192
|
}
|
|
331
|
-
return { rgb: fromNamed(name
|
|
193
|
+
return { rgb: fromNamed(name), wantBright: false };
|
|
332
194
|
};
|
|
333
|
-
|
|
334
|
-
const openForOp = (op: SgrOp): string => {
|
|
195
|
+
const openForOp = (op) => {
|
|
335
196
|
if (CURRENT_LEVEL === COLOR_LEVEL_OFF) {
|
|
336
197
|
return "";
|
|
337
198
|
}
|
|
@@ -354,8 +215,7 @@ const openForOp = (op: SgrOp): string => {
|
|
|
354
215
|
return "";
|
|
355
216
|
}
|
|
356
217
|
};
|
|
357
|
-
|
|
358
|
-
const opsToOpen = (ops: SgrOp[]): string => {
|
|
218
|
+
const opsToOpen = (ops) => {
|
|
359
219
|
if (CURRENT_LEVEL === COLOR_LEVEL_OFF) {
|
|
360
220
|
return "";
|
|
361
221
|
}
|
|
@@ -365,25 +225,17 @@ const opsToOpen = (ops: SgrOp[]): string => {
|
|
|
365
225
|
}
|
|
366
226
|
return out;
|
|
367
227
|
};
|
|
368
|
-
|
|
369
|
-
// Optimized multiline processing with fewer allocations and branches
|
|
370
|
-
const applyOpsToText = (ops: SgrOp[], input: ApplyInput): string => {
|
|
228
|
+
const applyOpsToText = (ops, input) => {
|
|
371
229
|
const text = String(input);
|
|
372
230
|
if (CURRENT_LEVEL === COLOR_LEVEL_OFF || ops.length === 0 || text.length === 0) {
|
|
373
231
|
return text;
|
|
374
232
|
}
|
|
375
|
-
|
|
376
233
|
const open = opsToOpen(ops);
|
|
377
|
-
|
|
378
|
-
// Fast path for single-line text (most common case)
|
|
379
234
|
if (!text.includes("\n")) {
|
|
380
235
|
return `${open}${text}${RESET}`;
|
|
381
236
|
}
|
|
382
|
-
|
|
383
|
-
// Optimized multiline handling with pre-calculated string lengths
|
|
384
237
|
const lines = text.split("\n");
|
|
385
238
|
const result = new Array(lines.length);
|
|
386
|
-
|
|
387
239
|
for (let i = 0; i < lines.length; i++) {
|
|
388
240
|
const line = lines[i];
|
|
389
241
|
if (line.endsWith("\r")) {
|
|
@@ -392,12 +244,9 @@ const applyOpsToText = (ops: SgrOp[], input: ApplyInput): string => {
|
|
|
392
244
|
result[i] = `${open}${line}${RESET}`;
|
|
393
245
|
}
|
|
394
246
|
}
|
|
395
|
-
|
|
396
247
|
return result.join("\n");
|
|
397
248
|
};
|
|
398
|
-
|
|
399
|
-
// Build operations for a color request according to CURRENT_LEVEL
|
|
400
|
-
const mkFgOpsFromRgb = (rgb: Rgb, wantBright = false): SgrOp[] => {
|
|
249
|
+
const mkFgOpsFromRgb = (rgb, wantBright = false) => {
|
|
401
250
|
if (CURRENT_LEVEL === COLOR_LEVEL_BASIC) {
|
|
402
251
|
const idx = nearestBasicIndex(rgb);
|
|
403
252
|
return [{ kind: "fg-basic", idx, bright: wantBright }];
|
|
@@ -407,8 +256,7 @@ const mkFgOpsFromRgb = (rgb: Rgb, wantBright = false): SgrOp[] => {
|
|
|
407
256
|
}
|
|
408
257
|
return [{ kind: "fg-true", rgb }];
|
|
409
258
|
};
|
|
410
|
-
|
|
411
|
-
const mkBgOpsFromRgb = (rgb: Rgb, wantBright = false): SgrOp[] => {
|
|
259
|
+
const mkBgOpsFromRgb = (rgb, wantBright = false) => {
|
|
412
260
|
if (CURRENT_LEVEL === COLOR_LEVEL_BASIC) {
|
|
413
261
|
const idx = nearestBasicIndex(rgb);
|
|
414
262
|
return [{ kind: "bg-basic", idx, bright: wantBright }];
|
|
@@ -418,9 +266,7 @@ const mkBgOpsFromRgb = (rgb: Rgb, wantBright = false): SgrOp[] => {
|
|
|
418
266
|
}
|
|
419
267
|
return [{ kind: "bg-true", rgb }];
|
|
420
268
|
};
|
|
421
|
-
|
|
422
|
-
// Style ops
|
|
423
|
-
const STYLE_TABLE: Record<ReStyleKey, SgrOp> = {
|
|
269
|
+
const STYLE_TABLE = {
|
|
424
270
|
reset: { kind: "style", open: [SGR_RESET] },
|
|
425
271
|
bold: { kind: "style", open: [SGR_BOLD] },
|
|
426
272
|
dim: { kind: "style", open: [SGR_DIM] },
|
|
@@ -428,11 +274,9 @@ const STYLE_TABLE: Record<ReStyleKey, SgrOp> = {
|
|
|
428
274
|
underline: { kind: "style", open: [SGR_UNDERLINE] },
|
|
429
275
|
inverse: { kind: "style", open: [SGR_INVERSE] },
|
|
430
276
|
hidden: { kind: "style", open: [SGR_HIDDEN] },
|
|
431
|
-
strikethrough: { kind: "style", open: [SGR_STRIKETHROUGH] }
|
|
277
|
+
strikethrough: { kind: "style", open: [SGR_STRIKETHROUGH] }
|
|
432
278
|
};
|
|
433
|
-
|
|
434
|
-
// Lookup maps
|
|
435
|
-
const STYLE_KEYS = new Set([
|
|
279
|
+
const STYLE_KEYS = /* @__PURE__ */ new Set([
|
|
436
280
|
"reset",
|
|
437
281
|
"bold",
|
|
438
282
|
"dim",
|
|
@@ -440,93 +284,72 @@ const STYLE_KEYS = new Set([
|
|
|
440
284
|
"underline",
|
|
441
285
|
"inverse",
|
|
442
286
|
"hidden",
|
|
443
|
-
"strikethrough"
|
|
287
|
+
"strikethrough"
|
|
444
288
|
]);
|
|
445
|
-
|
|
446
|
-
// Direct color/bg key checks
|
|
447
|
-
const isColorKey = (key: string): boolean => {
|
|
289
|
+
const isColorKey = (key) => {
|
|
448
290
|
if (!key || typeof key !== "string") {
|
|
449
291
|
return false;
|
|
450
292
|
}
|
|
451
|
-
// Base colors and extended colors
|
|
452
293
|
if (key in NAMED_COLORS) {
|
|
453
294
|
return true;
|
|
454
295
|
}
|
|
455
|
-
// Bright variants
|
|
456
296
|
if (key.endsWith("Bright") && key.length > BRIGHT_SUFFIX_LENGTH) {
|
|
457
297
|
const baseName = key.slice(0, -BRIGHT_SUFFIX_LENGTH);
|
|
458
298
|
return baseName in NAMED_COLORS;
|
|
459
299
|
}
|
|
460
300
|
return false;
|
|
461
301
|
};
|
|
462
|
-
|
|
463
|
-
const isBgKey = (key: string): boolean => {
|
|
302
|
+
const isBgKey = (key) => {
|
|
464
303
|
if (!key || typeof key !== "string" || !key.startsWith("bg") || key.length <= BG_PREFIX_LENGTH) {
|
|
465
304
|
return false;
|
|
466
305
|
}
|
|
467
306
|
const colorPart = key.charAt(BG_PREFIX_LENGTH).toLowerCase() + key.slice(BG_COLOR_START);
|
|
468
307
|
return isColorKey(colorPart);
|
|
469
308
|
};
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
const callableProxy = (ops: SgrOp[]): Re => {
|
|
473
|
-
const base = ((input: ApplyInput) => applyOpsToText(ops, input)) as FormatCallable;
|
|
309
|
+
const callableProxy = (ops) => {
|
|
310
|
+
const base = ((input) => applyOpsToText(ops, input));
|
|
474
311
|
Object.defineProperty(base, OP_SYMBOL, {
|
|
475
312
|
value: ops,
|
|
476
313
|
enumerable: false,
|
|
477
314
|
configurable: false,
|
|
478
|
-
writable: false
|
|
315
|
+
writable: false
|
|
479
316
|
});
|
|
480
|
-
|
|
481
|
-
return new Proxy(base as unknown as Re, {
|
|
317
|
+
return new Proxy(base, {
|
|
482
318
|
apply(_target, _thisArg, argArray) {
|
|
483
|
-
const [input] = argArray
|
|
319
|
+
const [input] = argArray;
|
|
484
320
|
return applyOpsToText(ops, input);
|
|
485
321
|
},
|
|
486
322
|
get(_target, prop) {
|
|
487
323
|
const key = String(prop);
|
|
488
|
-
|
|
489
|
-
// Ops extractor for chain()
|
|
490
324
|
if (prop === OP_SYMBOL) {
|
|
491
325
|
return ops;
|
|
492
326
|
}
|
|
493
|
-
|
|
494
|
-
// Fast path for styles using Set lookup
|
|
495
327
|
if (STYLE_KEYS.has(key)) {
|
|
496
|
-
const op = STYLE_TABLE[key
|
|
328
|
+
const op = STYLE_TABLE[key];
|
|
497
329
|
return callableProxy([...ops, op]);
|
|
498
330
|
}
|
|
499
|
-
|
|
500
|
-
// Fast path for colors
|
|
501
331
|
if (isBgKey(key)) {
|
|
502
|
-
const raw = key.slice(BG_PREFIX_LENGTH);
|
|
332
|
+
const raw = key.slice(BG_PREFIX_LENGTH);
|
|
503
333
|
if (!raw) {
|
|
504
|
-
return callableProxy(ops);
|
|
334
|
+
return callableProxy(ops);
|
|
505
335
|
}
|
|
506
336
|
const colorName = raw.charAt(0).toLowerCase() + raw.slice(1);
|
|
507
|
-
const { rgb, wantBright } = parseColorName(colorName
|
|
337
|
+
const { rgb, wantBright } = parseColorName(colorName);
|
|
508
338
|
return callableProxy([...ops, ...mkBgOpsFromRgb(rgb, wantBright)]);
|
|
509
339
|
}
|
|
510
|
-
|
|
511
340
|
if (isColorKey(key)) {
|
|
512
|
-
const { rgb, wantBright } = parseColorName(key
|
|
341
|
+
const { rgb, wantBright } = parseColorName(key);
|
|
513
342
|
return callableProxy([...ops, ...mkFgOpsFromRgb(rgb, wantBright)]);
|
|
514
343
|
}
|
|
515
|
-
|
|
516
|
-
// Unknown key → return self (no-op), keeps chain resilient
|
|
517
344
|
return callableProxy(ops);
|
|
518
|
-
}
|
|
345
|
+
}
|
|
519
346
|
});
|
|
520
347
|
};
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
// chain(re.bold, re.red, re.underline)("text")
|
|
526
|
-
export const chain = (...parts: FormatCallable[]): Re => {
|
|
527
|
-
const collected: SgrOp[] = [];
|
|
348
|
+
export const re = callableProxy([]);
|
|
349
|
+
export const chain = (...parts) => {
|
|
350
|
+
const collected = [];
|
|
528
351
|
for (const p of parts) {
|
|
529
|
-
const ops =
|
|
352
|
+
const ops = p[OP_SYMBOL];
|
|
530
353
|
if (ops && ops.length > 0) {
|
|
531
354
|
for (const op of ops) {
|
|
532
355
|
collected.push(op);
|
package/package.json
CHANGED
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reliverse/relico",
|
|
3
3
|
"author": "reliverse",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.2",
|
|
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
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"size": "bun examples/benchmarks/bundle-size.ts",
|
|
13
|
-
"bench": "bun examples/benchmarks/performance.ts",
|
|
14
|
-
"check": "bun tsc --noEmit && bun biome check --fix --unsafe"
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/mod.d.ts",
|
|
10
|
+
"default": "./dist/mod.js"
|
|
11
|
+
}
|
|
15
12
|
},
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"package.json"
|
|
16
|
+
],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT"
|
|
21
|
+
}
|