@husar.ai/cli 0.4.3 → 0.4.5
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/cli.js +132 -54
- package/dist/cli.js.map +1 -1
- package/dist/functions/create.d.ts +1 -1
- package/dist/functions/create.js +287 -152
- package/dist/functions/create.js.map +1 -1
- package/dist/ui.d.ts +51 -0
- package/dist/ui.js +181 -0
- package/dist/ui.js.map +1 -0
- package/package.json +10 -6
- package/src/cli.ts +183 -61
- package/src/functions/create.ts +356 -190
- package/src/ui.ts +260 -0
package/src/ui.ts
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI utilities for beautiful CLI output
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import boxen from 'boxen';
|
|
7
|
+
import ora, { Ora } from 'ora';
|
|
8
|
+
import logSymbols from 'log-symbols';
|
|
9
|
+
import Enquirer from 'enquirer';
|
|
10
|
+
|
|
11
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
12
|
+
// COLORS & THEME
|
|
13
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
14
|
+
|
|
15
|
+
export const theme = {
|
|
16
|
+
primary: chalk.hex('#6366f1'), // Indigo
|
|
17
|
+
secondary: chalk.hex('#8b5cf6'), // Purple
|
|
18
|
+
success: chalk.hex('#22c55e'), // Green
|
|
19
|
+
warning: chalk.hex('#f59e0b'), // Amber
|
|
20
|
+
error: chalk.hex('#ef4444'), // Red
|
|
21
|
+
info: chalk.hex('#3b82f6'), // Blue
|
|
22
|
+
muted: chalk.gray,
|
|
23
|
+
bold: chalk.bold,
|
|
24
|
+
dim: chalk.dim,
|
|
25
|
+
link: chalk.cyan.underline,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
29
|
+
// BANNER
|
|
30
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
31
|
+
|
|
32
|
+
const HUSAR_LOGO = `
|
|
33
|
+
██╗ ██╗██╗ ██╗███████╗ █████╗ ██████╔╗
|
|
34
|
+
██║ ██║██║ ██║██╔════╝██╔══██╗██╔══██╗
|
|
35
|
+
███████║██║ ██║███████╗███████║██████╔╝
|
|
36
|
+
██╔══██║██║ ██║╚════██║██╔══██║██╔══██╗
|
|
37
|
+
██║ ██║╚██████╔╝███████║██║ ██║██║ ██║
|
|
38
|
+
╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝`;
|
|
39
|
+
|
|
40
|
+
export function printBanner(subtitle?: string): void {
|
|
41
|
+
const gradient = HUSAR_LOGO.split('\n')
|
|
42
|
+
.map((line, i) => {
|
|
43
|
+
const colors = ['#818cf8', '#8b5cf6', '#a855f7', '#c084fc', '#d8b4fe', '#e9d5ff'];
|
|
44
|
+
return chalk.hex(colors[i % colors.length])(line);
|
|
45
|
+
})
|
|
46
|
+
.join('\n');
|
|
47
|
+
|
|
48
|
+
console.log(gradient);
|
|
49
|
+
console.log();
|
|
50
|
+
console.log(theme.muted(' Headless CMS for Modern Developers'));
|
|
51
|
+
if (subtitle) {
|
|
52
|
+
console.log(theme.primary(` ${subtitle}`));
|
|
53
|
+
}
|
|
54
|
+
console.log();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function printCompactBanner(): void {
|
|
58
|
+
console.log();
|
|
59
|
+
console.log(chalk.bold(theme.primary(' HUSAR.AI')) + theme.muted(' • Headless CMS'));
|
|
60
|
+
console.log();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
64
|
+
// BOXES
|
|
65
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
66
|
+
|
|
67
|
+
export function box(content: string, title?: string): void {
|
|
68
|
+
console.log(
|
|
69
|
+
boxen(content, {
|
|
70
|
+
padding: 1,
|
|
71
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
72
|
+
borderStyle: 'round',
|
|
73
|
+
borderColor: '#6366f1',
|
|
74
|
+
title: title,
|
|
75
|
+
titleAlignment: 'left',
|
|
76
|
+
}),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function successBox(content: string, title = 'Success'): void {
|
|
81
|
+
console.log(
|
|
82
|
+
boxen(content, {
|
|
83
|
+
padding: 1,
|
|
84
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
85
|
+
borderStyle: 'round',
|
|
86
|
+
borderColor: '#22c55e',
|
|
87
|
+
title: `${logSymbols.success} ${title}`,
|
|
88
|
+
titleAlignment: 'left',
|
|
89
|
+
}),
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function errorBox(content: string, title = 'Error'): void {
|
|
94
|
+
console.log(
|
|
95
|
+
boxen(content, {
|
|
96
|
+
padding: 1,
|
|
97
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
98
|
+
borderStyle: 'round',
|
|
99
|
+
borderColor: '#ef4444',
|
|
100
|
+
title: `${logSymbols.error} ${title}`,
|
|
101
|
+
titleAlignment: 'left',
|
|
102
|
+
}),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function infoBox(content: string, title = 'Info'): void {
|
|
107
|
+
console.log(
|
|
108
|
+
boxen(content, {
|
|
109
|
+
padding: 1,
|
|
110
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
111
|
+
borderStyle: 'round',
|
|
112
|
+
borderColor: '#3b82f6',
|
|
113
|
+
title: `${logSymbols.info} ${title}`,
|
|
114
|
+
titleAlignment: 'left',
|
|
115
|
+
}),
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
120
|
+
// SPINNERS
|
|
121
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
122
|
+
|
|
123
|
+
export function spinner(text: string): Ora {
|
|
124
|
+
return ora({
|
|
125
|
+
text,
|
|
126
|
+
color: 'magenta',
|
|
127
|
+
spinner: 'dots',
|
|
128
|
+
}).start();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
132
|
+
// LOG MESSAGES
|
|
133
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
134
|
+
|
|
135
|
+
export const log = {
|
|
136
|
+
success: (message: string) => console.log(`${logSymbols.success} ${theme.success(message)}`),
|
|
137
|
+
error: (message: string) => console.log(`${logSymbols.error} ${theme.error(message)}`),
|
|
138
|
+
warning: (message: string) => console.log(`${logSymbols.warning} ${theme.warning(message)}`),
|
|
139
|
+
info: (message: string) => console.log(`${logSymbols.info} ${theme.info(message)}`),
|
|
140
|
+
step: (message: string) => console.log(` ${theme.primary('→')} ${message}`),
|
|
141
|
+
substep: (message: string) => console.log(` ${theme.muted('•')} ${message}`),
|
|
142
|
+
done: (message: string) => console.log(` ${theme.success('✓')} ${message}`),
|
|
143
|
+
fail: (message: string) => console.log(` ${theme.error('✗')} ${message}`),
|
|
144
|
+
dim: (message: string) => console.log(theme.dim(` ${message}`)),
|
|
145
|
+
blank: () => console.log(),
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
149
|
+
// PROMPTS (using Enquirer)
|
|
150
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
151
|
+
|
|
152
|
+
interface SelectChoice {
|
|
153
|
+
name: string;
|
|
154
|
+
message: string;
|
|
155
|
+
hint?: string;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const enquirer = new Enquirer();
|
|
159
|
+
|
|
160
|
+
export async function select<T extends string>(message: string, choices: SelectChoice[]): Promise<T> {
|
|
161
|
+
const response = (await enquirer.prompt({
|
|
162
|
+
type: 'select',
|
|
163
|
+
name: 'value',
|
|
164
|
+
message: theme.primary(message),
|
|
165
|
+
choices: choices.map((c) => ({
|
|
166
|
+
name: c.name,
|
|
167
|
+
message: c.message,
|
|
168
|
+
hint: c.hint ? theme.dim(c.hint) : undefined,
|
|
169
|
+
})),
|
|
170
|
+
})) as { value: T };
|
|
171
|
+
return response.value;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export async function input(message: string, defaultValue?: string): Promise<string> {
|
|
175
|
+
const response = (await enquirer.prompt({
|
|
176
|
+
type: 'input',
|
|
177
|
+
name: 'value',
|
|
178
|
+
message: theme.primary(message),
|
|
179
|
+
initial: defaultValue,
|
|
180
|
+
})) as { value: string };
|
|
181
|
+
return response.value;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export async function confirm(message: string, defaultValue = true): Promise<boolean> {
|
|
185
|
+
const response = (await enquirer.prompt({
|
|
186
|
+
type: 'confirm',
|
|
187
|
+
name: 'value',
|
|
188
|
+
message: theme.primary(message),
|
|
189
|
+
initial: defaultValue,
|
|
190
|
+
})) as { value: boolean };
|
|
191
|
+
return response.value;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export async function password(message: string): Promise<string> {
|
|
195
|
+
const response = (await enquirer.prompt({
|
|
196
|
+
type: 'password',
|
|
197
|
+
name: 'value',
|
|
198
|
+
message: theme.primary(message),
|
|
199
|
+
})) as { value: string };
|
|
200
|
+
return response.value;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
204
|
+
// TABLES & LISTS
|
|
205
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
206
|
+
|
|
207
|
+
export function list(items: string[], title?: string): void {
|
|
208
|
+
if (title) {
|
|
209
|
+
console.log(chalk.bold(title));
|
|
210
|
+
}
|
|
211
|
+
items.forEach((item) => {
|
|
212
|
+
console.log(` ${theme.primary('•')} ${item}`);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function keyValue(pairs: Record<string, string | undefined>, title?: string): void {
|
|
217
|
+
if (title) {
|
|
218
|
+
console.log(chalk.bold(title));
|
|
219
|
+
}
|
|
220
|
+
const maxKeyLen = Math.max(...Object.keys(pairs).map((k) => k.length));
|
|
221
|
+
Object.entries(pairs).forEach(([key, value]) => {
|
|
222
|
+
if (value !== undefined) {
|
|
223
|
+
const paddedKey = key.padEnd(maxKeyLen);
|
|
224
|
+
console.log(` ${theme.muted(paddedKey)} ${value}`);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
230
|
+
// DIVIDERS
|
|
231
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
232
|
+
|
|
233
|
+
export function divider(char = '─', length = 50): void {
|
|
234
|
+
console.log(theme.muted(char.repeat(length)));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function header(text: string): void {
|
|
238
|
+
console.log();
|
|
239
|
+
console.log(theme.primary(chalk.bold(text)));
|
|
240
|
+
divider();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
244
|
+
// NEXT STEPS
|
|
245
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
246
|
+
|
|
247
|
+
export function nextSteps(steps: string[], title = 'Next Steps'): void {
|
|
248
|
+
console.log();
|
|
249
|
+
console.log(chalk.bold(title));
|
|
250
|
+
steps.forEach((step, i) => {
|
|
251
|
+
console.log(` ${theme.primary(`${i + 1}.`)} ${step}`);
|
|
252
|
+
});
|
|
253
|
+
console.log();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
257
|
+
// EXPORTS
|
|
258
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
259
|
+
|
|
260
|
+
export { chalk, boxen, ora, logSymbols, Enquirer };
|