@vue-skuilder/cli 0.1.4 ā 0.1.6
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/CLAUDE.md +84 -0
- package/dist/cli.js +7 -11
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +87 -12
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/pack.d.ts +3 -0
- package/dist/commands/pack.d.ts.map +1 -0
- package/dist/commands/pack.js +141 -0
- package/dist/commands/pack.js.map +1 -0
- package/dist/types.d.ts +29 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +192 -16
- package/dist/types.js.map +1 -1
- package/dist/utils/pack-courses.d.ts +13 -0
- package/dist/utils/pack-courses.d.ts.map +1 -0
- package/dist/utils/pack-courses.js +54 -0
- package/dist/utils/pack-courses.js.map +1 -0
- package/dist/utils/prompts.d.ts +5 -2
- package/dist/utils/prompts.d.ts.map +1 -1
- package/dist/utils/prompts.js +272 -72
- package/dist/utils/prompts.js.map +1 -1
- package/dist/utils/template.d.ts +4 -0
- package/dist/utils/template.d.ts.map +1 -1
- package/dist/utils/template.js +93 -11
- package/dist/utils/template.js.map +1 -1
- package/package.json +5 -3
- package/src/cli.ts +8 -12
- package/src/commands/init.ts +128 -22
- package/src/commands/pack.ts +186 -0
- package/src/types.ts +230 -18
- package/src/utils/pack-courses.ts +77 -0
- package/src/utils/prompts.ts +321 -81
- package/src/utils/template.ts +129 -41
package/src/utils/prompts.ts
CHANGED
|
@@ -1,6 +1,153 @@
|
|
|
1
1
|
import inquirer from 'inquirer';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import
|
|
3
|
+
import PouchDB from 'pouchdb';
|
|
4
|
+
import { CliOptions, ProjectConfig, PREDEFINED_THEMES } from '../types.js';
|
|
5
|
+
|
|
6
|
+
interface CourseInfo {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface CourseDocument {
|
|
13
|
+
name?: string;
|
|
14
|
+
title?: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Fetch available courses from a CouchDB server
|
|
20
|
+
*/
|
|
21
|
+
async function fetchAvailableCourses(
|
|
22
|
+
serverUrl: string,
|
|
23
|
+
username?: string,
|
|
24
|
+
password?: string
|
|
25
|
+
): Promise<CourseInfo[]> {
|
|
26
|
+
const dbUrl = `${serverUrl}/coursedb-lookup`;
|
|
27
|
+
const dbOptions: Record<string, unknown> = {};
|
|
28
|
+
|
|
29
|
+
if (username && password) {
|
|
30
|
+
dbOptions.auth = {
|
|
31
|
+
username,
|
|
32
|
+
password,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log(chalk.gray(`š” Connecting to: ${dbUrl}`));
|
|
37
|
+
const lookupDB = new PouchDB(dbUrl, dbOptions);
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
await lookupDB.info();
|
|
41
|
+
console.log(chalk.green('ā
Connected to course lookup database'));
|
|
42
|
+
} catch (error: unknown) {
|
|
43
|
+
let errorMessage = 'Unknown error';
|
|
44
|
+
if (error instanceof Error) {
|
|
45
|
+
errorMessage = error.message;
|
|
46
|
+
} else if (typeof error === 'string') {
|
|
47
|
+
errorMessage = error;
|
|
48
|
+
} else if (error && typeof error === 'object' && 'message' in error) {
|
|
49
|
+
errorMessage = String((error as { message: unknown }).message);
|
|
50
|
+
}
|
|
51
|
+
throw new Error(`Failed to connect to course lookup database: ${errorMessage}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const result = await lookupDB.allDocs({ include_docs: true });
|
|
56
|
+
const courses: CourseInfo[] = result.rows
|
|
57
|
+
.filter((row) => row.doc && !row.id.startsWith('_'))
|
|
58
|
+
.map((row) => {
|
|
59
|
+
const doc = row.doc as CourseDocument;
|
|
60
|
+
return {
|
|
61
|
+
id: row.id,
|
|
62
|
+
name: doc.name || doc.title || `Course ${row.id}`,
|
|
63
|
+
description: doc.description || undefined,
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
console.log(chalk.green(`ā
Found ${courses.length} available courses`));
|
|
68
|
+
return courses;
|
|
69
|
+
} catch {
|
|
70
|
+
console.warn(chalk.yellow('ā ļø Could not list courses from lookup database'));
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Convert hex color to closest ANSI color code
|
|
77
|
+
*/
|
|
78
|
+
function hexToAnsi(hex: string): string {
|
|
79
|
+
// Remove # if present
|
|
80
|
+
hex = hex.replace('#', '');
|
|
81
|
+
|
|
82
|
+
// Convert hex to RGB
|
|
83
|
+
const r = parseInt(hex.substr(0, 2), 16);
|
|
84
|
+
const g = parseInt(hex.substr(2, 2), 16);
|
|
85
|
+
const b = parseInt(hex.substr(4, 2), 16);
|
|
86
|
+
|
|
87
|
+
// Convert to 256-color ANSI
|
|
88
|
+
const ansiCode =
|
|
89
|
+
16 + 36 * Math.round((r / 255) * 5) + 6 * Math.round((g / 255) * 5) + Math.round((b / 255) * 5);
|
|
90
|
+
return `\x1b[48;5;${ansiCode}m`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Create a color swatch for terminal display
|
|
95
|
+
*/
|
|
96
|
+
function createColorSwatch(hex: string, label: string): string {
|
|
97
|
+
const colorCode = hexToAnsi(hex);
|
|
98
|
+
const reset = '\x1b[0m';
|
|
99
|
+
return `${colorCode} ${reset} ${label}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Create theme preview with color swatches
|
|
104
|
+
*/
|
|
105
|
+
function createThemePreview(themeName: string): string {
|
|
106
|
+
const theme = PREDEFINED_THEMES[themeName];
|
|
107
|
+
const lightColors = theme.light.colors;
|
|
108
|
+
|
|
109
|
+
const primarySwatch = createColorSwatch(lightColors.primary, 'Primary');
|
|
110
|
+
const secondarySwatch = createColorSwatch(lightColors.secondary, 'Secondary');
|
|
111
|
+
const accentSwatch = createColorSwatch(lightColors.accent, 'Accent');
|
|
112
|
+
|
|
113
|
+
return `${primarySwatch} ${secondarySwatch} ${accentSwatch}`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Display comprehensive theme preview after selection
|
|
118
|
+
*/
|
|
119
|
+
export function displayThemePreview(themeName: string): void {
|
|
120
|
+
const theme = PREDEFINED_THEMES[themeName];
|
|
121
|
+
|
|
122
|
+
console.log(chalk.cyan('\nšØ Theme Color Palette:'));
|
|
123
|
+
console.log(chalk.white(` ${theme.name.toUpperCase()} THEME`));
|
|
124
|
+
|
|
125
|
+
// Light theme colors
|
|
126
|
+
console.log(chalk.white('\n Light Mode:'));
|
|
127
|
+
const lightColors = theme.light.colors;
|
|
128
|
+
console.log(` ${createColorSwatch(lightColors.primary, `Primary: ${lightColors.primary}`)}`);
|
|
129
|
+
console.log(
|
|
130
|
+
` ${createColorSwatch(lightColors.secondary, `Secondary: ${lightColors.secondary}`)}`
|
|
131
|
+
);
|
|
132
|
+
console.log(` ${createColorSwatch(lightColors.accent, `Accent: ${lightColors.accent}`)}`);
|
|
133
|
+
console.log(` ${createColorSwatch(lightColors.success, `Success: ${lightColors.success}`)}`);
|
|
134
|
+
console.log(` ${createColorSwatch(lightColors.warning, `Warning: ${lightColors.warning}`)}`);
|
|
135
|
+
console.log(` ${createColorSwatch(lightColors.error, `Error: ${lightColors.error}`)}`);
|
|
136
|
+
|
|
137
|
+
// Dark theme colors
|
|
138
|
+
console.log(chalk.white('\n Dark Mode:'));
|
|
139
|
+
const darkColors = theme.dark.colors;
|
|
140
|
+
console.log(` ${createColorSwatch(darkColors.primary, `Primary: ${darkColors.primary}`)}`);
|
|
141
|
+
console.log(
|
|
142
|
+
` ${createColorSwatch(darkColors.secondary, `Secondary: ${darkColors.secondary}`)}`
|
|
143
|
+
);
|
|
144
|
+
console.log(` ${createColorSwatch(darkColors.accent, `Accent: ${darkColors.accent}`)}`);
|
|
145
|
+
console.log(` ${createColorSwatch(darkColors.success, `Success: ${darkColors.success}`)}`);
|
|
146
|
+
console.log(` ${createColorSwatch(darkColors.warning, `Warning: ${darkColors.warning}`)}`);
|
|
147
|
+
console.log(` ${createColorSwatch(darkColors.error, `Error: ${darkColors.error}`)}`);
|
|
148
|
+
|
|
149
|
+
console.log(chalk.gray(`\n Default mode: ${theme.defaultMode}`));
|
|
150
|
+
}
|
|
4
151
|
|
|
5
152
|
export async function gatherProjectConfig(
|
|
6
153
|
projectName: string,
|
|
@@ -9,7 +156,7 @@ export async function gatherProjectConfig(
|
|
|
9
156
|
console.log(chalk.cyan('\nš Creating a new Skuilder course application\n'));
|
|
10
157
|
|
|
11
158
|
let config: Partial<ProjectConfig> = {
|
|
12
|
-
projectName
|
|
159
|
+
projectName,
|
|
13
160
|
};
|
|
14
161
|
|
|
15
162
|
if (options.interactive) {
|
|
@@ -19,7 +166,7 @@ export async function gatherProjectConfig(
|
|
|
19
166
|
name: 'title',
|
|
20
167
|
message: 'Course title:',
|
|
21
168
|
default: formatProjectName(projectName),
|
|
22
|
-
validate: (input: string) => input.trim().length > 0 || 'Course title is required'
|
|
169
|
+
validate: (input: string) => input.trim().length > 0 || 'Course title is required',
|
|
23
170
|
},
|
|
24
171
|
{
|
|
25
172
|
type: 'list',
|
|
@@ -28,14 +175,14 @@ export async function gatherProjectConfig(
|
|
|
28
175
|
choices: [
|
|
29
176
|
{
|
|
30
177
|
name: 'Dynamic (Connect to CouchDB server)',
|
|
31
|
-
value: 'couch'
|
|
178
|
+
value: 'couch',
|
|
32
179
|
},
|
|
33
180
|
{
|
|
34
181
|
name: 'Static (Self-contained JSON files)',
|
|
35
|
-
value: 'static'
|
|
36
|
-
}
|
|
182
|
+
value: 'static',
|
|
183
|
+
},
|
|
37
184
|
],
|
|
38
|
-
default: options.dataLayer === 'dynamic' ? 'couch' : 'static'
|
|
185
|
+
default: options.dataLayer === 'dynamic' ? 'couch' : 'static',
|
|
39
186
|
},
|
|
40
187
|
{
|
|
41
188
|
type: 'input',
|
|
@@ -51,13 +198,48 @@ export async function gatherProjectConfig(
|
|
|
51
198
|
} catch {
|
|
52
199
|
return 'Please enter a valid URL';
|
|
53
200
|
}
|
|
54
|
-
}
|
|
201
|
+
},
|
|
55
202
|
},
|
|
56
203
|
{
|
|
57
204
|
type: 'input',
|
|
58
205
|
name: 'courseId',
|
|
59
206
|
message: 'Course ID to import (optional):',
|
|
60
|
-
when: (answers) => answers.dataLayerType === 'couch'
|
|
207
|
+
when: (answers) => answers.dataLayerType === 'couch',
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
type: 'confirm',
|
|
211
|
+
name: 'importCourseData',
|
|
212
|
+
message: 'Would you like to import course data from a CouchDB server?',
|
|
213
|
+
default: false,
|
|
214
|
+
when: (answers) => answers.dataLayerType === 'static',
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
type: 'input',
|
|
218
|
+
name: 'importServerUrl',
|
|
219
|
+
message: 'CouchDB server URL:',
|
|
220
|
+
default: 'http://localhost:5984',
|
|
221
|
+
when: (answers) => answers.dataLayerType === 'static' && answers.importCourseData,
|
|
222
|
+
validate: (input: string) => {
|
|
223
|
+
if (!input.trim()) return 'CouchDB URL is required';
|
|
224
|
+
try {
|
|
225
|
+
new URL(input);
|
|
226
|
+
return true;
|
|
227
|
+
} catch {
|
|
228
|
+
return 'Please enter a valid URL';
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
type: 'input',
|
|
234
|
+
name: 'importUsername',
|
|
235
|
+
message: 'Username:',
|
|
236
|
+
when: (answers) => answers.dataLayerType === 'static' && answers.importCourseData,
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
type: 'password',
|
|
240
|
+
name: 'importPassword',
|
|
241
|
+
message: 'Password:',
|
|
242
|
+
when: (answers) => answers.dataLayerType === 'static' && answers.importCourseData,
|
|
61
243
|
},
|
|
62
244
|
{
|
|
63
245
|
type: 'list',
|
|
@@ -65,24 +247,24 @@ export async function gatherProjectConfig(
|
|
|
65
247
|
message: 'Select theme:',
|
|
66
248
|
choices: [
|
|
67
249
|
{
|
|
68
|
-
name:
|
|
69
|
-
value: 'default'
|
|
250
|
+
name: `Default (Material Blue) ${createThemePreview('default')}`,
|
|
251
|
+
value: 'default',
|
|
70
252
|
},
|
|
71
253
|
{
|
|
72
|
-
name:
|
|
73
|
-
value: 'medical'
|
|
254
|
+
name: `Medical (Healthcare Green) ${createThemePreview('medical')}`,
|
|
255
|
+
value: 'medical',
|
|
74
256
|
},
|
|
75
257
|
{
|
|
76
|
-
name:
|
|
77
|
-
value: 'educational'
|
|
258
|
+
name: `Educational (Academic Orange) ${createThemePreview('educational')}`,
|
|
259
|
+
value: 'educational',
|
|
78
260
|
},
|
|
79
261
|
{
|
|
80
|
-
name:
|
|
81
|
-
value: 'corporate'
|
|
82
|
-
}
|
|
262
|
+
name: `Corporate (Professional Gray) ${createThemePreview('corporate')}`,
|
|
263
|
+
value: 'corporate',
|
|
264
|
+
},
|
|
83
265
|
],
|
|
84
|
-
default: options.theme
|
|
85
|
-
}
|
|
266
|
+
default: options.theme,
|
|
267
|
+
},
|
|
86
268
|
]);
|
|
87
269
|
|
|
88
270
|
config = {
|
|
@@ -91,8 +273,113 @@ export async function gatherProjectConfig(
|
|
|
91
273
|
dataLayerType: answers.dataLayerType,
|
|
92
274
|
couchdbUrl: answers.couchdbUrl,
|
|
93
275
|
course: answers.courseId,
|
|
94
|
-
theme: PREDEFINED_THEMES[answers.themeName]
|
|
276
|
+
theme: PREDEFINED_THEMES[answers.themeName],
|
|
277
|
+
importCourseData: answers.importCourseData,
|
|
278
|
+
importServerUrl: answers.importServerUrl,
|
|
279
|
+
importUsername: answers.importUsername,
|
|
280
|
+
importPassword: answers.importPassword,
|
|
95
281
|
};
|
|
282
|
+
|
|
283
|
+
// If user wants to import course data, fetch available courses and let them select
|
|
284
|
+
if (answers.importCourseData && answers.importServerUrl) {
|
|
285
|
+
try {
|
|
286
|
+
console.log(chalk.cyan('\nš Fetching available courses...'));
|
|
287
|
+
const availableCourses = await fetchAvailableCourses(
|
|
288
|
+
answers.importServerUrl,
|
|
289
|
+
answers.importUsername,
|
|
290
|
+
answers.importPassword
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
if (availableCourses.length > 0) {
|
|
294
|
+
const courseSelectionAnswers = await inquirer.prompt([
|
|
295
|
+
{
|
|
296
|
+
type: 'checkbox',
|
|
297
|
+
name: 'selectedCourseIds',
|
|
298
|
+
message: 'Select courses to import:',
|
|
299
|
+
choices: availableCourses.map((course) => ({
|
|
300
|
+
name: `${course.name} (${course.id})`,
|
|
301
|
+
value: course.id,
|
|
302
|
+
short: course.name,
|
|
303
|
+
})),
|
|
304
|
+
validate: (selected: string[]) => {
|
|
305
|
+
if (selected.length === 0) {
|
|
306
|
+
return 'Please select at least one course to import';
|
|
307
|
+
}
|
|
308
|
+
return true;
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
]);
|
|
312
|
+
|
|
313
|
+
config.importCourseIds = courseSelectionAnswers.selectedCourseIds;
|
|
314
|
+
} else {
|
|
315
|
+
console.log(chalk.yellow('ā ļø No courses found in the lookup database.'));
|
|
316
|
+
const manualCourseAnswers = await inquirer.prompt([
|
|
317
|
+
{
|
|
318
|
+
type: 'input',
|
|
319
|
+
name: 'manualCourseIds',
|
|
320
|
+
message: 'Enter course IDs to import (comma-separated):',
|
|
321
|
+
validate: (input: string) => {
|
|
322
|
+
if (!input.trim()) {
|
|
323
|
+
return 'Please enter at least one course ID';
|
|
324
|
+
}
|
|
325
|
+
return true;
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
]);
|
|
329
|
+
|
|
330
|
+
config.importCourseIds = manualCourseAnswers.manualCourseIds
|
|
331
|
+
.split(',')
|
|
332
|
+
.map((id: string) => id.trim())
|
|
333
|
+
.filter((id: string) => id.length > 0);
|
|
334
|
+
}
|
|
335
|
+
} catch (error: unknown) {
|
|
336
|
+
console.error(chalk.red('ā Failed to fetch courses:'));
|
|
337
|
+
let errorMessage = 'Unknown error';
|
|
338
|
+
if (error instanceof Error) {
|
|
339
|
+
errorMessage = error.message;
|
|
340
|
+
} else if (typeof error === 'string') {
|
|
341
|
+
errorMessage = error;
|
|
342
|
+
} else if (error && typeof error === 'object' && 'message' in error) {
|
|
343
|
+
errorMessage = String((error as { message: unknown }).message);
|
|
344
|
+
}
|
|
345
|
+
console.error(chalk.red(errorMessage));
|
|
346
|
+
|
|
347
|
+
const fallbackAnswers = await inquirer.prompt([
|
|
348
|
+
{
|
|
349
|
+
type: 'confirm',
|
|
350
|
+
name: 'continueAnyway',
|
|
351
|
+
message: 'Continue with manual course ID entry?',
|
|
352
|
+
default: true,
|
|
353
|
+
},
|
|
354
|
+
]);
|
|
355
|
+
|
|
356
|
+
if (fallbackAnswers.continueAnyway) {
|
|
357
|
+
const manualCourseAnswers = await inquirer.prompt([
|
|
358
|
+
{
|
|
359
|
+
type: 'input',
|
|
360
|
+
name: 'manualCourseIds',
|
|
361
|
+
message: 'Enter course IDs to import (comma-separated):',
|
|
362
|
+
validate: (input: string) => {
|
|
363
|
+
if (!input.trim()) {
|
|
364
|
+
return 'Please enter at least one course ID';
|
|
365
|
+
}
|
|
366
|
+
return true;
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
]);
|
|
370
|
+
|
|
371
|
+
config.importCourseIds = manualCourseAnswers.manualCourseIds
|
|
372
|
+
.split(',')
|
|
373
|
+
.map((id: string) => id.trim())
|
|
374
|
+
.filter((id: string) => id.length > 0);
|
|
375
|
+
} else {
|
|
376
|
+
config.importCourseData = false;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Show comprehensive theme preview
|
|
382
|
+
displayThemePreview(answers.themeName);
|
|
96
383
|
} else {
|
|
97
384
|
// Non-interactive mode: use provided options
|
|
98
385
|
config = {
|
|
@@ -101,12 +388,14 @@ export async function gatherProjectConfig(
|
|
|
101
388
|
dataLayerType: options.dataLayer === 'dynamic' ? 'couch' : 'static',
|
|
102
389
|
couchdbUrl: options.couchdbUrl,
|
|
103
390
|
course: options.courseId,
|
|
104
|
-
theme: PREDEFINED_THEMES[options.theme]
|
|
391
|
+
theme: PREDEFINED_THEMES[options.theme],
|
|
105
392
|
};
|
|
106
393
|
|
|
107
394
|
// Validate required fields for non-interactive mode
|
|
108
395
|
if (config.dataLayerType === 'couch' && !config.couchdbUrl) {
|
|
109
|
-
throw new Error(
|
|
396
|
+
throw new Error(
|
|
397
|
+
'CouchDB URL is required when using dynamic data layer. Use --couchdb-url option.'
|
|
398
|
+
);
|
|
110
399
|
}
|
|
111
400
|
}
|
|
112
401
|
|
|
@@ -121,16 +410,18 @@ export async function confirmProjectCreation(
|
|
|
121
410
|
console.log(` Project Name: ${chalk.white(config.projectName)}`);
|
|
122
411
|
console.log(` Course Title: ${chalk.white(config.title)}`);
|
|
123
412
|
console.log(` Data Layer: ${chalk.white(config.dataLayerType)}`);
|
|
124
|
-
|
|
413
|
+
|
|
125
414
|
if (config.couchdbUrl) {
|
|
126
415
|
console.log(` CouchDB URL: ${chalk.white(config.couchdbUrl)}`);
|
|
127
416
|
}
|
|
128
|
-
|
|
417
|
+
|
|
129
418
|
if (config.course) {
|
|
130
419
|
console.log(` Course ID: ${chalk.white(config.course)}`);
|
|
131
420
|
}
|
|
132
|
-
|
|
133
|
-
console.log(
|
|
421
|
+
|
|
422
|
+
console.log(
|
|
423
|
+
` Theme: ${chalk.white(config.theme.name)} ${createThemePreview(config.theme.name)}`
|
|
424
|
+
);
|
|
134
425
|
console.log(` Directory: ${chalk.white(projectPath)}`);
|
|
135
426
|
|
|
136
427
|
const { confirmed } = await inquirer.prompt([
|
|
@@ -138,67 +429,16 @@ export async function confirmProjectCreation(
|
|
|
138
429
|
type: 'confirm',
|
|
139
430
|
name: 'confirmed',
|
|
140
431
|
message: 'Create project with these settings?',
|
|
141
|
-
default: true
|
|
142
|
-
}
|
|
143
|
-
]);
|
|
144
|
-
|
|
145
|
-
return confirmed;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export async function promptForCustomTheme(): Promise<ThemeConfig> {
|
|
149
|
-
console.log(chalk.cyan('\nšØ Custom Theme Configuration\n'));
|
|
150
|
-
|
|
151
|
-
const answers = await inquirer.prompt([
|
|
152
|
-
{
|
|
153
|
-
type: 'input',
|
|
154
|
-
name: 'name',
|
|
155
|
-
message: 'Theme name:',
|
|
156
|
-
validate: (input: string) => input.trim().length > 0 || 'Theme name is required'
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
type: 'input',
|
|
160
|
-
name: 'primary',
|
|
161
|
-
message: 'Primary color (hex):',
|
|
162
|
-
default: '#1976D2',
|
|
163
|
-
validate: validateHexColor
|
|
432
|
+
default: true,
|
|
164
433
|
},
|
|
165
|
-
{
|
|
166
|
-
type: 'input',
|
|
167
|
-
name: 'secondary',
|
|
168
|
-
message: 'Secondary color (hex):',
|
|
169
|
-
default: '#424242',
|
|
170
|
-
validate: validateHexColor
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
type: 'input',
|
|
174
|
-
name: 'accent',
|
|
175
|
-
message: 'Accent color (hex):',
|
|
176
|
-
default: '#82B1FF',
|
|
177
|
-
validate: validateHexColor
|
|
178
|
-
}
|
|
179
434
|
]);
|
|
180
435
|
|
|
181
|
-
return
|
|
182
|
-
name: answers.name,
|
|
183
|
-
colors: {
|
|
184
|
-
primary: answers.primary,
|
|
185
|
-
secondary: answers.secondary,
|
|
186
|
-
accent: answers.accent
|
|
187
|
-
}
|
|
188
|
-
};
|
|
436
|
+
return confirmed;
|
|
189
437
|
}
|
|
190
438
|
|
|
191
439
|
function formatProjectName(projectName: string): string {
|
|
192
440
|
return projectName
|
|
193
441
|
.split(/[-_\s]+/)
|
|
194
|
-
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
442
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
195
443
|
.join(' ');
|
|
196
444
|
}
|
|
197
|
-
|
|
198
|
-
function validateHexColor(input: string): boolean | string {
|
|
199
|
-
const hexColorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
|
|
200
|
-
if (!hexColorRegex.test(input)) {
|
|
201
|
-
return 'Please enter a valid hex color (e.g., #1976D2)';
|
|
202
|
-
}
|
|
203
|
-
return true;
|
|
204
|
-
}
|