@kljj04/cli-kit 1.1.0 → 1.2.1

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.long.md ADDED
@@ -0,0 +1,245 @@
1
+ # @kljj04/cli-kit — Full Documentation
2
+
3
+ All-in-one terminal styling library for Node.js.
4
+ RGB truecolor, boxes, tables, spinners, and progress bars — all in one package, zero dependencies.
5
+
6
+ ---
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm install @kljj04/cli-kit
12
+ ```
13
+
14
+ ---
15
+
16
+ ## Core Concept
17
+
18
+ All styling follows a consistent 3-step chain:
19
+
20
+ ```js
21
+ kit.color(R, G, B).style(...args).context(value)
22
+ ```
23
+
24
+ | Step | Method | Description |
25
+ |---|---|---|
26
+ | 1 | `color(r, g, b)` | Set RGB truecolor (0–255 each) |
27
+ | 2 | `style(...args)` | Set text styles + output type |
28
+ | 3 | `context(value)` | Render the output |
29
+
30
+ You can also write it across multiple lines:
31
+
32
+ ```js
33
+ kit
34
+ .color(255, 100, 50)
35
+ .style('bold', 'box')
36
+ .context('Hello!')
37
+ ```
38
+
39
+ ---
40
+
41
+ ## color(r, g, b)
42
+
43
+ Sets the foreground color using RGB truecolor.
44
+ Requires a terminal with truecolor support (Windows Terminal, iTerm2, most modern terminals).
45
+
46
+ ```js
47
+ kit.color(255, 0, 0) // red
48
+ kit.color(0, 255, 0) // green
49
+ kit.color(0, 0, 255) // blue
50
+ kit.color(255, 255, 255) // white
51
+ ```
52
+
53
+ ---
54
+
55
+ ## style(...args)
56
+
57
+ Accepts any combination of text styles and one output type.
58
+
59
+ ### Text Styles
60
+
61
+ | Value | Description |
62
+ |---|---|
63
+ | `bold` | Bold text |
64
+ | `dim` | Dimmed text |
65
+ | `italic` | Italic text |
66
+ | `underline` | Underlined text |
67
+ | `inverse` | Inverted fg/bg colors |
68
+ | `strikethrough` | Strikethrough text |
69
+
70
+ ### Output Types
71
+
72
+ | Value | Description |
73
+ |---|---|
74
+ | `print` | Plain text (default) |
75
+ | `box` | Unicode box border |
76
+ | `spinner` | Animated spinner |
77
+ | `progress` | Progress bar |
78
+ | `table` | Formatted table |
79
+
80
+ Multiple text styles can be combined. Only one output type should be passed.
81
+
82
+ ```js
83
+ kit.color(255, 100, 50).style('bold', 'italic', 'box').context('Hello!')
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Output Types
89
+
90
+ ### print (default)
91
+
92
+ Prints styled text to stdout. Output type can be omitted.
93
+
94
+ ```js
95
+ kit.color(255, 100, 50).style('bold').context('Hello!')
96
+ kit.color(255, 100, 50).style('bold', 'print').context('Hello!')
97
+ ```
98
+
99
+ ---
100
+
101
+ ### box
102
+
103
+ Wraps text in a Unicode box border. Supports multiline strings (`\n`).
104
+
105
+ ```js
106
+ kit.color(100, 200, 255).style('box').context('Hello!')
107
+ ```
108
+
109
+ ```
110
+ ┌────────┐
111
+ │ Hello! │
112
+ └────────┘
113
+ ```
114
+
115
+ Multiline:
116
+
117
+ ```js
118
+ kit.color(150, 255, 100).style('box').context('Line one\nLine two\nLine three')
119
+ ```
120
+
121
+ ```
122
+ ┌────────────┐
123
+ │ Line one │
124
+ │ Line two │
125
+ │ Line three │
126
+ └────────────┘
127
+ ```
128
+
129
+ ---
130
+
131
+ ### spinner
132
+
133
+ Returns a spinner object with a `.stop(text)` method.
134
+
135
+ ```js
136
+ const s = kit.color(0, 255, 200).style('spinner').context('Loading...')
137
+
138
+ setTimeout(() => {
139
+ s.stop('Done!') // clears spinner, prints final message
140
+ }, 2000)
141
+ ```
142
+
143
+ | Method | Description |
144
+ |---|---|
145
+ | `s.stop(text?)` | Stop spinner. Optionally print a final message. |
146
+
147
+ ---
148
+
149
+ ### progress
150
+
151
+ Pass a number from 0 to 100.
152
+
153
+ ```js
154
+ kit.color(255, 200, 0).style('progress').context(72)
155
+ // [██████████████████████░░░░░░░░] 72%
156
+ ```
157
+
158
+ **Live mode** — pass `{ value, override: true }` to overwrite the same line.
159
+ Useful inside loops or intervals.
160
+
161
+ ```js
162
+ // style에 override 추가
163
+ kit.color(0, 255, 150).style('progress', 'override').context(i)
164
+ ```
165
+
166
+ Example with interval:
167
+
168
+ ```js
169
+ let i = 0
170
+ const interval = setInterval(() => {
171
+ i++
172
+ kit.color(0, 255, 150).style('progress', 'override').context(i)
173
+ if (i >= 100) clearInterval(interval)
174
+ }, 30)
175
+ ```
176
+
177
+ ---
178
+
179
+ ### table
180
+
181
+ Pass an array of objects. Keys are used as column headers automatically.
182
+
183
+ ```js
184
+ kit.color(200, 150, 255).style('table').context([
185
+ { name: 'Alice', age: 20, role: 'dev' },
186
+ { name: 'Bob', age: 25, role: 'designer' },
187
+ { name: 'Carol', age: 30, role: 'PM' },
188
+ ])
189
+ ```
190
+
191
+ ```
192
+ ┌───────┬─────┬──────────┐
193
+ │ name │ age │ role │
194
+ ┼───────┼─────┼──────────┼
195
+ │ Alice │ 20 │ dev │
196
+ │ Bob │ 25 │ designer │
197
+ │ Carol │ 30 │ PM │
198
+ └───────┴─────┴──────────┘
199
+ ```
200
+
201
+ ---
202
+
203
+ ## Full Example
204
+
205
+ ```js
206
+ const kit = require('@kljj04/cli-kit')
207
+
208
+ // box
209
+ kit.color(0, 255, 100).style('bold', 'box').context('Welcome!')
210
+
211
+ // spinner
212
+ const s = kit.color(100, 200, 255).style('spinner').context('Loading...')
213
+ setTimeout(() => s.stop('Done!'), 1500)
214
+
215
+ // table
216
+ kit.color(200, 150, 255).style('table').context([
217
+ { task: 'Build', status: 'OK' },
218
+ { task: 'Deploy', status: 'OK' },
219
+ ])
220
+
221
+ // progress (live)
222
+ let i = 0
223
+ const iv = setInterval(() => {
224
+ i++
225
+ kit.color(255, 200, 0).style('progress', 'override').context(i)
226
+ if (i >= 100) {
227
+ clearInterval(iv)
228
+ kit.color(0, 255, 100).style('bold').context('Complete!')
229
+ }
230
+ }, 20)
231
+ ```
232
+
233
+ ---
234
+
235
+ ## Planned Features
236
+
237
+ - `gradient([r,g,b], [r,g,b])` — per-character color interpolation for progress bars
238
+ - `style('round', 'box')` — rounded box corners
239
+ - `bgColor(r, g, b)` — background color support
240
+
241
+ ---
242
+
243
+ ## License
244
+
245
+ MIT
package/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ > [!WARNING]
2
+ > This README is old and outdated. It will be updated as soon as possible.
3
+ > AI.md, README.long.md too.
4
+
1
5
  # @kljj04/cli-kit
2
6
 
3
7
  All-in-one terminal styling library for Node.js.
package/index.js CHANGED
@@ -6,6 +6,9 @@ const kit = {
6
6
  },
7
7
  style(...args) {
8
8
  return new KitBuilder().style(...args);
9
+ },
10
+ gradient(from, to) {
11
+ return new KitBuilder().gradient(from, to);
9
12
  }
10
13
  };
11
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kljj04/cli-kit",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "All-in-one terminal styling library. Colors, boxes, tables, spinners, and progress bars.",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/src/color.js CHANGED
@@ -24,6 +24,9 @@ class KitBuilder {
24
24
  this._b = 255;
25
25
  this._styles = [];
26
26
  this._outputType = 'print';
27
+ this._override = false;
28
+ this._gradientColors = null;
29
+ this._fixedGradient = false;
27
30
  }
28
31
 
29
32
  color(r, g, b) {
@@ -33,10 +36,19 @@ class KitBuilder {
33
36
  return this;
34
37
  }
35
38
 
39
+ gradient(from, to) {
40
+ this._gradientColors = [from, to];
41
+ return this;
42
+ }
43
+
36
44
  style(...args) {
37
45
  const outputTypes = ['print', 'box', 'spinner', 'progress', 'table'];
38
46
  args.forEach(arg => {
39
- if (outputTypes.includes(arg)) {
47
+ if (arg === 'override') {
48
+ this._override = true;
49
+ } else if (arg === 'fixedgradient') {
50
+ this._fixedGradient = true;
51
+ } else if (outputTypes.includes(arg)) {
40
52
  this._outputType = arg;
41
53
  } else if (STYLES[arg]) {
42
54
  this._styles.push(arg);
@@ -46,7 +58,7 @@ class KitBuilder {
46
58
  }
47
59
 
48
60
  _buildPrefix() {
49
- const colorCode = rgb(this._r, this._g, this._b);
61
+ const colorCode = this._gradientColors ? '' : rgb(this._r, this._g, this._b);
50
62
  const styleCodes = this._styles.map(s => STYLES[s]).join('');
51
63
  return `${colorCode}${styleCodes}`;
52
64
  }
@@ -63,15 +75,17 @@ class KitBuilder {
63
75
  case 'spinner':
64
76
  return require('./spinner').spinner(prefix, value);
65
77
  case 'progress':
66
- require('./progress').progress(prefix, value);
78
+ require('./progress').progress(prefix, value, this._override, this._gradientColors, this._fixedGradient);
67
79
  break;
68
80
  case 'table':
69
81
  require('./table').table(prefix, value);
70
82
  break;
71
83
  }
72
- // reset for next use
73
84
  this._styles = [];
74
85
  this._outputType = 'print';
86
+ this._override = false;
87
+ this._gradientColors = null;
88
+ this._fixedGradient = false;
75
89
  }
76
90
  }
77
91
 
package/src/progress.js CHANGED
@@ -1,21 +1,89 @@
1
1
  const { RESET } = require('./color');
2
2
 
3
- function renderBar(prefix, pct) {
3
+ function linearize(c) {
4
+ c /= 255;
5
+ return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
6
+ }
7
+
8
+ function delinearize(c) {
9
+ c = c <= 0.0031308 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
10
+ return Math.round(Math.min(1, Math.max(0, c)) * 255);
11
+ }
12
+
13
+ function rgbToOklch(r, g, b) {
14
+ const lr = linearize(r), lg = linearize(g), lb = linearize(b);
15
+ const l = 0.4122214708*lr + 0.5363325363*lg + 0.0514459929*lb;
16
+ const m = 0.2119034982*lr + 0.6806995451*lg + 0.1073969566*lb;
17
+ const s = 0.0883024619*lr + 0.2817188376*lg + 0.6299787005*lb;
18
+ const l_ = Math.cbrt(l), m_ = Math.cbrt(m), s_ = Math.cbrt(s);
19
+ const L = 0.2104542553*l_ + 0.7936177850*m_ - 0.0040720468*s_;
20
+ const a = 1.9779984951*l_ - 2.4285922050*m_ + 0.4505937099*s_;
21
+ const bVal = 0.0259040371*l_ + 0.7827717662*m_ - 0.8086757660*s_;
22
+ const C = Math.sqrt(a*a + bVal*bVal);
23
+ const H = Math.atan2(bVal, a) * (180/Math.PI);
24
+ return [L, C, H < 0 ? H+360 : H];
25
+ }
26
+
27
+ function oklchToRgb(L, C, H) {
28
+ const hRad = H * (Math.PI/180);
29
+ const a = C*Math.cos(hRad);
30
+ const b = C*Math.sin(hRad);
31
+ const l_ = L+0.3963377774*a+0.2158037573*b;
32
+ const m_ = L-0.1055613458*a-0.0638541728*b;
33
+ const s_ = L-0.0894841775*a-1.2914855480*b;
34
+ const lc=l_*l_*l_, mc=m_*m_*m_, sc=s_*s_*s_;
35
+ const lr = 4.0767416621*lc - 3.3077115913*mc + 0.2309699292*sc;
36
+ const lg = -1.2684380046*lc + 2.6097574011*mc - 0.3413193965*sc;
37
+ const lb = -0.0041960863*lc - 0.7034186147*mc + 1.7076147010*sc;
38
+ return [delinearize(lr), delinearize(lg), delinearize(lb)];
39
+ }
40
+
41
+ function lerpOklch(from, to, t) {
42
+ const a = rgbToOklch(...from);
43
+ const b = rgbToOklch(...to);
44
+ let dH = b[2]-a[2];
45
+ if (dH > 180) dH -= 360;
46
+ if (dH < -180) dH += 360;
47
+ return oklchToRgb(a[0]+(b[0]-a[0])*t, a[1]+(b[1]-a[1])*t, a[2]+dH*t);
48
+ }
49
+
50
+ function renderBar(prefix, pct, gradientColors, fixedGradient) {
4
51
  const width = 30;
5
52
  const filled = Math.round((pct / 100) * width);
6
53
  const empty = width - filled;
7
- const bar = '█'.repeat(filled) + '░'.repeat(empty);
54
+
55
+ let bar = '';
56
+ if (gradientColors && fixedGradient) {
57
+ // 전체 30칸 기준으로 색 미리 계산, 채워진 만큼만 표시
58
+ for (let i = 0; i < filled; i++) {
59
+ const t = (width > 1) ? i / (width - 1) : 0;
60
+ const [r, g, b] = lerpOklch(gradientColors[0], gradientColors[1], t);
61
+ bar += `\x1b[38;2;${r};${g};${b}m█`;
62
+ }
63
+ bar += `${RESET}${'░'.repeat(empty)}`;
64
+ } else if (gradientColors) {
65
+ // 채워진 칸 기준 보간 (기존 gradient)
66
+ for (let i = 0; i < filled; i++) {
67
+ const t = filled > 1 ? i / (filled - 1) : 0;
68
+ const [r, g, b] = lerpOklch(gradientColors[0], gradientColors[1], t);
69
+ bar += `\x1b[38;2;${r};${g};${b}m█`;
70
+ }
71
+ bar += `${RESET}${'░'.repeat(empty)}`;
72
+ } else {
73
+ bar = '█'.repeat(filled) + '░'.repeat(empty);
74
+ }
75
+
8
76
  return `${prefix}[${bar}] ${pct}%${RESET}`;
9
77
  }
10
78
 
11
- function progress(prefix, value) {
12
- if (typeof value === 'object' && value !== null && value.live) {
13
- const pct = Math.min(100, Math.max(0, Number(value.value)));
14
- process.stdout.write(`\r${renderBar(prefix, pct)}`);
79
+ function progress(prefix, value, override, gradientColors, fixedGradient) {
80
+ const pct = Math.min(100, Math.max(0, Number(value)));
81
+ const bar = renderBar(prefix, pct, gradientColors, fixedGradient);
82
+ if (override) {
83
+ process.stdout.write(`\r${bar}`);
15
84
  if (pct >= 100) process.stdout.write('\n');
16
85
  } else {
17
- const pct = Math.min(100, Math.max(0, Number(value)));
18
- console.log(renderBar(prefix, pct));
86
+ console.log(bar);
19
87
  }
20
88
  }
21
89