kailogger 1.0.1-dark.red → 1.0.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/README.md +60 -48
- package/dist/core/Logger.d.ts +1 -1
- package/dist/core/Logger.js +36 -39
- package/dist/features/Chart.js +8 -9
- package/dist/features/Diff.js +4 -8
- package/dist/features/Encrypt.js +5 -5
- package/dist/features/Notify.js +40 -1
- package/dist/features/Screenshot.js +0 -3
- package/dist/features/Sound.d.ts +1 -0
- package/dist/features/Sound.js +37 -39
- package/dist/features/Timer.js +3 -7
- package/dist/features/Tree.js +5 -8
- package/dist/icon/logo.png +0 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -7
- package/dist/sounds/error.wav +0 -0
- package/dist/sounds/notification.wav +0 -0
- package/dist/sounds/success.wav +0 -0
- package/dist/sounds/warning.wav +0 -0
- package/dist/styles/KaiChroma.d.ts +85 -0
- package/dist/styles/KaiChroma.js +407 -0
- package/dist/styles/palettes.d.ts +21 -57
- package/dist/styles/palettes.js +160 -37
- package/dist/transports/ConsoleTransport.js +2 -2
- package/dist/utils/json.js +8 -11
- package/dist/utils/prettyError.js +16 -18
- package/dist/utils/progress.js +5 -7
- package/dist/utils/prompt.js +3 -3
- package/dist/utils/selection.js +14 -24
- package/dist/utils/spinner.d.ts +1 -1
- package/dist/utils/spinner.js +9 -13
- package/dist/utils/stripAnsi.js +2 -1
- package/dist/utils/table.js +4 -7
- package/examples/demo.js +134 -0
- package/package.json +4 -9
- package/scripts/copy-assets.js +37 -0
- package/src/core/Logger.ts +148 -31
- package/src/features/Chart.ts +25 -5
- package/src/features/Diff.ts +2 -2
- package/src/features/Encrypt.ts +13 -5
- package/src/features/Sound.ts +51 -25
- package/src/features/Timer.ts +3 -3
- package/src/features/Tree.ts +6 -5
- package/src/index.ts +1 -1
- package/src/styles/KaiChroma.ts +370 -0
- package/src/styles/palettes.ts +190 -38
- package/src/transports/ConsoleTransport.ts +2 -2
- package/src/utils/json.ts +14 -6
- package/src/utils/prettyError.ts +26 -13
- package/src/utils/progress.ts +19 -5
- package/src/utils/prompt.ts +6 -2
- package/src/utils/selection.ts +40 -17
- package/src/utils/spinner.ts +11 -7
- package/src/utils/stripAnsi.ts +4 -1
- package/src/utils/table.ts +10 -3
- package/src/styles/gradients.ts +0 -22
package/src/utils/prettyError.ts
CHANGED
|
@@ -1,50 +1,63 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
2
|
import * as stackTrace from 'stack-trace';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
|
-
import {
|
|
4
|
+
import { KaiChroma } from '../styles/KaiChroma';
|
|
5
|
+
import { paint } from '../styles/palettes';
|
|
5
6
|
|
|
6
7
|
export class PrettyError {
|
|
7
8
|
static handle(error: Error, theme: any) {
|
|
8
9
|
const trace = stackTrace.parse(error);
|
|
10
|
+
|
|
9
11
|
console.log('');
|
|
10
12
|
console.log(paint.apply(` 💥 ${error.name} `, theme.error));
|
|
11
|
-
console.log(
|
|
12
|
-
|
|
13
|
+
console.log(KaiChroma.bold(error.message));
|
|
14
|
+
|
|
15
|
+
// Find the first relevant frame (not node internal)
|
|
13
16
|
const frame = trace.find(t => {
|
|
14
17
|
const file = t.getFileName();
|
|
15
18
|
return file && !file.includes('node_modules') && !file.startsWith('node:');
|
|
16
19
|
});
|
|
20
|
+
|
|
17
21
|
if (frame) {
|
|
18
22
|
const fileName = frame.getFileName();
|
|
19
23
|
const lineNumber = frame.getLineNumber();
|
|
20
|
-
|
|
21
|
-
console.log(
|
|
24
|
+
|
|
25
|
+
console.log(KaiChroma.hex('#666666', `at ${fileName}:${lineNumber}`));
|
|
26
|
+
console.log(KaiChroma.hex('#666666', '─'.repeat(50)));
|
|
27
|
+
|
|
22
28
|
try {
|
|
23
29
|
const content = fs.readFileSync(fileName, 'utf-8');
|
|
24
30
|
const lines = content.split('\n');
|
|
25
31
|
const start = Math.max(0, lineNumber - 3);
|
|
26
32
|
const end = Math.min(lines.length, lineNumber + 2);
|
|
33
|
+
|
|
27
34
|
for (let i = start; i < end; i++) {
|
|
28
35
|
const isErrorLine = i + 1 === lineNumber;
|
|
29
36
|
const lineNumStr = (i + 1).toString().padEnd(4);
|
|
37
|
+
|
|
30
38
|
if (isErrorLine) {
|
|
31
|
-
const lineContent =
|
|
39
|
+
const lineContent = KaiChroma.bold(lines[i]);
|
|
32
40
|
console.log(paint.apply(` > ${lineNumStr} | ${lineContent}`, theme.error));
|
|
33
41
|
} else {
|
|
34
|
-
console.log(
|
|
42
|
+
console.log(KaiChroma.hex('#888888', ` ${lineNumStr} | ${lines[i]}`));
|
|
35
43
|
}
|
|
36
44
|
}
|
|
37
45
|
} catch (e) {
|
|
38
|
-
|
|
46
|
+
// Fallback if file cannot be read
|
|
47
|
+
console.log(KaiChroma.dim(' (Source code unavailable)'));
|
|
39
48
|
}
|
|
40
|
-
console.log(
|
|
49
|
+
console.log(KaiChroma.hex('#666666', '─'.repeat(50)));
|
|
41
50
|
}
|
|
51
|
+
|
|
52
|
+
// Show simplified stack
|
|
42
53
|
console.log('');
|
|
43
|
-
console.log(
|
|
54
|
+
console.log(KaiChroma.dim('Stack Trace:'));
|
|
44
55
|
trace.forEach(t => {
|
|
56
|
+
const fn = t.getFunctionName() || '<anonymous>';
|
|
45
57
|
const file = t.getFileName();
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
const line = t.getLineNumber();
|
|
59
|
+
if (file && !file.includes('node_modules')) {
|
|
60
|
+
console.log(KaiChroma.dim(` at ${fn} (${file}:${line})`));
|
|
48
61
|
}
|
|
49
62
|
});
|
|
50
63
|
console.log('');
|
package/src/utils/progress.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
import { KaiChroma } from '../styles/KaiChroma';
|
|
2
3
|
import { palettes, ThemeName } from '../styles/palettes';
|
|
3
4
|
|
|
4
5
|
export class KaiProgress {
|
|
@@ -6,35 +7,48 @@ export class KaiProgress {
|
|
|
6
7
|
private current: number;
|
|
7
8
|
private width: number;
|
|
8
9
|
private theme: ThemeName;
|
|
10
|
+
|
|
9
11
|
constructor(total: number, width: number = 40, theme: ThemeName = 'zen') {
|
|
10
12
|
this.total = total;
|
|
11
13
|
this.current = 0;
|
|
12
14
|
this.width = width;
|
|
13
15
|
this.theme = theme;
|
|
14
16
|
}
|
|
17
|
+
|
|
15
18
|
public update(current: number) {
|
|
16
19
|
this.current = current;
|
|
17
20
|
this.render();
|
|
18
21
|
}
|
|
22
|
+
|
|
19
23
|
public increment(amount: number = 1) {
|
|
20
24
|
this.current = Math.min(this.total, this.current + amount);
|
|
21
25
|
this.render();
|
|
22
26
|
}
|
|
27
|
+
|
|
23
28
|
private render() {
|
|
24
29
|
const percentage = Math.min(1, this.current / this.total);
|
|
25
30
|
const filledWidth = Math.round(this.width * percentage);
|
|
26
|
-
const emptyWidth = this.width - filledWidth;
|
|
31
|
+
const emptyWidth = this.width - filledWidth;
|
|
32
|
+
|
|
27
33
|
const filledChar = '█';
|
|
28
34
|
const emptyChar = '░';
|
|
35
|
+
|
|
29
36
|
const filled = filledChar.repeat(filledWidth);
|
|
30
37
|
const empty = emptyChar.repeat(emptyWidth);
|
|
31
|
-
|
|
38
|
+
|
|
39
|
+
const palette = palettes[this.theme];
|
|
32
40
|
const colors = palette.info;
|
|
33
41
|
const dimColor = palette.dim;
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
|
|
43
|
+
// Utilizamos KaiChroma.gradient en lugar de gradient-string
|
|
44
|
+
const barFilled = KaiChroma.gradient(colors, filled);
|
|
45
|
+
const barEmpty = KaiChroma.hex(dimColor, empty);
|
|
46
|
+
const bar = barFilled + barEmpty;
|
|
47
|
+
|
|
36
48
|
const percentText = Math.round(percentage * 100).toString().padStart(3);
|
|
49
|
+
|
|
37
50
|
process.stdout.write(`\r${bar} ${percentText}%`);
|
|
51
|
+
|
|
38
52
|
if (this.current >= this.total) {
|
|
39
53
|
process.stdout.write('\n');
|
|
40
54
|
}
|
package/src/utils/prompt.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
|
|
1
2
|
import * as readline from 'readline';
|
|
2
|
-
import { paint } from '../styles/
|
|
3
|
+
import { paint } from '../styles/palettes';
|
|
3
4
|
|
|
4
5
|
export class KaiPrompt {
|
|
5
6
|
static ask(question: string, theme: any): Promise<string> {
|
|
@@ -7,14 +8,17 @@ export class KaiPrompt {
|
|
|
7
8
|
input: process.stdin,
|
|
8
9
|
output: process.stdout
|
|
9
10
|
});
|
|
11
|
+
|
|
10
12
|
const q = paint.apply(`? ${question} `, theme.info);
|
|
13
|
+
|
|
11
14
|
return new Promise(resolve => {
|
|
12
15
|
rl.question(q, (answer) => {
|
|
13
16
|
rl.close();
|
|
14
|
-
resolve(answer
|
|
17
|
+
resolve(answer);
|
|
15
18
|
});
|
|
16
19
|
});
|
|
17
20
|
}
|
|
21
|
+
|
|
18
22
|
static confirm(question: string, theme: any): Promise<boolean> {
|
|
19
23
|
return this.ask(`${question} (y/n)`, theme).then(ans => {
|
|
20
24
|
return ans.toLowerCase().startsWith('y');
|
package/src/utils/selection.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
|
|
2
|
+
import { KaiChroma } from '../styles/KaiChroma';
|
|
3
|
+
import { paint } from '../styles/palettes';
|
|
1
4
|
import * as readline from 'readline';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { paint } from '../styles/gradients';
|
|
4
5
|
|
|
5
6
|
export class KaiSelection {
|
|
6
7
|
static async select(question: string, options: string[], theme: any): Promise<string> {
|
|
@@ -8,43 +9,52 @@ export class KaiSelection {
|
|
|
8
9
|
let selectedIndex = 0;
|
|
9
10
|
const stdin = process.stdin;
|
|
10
11
|
const stdout = process.stdout;
|
|
12
|
+
|
|
11
13
|
stdin.setRawMode(true);
|
|
12
14
|
stdin.resume();
|
|
13
15
|
stdin.setEncoding('utf8');
|
|
16
|
+
|
|
14
17
|
const render = () => {
|
|
15
18
|
readline.moveCursor(stdout, 0, -(options.length + 1));
|
|
16
19
|
readline.clearScreenDown(stdout);
|
|
20
|
+
|
|
17
21
|
const q = paint.apply(`? ${question} `, theme.info);
|
|
18
22
|
console.log(q);
|
|
23
|
+
|
|
19
24
|
options.forEach((opt, i) => {
|
|
20
25
|
if (i === selectedIndex) {
|
|
21
26
|
const pointer = paint.apply('>', theme.success);
|
|
22
27
|
const text = paint.apply(opt, theme.success);
|
|
23
|
-
console.log(
|
|
28
|
+
console.log(`${pointer} ${text}`);
|
|
24
29
|
} else {
|
|
25
|
-
console.log(
|
|
30
|
+
console.log(` ${opt}`);
|
|
26
31
|
}
|
|
27
32
|
});
|
|
28
33
|
};
|
|
34
|
+
|
|
29
35
|
console.log('\n'.repeat(options.length));
|
|
30
36
|
render();
|
|
37
|
+
|
|
31
38
|
const handler = (key: string) => {
|
|
32
|
-
if (key === '\u0003') {
|
|
39
|
+
if (key === '\u0003') { // Ctrl+C
|
|
33
40
|
process.exit();
|
|
34
41
|
}
|
|
35
|
-
|
|
42
|
+
|
|
43
|
+
if (key === '\u001b[A') { // Up arrow
|
|
36
44
|
selectedIndex = (selectedIndex > 0) ? selectedIndex - 1 : options.length - 1;
|
|
37
45
|
render();
|
|
38
|
-
} else if (key === '\u001b[B') {
|
|
46
|
+
} else if (key === '\u001b[B') { // Down arrow
|
|
39
47
|
selectedIndex = (selectedIndex < options.length - 1) ? selectedIndex + 1 : 0;
|
|
40
48
|
render();
|
|
41
|
-
} else if (key === '\r') {
|
|
49
|
+
} else if (key === '\r') { // Enter
|
|
42
50
|
stdin.removeListener('data', handler);
|
|
43
51
|
stdin.setRawMode(false);
|
|
44
52
|
stdin.pause();
|
|
53
|
+
|
|
45
54
|
readline.moveCursor(stdout, 0, -(options.length + 1));
|
|
46
55
|
readline.clearScreenDown(stdout);
|
|
47
|
-
console.log(`${paint.apply(`✔ ${question}`, theme.success)} ${
|
|
56
|
+
console.log(`${paint.apply(`✔ ${question}`, theme.success)} ${KaiChroma.bold(options[selectedIndex])}`);
|
|
57
|
+
|
|
48
58
|
resolve(options[selectedIndex]);
|
|
49
59
|
}
|
|
50
60
|
};
|
|
@@ -52,61 +62,74 @@ export class KaiSelection {
|
|
|
52
62
|
stdin.on('data', handler);
|
|
53
63
|
});
|
|
54
64
|
}
|
|
65
|
+
|
|
55
66
|
static async multiselect(question: string, options: string[], theme: any): Promise<string[]> {
|
|
56
67
|
return new Promise((resolve) => {
|
|
57
68
|
let selectedIndex = 0;
|
|
58
69
|
const selected = new Set<number>();
|
|
59
70
|
const stdin = process.stdin;
|
|
60
71
|
const stdout = process.stdout;
|
|
72
|
+
|
|
61
73
|
stdin.setRawMode(true);
|
|
62
74
|
stdin.resume();
|
|
63
75
|
stdin.setEncoding('utf8');
|
|
76
|
+
|
|
64
77
|
const render = () => {
|
|
65
78
|
readline.moveCursor(stdout, 0, -(options.length + 1));
|
|
66
79
|
readline.clearScreenDown(stdout);
|
|
80
|
+
|
|
67
81
|
const q = paint.apply(`? ${question} `, theme.info);
|
|
68
|
-
console.log(`${q} ${
|
|
82
|
+
console.log(`${q} ${KaiChroma.dim('(Space to select, Enter to confirm)')}`);
|
|
83
|
+
|
|
69
84
|
options.forEach((opt, i) => {
|
|
70
85
|
const isSelected = selected.has(i);
|
|
71
86
|
const isHovered = i === selectedIndex;
|
|
87
|
+
|
|
72
88
|
let prefix = isSelected ? paint.apply('◉', theme.success) : '◯';
|
|
73
89
|
let text = opt;
|
|
90
|
+
|
|
74
91
|
if (isHovered) {
|
|
75
92
|
prefix = paint.apply('>', theme.info) + ' ' + prefix;
|
|
76
93
|
text = paint.apply(text, theme.info);
|
|
77
94
|
} else {
|
|
78
95
|
prefix = ' ' + prefix;
|
|
79
|
-
text = chalk.gray(text);
|
|
80
96
|
}
|
|
81
|
-
|
|
97
|
+
|
|
82
98
|
console.log(`${prefix} ${text}`);
|
|
83
99
|
});
|
|
84
100
|
};
|
|
101
|
+
|
|
85
102
|
console.log('\n'.repeat(options.length));
|
|
86
103
|
render();
|
|
104
|
+
|
|
87
105
|
const handler = (key: string) => {
|
|
88
106
|
if (key === '\u0003') process.exit();
|
|
89
|
-
|
|
107
|
+
|
|
108
|
+
if (key === '\u001b[A') { // Up
|
|
90
109
|
selectedIndex = (selectedIndex > 0) ? selectedIndex - 1 : options.length - 1;
|
|
91
110
|
render();
|
|
92
|
-
} else if (key === '\u001b[B') {
|
|
111
|
+
} else if (key === '\u001b[B') { // Down
|
|
93
112
|
selectedIndex = (selectedIndex < options.length - 1) ? selectedIndex + 1 : 0;
|
|
94
113
|
render();
|
|
95
|
-
} else if (key === ' ') {
|
|
114
|
+
} else if (key === ' ') { // Space
|
|
96
115
|
if (selected.has(selectedIndex)) selected.delete(selectedIndex);
|
|
97
116
|
else selected.add(selectedIndex);
|
|
98
117
|
render();
|
|
99
|
-
} else if (key === '\r') {
|
|
118
|
+
} else if (key === '\r') { // Enter
|
|
100
119
|
stdin.removeListener('data', handler);
|
|
101
120
|
stdin.setRawMode(false);
|
|
102
121
|
stdin.pause();
|
|
122
|
+
|
|
103
123
|
readline.moveCursor(stdout, 0, -(options.length + 1));
|
|
104
124
|
readline.clearScreenDown(stdout);
|
|
125
|
+
|
|
105
126
|
const result = options.filter((_, i) => selected.has(i));
|
|
106
|
-
console.log(`${paint.apply(`✔ ${question}`, theme.success)} ${
|
|
127
|
+
console.log(`${paint.apply(`✔ ${question}`, theme.success)} ${KaiChroma.bold(result.join(', '))}`);
|
|
128
|
+
|
|
107
129
|
resolve(result);
|
|
108
130
|
}
|
|
109
131
|
};
|
|
132
|
+
|
|
110
133
|
stdin.on('data', handler);
|
|
111
134
|
});
|
|
112
135
|
}
|
package/src/utils/spinner.ts
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
1
|
+
|
|
2
|
+
import { KaiChroma } from '../styles/KaiChroma';
|
|
3
3
|
|
|
4
4
|
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
5
|
+
|
|
5
6
|
export class KaiSpinner {
|
|
6
7
|
private timer: NodeJS.Timeout | null = null;
|
|
7
8
|
private index = 0;
|
|
8
9
|
private text = '';
|
|
9
|
-
private
|
|
10
|
+
private colorHex = '#00FFFF'; // Default cyan-ish
|
|
11
|
+
|
|
10
12
|
start(text: string, colorHex: string = '#00FFFF') {
|
|
11
13
|
this.stop();
|
|
12
14
|
this.text = text;
|
|
13
|
-
this.
|
|
15
|
+
this.colorHex = colorHex;
|
|
14
16
|
process.stdout.write('\x1B[?25l');
|
|
15
17
|
this.timer = setInterval(() => {
|
|
16
18
|
const frame = frames[this.index = ++this.index % frames.length];
|
|
17
|
-
process.stdout.write(`\r${this.
|
|
19
|
+
process.stdout.write(`\r${KaiChroma.hex(this.colorHex, frame)} ${this.text}`);
|
|
18
20
|
}, 80);
|
|
19
21
|
}
|
|
22
|
+
|
|
20
23
|
stop(symbol: string = '✔', endText?: string, colorHex?: string) {
|
|
21
24
|
if (this.timer) {
|
|
22
25
|
clearInterval(this.timer);
|
|
@@ -24,11 +27,12 @@ export class KaiSpinner {
|
|
|
24
27
|
process.stdout.write('\r\x1B[K');
|
|
25
28
|
process.stdout.write('\x1B[?25h');
|
|
26
29
|
if (endText) {
|
|
27
|
-
const finalColor = colorHex
|
|
28
|
-
console.log(`${finalColor
|
|
30
|
+
const finalColor = colorHex || this.colorHex;
|
|
31
|
+
console.log(`${KaiChroma.hex(finalColor, symbol)} ${endText}`);
|
|
29
32
|
}
|
|
30
33
|
}
|
|
31
34
|
}
|
|
35
|
+
|
|
32
36
|
fail(text: string) {
|
|
33
37
|
this.stop('✖', text, '#FF0000');
|
|
34
38
|
}
|
package/src/utils/stripAnsi.ts
CHANGED
package/src/utils/table.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
|
|
2
|
+
import { KaiChroma } from '../styles/KaiChroma';
|
|
3
|
+
import { paint } from '../styles/palettes';
|
|
3
4
|
|
|
4
5
|
export class KaiTable {
|
|
5
6
|
static print(data: any[], theme: any) {
|
|
6
7
|
if (data.length === 0) return;
|
|
8
|
+
|
|
7
9
|
const keys = Object.keys(data[0]);
|
|
8
10
|
const colWidths = keys.map(key => {
|
|
9
11
|
const maxValLen = Math.max(...data.map(row => String(row[key]).length));
|
|
10
12
|
return Math.max(key.length, maxValLen) + 2;
|
|
11
13
|
});
|
|
14
|
+
|
|
12
15
|
const createRow = (rowItems: string[], isHeader = false) => {
|
|
13
16
|
return rowItems.map((item, i) => {
|
|
14
17
|
const cell = item.padEnd(colWidths[i]);
|
|
@@ -17,15 +20,19 @@ export class KaiTable {
|
|
|
17
20
|
: cell;
|
|
18
21
|
}).join(' │ ');
|
|
19
22
|
};
|
|
23
|
+
|
|
20
24
|
const separator = colWidths.map(w => '─'.repeat(w)).join('─┼─');
|
|
21
|
-
const dimColor =
|
|
25
|
+
const dimColor = (text: string) => KaiChroma.hex(theme.dim, text);
|
|
26
|
+
|
|
22
27
|
console.log(dimColor(separator));
|
|
23
28
|
console.log(createRow(keys, true));
|
|
24
29
|
console.log(dimColor(separator));
|
|
30
|
+
|
|
25
31
|
data.forEach(row => {
|
|
26
32
|
const values = keys.map(k => String(row[k]));
|
|
27
33
|
console.log(createRow(values));
|
|
28
34
|
});
|
|
35
|
+
|
|
29
36
|
console.log(dimColor(separator));
|
|
30
37
|
}
|
|
31
38
|
}
|
package/src/styles/gradients.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import gradient from 'gradient-string';
|
|
2
|
-
import { palettes, ThemeName } from './palettes';
|
|
3
|
-
|
|
4
|
-
export class GradientEngine {
|
|
5
|
-
private currentTheme: ThemeName = 'zen';
|
|
6
|
-
setTheme(theme: ThemeName) {
|
|
7
|
-
if (palettes[theme]) {
|
|
8
|
-
this.currentTheme = theme;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
get text() {
|
|
12
|
-
return palettes[this.currentTheme];
|
|
13
|
-
}
|
|
14
|
-
apply(text: string, colors: string[]) {
|
|
15
|
-
return gradient(colors)(text);
|
|
16
|
-
}
|
|
17
|
-
multiline(text: string, colors: string[]) {
|
|
18
|
-
return gradient(colors).multiline(text);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const paint = new GradientEngine();
|