shell-logo 0.1.2 → 0.1.3
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/package.json +1 -1
- package/src/config.js +25 -5
- package/src/index.js +28 -7
- package/src/renderer.js +13 -0
- package/src/shapes.js +239 -0
- package/src/themes.js +60 -15
- package/src/ui.js +56 -31
package/package.json
CHANGED
package/src/config.js
CHANGED
|
@@ -13,6 +13,7 @@ import { readFileSync } from 'node:fs';
|
|
|
13
13
|
import chalk from 'chalk';
|
|
14
14
|
import { configPath, legacyConfigPath } from './paths.js';
|
|
15
15
|
import { writeConfig } from './generate.js';
|
|
16
|
+
import { SHAPE_NAMES } from './shapes.js';
|
|
16
17
|
|
|
17
18
|
export const DEFAULTS = {
|
|
18
19
|
colors: ['#ff6b6b', '#feca57', '#48dbfb'],
|
|
@@ -21,7 +22,11 @@ export const DEFAULTS = {
|
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Parse a JSON string and validate it as a shell-logo config.
|
|
24
|
-
*
|
|
25
|
+
*
|
|
26
|
+
* Supports two modes:
|
|
27
|
+
* - "text" (default): requires non-empty `text`, optional `colors`/`font`
|
|
28
|
+
* - "shape": requires valid `shape` name, optional `colors`, no `text`/`font` needed
|
|
29
|
+
*
|
|
25
30
|
* Returns a normalized config object, or null if invalid.
|
|
26
31
|
*/
|
|
27
32
|
function parseAndValidate(raw) {
|
|
@@ -32,17 +37,32 @@ function parseAndValidate(raw) {
|
|
|
32
37
|
return null;
|
|
33
38
|
}
|
|
34
39
|
|
|
35
|
-
if (!config.text || typeof config.text !== 'string' || config.text.trim() === '') {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
40
|
if (config.colors !== undefined) {
|
|
40
41
|
if (!Array.isArray(config.colors) || config.colors.length < 2) {
|
|
41
42
|
return null;
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
const mode = config.mode ?? 'text';
|
|
47
|
+
|
|
48
|
+
if (mode === 'shape') {
|
|
49
|
+
if (!config.shape || !SHAPE_NAMES.includes(config.shape)) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
mode: 'shape',
|
|
54
|
+
shape: config.shape,
|
|
55
|
+
colors: config.colors ?? DEFAULTS.colors,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// mode === 'text' (or absent for backward compatibility)
|
|
60
|
+
if (!config.text || typeof config.text !== 'string' || config.text.trim() === '') {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
45
64
|
return {
|
|
65
|
+
mode: 'text',
|
|
46
66
|
text: config.text.trim(),
|
|
47
67
|
colors: config.colors ?? DEFAULTS.colors,
|
|
48
68
|
font: config.font ?? DEFAULTS.font,
|
package/src/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { writeConfig } from './generate.js';
|
|
|
13
13
|
import { render } from './renderer.js';
|
|
14
14
|
import { getTerminalSize, clearScreen, hideCursor, showCursor, centerContent } from './terminal.js';
|
|
15
15
|
import { THEMES, FONTS } from './themes.js';
|
|
16
|
+
import { SHAPES } from './shapes.js';
|
|
16
17
|
import chalk from 'chalk';
|
|
17
18
|
import * as p from '@clack/prompts';
|
|
18
19
|
|
|
@@ -38,6 +39,8 @@ startRenderLoop(config, persistent);
|
|
|
38
39
|
function startRenderLoop(config, persistent) {
|
|
39
40
|
let resizeTimer;
|
|
40
41
|
|
|
42
|
+
const isShapeMode = config.mode === 'shape';
|
|
43
|
+
|
|
41
44
|
// Find the current theme index by matching colors
|
|
42
45
|
let themeIndex = THEMES.findIndex(
|
|
43
46
|
(t) => JSON.stringify(t.colors) === JSON.stringify(config.colors)
|
|
@@ -47,6 +50,11 @@ function startRenderLoop(config, persistent) {
|
|
|
47
50
|
let fontIndex = FONTS.indexOf(config.font);
|
|
48
51
|
if (fontIndex === -1) fontIndex = 0;
|
|
49
52
|
|
|
53
|
+
let shapeIndex = isShapeMode
|
|
54
|
+
? SHAPES.findIndex((s) => s.name === config.shape)
|
|
55
|
+
: 0;
|
|
56
|
+
if (shapeIndex === -1) shapeIndex = 0;
|
|
57
|
+
|
|
50
58
|
let showStatus = false;
|
|
51
59
|
let statusTimer;
|
|
52
60
|
|
|
@@ -56,7 +64,10 @@ function startRenderLoop(config, persistent) {
|
|
|
56
64
|
clearScreen();
|
|
57
65
|
process.stdout.write(centerContent(art, columns, rows));
|
|
58
66
|
if (showStatus) {
|
|
59
|
-
const
|
|
67
|
+
const lrLabel = isShapeMode
|
|
68
|
+
? `${SHAPES[shapeIndex].name} · ←→ shape`
|
|
69
|
+
: `${FONTS[fontIndex]} · ←→ font`;
|
|
70
|
+
const status = ` ${THEMES[themeIndex].name} · ${lrLabel} · ↑↓ theme q quit`;
|
|
60
71
|
process.stdout.write(`\x1B[${rows};1H` + chalk.dim(status));
|
|
61
72
|
}
|
|
62
73
|
}
|
|
@@ -109,12 +120,22 @@ function startRenderLoop(config, persistent) {
|
|
|
109
120
|
} else if (key[2] === 66) { // Arrow Down — next theme
|
|
110
121
|
themeIndex = (themeIndex + 1) % THEMES.length;
|
|
111
122
|
config.colors = THEMES[themeIndex].colors;
|
|
112
|
-
} else if (key[2] === 67) { // Arrow Right — next font
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
123
|
+
} else if (key[2] === 67) { // Arrow Right — next font/shape
|
|
124
|
+
if (isShapeMode) {
|
|
125
|
+
shapeIndex = (shapeIndex + 1) % SHAPES.length;
|
|
126
|
+
config.shape = SHAPES[shapeIndex].name;
|
|
127
|
+
} else {
|
|
128
|
+
fontIndex = (fontIndex + 1) % FONTS.length;
|
|
129
|
+
config.font = FONTS[fontIndex];
|
|
130
|
+
}
|
|
131
|
+
} else if (key[2] === 68) { // Arrow Left — prev font/shape
|
|
132
|
+
if (isShapeMode) {
|
|
133
|
+
shapeIndex = (shapeIndex - 1 + SHAPES.length) % SHAPES.length;
|
|
134
|
+
config.shape = SHAPES[shapeIndex].name;
|
|
135
|
+
} else {
|
|
136
|
+
fontIndex = (fontIndex - 1 + FONTS.length) % FONTS.length;
|
|
137
|
+
config.font = FONTS[fontIndex];
|
|
138
|
+
}
|
|
118
139
|
} else {
|
|
119
140
|
return;
|
|
120
141
|
}
|
package/src/renderer.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import figlet from 'figlet';
|
|
2
2
|
import gradient from 'gradient-string';
|
|
3
3
|
import { scaleArt } from './scaling.js';
|
|
4
|
+
import { SHAPES } from './shapes.js';
|
|
4
5
|
|
|
5
6
|
function figletSync(text, font) {
|
|
6
7
|
try {
|
|
@@ -11,6 +12,18 @@ function figletSync(text, font) {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export function render(config, columns, rows) {
|
|
15
|
+
if (config.mode === 'shape') {
|
|
16
|
+
const shape = SHAPES.find((s) => s.name === config.shape);
|
|
17
|
+
if (!shape) {
|
|
18
|
+
const grad = gradient(config.colors);
|
|
19
|
+
return grad(config.shape);
|
|
20
|
+
}
|
|
21
|
+
const trimmed = shape.art.trim();
|
|
22
|
+
const scaled = scaleArt(trimmed, columns, rows - 2);
|
|
23
|
+
const grad = gradient(config.colors);
|
|
24
|
+
return grad.multiline(scaled);
|
|
25
|
+
}
|
|
26
|
+
|
|
14
27
|
const art = figletSync(config.text, config.font);
|
|
15
28
|
if (!art) {
|
|
16
29
|
const grad = gradient(config.colors);
|
package/src/shapes.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
export const SHAPES = [
|
|
2
|
+
{
|
|
3
|
+
name: 'Pixel',
|
|
4
|
+
art: `
|
|
5
|
+
▄█▄ ▄█▄
|
|
6
|
+
█████ ▄▄▄ █████
|
|
7
|
+
████████████████
|
|
8
|
+
██ ▀ █████ ▀ ██
|
|
9
|
+
████████████████
|
|
10
|
+
█████ ▀▀▀ █████
|
|
11
|
+
██████████████
|
|
12
|
+
████████████
|
|
13
|
+
██ █████ ██
|
|
14
|
+
▀▀ ▀▀
|
|
15
|
+
`,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'Bloop',
|
|
19
|
+
art: `
|
|
20
|
+
▄████████▄
|
|
21
|
+
██████████████
|
|
22
|
+
████████████████
|
|
23
|
+
███▀██████▀█████
|
|
24
|
+
████████████████
|
|
25
|
+
█████ ▀▀▀▀ █████
|
|
26
|
+
████████████████
|
|
27
|
+
██████████████
|
|
28
|
+
▀██████████▀
|
|
29
|
+
▀▀ ▀▀
|
|
30
|
+
`,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'Octo',
|
|
34
|
+
art: `
|
|
35
|
+
▄████████▄
|
|
36
|
+
██████████████
|
|
37
|
+
████████████████
|
|
38
|
+
███▀██████▀█████
|
|
39
|
+
████████████████
|
|
40
|
+
██████████████
|
|
41
|
+
▐█▌▐█▌ ██ ▐█▌▐█▌
|
|
42
|
+
▐█▌▐█▌ ██ ▐█▌▐█▌
|
|
43
|
+
▀ ▀ ▀▀ ▀ ▀
|
|
44
|
+
`,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'Ghosty',
|
|
48
|
+
art: `
|
|
49
|
+
▄████████▄
|
|
50
|
+
██████████████
|
|
51
|
+
████████████████
|
|
52
|
+
████▀████▀██████
|
|
53
|
+
████████████████
|
|
54
|
+
████████████████
|
|
55
|
+
████████████████
|
|
56
|
+
████████████████
|
|
57
|
+
█▀█ █▀██ █▀█ █▀
|
|
58
|
+
`,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'Foxie',
|
|
62
|
+
art: `
|
|
63
|
+
▄██▄ ▄██▄
|
|
64
|
+
████▄ ▄████
|
|
65
|
+
██████▄▄▄▄▄▄▄▄██████
|
|
66
|
+
████ ▀████████▀ ████
|
|
67
|
+
█████ ▀██████▀ █████
|
|
68
|
+
████████████████████
|
|
69
|
+
██████████████████
|
|
70
|
+
████████████████
|
|
71
|
+
██████████████
|
|
72
|
+
▀██████████▀
|
|
73
|
+
`,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'Ducky',
|
|
77
|
+
art: `
|
|
78
|
+
▄████▄
|
|
79
|
+
████████▄
|
|
80
|
+
██▀████████▄
|
|
81
|
+
██████████████
|
|
82
|
+
████████████████
|
|
83
|
+
█████████████▀
|
|
84
|
+
▀██████████
|
|
85
|
+
▀████████▀
|
|
86
|
+
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
|
87
|
+
`,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'Robo',
|
|
91
|
+
art: `
|
|
92
|
+
▄█▄
|
|
93
|
+
▄█████▄
|
|
94
|
+
██████████
|
|
95
|
+
███▀██▀███
|
|
96
|
+
██████████
|
|
97
|
+
███▄▀▀▄███
|
|
98
|
+
██████████
|
|
99
|
+
▐██████████▌
|
|
100
|
+
███ ████ ██
|
|
101
|
+
██ ██
|
|
102
|
+
▀▀ ▀▀
|
|
103
|
+
`,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'Cactus',
|
|
107
|
+
art: `
|
|
108
|
+
▄██▄
|
|
109
|
+
████
|
|
110
|
+
▄██▄ ████
|
|
111
|
+
████ ████ ▄██▄
|
|
112
|
+
████ ████ ████
|
|
113
|
+
▀███▄████▄███▀
|
|
114
|
+
████
|
|
115
|
+
████
|
|
116
|
+
██████
|
|
117
|
+
▄████████▄
|
|
118
|
+
██████████
|
|
119
|
+
`,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'Rocket',
|
|
123
|
+
art: `
|
|
124
|
+
▄▄
|
|
125
|
+
████
|
|
126
|
+
██████
|
|
127
|
+
██████
|
|
128
|
+
████████
|
|
129
|
+
██ ██ ██
|
|
130
|
+
████████
|
|
131
|
+
██████████
|
|
132
|
+
▄██████████▄
|
|
133
|
+
██▀██████▀██
|
|
134
|
+
██████
|
|
135
|
+
▐██████▌
|
|
136
|
+
▄██▀ ▀██▄
|
|
137
|
+
▀▀ ▀▀
|
|
138
|
+
`,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'Skull',
|
|
142
|
+
art: `
|
|
143
|
+
▄██████▄
|
|
144
|
+
████████████
|
|
145
|
+
██████████████
|
|
146
|
+
███▀██████▀███
|
|
147
|
+
██████████████
|
|
148
|
+
█████▄▄█████
|
|
149
|
+
████████████
|
|
150
|
+
█▀█ ▀▀ █▀█
|
|
151
|
+
█ █ ██ █ █
|
|
152
|
+
▀ ▀▀ ▀
|
|
153
|
+
`,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'Penguin',
|
|
157
|
+
art: `
|
|
158
|
+
▄████▄
|
|
159
|
+
████████████
|
|
160
|
+
██████████████
|
|
161
|
+
████▀████▀████
|
|
162
|
+
█████▀▀▀▀█████
|
|
163
|
+
██▌ ▄████▄ ▐██
|
|
164
|
+
██▌ ██████ ▐██
|
|
165
|
+
██▌ ██████ ▐██
|
|
166
|
+
▀▌ ██████ ▐▀
|
|
167
|
+
██████
|
|
168
|
+
▄██ ██▄
|
|
169
|
+
▀▀▀ ▀▀▀
|
|
170
|
+
`,
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: 'Mushroom',
|
|
174
|
+
art: `
|
|
175
|
+
▄████▄
|
|
176
|
+
▄████████▄
|
|
177
|
+
██ ▀████▀ ██
|
|
178
|
+
██████████████
|
|
179
|
+
██ ▀████▀ ██
|
|
180
|
+
██████████████
|
|
181
|
+
▀██████████▀
|
|
182
|
+
██████
|
|
183
|
+
██████
|
|
184
|
+
██████
|
|
185
|
+
████████
|
|
186
|
+
▀▀▀▀▀▀▀▀
|
|
187
|
+
`,
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: 'Alien',
|
|
191
|
+
art: `
|
|
192
|
+
▄██████████▄
|
|
193
|
+
██████████████
|
|
194
|
+
██ ▄████████▄ ██
|
|
195
|
+
██ █▀██████▀█ ██
|
|
196
|
+
██ ██████████ ██
|
|
197
|
+
████ ▀▀▀▀ ████
|
|
198
|
+
▀██████████▀
|
|
199
|
+
████████
|
|
200
|
+
██████
|
|
201
|
+
████
|
|
202
|
+
████
|
|
203
|
+
`,
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
name: 'Coffee',
|
|
207
|
+
art: `
|
|
208
|
+
▐█▌ ▐█▌ ▐█▌
|
|
209
|
+
▐█▌ ▐█▌ ▐█▌
|
|
210
|
+
▐█▌ ▐█▌ ▐█▌
|
|
211
|
+
████████████████
|
|
212
|
+
████████████████▄
|
|
213
|
+
█████████████████
|
|
214
|
+
█████████████████
|
|
215
|
+
████████████████▀
|
|
216
|
+
████████████████
|
|
217
|
+
▀████████████▀
|
|
218
|
+
████████████████
|
|
219
|
+
`,
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: 'Diamond',
|
|
223
|
+
art: `
|
|
224
|
+
▄██▄
|
|
225
|
+
██████
|
|
226
|
+
████████
|
|
227
|
+
██████████
|
|
228
|
+
████████████
|
|
229
|
+
██████████████
|
|
230
|
+
████████████
|
|
231
|
+
██████████
|
|
232
|
+
████████
|
|
233
|
+
██████
|
|
234
|
+
▀██▀
|
|
235
|
+
`,
|
|
236
|
+
},
|
|
237
|
+
];
|
|
238
|
+
|
|
239
|
+
export const SHAPE_NAMES = SHAPES.map((s) => s.name);
|
package/src/themes.js
CHANGED
|
@@ -1,23 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Font and gradient theme definitions for shell-logo.
|
|
3
|
+
*
|
|
4
|
+
* FONTS — Figlet font names available for text-mode logos.
|
|
5
|
+
* The render loop in index.js cycles through these with arrow keys.
|
|
6
|
+
* Indices 0–14 are the original set; new entries are appended so
|
|
7
|
+
* saved configs that store a font name keep working.
|
|
8
|
+
*
|
|
9
|
+
* THEMES — Named gradient color palettes (3 hex stops each).
|
|
10
|
+
* Used by gradient-string to colorize the rendered ASCII art.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// ── Fonts ──────────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
1
15
|
export const FONTS = [
|
|
16
|
+
// Classic / general-purpose
|
|
2
17
|
'Standard', 'Big', 'ANSI Shadow', 'Slant', 'Small',
|
|
3
18
|
'ANSI Regular', 'Bloody', 'DOS Rebel', 'Graffiti', 'Larry 3D',
|
|
4
19
|
'Star Wars', 'Doh', 'Ghost', 'Fraktur', 'Fire Font-k',
|
|
20
|
+
|
|
21
|
+
// Block / pixel style — use filled Unicode characters (▄█▀) instead of
|
|
22
|
+
// typical ASCII slashes and pipes, giving a "painted pixels" look.
|
|
23
|
+
'Block', 'Blocks', 'Shaded Blocky', 'Small Block',
|
|
24
|
+
'Dot Matrix', 'Tiles', 'Rectangles',
|
|
25
|
+
|
|
26
|
+
// Popular classic figlet fonts
|
|
27
|
+
'Doom', 'Banner3-D', 'Colossal', 'Epic',
|
|
28
|
+
'Calvin S', 'Speed', '3D-ASCII', 'Isometric1',
|
|
5
29
|
];
|
|
6
30
|
|
|
31
|
+
// ── Gradient themes ────────────────────────────────────────────────────────
|
|
32
|
+
|
|
7
33
|
export const THEMES = [
|
|
8
|
-
|
|
9
|
-
{ name: '
|
|
10
|
-
{ name: '
|
|
11
|
-
{ name: '
|
|
12
|
-
{ name: '
|
|
13
|
-
{ name: '
|
|
14
|
-
{ name: '
|
|
15
|
-
{ name: '
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
{ name: '
|
|
19
|
-
{ name: 'Arctic',
|
|
20
|
-
{ name: '
|
|
21
|
-
{ name: '
|
|
22
|
-
|
|
34
|
+
// Warm
|
|
35
|
+
{ name: 'Sunset', colors: ['#ff6b6b', '#ff9f43', '#feca57'] },
|
|
36
|
+
{ name: 'Fire', colors: ['#ff4757', '#ff6348', '#ffdd59'] },
|
|
37
|
+
{ name: 'Cherry', colors: ['#eb3349', '#f45c43', '#ff8a80'] },
|
|
38
|
+
{ name: 'Rose Gold', colors: ['#f4c4f3', '#fc5c7d', '#fda085'] },
|
|
39
|
+
{ name: 'Magma', colors: ['#f83600', '#f9d423', '#fe8c00'] },
|
|
40
|
+
{ name: 'Sunrise', colors: ['#ff512f', '#f09819', '#ffed4a'] },
|
|
41
|
+
{ name: 'Copper', colors: ['#b87333', '#da9855', '#f0c27f'] },
|
|
42
|
+
|
|
43
|
+
// Cool
|
|
44
|
+
{ name: 'Ocean', colors: ['#0abde3', '#48dbfb', '#54a0ff'] },
|
|
45
|
+
{ name: 'Arctic', colors: ['#e0eafc', '#cfdef3', '#74b9ff'] },
|
|
46
|
+
{ name: 'Frost', colors: ['#e8f0ff', '#b8d4f0', '#88b8e0'] },
|
|
47
|
+
{ name: 'Electric', colors: ['#00f2fe', '#4facfe', '#667eea'] },
|
|
48
|
+
|
|
49
|
+
// Green
|
|
50
|
+
{ name: 'Forest', colors: ['#1dd1a1', '#2ed573', '#26de81'] },
|
|
51
|
+
{ name: 'Emerald', colors: ['#11998e', '#38ef7d', '#b8ff96'] },
|
|
52
|
+
{ name: 'Toxic', colors: ['#a8ff78', '#78ffd6', '#00e676'] },
|
|
53
|
+
{ name: 'Matrix', colors: ['#00ff41', '#008f11', '#003b00'] },
|
|
54
|
+
|
|
55
|
+
// Purple / pink
|
|
56
|
+
{ name: 'Neon', colors: ['#c56cf0', '#fd79a8', '#ff6b6b'] },
|
|
57
|
+
{ name: 'Twilight', colors: ['#5f27cd', '#c56cf0', '#48dbfb'] },
|
|
58
|
+
{ name: 'Cyberpunk', colors: ['#f72585', '#7209b7', '#4cc9f0'] },
|
|
59
|
+
{ name: 'Grape', colors: ['#6a0572', '#ab83a1', '#e0aaff'] },
|
|
60
|
+
{ name: 'Lavender Dream', colors: ['#a18cd1', '#fbc2eb', '#f6d5f7'] },
|
|
61
|
+
{ name: 'Bubblegum', colors: ['#ff77ab', '#ff99cc', '#ffbbdd'] },
|
|
62
|
+
|
|
63
|
+
// Multi / neutral
|
|
64
|
+
{ name: 'Pastel', colors: ['#fd79a8', '#feca57', '#48dbfb'] },
|
|
65
|
+
{ name: 'Monochrome', colors: ['#ffffff', '#54a0ff', '#5f27cd'] },
|
|
66
|
+
{ name: 'Aurora', colors: ['#00d2ff', '#3a7bd5', '#7b2ff7'] },
|
|
67
|
+
{ name: 'Midnight', colors: ['#0f0c29', '#302b63', '#24c6dc'] },
|
|
23
68
|
];
|
package/src/ui.js
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
import * as p from '@clack/prompts';
|
|
16
16
|
import chalk from 'chalk';
|
|
17
17
|
import { tryLoadConfig } from './config.js';
|
|
18
|
+
import { SHAPES } from './shapes.js';
|
|
19
|
+
import { FONTS } from './themes.js';
|
|
18
20
|
|
|
19
21
|
const COLOR_PALETTE = [
|
|
20
22
|
{ value: '#ff6b6b', label: `${chalk.bgHex('#ff6b6b')(' ')} Coral` },
|
|
@@ -35,23 +37,7 @@ const COLOR_PALETTE = [
|
|
|
35
37
|
{ value: '#ffffff', label: `${chalk.bgHex('#ffffff')(' ')} White` },
|
|
36
38
|
];
|
|
37
39
|
|
|
38
|
-
const FONT_OPTIONS =
|
|
39
|
-
{ value: 'Standard', label: 'Standard' },
|
|
40
|
-
{ value: 'Big', label: 'Big' },
|
|
41
|
-
{ value: 'ANSI Shadow', label: 'ANSI Shadow' },
|
|
42
|
-
{ value: 'Slant', label: 'Slant' },
|
|
43
|
-
{ value: 'Small', label: 'Small' },
|
|
44
|
-
{ value: 'ANSI Regular', label: 'ANSI Regular' },
|
|
45
|
-
{ value: 'Bloody', label: 'Bloody' },
|
|
46
|
-
{ value: 'DOS Rebel', label: 'DOS Rebel' },
|
|
47
|
-
{ value: 'Graffiti', label: 'Graffiti' },
|
|
48
|
-
{ value: 'Larry 3D', label: 'Larry 3D' },
|
|
49
|
-
{ value: 'Star Wars', label: 'Star Wars' },
|
|
50
|
-
{ value: 'Doh', label: 'Doh' },
|
|
51
|
-
{ value: 'Ghost', label: 'Ghost' },
|
|
52
|
-
{ value: 'Fraktur', label: 'Fraktur' },
|
|
53
|
-
{ value: 'Fire Font-k', label: 'Fire Font-k' },
|
|
54
|
-
];
|
|
40
|
+
const FONT_OPTIONS = FONTS.map(f => ({ value: f, label: f }));
|
|
55
41
|
|
|
56
42
|
/** Exit gracefully if the user presses Ctrl+C / Escape during a prompt. */
|
|
57
43
|
function handleCancel(value) {
|
|
@@ -62,19 +48,8 @@ function handleCancel(value) {
|
|
|
62
48
|
return value;
|
|
63
49
|
}
|
|
64
50
|
|
|
65
|
-
/**
|
|
66
|
-
async function
|
|
67
|
-
const text = handleCancel(
|
|
68
|
-
await p.text({
|
|
69
|
-
message: 'What text should the logo display?',
|
|
70
|
-
placeholder: 'HELLO',
|
|
71
|
-
validate: (val) => {
|
|
72
|
-
if (!val || val.trim() === '') return 'Text is required.';
|
|
73
|
-
},
|
|
74
|
-
})
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
// Pick 3 random unique colors as defaults
|
|
51
|
+
/** Prompt for gradient colors (shared by text and shape modes). */
|
|
52
|
+
async function promptColors() {
|
|
78
53
|
const shuffled = [...COLOR_PALETTE].sort(() => Math.random() - 0.5);
|
|
79
54
|
const initialColors = shuffled.slice(0, 3).map(c => c.value);
|
|
80
55
|
|
|
@@ -92,6 +67,40 @@ async function promptGenerate() {
|
|
|
92
67
|
if (colors.length >= 2) break;
|
|
93
68
|
p.log.warning('Please select at least 2 colors.');
|
|
94
69
|
}
|
|
70
|
+
return colors;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Walk the user through the Shape wizard: shape picker, colors. */
|
|
74
|
+
async function promptShapeGenerate() {
|
|
75
|
+
const shape = handleCancel(
|
|
76
|
+
await p.select({
|
|
77
|
+
message: 'Pick a mascot:',
|
|
78
|
+
options: SHAPES.map((s) => ({ value: s.name, label: s.name })),
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const colors = await promptColors();
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
action: 'generate',
|
|
86
|
+
config: { mode: 'shape', shape, colors },
|
|
87
|
+
persistent: true,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Walk the user through the Text wizard: text, colors, font. */
|
|
92
|
+
async function promptTextGenerate() {
|
|
93
|
+
const text = handleCancel(
|
|
94
|
+
await p.text({
|
|
95
|
+
message: 'What text should the logo display?',
|
|
96
|
+
placeholder: 'HELLO',
|
|
97
|
+
validate: (val) => {
|
|
98
|
+
if (!val || val.trim() === '') return 'Text is required.';
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const colors = await promptColors();
|
|
95
104
|
|
|
96
105
|
const font = handleCancel(
|
|
97
106
|
await p.select({
|
|
@@ -102,11 +111,27 @@ async function promptGenerate() {
|
|
|
102
111
|
|
|
103
112
|
return {
|
|
104
113
|
action: 'generate',
|
|
105
|
-
config: { text: text.trim(), colors, font },
|
|
114
|
+
config: { mode: 'text', text: text.trim(), colors, font },
|
|
106
115
|
persistent: true,
|
|
107
116
|
};
|
|
108
117
|
}
|
|
109
118
|
|
|
119
|
+
/** Walk the user through the Generate wizard: mode selection, then mode-specific prompts. */
|
|
120
|
+
async function promptGenerate() {
|
|
121
|
+
const mode = handleCancel(
|
|
122
|
+
await p.select({
|
|
123
|
+
message: 'What kind of logo?',
|
|
124
|
+
options: [
|
|
125
|
+
{ value: 'text', label: 'Text', hint: 'type your own text' },
|
|
126
|
+
{ value: 'shape', label: 'Shape', hint: 'pick a mascot/pet' },
|
|
127
|
+
],
|
|
128
|
+
})
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
if (mode === 'shape') return promptShapeGenerate();
|
|
132
|
+
return promptTextGenerate();
|
|
133
|
+
}
|
|
134
|
+
|
|
110
135
|
/**
|
|
111
136
|
* Main entry point for the interactive CLI.
|
|
112
137
|
* Returns { action: 'generate'|'run', config, persistent }.
|