@reliverse/relico 1.4.2 → 2.2.7
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/README.md +16 -20
- package/dist/mod.d.ts +9 -0
- package/dist/mod.js +270 -175
- package/package.json +6 -5
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
# Reliverse Relico
|
|
2
2
|
|
|
3
|
-
> @reliverse/
|
|
3
|
+
> @reliverse/relico is a themeable, chainable, typed, truecolor-powered terminal styling toolkit — built for humans, not just terminals. It makes your CLI output beautiful, accessible, and expressive — with developer-first ergonomics, smart config, and blazing-fast performance.
|
|
4
4
|
|
|
5
|
-
[sponsor](https://github.com/sponsors/blefnk) — [discord](https://discord.gg/Pb8uKbwpsJ) — [repo](https://github.com/reliverse/relico) — [npm](https://npmjs.com/@reliverse/
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
‼️Attention! The latest implementation of `@reliverse/relico` now lives in the framework repository for building libraries — `@reliverse/dler`. Please use [@reliverse/dler-colors](https://github.com/reliverse/dler/tree/main/packages/colors) instead of `@reliverse/relico`.
|
|
5
|
+
[sponsor](https://github.com/sponsors/blefnk) — [discord](https://discord.gg/Pb8uKbwpsJ) — [repo](https://github.com/reliverse/relico) — [npm](https://npmjs.com/@reliverse/relico)
|
|
10
6
|
|
|
11
7
|
## Why Relico?
|
|
12
8
|
|
|
@@ -28,7 +24,7 @@ Because terminal styling shouldn't feel like duct tape. **Relico** brings design
|
|
|
28
24
|
## Installation
|
|
29
25
|
|
|
30
26
|
```bash
|
|
31
|
-
bun add @reliverse/
|
|
27
|
+
bun add @reliverse/relico
|
|
32
28
|
# bun • pnpm • yarn • npm
|
|
33
29
|
```
|
|
34
30
|
|
|
@@ -95,7 +91,7 @@ The readme will be updated soon.
|
|
|
95
91
|
**If you're end-user OR developer, create `relico.config.ts` in your root**:
|
|
96
92
|
|
|
97
93
|
```ts
|
|
98
|
-
import { defineConfig } from "@reliverse/
|
|
94
|
+
import { defineConfig } from "@reliverse/relico";
|
|
99
95
|
|
|
100
96
|
export default defineConfig({
|
|
101
97
|
colorLevel: 3, // 0 = off, 1 = basic, 2 = 256, 3 = truecolor
|
|
@@ -112,7 +108,7 @@ export default defineConfig({
|
|
|
112
108
|
**If you're developer, initialize in your app (optional)**:
|
|
113
109
|
|
|
114
110
|
```ts
|
|
115
|
-
import { initUserConfig, re } from "@reliverse/
|
|
111
|
+
import { initUserConfig, re } from "@reliverse/relico";
|
|
116
112
|
|
|
117
113
|
// Use this to override Relico's
|
|
118
114
|
// default settings for your app
|
|
@@ -124,7 +120,7 @@ console.log(re.info("Custom config loaded!"));
|
|
|
124
120
|
## API Sneak Peek
|
|
125
121
|
|
|
126
122
|
```ts
|
|
127
|
-
import { re, rgb } from "@reliverse/
|
|
123
|
+
import { re, rgb } from "@reliverse/relico";
|
|
128
124
|
|
|
129
125
|
console.log(re.red("Red!"));
|
|
130
126
|
console.log(re.bold(re.green("Bold green")));
|
|
@@ -175,7 +171,7 @@ console.log(boldRed("This text is bold and red"));
|
|
|
175
171
|
### Want to Get Only Certain Colors?
|
|
176
172
|
|
|
177
173
|
```ts
|
|
178
|
-
import type { DefaultColorKeys } from "@reliverse/
|
|
174
|
+
import type { DefaultColorKeys } from "@reliverse/relico";
|
|
179
175
|
const brandColors: DefaultColorKeys[] = ["magentaBright", "maroon"];
|
|
180
176
|
```
|
|
181
177
|
|
|
@@ -184,7 +180,7 @@ const brandColors: DefaultColorKeys[] = ["magentaBright", "maroon"];
|
|
|
184
180
|
Relico detects your terminal's capability:
|
|
185
181
|
|
|
186
182
|
```ts
|
|
187
|
-
import { colorSupport } from "@reliverse/
|
|
183
|
+
import { colorSupport } from "@reliverse/relico";
|
|
188
184
|
|
|
189
185
|
console.log(colorSupport.terminalName); // iTerm2, Windows Terminal, etc.
|
|
190
186
|
console.log(colorSupport.level); // 0, 1, 2, or 3
|
|
@@ -200,7 +196,7 @@ console.log(colorSupport.level); // 0, 1, 2, or 3
|
|
|
200
196
|
### Custom RGB + Hex
|
|
201
197
|
|
|
202
198
|
```ts
|
|
203
|
-
import { rgb, bgHex, hex } from "@reliverse/
|
|
199
|
+
import { rgb, bgHex, hex } from "@reliverse/relico";
|
|
204
200
|
|
|
205
201
|
console.log(rgb(255, 105, 180)("Hot pink"));
|
|
206
202
|
console.log(bgHex("#1e90ff")("Dodger blue background"));
|
|
@@ -209,7 +205,7 @@ console.log(bgHex("#1e90ff")("Dodger blue background"));
|
|
|
209
205
|
### Gradients & Rainbow
|
|
210
206
|
|
|
211
207
|
```ts
|
|
212
|
-
import { gradient, multiGradient, rainbow } from "@reliverse/
|
|
208
|
+
import { gradient, multiGradient, rainbow } from "@reliverse/relico";
|
|
213
209
|
|
|
214
210
|
console.log(rainbow("🎉 Woohoo!"));
|
|
215
211
|
console.log(gradient("From red to blue", "#ff0000", "#0000ff"));
|
|
@@ -222,7 +218,7 @@ This function allows you to combine multiple color formatters into a single form
|
|
|
222
218
|
## Basic Usage
|
|
223
219
|
|
|
224
220
|
```typescript
|
|
225
|
-
import { re, chain } from "@reliverse/
|
|
221
|
+
import { re, chain } from "@reliverse/relico";
|
|
226
222
|
|
|
227
223
|
// Create a custom style that combines bold and red text
|
|
228
224
|
const boldRed = chain(re.bold, re.red);
|
|
@@ -240,7 +236,7 @@ console.log(importantError("CRITICAL ERROR: System failure"));
|
|
|
240
236
|
## Creating Theme Combinations
|
|
241
237
|
|
|
242
238
|
```typescript
|
|
243
|
-
import { re, chain } from "@reliverse/
|
|
239
|
+
import { re, chain } from "@reliverse/relico";
|
|
244
240
|
|
|
245
241
|
// Create themed message styles
|
|
246
242
|
const successStyle = chain(re.bold, re.green);
|
|
@@ -258,7 +254,7 @@ console.log(warnStyle("⚠ API rate limit approaching"));
|
|
|
258
254
|
## Custom RGB Combinations
|
|
259
255
|
|
|
260
256
|
```typescript
|
|
261
|
-
import { re, rgb, bgRgb, chain } from "@reliverse/
|
|
257
|
+
import { re, rgb, bgRgb, chain } from "@reliverse/relico";
|
|
262
258
|
|
|
263
259
|
// Create a custom color scheme with RGB values
|
|
264
260
|
const customHeader = chain(
|
|
@@ -282,7 +278,7 @@ console.log(danger("Danger: High voltage detected!"));
|
|
|
282
278
|
The `chain()` function automatically handles multiline text to prevent style leakage:
|
|
283
279
|
|
|
284
280
|
```typescript
|
|
285
|
-
import { re, chain } from "@reliverse/
|
|
281
|
+
import { re, chain } from "@reliverse/relico";
|
|
286
282
|
|
|
287
283
|
const highlight = chain(re.bgYellow, re.black, re.bold);
|
|
288
284
|
|
|
@@ -297,7 +293,7 @@ console.log(highlight(multilineText));
|
|
|
297
293
|
## Creating a Simple Logger
|
|
298
294
|
|
|
299
295
|
```typescript
|
|
300
|
-
import { re, chain } from "@reliverse/
|
|
296
|
+
import { re, chain } from "@reliverse/relico";
|
|
301
297
|
|
|
302
298
|
// Create logger styles
|
|
303
299
|
const styles = {
|
|
@@ -320,7 +316,7 @@ const logger = {
|
|
|
320
316
|
// Usage
|
|
321
317
|
logger.info("Application started");
|
|
322
318
|
logger.success("Data loaded successfully");
|
|
323
|
-
logger.
|
|
319
|
+
logger.warn("Cache expired, refreshing data");
|
|
324
320
|
logger.error("Failed to connect to database");
|
|
325
321
|
logger.debug("Request payload: " + JSON.stringify({id: 123}));
|
|
326
322
|
```
|
package/dist/mod.d.ts
CHANGED
|
@@ -48,4 +48,13 @@ declare const OP_SYMBOL: unique symbol;
|
|
|
48
48
|
export declare const setColorLevel: (level: ColorLevel) => void;
|
|
49
49
|
export declare const re: Re;
|
|
50
50
|
export declare const chain: (...parts: FormatCallable[]) => Re;
|
|
51
|
+
type BunColorInput = {
|
|
52
|
+
r: number;
|
|
53
|
+
g: number;
|
|
54
|
+
b: number;
|
|
55
|
+
a?: number;
|
|
56
|
+
} | [number, number, number] | [number, number, number, number] | string | number | {
|
|
57
|
+
toString(): string;
|
|
58
|
+
};
|
|
59
|
+
export declare const color: (input: BunColorInput, isBg?: boolean) => Re;
|
|
51
60
|
export {};
|
package/dist/mod.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const OP_SYMBOL = Symbol("re.ops");
|
|
1
|
+
const RESET = "\x1B[0m";
|
|
2
|
+
const OP_SYMBOL = /* @__PURE__ */ Symbol("re.ops");
|
|
4
3
|
const COLOR_LEVEL_OFF = 0;
|
|
5
4
|
const COLOR_LEVEL_BASIC = 1;
|
|
6
5
|
const COLOR_LEVEL_256 = 2;
|
|
@@ -30,17 +29,12 @@ const SGR_UNDERLINE = 4;
|
|
|
30
29
|
const SGR_INVERSE = 7;
|
|
31
30
|
const SGR_HIDDEN = 8;
|
|
32
31
|
const SGR_STRIKETHROUGH = 9;
|
|
33
|
-
const HEX_BYTE_LENGTH = 2;
|
|
34
|
-
const HEX_RED_START = 0;
|
|
35
|
-
const HEX_GREEN_START = 2;
|
|
36
|
-
const HEX_BLUE_START = 4;
|
|
37
|
-
const HEX_BLUE_END = 6;
|
|
38
32
|
const HEX_RADIX = 16;
|
|
39
33
|
const BRIGHT_SUFFIX_LENGTH = 6;
|
|
40
34
|
const BG_PREFIX_LENGTH = 2;
|
|
41
|
-
const BG_COLOR_START = 3;
|
|
42
35
|
const BRIGHT_MIX_FACTOR = 0.25;
|
|
43
|
-
const
|
|
36
|
+
const BRIGHT_SUFFIX = "Bright";
|
|
37
|
+
const IS_BUN = typeof process !== "undefined" && process.versions?.bun !== void 0 && typeof Bun !== "undefined" && typeof Bun.color === "function";
|
|
44
38
|
let CURRENT_LEVEL = COLOR_LEVEL_TRUECOLOR;
|
|
45
39
|
export const setColorLevel = (level) => {
|
|
46
40
|
if (level !== COLOR_LEVEL_OFF && level !== COLOR_LEVEL_BASIC && level !== COLOR_LEVEL_256 && level !== COLOR_LEVEL_TRUECOLOR) {
|
|
@@ -49,44 +43,55 @@ export const setColorLevel = (level) => {
|
|
|
49
43
|
CURRENT_LEVEL = level;
|
|
50
44
|
};
|
|
51
45
|
const clampByte = (n) => {
|
|
52
|
-
if (!Number.isFinite(n))
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
if (n < MIN_BYTE) {
|
|
56
|
-
return MIN_BYTE;
|
|
57
|
-
}
|
|
58
|
-
if (n > MAX_BYTE) {
|
|
59
|
-
return MAX_BYTE;
|
|
60
|
-
}
|
|
46
|
+
if (n <= MIN_BYTE || !Number.isFinite(n)) return MIN_BYTE;
|
|
47
|
+
if (n >= MAX_BYTE) return MAX_BYTE;
|
|
61
48
|
return Math.round(n);
|
|
62
49
|
};
|
|
63
|
-
const BASIC8 = [
|
|
64
|
-
{ r: 0, g: 0, b: 0 },
|
|
65
|
-
|
|
66
|
-
{ r:
|
|
67
|
-
|
|
68
|
-
{ r: 0, g:
|
|
69
|
-
|
|
70
|
-
{ r:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
50
|
+
const BASIC8 = Object.freeze([
|
|
51
|
+
Object.freeze({ r: 0, g: 0, b: 0 }),
|
|
52
|
+
Object.freeze({ r: 205, g: 0, b: 0 }),
|
|
53
|
+
Object.freeze({ r: 0, g: 205, b: 0 }),
|
|
54
|
+
Object.freeze({ r: 205, g: 205, b: 0 }),
|
|
55
|
+
Object.freeze({ r: 0, g: 0, b: 238 }),
|
|
56
|
+
Object.freeze({ r: 205, g: 0, b: 205 }),
|
|
57
|
+
Object.freeze({ r: 0, g: 205, b: 205 }),
|
|
58
|
+
Object.freeze({ r: 229, g: 229, b: 229 })
|
|
59
|
+
]);
|
|
60
|
+
const sgrCache = /* @__PURE__ */ new Map();
|
|
61
|
+
const sgr = (codes) => {
|
|
62
|
+
if (codes.length === 1) {
|
|
63
|
+
const code = codes[0];
|
|
64
|
+
const cached2 = sgrCache.get(String(code));
|
|
65
|
+
if (cached2) return cached2;
|
|
66
|
+
const seq2 = `\x1B[${code}m`;
|
|
67
|
+
sgrCache.set(String(code), seq2);
|
|
68
|
+
return seq2;
|
|
69
|
+
}
|
|
70
|
+
const key = codes.join(";");
|
|
71
|
+
const cached = sgrCache.get(key);
|
|
72
|
+
if (cached) return cached;
|
|
73
|
+
const seq = `\x1B[${key}m`;
|
|
74
|
+
sgrCache.set(key, seq);
|
|
75
|
+
return seq;
|
|
76
|
+
};
|
|
82
77
|
const nearestBasicIndex = (rgb) => {
|
|
78
|
+
if (IS_BUN) {
|
|
79
|
+
const ansiStr = Bun.color(rgb, "ansi-16");
|
|
80
|
+
if (ansiStr) {
|
|
81
|
+
const match = ansiStr.match(/38;5;(\d+)/);
|
|
82
|
+
if (match?.[1]) {
|
|
83
|
+
return Number.parseInt(match[1], 10) & 7;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
83
87
|
let best = 0;
|
|
84
88
|
let bestDist = Number.POSITIVE_INFINITY;
|
|
85
|
-
for (let i = 0; i <
|
|
89
|
+
for (let i = 0; i < 8; i++) {
|
|
86
90
|
const c = BASIC8[i];
|
|
87
91
|
const dr = c.r - rgb.r;
|
|
88
92
|
const dg = c.g - rgb.g;
|
|
89
93
|
const db = c.b - rgb.b;
|
|
94
|
+
if (dr === 0 && dg === 0 && db === 0) return i;
|
|
90
95
|
const d = dr * dr + dg * dg + db * db;
|
|
91
96
|
if (d < bestDist) {
|
|
92
97
|
bestDist = d;
|
|
@@ -96,22 +101,27 @@ const nearestBasicIndex = (rgb) => {
|
|
|
96
101
|
return best;
|
|
97
102
|
};
|
|
98
103
|
const rgbToAnsi256 = (rgb) => {
|
|
99
|
-
if (
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
if (IS_BUN) {
|
|
105
|
+
const ansiStr = Bun.color(rgb, "ansi-256");
|
|
106
|
+
if (ansiStr) {
|
|
107
|
+
const match = ansiStr.match(/38;5;(\d+)/);
|
|
108
|
+
if (match?.[1]) {
|
|
109
|
+
return Number.parseInt(match[1], 10);
|
|
110
|
+
}
|
|
105
111
|
}
|
|
112
|
+
}
|
|
113
|
+
if (rgb.r === rgb.g && rgb.g === rgb.b) {
|
|
114
|
+
if (rgb.r < ANSI_256_GRAYSCALE_MIN) return ANSI_256_BASE_OFFSET;
|
|
115
|
+
if (rgb.r > ANSI_256_GRAYSCALE_MAX) return ANSI_256_BRIGHT_THRESHOLD;
|
|
106
116
|
const step = Math.round(
|
|
107
117
|
(rgb.r - ANSI_256_GRAYSCALE_MIN) / ANSI_256_GRAYSCALE_RANGE * ANSI_256_GRAYSCALE_STEPS
|
|
108
118
|
);
|
|
109
119
|
return ANSI_256_GRAYSCALE_BASE + step;
|
|
110
120
|
}
|
|
111
|
-
const r =
|
|
112
|
-
const g =
|
|
113
|
-
const b =
|
|
114
|
-
return ANSI_256_BASE_OFFSET +
|
|
121
|
+
const r = (rgb.r * ANSI_256_RGB_LEVELS + 127) / MAX_BYTE | 0;
|
|
122
|
+
const g = (rgb.g * ANSI_256_RGB_LEVELS + 127) / MAX_BYTE | 0;
|
|
123
|
+
const b = (rgb.b * ANSI_256_RGB_LEVELS + 127) / MAX_BYTE | 0;
|
|
124
|
+
return ANSI_256_BASE_OFFSET + r * ANSI_256_RGB_RED_MULTIPLIER + g * ANSI_256_RGB_GREEN_MULTIPLIER + b;
|
|
115
125
|
};
|
|
116
126
|
const NAMED_COLORS = {
|
|
117
127
|
black: "#000000",
|
|
@@ -134,57 +144,70 @@ const NAMED_COLORS = {
|
|
|
134
144
|
olive: "#808000",
|
|
135
145
|
silver: "#c0c0c0"
|
|
136
146
|
};
|
|
147
|
+
const rgbCache = /* @__PURE__ */ new Map();
|
|
148
|
+
for (const name of ["black", "white", "red", "green", "blue"]) {
|
|
149
|
+
const hex = NAMED_COLORS[name];
|
|
150
|
+
if (hex) {
|
|
151
|
+
const clean = hex.slice(1);
|
|
152
|
+
const r = Number.parseInt(clean.slice(0, 2), HEX_RADIX);
|
|
153
|
+
const g = Number.parseInt(clean.slice(2, 4), HEX_RADIX);
|
|
154
|
+
const b = Number.parseInt(clean.slice(4, 6), HEX_RADIX);
|
|
155
|
+
rgbCache.set(name, Object.freeze({ r, g, b }));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
137
158
|
const mixWithWhite = (rgb, factor) => {
|
|
138
|
-
const
|
|
159
|
+
const invFactor = 1 - factor;
|
|
139
160
|
return {
|
|
140
|
-
r: clampByte(rgb.r *
|
|
141
|
-
g: clampByte(rgb.g *
|
|
142
|
-
b: clampByte(rgb.b *
|
|
161
|
+
r: clampByte(rgb.r * invFactor + WHITE_RGB * factor),
|
|
162
|
+
g: clampByte(rgb.g * invFactor + WHITE_RGB * factor),
|
|
163
|
+
b: clampByte(rgb.b * invFactor + WHITE_RGB * factor)
|
|
143
164
|
};
|
|
144
165
|
};
|
|
145
166
|
const fromNamed = (name) => {
|
|
167
|
+
const cached = rgbCache.get(name);
|
|
168
|
+
if (cached) return cached;
|
|
146
169
|
const hex = NAMED_COLORS[name];
|
|
147
|
-
if (!hex
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
170
|
+
if (!hex) return { r: 0, g: 0, b: 0 };
|
|
171
|
+
if (IS_BUN) {
|
|
172
|
+
const rgb = Bun.color(hex, "{rgb}");
|
|
173
|
+
if (rgb) {
|
|
174
|
+
rgbCache.set(name, rgb);
|
|
175
|
+
return rgb;
|
|
176
|
+
}
|
|
153
177
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
178
|
+
const clean = hex[0] === "#" ? hex.slice(1) : hex;
|
|
179
|
+
const len = clean.length;
|
|
180
|
+
let r, g, b;
|
|
181
|
+
if (len === 3) {
|
|
182
|
+
const rv = Number.parseInt(clean[0], HEX_RADIX);
|
|
183
|
+
const gv = Number.parseInt(clean[1], HEX_RADIX);
|
|
184
|
+
const bv = Number.parseInt(clean[2], HEX_RADIX);
|
|
185
|
+
r = rv << 4 | rv;
|
|
186
|
+
g = gv << 4 | gv;
|
|
187
|
+
b = bv << 4 | bv;
|
|
188
|
+
} else if (len === 6) {
|
|
189
|
+
r = Number.parseInt(clean.slice(0, 2), HEX_RADIX);
|
|
190
|
+
g = Number.parseInt(clean.slice(2, 4), HEX_RADIX);
|
|
191
|
+
b = Number.parseInt(clean.slice(4, 6), HEX_RADIX);
|
|
159
192
|
} else {
|
|
160
|
-
|
|
161
|
-
gHex = clean.slice(HEX_GREEN_START, HEX_BLUE_START);
|
|
162
|
-
bHex = clean.slice(HEX_BLUE_START, HEX_BLUE_END);
|
|
193
|
+
return { r: 0, g: 0, b: 0 };
|
|
163
194
|
}
|
|
164
|
-
const r = Number.parseInt(rHex, HEX_RADIX);
|
|
165
|
-
const g = Number.parseInt(gHex, HEX_RADIX);
|
|
166
|
-
const b = Number.parseInt(bHex, HEX_RADIX);
|
|
167
195
|
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {
|
|
168
196
|
return { r: 0, g: 0, b: 0 };
|
|
169
197
|
}
|
|
170
|
-
|
|
198
|
+
const result = Object.freeze({ r, g, b });
|
|
199
|
+
rgbCache.set(name, result);
|
|
200
|
+
return result;
|
|
171
201
|
};
|
|
172
202
|
const toBaseName = (compound) => {
|
|
173
|
-
if (!compound
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if (!base) {
|
|
178
|
-
return "black";
|
|
179
|
-
}
|
|
180
|
-
const key = base.charAt(0).toLowerCase() + base.slice(1);
|
|
181
|
-
return key;
|
|
203
|
+
if (!compound) return "black";
|
|
204
|
+
const base = compound.slice(0, -BRIGHT_SUFFIX_LENGTH);
|
|
205
|
+
if (!base) return "black";
|
|
206
|
+
return base[0]?.toLowerCase() + base.slice(1);
|
|
182
207
|
};
|
|
183
208
|
const parseColorName = (name) => {
|
|
184
|
-
if (!name
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
if (name.endsWith("Bright")) {
|
|
209
|
+
if (!name) return { rgb: { r: 0, g: 0, b: 0 }, wantBright: false };
|
|
210
|
+
if (name.endsWith(BRIGHT_SUFFIX)) {
|
|
188
211
|
const base = toBaseName(name);
|
|
189
212
|
const rgb = fromNamed(base);
|
|
190
213
|
const rgbAdj = mixWithWhite(rgb, BRIGHT_MIX_FACTOR);
|
|
@@ -193,63 +216,110 @@ const parseColorName = (name) => {
|
|
|
193
216
|
return { rgb: fromNamed(name), wantBright: false };
|
|
194
217
|
};
|
|
195
218
|
const openForOp = (op) => {
|
|
196
|
-
if (CURRENT_LEVEL === COLOR_LEVEL_OFF)
|
|
197
|
-
return "";
|
|
198
|
-
}
|
|
219
|
+
if (CURRENT_LEVEL === COLOR_LEVEL_OFF) return "";
|
|
199
220
|
switch (op.kind) {
|
|
200
221
|
case "style":
|
|
201
222
|
return sgr(op.open);
|
|
202
|
-
case "fg-basic":
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
case "
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
case "fg-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
223
|
+
case "fg-basic": {
|
|
224
|
+
const code = (op.bright ? SGR_FG_BRIGHT_BASE : SGR_FG_BASE) + op.idx;
|
|
225
|
+
return sgr([code]);
|
|
226
|
+
}
|
|
227
|
+
case "bg-basic": {
|
|
228
|
+
const code = (op.bright ? SGR_BG_BRIGHT_BASE : SGR_BG_BASE) + op.idx;
|
|
229
|
+
return sgr([code]);
|
|
230
|
+
}
|
|
231
|
+
case "fg-256": {
|
|
232
|
+
if (IS_BUN) {
|
|
233
|
+
const ansi = Bun.color(op.code, "ansi-256");
|
|
234
|
+
if (ansi) return ansi;
|
|
235
|
+
}
|
|
236
|
+
return `\x1B[38;5;${op.code}m`;
|
|
237
|
+
}
|
|
238
|
+
case "bg-256": {
|
|
239
|
+
if (IS_BUN) {
|
|
240
|
+
const fgAnsi = Bun.color(op.code, "ansi-256");
|
|
241
|
+
if (fgAnsi) return fgAnsi.replace("38;5;", "48;5;");
|
|
242
|
+
}
|
|
243
|
+
return `\x1B[48;5;${op.code}m`;
|
|
244
|
+
}
|
|
245
|
+
case "fg-true": {
|
|
246
|
+
if (IS_BUN) {
|
|
247
|
+
const ansi = Bun.color(op.rgb, "ansi-16m");
|
|
248
|
+
if (ansi) return ansi;
|
|
249
|
+
}
|
|
250
|
+
const { r, g, b } = op.rgb;
|
|
251
|
+
return `\x1B[38;2;${r};${g};${b}m`;
|
|
252
|
+
}
|
|
253
|
+
case "bg-true": {
|
|
254
|
+
if (IS_BUN) {
|
|
255
|
+
const fgAnsi = Bun.color(op.rgb, "ansi-16m");
|
|
256
|
+
if (fgAnsi) return fgAnsi.replace("38;2;", "48;2;");
|
|
257
|
+
}
|
|
258
|
+
const { r, g, b } = op.rgb;
|
|
259
|
+
return `\x1B[48;2;${r};${g};${b}m`;
|
|
260
|
+
}
|
|
214
261
|
default:
|
|
215
262
|
return "";
|
|
216
263
|
}
|
|
217
264
|
};
|
|
218
265
|
const opsToOpen = (ops) => {
|
|
219
|
-
if (CURRENT_LEVEL === COLOR_LEVEL_OFF)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
266
|
+
if (CURRENT_LEVEL === COLOR_LEVEL_OFF) return "";
|
|
267
|
+
const len = ops.length;
|
|
268
|
+
if (len === 0) return "";
|
|
269
|
+
if (len === 1) return openForOp(ops[0]);
|
|
270
|
+
let result = "";
|
|
271
|
+
for (let i = 0; i < len; i++) {
|
|
272
|
+
result += openForOp(ops[i]);
|
|
225
273
|
}
|
|
226
|
-
return
|
|
274
|
+
return result;
|
|
227
275
|
};
|
|
228
276
|
const applyOpsToText = (ops, input) => {
|
|
229
277
|
const text = String(input);
|
|
230
|
-
|
|
278
|
+
const textLen = text.length;
|
|
279
|
+
if (CURRENT_LEVEL === COLOR_LEVEL_OFF || ops.length === 0 || textLen === 0) {
|
|
231
280
|
return text;
|
|
232
281
|
}
|
|
233
282
|
const open = opsToOpen(ops);
|
|
234
|
-
if (!
|
|
283
|
+
if (!open) return text;
|
|
284
|
+
const nlIdx = text.indexOf("\n");
|
|
285
|
+
if (nlIdx === -1) {
|
|
235
286
|
return `${open}${text}${RESET}`;
|
|
236
287
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const
|
|
241
|
-
if (
|
|
242
|
-
|
|
288
|
+
let result = "";
|
|
289
|
+
let start = 0;
|
|
290
|
+
while (start < textLen) {
|
|
291
|
+
const end = text.indexOf("\n", start);
|
|
292
|
+
if (end === -1) {
|
|
293
|
+
const line2 = text.slice(start);
|
|
294
|
+
if (line2) {
|
|
295
|
+
if (line2.endsWith("\r")) {
|
|
296
|
+
result += `${open}${line2.slice(0, -1)}\r${RESET}`;
|
|
297
|
+
} else {
|
|
298
|
+
result += `${open}${line2}${RESET}`;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
if (start > 0) result += "\n";
|
|
304
|
+
const line = text.slice(start, end);
|
|
305
|
+
if (line) {
|
|
306
|
+
if (line.endsWith("\r")) {
|
|
307
|
+
result += `${open}${line.slice(0, -1)}\r${RESET}`;
|
|
308
|
+
} else {
|
|
309
|
+
result += `${open}${line}${RESET}`;
|
|
310
|
+
}
|
|
243
311
|
} else {
|
|
244
|
-
result
|
|
312
|
+
result += `${open}${RESET}`;
|
|
245
313
|
}
|
|
314
|
+
start = end + 1;
|
|
246
315
|
}
|
|
247
|
-
return result
|
|
316
|
+
return result;
|
|
248
317
|
};
|
|
249
318
|
const mkFgOpsFromRgb = (rgb, wantBright = false) => {
|
|
250
319
|
if (CURRENT_LEVEL === COLOR_LEVEL_BASIC) {
|
|
251
|
-
|
|
252
|
-
|
|
320
|
+
return [
|
|
321
|
+
{ kind: "fg-basic", idx: nearestBasicIndex(rgb), bright: wantBright }
|
|
322
|
+
];
|
|
253
323
|
}
|
|
254
324
|
if (CURRENT_LEVEL === COLOR_LEVEL_256) {
|
|
255
325
|
return [{ kind: "fg-256", code: rgbToAnsi256(rgb) }];
|
|
@@ -258,8 +328,9 @@ const mkFgOpsFromRgb = (rgb, wantBright = false) => {
|
|
|
258
328
|
};
|
|
259
329
|
const mkBgOpsFromRgb = (rgb, wantBright = false) => {
|
|
260
330
|
if (CURRENT_LEVEL === COLOR_LEVEL_BASIC) {
|
|
261
|
-
|
|
262
|
-
|
|
331
|
+
return [
|
|
332
|
+
{ kind: "bg-basic", idx: nearestBasicIndex(rgb), bright: wantBright }
|
|
333
|
+
];
|
|
263
334
|
}
|
|
264
335
|
if (CURRENT_LEVEL === COLOR_LEVEL_256) {
|
|
265
336
|
return [{ kind: "bg-256", code: rgbToAnsi256(rgb) }];
|
|
@@ -267,46 +338,57 @@ const mkBgOpsFromRgb = (rgb, wantBright = false) => {
|
|
|
267
338
|
return [{ kind: "bg-true", rgb }];
|
|
268
339
|
};
|
|
269
340
|
const STYLE_TABLE = {
|
|
270
|
-
reset: { kind: "style", open: [SGR_RESET] },
|
|
271
|
-
bold: { kind: "style", open: [SGR_BOLD] },
|
|
272
|
-
dim: { kind: "style", open: [SGR_DIM] },
|
|
273
|
-
italic: { kind: "style", open: [SGR_ITALIC] },
|
|
274
|
-
underline: { kind: "style", open: [SGR_UNDERLINE] },
|
|
275
|
-
inverse: { kind: "style", open: [SGR_INVERSE] },
|
|
276
|
-
hidden: { kind: "style", open: [SGR_HIDDEN] },
|
|
277
|
-
strikethrough: { kind: "style", open: [SGR_STRIKETHROUGH] }
|
|
278
|
-
};
|
|
279
|
-
const STYLE_KEYS = /* @__PURE__ */ new Set([
|
|
280
|
-
"reset",
|
|
281
|
-
"bold",
|
|
282
|
-
"dim",
|
|
283
|
-
"italic",
|
|
284
|
-
"underline",
|
|
285
|
-
"inverse",
|
|
286
|
-
"hidden",
|
|
287
|
-
"strikethrough"
|
|
288
|
-
]);
|
|
289
|
-
const isColorKey = (key) => {
|
|
290
|
-
if (!key || typeof key !== "string") {
|
|
291
|
-
return false;
|
|
292
|
-
}
|
|
293
|
-
if (key in NAMED_COLORS) {
|
|
294
|
-
return true;
|
|
295
|
-
}
|
|
296
|
-
if (key.endsWith("Bright") && key.length > BRIGHT_SUFFIX_LENGTH) {
|
|
297
|
-
const baseName = key.slice(0, -BRIGHT_SUFFIX_LENGTH);
|
|
298
|
-
return baseName in NAMED_COLORS;
|
|
299
|
-
}
|
|
300
|
-
return false;
|
|
341
|
+
reset: Object.freeze({ kind: "style", open: [SGR_RESET] }),
|
|
342
|
+
bold: Object.freeze({ kind: "style", open: [SGR_BOLD] }),
|
|
343
|
+
dim: Object.freeze({ kind: "style", open: [SGR_DIM] }),
|
|
344
|
+
italic: Object.freeze({ kind: "style", open: [SGR_ITALIC] }),
|
|
345
|
+
underline: Object.freeze({ kind: "style", open: [SGR_UNDERLINE] }),
|
|
346
|
+
inverse: Object.freeze({ kind: "style", open: [SGR_INVERSE] }),
|
|
347
|
+
hidden: Object.freeze({ kind: "style", open: [SGR_HIDDEN] }),
|
|
348
|
+
strikethrough: Object.freeze({ kind: "style", open: [SGR_STRIKETHROUGH] })
|
|
301
349
|
};
|
|
350
|
+
const STYLE_KEYS = Object.freeze(
|
|
351
|
+
/* @__PURE__ */ new Set([
|
|
352
|
+
"reset",
|
|
353
|
+
"bold",
|
|
354
|
+
"dim",
|
|
355
|
+
"italic",
|
|
356
|
+
"underline",
|
|
357
|
+
"inverse",
|
|
358
|
+
"hidden",
|
|
359
|
+
"strikethrough"
|
|
360
|
+
])
|
|
361
|
+
);
|
|
362
|
+
const COLOR_KEYS = Object.freeze(new Set(Object.keys(NAMED_COLORS)));
|
|
363
|
+
const BRIGHT_COLOR_KEYS = Object.freeze(
|
|
364
|
+
new Set(Object.keys(NAMED_COLORS).map((name) => `${name}Bright`))
|
|
365
|
+
);
|
|
366
|
+
const BG_COLOR_KEYS = Object.freeze(
|
|
367
|
+
new Set(
|
|
368
|
+
Object.keys(NAMED_COLORS).map(
|
|
369
|
+
(name) => `bg${name[0]?.toUpperCase()}${name.slice(1)}`
|
|
370
|
+
)
|
|
371
|
+
)
|
|
372
|
+
);
|
|
373
|
+
const BG_BRIGHT_COLOR_KEYS = Object.freeze(
|
|
374
|
+
new Set(
|
|
375
|
+
Object.keys(NAMED_COLORS).map(
|
|
376
|
+
(name) => `bg${name[0]?.toUpperCase()}${name.slice(1)}Bright`
|
|
377
|
+
)
|
|
378
|
+
)
|
|
379
|
+
);
|
|
380
|
+
const isColorKey = (key) => COLOR_KEYS.has(key) || BRIGHT_COLOR_KEYS.has(key);
|
|
302
381
|
const isBgKey = (key) => {
|
|
303
|
-
|
|
382
|
+
const len = key.length;
|
|
383
|
+
if (len <= BG_PREFIX_LENGTH || key[0] !== "b" || key[1] !== "g") {
|
|
304
384
|
return false;
|
|
305
385
|
}
|
|
306
|
-
|
|
307
|
-
return isColorKey(colorPart);
|
|
386
|
+
return BG_COLOR_KEYS.has(key) || BG_BRIGHT_COLOR_KEYS.has(key);
|
|
308
387
|
};
|
|
388
|
+
const proxyCache = /* @__PURE__ */ new WeakMap();
|
|
309
389
|
const callableProxy = (ops) => {
|
|
390
|
+
const cached = proxyCache.get(ops);
|
|
391
|
+
if (cached) return cached;
|
|
310
392
|
const base = ((input) => applyOpsToText(ops, input));
|
|
311
393
|
Object.defineProperty(base, OP_SYMBOL, {
|
|
312
394
|
value: ops,
|
|
@@ -314,47 +396,60 @@ const callableProxy = (ops) => {
|
|
|
314
396
|
configurable: false,
|
|
315
397
|
writable: false
|
|
316
398
|
});
|
|
317
|
-
|
|
399
|
+
const proxy = new Proxy(base, {
|
|
318
400
|
apply(_target, _thisArg, argArray) {
|
|
319
|
-
|
|
320
|
-
return applyOpsToText(ops, input);
|
|
401
|
+
return applyOpsToText(ops, argArray[0]);
|
|
321
402
|
},
|
|
322
403
|
get(_target, prop) {
|
|
404
|
+
if (prop === OP_SYMBOL) return ops;
|
|
323
405
|
const key = String(prop);
|
|
324
|
-
if (prop === OP_SYMBOL) {
|
|
325
|
-
return ops;
|
|
326
|
-
}
|
|
327
406
|
if (STYLE_KEYS.has(key)) {
|
|
328
407
|
const op = STYLE_TABLE[key];
|
|
329
|
-
|
|
408
|
+
const newOps = [...ops, op];
|
|
409
|
+
return callableProxy(newOps);
|
|
330
410
|
}
|
|
331
411
|
if (isBgKey(key)) {
|
|
332
|
-
const
|
|
333
|
-
if (!raw) {
|
|
334
|
-
return callableProxy(ops);
|
|
335
|
-
}
|
|
336
|
-
const colorName = raw.charAt(0).toLowerCase() + raw.slice(1);
|
|
412
|
+
const colorName = key[2]?.toLowerCase() + key.slice(3);
|
|
337
413
|
const { rgb, wantBright } = parseColorName(colorName);
|
|
338
|
-
|
|
414
|
+
const bgOps = mkBgOpsFromRgb(rgb, wantBright);
|
|
415
|
+
const newOps = [...ops, ...bgOps];
|
|
416
|
+
return callableProxy(newOps);
|
|
339
417
|
}
|
|
340
418
|
if (isColorKey(key)) {
|
|
341
419
|
const { rgb, wantBright } = parseColorName(key);
|
|
342
|
-
|
|
420
|
+
const fgOps = mkFgOpsFromRgb(rgb, wantBright);
|
|
421
|
+
const newOps = [...ops, ...fgOps];
|
|
422
|
+
return callableProxy(newOps);
|
|
343
423
|
}
|
|
344
|
-
return
|
|
424
|
+
return proxy;
|
|
345
425
|
}
|
|
346
426
|
});
|
|
427
|
+
proxyCache.set(ops, proxy);
|
|
428
|
+
return proxy;
|
|
347
429
|
};
|
|
348
430
|
export const re = callableProxy([]);
|
|
349
431
|
export const chain = (...parts) => {
|
|
432
|
+
if (parts.length === 0) return re;
|
|
433
|
+
if (parts.length === 1) return parts[0];
|
|
350
434
|
const collected = [];
|
|
351
|
-
for (
|
|
352
|
-
const ops =
|
|
353
|
-
if (ops
|
|
354
|
-
|
|
355
|
-
collected.push(op);
|
|
356
|
-
}
|
|
435
|
+
for (let i = 0; i < parts.length; i++) {
|
|
436
|
+
const ops = parts[i][OP_SYMBOL];
|
|
437
|
+
if (ops?.length) {
|
|
438
|
+
collected.push(...ops);
|
|
357
439
|
}
|
|
358
440
|
}
|
|
359
441
|
return callableProxy(collected);
|
|
360
442
|
};
|
|
443
|
+
export const color = (input, isBg = false) => {
|
|
444
|
+
if (!IS_BUN) {
|
|
445
|
+
if (typeof input === "object" && "r" in input && "g" in input && "b" in input) {
|
|
446
|
+
const ops2 = isBg ? mkBgOpsFromRgb(input, false) : mkFgOpsFromRgb(input, false);
|
|
447
|
+
return callableProxy(ops2);
|
|
448
|
+
}
|
|
449
|
+
return re;
|
|
450
|
+
}
|
|
451
|
+
const rgb = Bun.color(input, "{rgb}");
|
|
452
|
+
if (!rgb) return re;
|
|
453
|
+
const ops = isBg ? mkBgOpsFromRgb(rgb, false) : mkFgOpsFromRgb(rgb, false);
|
|
454
|
+
return callableProxy(ops);
|
|
455
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reliverse/relico",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
3
|
+
"version": "2.2.7",
|
|
4
|
+
"private": false,
|
|
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": {
|
|
@@ -10,12 +10,13 @@
|
|
|
10
10
|
"default": "./dist/mod.js"
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {},
|
|
13
17
|
"files": [
|
|
14
18
|
"dist",
|
|
15
19
|
"package.json"
|
|
16
20
|
],
|
|
17
|
-
"publishConfig": {
|
|
18
|
-
"access": "public"
|
|
19
|
-
},
|
|
20
21
|
"license": "MIT"
|
|
21
22
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) Nazar Kornienko (blefnk), Bleverse, Reliverse
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|