create-waypoint-app 1.0.0

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/index.js ADDED
@@ -0,0 +1,1806 @@
1
+ // src/index.ts
2
+ import { program } from "commander";
3
+ import chalk4 from "chalk";
4
+
5
+ // src/prompts.ts
6
+ import prompts from "prompts";
7
+ import chalk from "chalk";
8
+ async function gatherProjectConfig(initialName) {
9
+ const response = await prompts([
10
+ {
11
+ type: "text",
12
+ name: "projectName",
13
+ message: "Project name:",
14
+ initial: initialName || "my-waypoint-app",
15
+ validate: (value) => {
16
+ if (!value) return "Project name is required";
17
+ if (!/^[a-z0-9-]+$/.test(value)) {
18
+ return "Project name can only contain lowercase letters, numbers, and hyphens";
19
+ }
20
+ return true;
21
+ }
22
+ },
23
+ {
24
+ type: "text",
25
+ name: "description",
26
+ message: "Project description:",
27
+ initial: "A Waypoint 2.0 design system project"
28
+ },
29
+ {
30
+ type: "multiselect",
31
+ name: "themeModes",
32
+ message: "Select theme modes:",
33
+ choices: [
34
+ { title: "Light Mode", value: "light", selected: true },
35
+ { title: "Dark Mode", value: "dark", selected: true },
36
+ { title: "High Contrast Light", value: "high-contrast-light" },
37
+ { title: "High Contrast Dark", value: "high-contrast-dark" }
38
+ ],
39
+ hint: "- Space to select, Enter to confirm",
40
+ min: 1
41
+ },
42
+ {
43
+ type: "select",
44
+ name: "themePreset",
45
+ message: "Default theme preset:",
46
+ choices: [
47
+ { title: "Default", value: "default", description: "Neutral grays with accent colors" },
48
+ { title: "Warm", value: "warm", description: "Oranges and earthy tones" },
49
+ { title: "Cool", value: "cool", description: "Blues and teals" },
50
+ { title: "Pastel", value: "pastel", description: "Soft, muted colors" },
51
+ { title: "Bold", value: "bold", description: "High contrast, vibrant colors" },
52
+ { title: "Ocean", value: "ocean", description: "Deep blues and aquatics" }
53
+ ]
54
+ },
55
+ {
56
+ type: "confirm",
57
+ name: "enableRTL",
58
+ message: "Enable RTL (Right-to-Left) support?",
59
+ initial: false
60
+ },
61
+ {
62
+ type: "confirm",
63
+ name: "enableFontScaling",
64
+ message: "Enable font scaling accessibility?",
65
+ initial: true
66
+ },
67
+ {
68
+ type: "multiselect",
69
+ name: "features",
70
+ message: "Select component groups to include:",
71
+ choices: [
72
+ { title: "Core Components", value: "core", selected: true, description: "Button, Card, Input, Dialog, etc." },
73
+ { title: "Form Components", value: "forms", selected: true, description: "Form, Checkbox, Select, Switch, etc." },
74
+ { title: "Feedback Components", value: "feedback", selected: true, description: "Toast, Alert, Progress, Skeleton" },
75
+ { title: "Navigation Components", value: "navigation", selected: true, description: "Tabs, Breadcrumb, Pagination, Menu" },
76
+ { title: "Data Components", value: "data", description: "Table, Calendar, Data display" },
77
+ { title: "Advanced Components", value: "advanced", description: "Command, Carousel, Resizable, Drawer" },
78
+ { title: "Chart Components", value: "charts", description: "Recharts-based chart components" }
79
+ ],
80
+ hint: "- Space to select, Enter to confirm"
81
+ },
82
+ {
83
+ type: "confirm",
84
+ name: "includePageTemplates",
85
+ message: "Include page templates (27+ layouts)?",
86
+ initial: false
87
+ },
88
+ {
89
+ type: "confirm",
90
+ name: "includeComposer",
91
+ message: "Include Composer visual builder tool?",
92
+ initial: false
93
+ },
94
+ {
95
+ type: "confirm",
96
+ name: "includeAuth",
97
+ message: "Include authentication scaffolding?",
98
+ initial: false
99
+ }
100
+ ], {
101
+ onCancel: () => {
102
+ console.log(chalk.red("\n Setup cancelled.\n"));
103
+ process.exit(0);
104
+ }
105
+ });
106
+ return {
107
+ projectName: response.projectName,
108
+ description: response.description,
109
+ themeModes: response.themeModes,
110
+ themePreset: response.themePreset,
111
+ enableRTL: response.enableRTL,
112
+ enableFontScaling: response.enableFontScaling,
113
+ features: response.features,
114
+ includeAuth: response.includeAuth,
115
+ includePageTemplates: response.includePageTemplates,
116
+ includeComposer: response.includeComposer
117
+ };
118
+ }
119
+
120
+ // src/scaffolder.ts
121
+ import path3 from "path";
122
+ import fs3 from "fs-extra";
123
+ import ora from "ora";
124
+
125
+ // src/generators/package.ts
126
+ function generatePackageJson(config) {
127
+ const dependencies = {
128
+ "react": "^18.3.1",
129
+ "react-dom": "^18.3.1",
130
+ "react-router-dom": "^7.12.0",
131
+ "class-variance-authority": "^0.7.1",
132
+ "clsx": "^2.1.1",
133
+ "tailwind-merge": "^2.5.2",
134
+ "lucide-react": "^0.462.0",
135
+ "@radix-ui/react-slot": "^1.1.0"
136
+ };
137
+ const devDependencies = {
138
+ "@types/react": "^18.3.12",
139
+ "@types/react-dom": "^18.3.1",
140
+ "@vitejs/plugin-react-swc": "^3.5.0",
141
+ "autoprefixer": "^10.4.20",
142
+ "postcss": "^8.4.49",
143
+ "tailwindcss": "^3.4.14",
144
+ "tailwindcss-animate": "^1.0.7",
145
+ "typescript": "^5.6.3",
146
+ "vite": "^5.4.10"
147
+ };
148
+ if (config.features.includes("core")) {
149
+ Object.assign(dependencies, {
150
+ "@radix-ui/react-dialog": "^1.1.2",
151
+ "@radix-ui/react-dropdown-menu": "^2.1.1",
152
+ "@radix-ui/react-tooltip": "^1.1.4",
153
+ "@radix-ui/react-popover": "^1.1.1",
154
+ "@radix-ui/react-separator": "^1.1.0",
155
+ "@radix-ui/react-scroll-area": "^1.1.0",
156
+ "@radix-ui/react-avatar": "^1.1.0",
157
+ "@radix-ui/react-aspect-ratio": "^1.1.0",
158
+ "@radix-ui/react-collapsible": "^1.1.0",
159
+ "@radix-ui/react-accordion": "^1.2.0"
160
+ });
161
+ }
162
+ if (config.features.includes("forms")) {
163
+ Object.assign(dependencies, {
164
+ "@radix-ui/react-checkbox": "^1.1.1",
165
+ "@radix-ui/react-radio-group": "^1.2.0",
166
+ "@radix-ui/react-select": "^2.1.1",
167
+ "@radix-ui/react-switch": "^1.1.0",
168
+ "@radix-ui/react-slider": "^1.2.0",
169
+ "@radix-ui/react-label": "^2.1.0",
170
+ "@hookform/resolvers": "^3.9.0",
171
+ "react-hook-form": "^7.53.0",
172
+ "zod": "^3.23.8",
173
+ "input-otp": "^1.2.4"
174
+ });
175
+ }
176
+ if (config.features.includes("feedback")) {
177
+ Object.assign(dependencies, {
178
+ "@radix-ui/react-toast": "^1.2.1",
179
+ "@radix-ui/react-alert-dialog": "^1.1.1",
180
+ "@radix-ui/react-progress": "^1.1.0",
181
+ "sonner": "^1.5.0"
182
+ });
183
+ }
184
+ if (config.features.includes("navigation")) {
185
+ Object.assign(dependencies, {
186
+ "@radix-ui/react-tabs": "^1.1.0",
187
+ "@radix-ui/react-navigation-menu": "^1.2.0",
188
+ "@radix-ui/react-menubar": "^1.1.1",
189
+ "@radix-ui/react-context-menu": "^2.2.1",
190
+ "@radix-ui/react-toggle": "^1.1.0",
191
+ "@radix-ui/react-toggle-group": "^1.1.0"
192
+ });
193
+ }
194
+ if (config.features.includes("data")) {
195
+ Object.assign(dependencies, {
196
+ "react-day-picker": "^8.10.1",
197
+ "date-fns": "^3.6.0"
198
+ });
199
+ }
200
+ if (config.features.includes("advanced")) {
201
+ Object.assign(dependencies, {
202
+ "cmdk": "^1.0.0",
203
+ "embla-carousel-react": "^8.3.0",
204
+ "embla-carousel-autoplay": "^8.6.0",
205
+ "react-resizable-panels": "^2.1.3",
206
+ "vaul": "^1.1.2",
207
+ "@radix-ui/react-hover-card": "^1.1.1"
208
+ });
209
+ }
210
+ if (config.features.includes("charts")) {
211
+ Object.assign(dependencies, {
212
+ "recharts": "^2.12.7"
213
+ });
214
+ }
215
+ dependencies["motion"] = "^12.18.1";
216
+ dependencies["next-themes"] = "^0.3.0";
217
+ if (config.includeAuth) {
218
+ dependencies["@supabase/supabase-js"] = "^2.84.0";
219
+ dependencies["@tanstack/react-query"] = "^5.56.2";
220
+ }
221
+ return {
222
+ name: config.projectName,
223
+ private: true,
224
+ version: "0.1.0",
225
+ description: config.description,
226
+ type: "module",
227
+ scripts: {
228
+ "dev": "vite",
229
+ "build": "tsc && vite build",
230
+ "preview": "vite preview",
231
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
232
+ },
233
+ dependencies,
234
+ devDependencies
235
+ };
236
+ }
237
+
238
+ // src/generators/tailwind.ts
239
+ function generateTailwindConfig(config) {
240
+ return `import type { Config } from "tailwindcss";
241
+
242
+ const config = {
243
+ darkMode: ["class"],
244
+ content: [
245
+ "./pages/**/*.{ts,tsx}",
246
+ "./components/**/*.{ts,tsx}",
247
+ "./app/**/*.{ts,tsx}",
248
+ "./src/**/*.{ts,tsx}",
249
+ ],
250
+ prefix: "",
251
+ theme: {
252
+ container: {
253
+ center: true,
254
+ padding: "2rem",
255
+ screens: {
256
+ "2xl": "1400px",
257
+ },
258
+ },
259
+ screens: {
260
+ sm: "640px",
261
+ md: "768px",
262
+ lg: "1024px",
263
+ xl: "1200px",
264
+ "2xl": "1400px",
265
+ },
266
+ extend: {
267
+ fontFamily: {
268
+ sans: ['Circular', 'ui-sans-serif', 'system-ui', 'sans-serif'],
269
+ heading: ['"Circular 20"', 'Circular', 'ui-sans-serif', 'system-ui', 'sans-serif'],
270
+ },
271
+ colors: {
272
+ border: "hsl(var(--border))",
273
+ input: "hsl(var(--input))",
274
+ ring: "hsl(var(--ring))",
275
+ background: "hsl(var(--background))",
276
+ foreground: "hsl(var(--foreground))",
277
+ primary: {
278
+ DEFAULT: "hsl(var(--primary))",
279
+ foreground: "hsl(var(--primary-foreground))",
280
+ },
281
+ secondary: {
282
+ DEFAULT: "hsl(var(--secondary))",
283
+ foreground: "hsl(var(--secondary-foreground))",
284
+ },
285
+ destructive: {
286
+ DEFAULT: "hsl(var(--destructive))",
287
+ foreground: "hsl(var(--destructive-foreground))",
288
+ text: "hsl(var(--destructive-text))",
289
+ },
290
+ success: {
291
+ DEFAULT: "hsl(var(--success))",
292
+ foreground: "hsl(var(--success-foreground))",
293
+ },
294
+ warning: {
295
+ DEFAULT: "hsl(var(--warning))",
296
+ foreground: "hsl(var(--warning-foreground))",
297
+ },
298
+ muted: {
299
+ DEFAULT: "hsl(var(--muted))",
300
+ foreground: "hsl(var(--muted-foreground))",
301
+ },
302
+ accent: {
303
+ DEFAULT: "hsl(var(--accent))",
304
+ foreground: "hsl(var(--accent-foreground))",
305
+ },
306
+ popover: {
307
+ DEFAULT: "hsl(var(--popover))",
308
+ foreground: "hsl(var(--popover-foreground))",
309
+ },
310
+ card: {
311
+ DEFAULT: "hsl(var(--card))",
312
+ foreground: "hsl(var(--card-foreground))",
313
+ },
314
+ sidebar: {
315
+ DEFAULT: "hsl(var(--sidebar-background))",
316
+ foreground: "hsl(var(--sidebar-foreground))",
317
+ primary: "hsl(var(--sidebar-primary))",
318
+ "primary-foreground": "hsl(var(--sidebar-primary-foreground))",
319
+ accent: "hsl(var(--sidebar-accent))",
320
+ "accent-foreground": "hsl(var(--sidebar-accent-foreground))",
321
+ border: "hsl(var(--sidebar-border))",
322
+ ring: "hsl(var(--sidebar-ring))",
323
+ },
324
+ brand: {
325
+ DEFAULT: "hsl(var(--brand))",
326
+ dark: "hsl(var(--brand-dark))",
327
+ },
328
+ },
329
+ borderRadius: {
330
+ lg: "var(--radius)",
331
+ md: "calc(var(--radius) - 2px)",
332
+ sm: "calc(var(--radius) - 4px)",
333
+ },
334
+ keyframes: {
335
+ "accordion-down": {
336
+ from: { height: "0" },
337
+ to: { height: "var(--radix-accordion-content-height)" },
338
+ },
339
+ "accordion-up": {
340
+ from: { height: "var(--radix-accordion-content-height)" },
341
+ to: { height: "0" },
342
+ },
343
+ },
344
+ animation: {
345
+ "accordion-down": "accordion-down 0.2s ease-out",
346
+ "accordion-up": "accordion-up 0.2s ease-out",
347
+ "spin-slow": "spin 8s linear infinite",
348
+ },
349
+ },
350
+ },
351
+ plugins: [require("tailwindcss-animate")],
352
+ } satisfies Config;
353
+
354
+ export default config;
355
+ `;
356
+ }
357
+
358
+ // src/generators/vite.ts
359
+ function generateViteConfig() {
360
+ return `import { defineConfig } from "vite";
361
+ import react from "@vitejs/plugin-react-swc";
362
+ import path from "path";
363
+
364
+ // https://vitejs.dev/config/
365
+ export default defineConfig({
366
+ server: {
367
+ host: "::",
368
+ port: 8080,
369
+ },
370
+ plugins: [react()],
371
+ resolve: {
372
+ alias: {
373
+ "@": path.resolve(__dirname, "./src"),
374
+ },
375
+ },
376
+ });
377
+ `;
378
+ }
379
+
380
+ // src/generators/styles.ts
381
+ function generateIndexCSS(config) {
382
+ let css = `@tailwind base;
383
+ @tailwind components;
384
+ @tailwind utilities;
385
+
386
+ /* Waypoint 2.0 Design System */
387
+
388
+ `;
389
+ css += `@layer base {
390
+ `;
391
+ if (config.themeModes.includes("light")) {
392
+ css += ` :root {
393
+ --background: 0 0% 100%;
394
+ --foreground: 222.2 84% 4.9%;
395
+
396
+ --card: 0 0% 100%;
397
+ --card-foreground: 222.2 84% 4.9%;
398
+
399
+ --popover: 0 0% 100%;
400
+ --popover-foreground: 222.2 84% 4.9%;
401
+
402
+ --primary: 222.2 47.4% 11.2%;
403
+ --primary-foreground: 210 40% 98%;
404
+
405
+ --secondary: 210 40% 96.1%;
406
+ --secondary-foreground: 222.2 47.4% 11.2%;
407
+
408
+ --muted: 210 40% 96.1%;
409
+ --muted-foreground: 215.4 16.3% 46.9%;
410
+
411
+ --accent: 210 40% 96.1%;
412
+ --accent-foreground: 222.2 47.4% 11.2%;
413
+
414
+ --destructive: 0 84.2% 60.2%;
415
+ --destructive-foreground: 210 40% 98%;
416
+ --destructive-text: 0 84.2% 45%;
417
+
418
+ --success: 142 76% 36%;
419
+ --success-foreground: 0 0% 100%;
420
+ --warning: 38 92% 50%;
421
+ --warning-foreground: 38 92% 20%;
422
+
423
+ --border: 214.3 31.8% 91.4%;
424
+ --input: 214.3 31.8% 91.4%;
425
+ --ring: 222.2 84% 4.9%;
426
+
427
+ --radius: 0.5rem;
428
+
429
+ --brand: 217 100% 50%;
430
+ --brand-dark: 218 100% 33%;
431
+
432
+ --sidebar-background: 0 0% 98%;
433
+ --sidebar-foreground: 240 5.3% 26.1%;
434
+ --sidebar-primary: 240 5.9% 10%;
435
+ --sidebar-primary-foreground: 0 0% 98%;
436
+ --sidebar-accent: 240 4.8% 95.9%;
437
+ --sidebar-accent-foreground: 240 5.9% 10%;
438
+ --sidebar-border: 220 13% 91%;
439
+ --sidebar-ring: 217.2 91.2% 59.8%;
440
+ }
441
+
442
+ `;
443
+ }
444
+ if (config.themeModes.includes("dark")) {
445
+ css += ` .dark {
446
+ --background: 222.2 84% 4.9%;
447
+ --foreground: 210 40% 98%;
448
+
449
+ --card: 222.2 84% 4.9%;
450
+ --card-foreground: 210 40% 98%;
451
+
452
+ --popover: 222.2 84% 4.9%;
453
+ --popover-foreground: 210 40% 98%;
454
+
455
+ --primary: 210 40% 98%;
456
+ --primary-foreground: 222.2 47.4% 11.2%;
457
+
458
+ --secondary: 217.2 32.6% 17.5%;
459
+ --secondary-foreground: 210 40% 98%;
460
+
461
+ --muted: 217.2 32.6% 17.5%;
462
+ --muted-foreground: 215 20.2% 65.1%;
463
+
464
+ --accent: 217.2 32.6% 17.5%;
465
+ --accent-foreground: 210 40% 98%;
466
+
467
+ --destructive: 0 62.8% 30.6%;
468
+ --destructive-foreground: 210 40% 98%;
469
+ --destructive-text: 0 84.2% 65%;
470
+
471
+ --success: 142 76% 46%;
472
+ --success-foreground: 0 0% 100%;
473
+ --warning: 38 92% 50%;
474
+ --warning-foreground: 38 92% 90%;
475
+
476
+ --border: 217.2 32.6% 17.5%;
477
+ --input: 217.2 32.6% 17.5%;
478
+ --ring: 212.7 26.8% 83.9%;
479
+
480
+ --brand: 217 100% 50%;
481
+ --brand-dark: 218 100% 33%;
482
+
483
+ --sidebar-background: 240 5.9% 10%;
484
+ --sidebar-foreground: 240 4.8% 95.9%;
485
+ --sidebar-primary: 224.3 76.3% 48%;
486
+ --sidebar-primary-foreground: 0 0% 100%;
487
+ --sidebar-accent: 240 3.7% 15.9%;
488
+ --sidebar-accent-foreground: 240 4.8% 95.9%;
489
+ --sidebar-border: 240 3.7% 15.9%;
490
+ --sidebar-ring: 217.2 91.2% 59.8%;
491
+ }
492
+
493
+ `;
494
+ }
495
+ if (config.themeModes.includes("high-contrast-light")) {
496
+ css += ` /* High Contrast Light Mode */
497
+ .high-contrast {
498
+ --background: 0 0% 100%;
499
+ --foreground: 0 0% 0%;
500
+
501
+ --card: 0 0% 100%;
502
+ --card-foreground: 0 0% 0%;
503
+
504
+ --popover: 0 0% 100%;
505
+ --popover-foreground: 0 0% 0%;
506
+
507
+ --primary: 0 0% 0%;
508
+ --primary-foreground: 0 0% 100%;
509
+
510
+ --secondary: 0 0% 96%;
511
+ --secondary-foreground: 0 0% 0%;
512
+
513
+ --muted: 0 0% 96%;
514
+ --muted-foreground: 0 0% 25%;
515
+
516
+ --accent: 0 0% 96%;
517
+ --accent-foreground: 0 0% 0%;
518
+
519
+ --destructive: 0 100% 40%;
520
+ --destructive-foreground: 0 0% 100%;
521
+
522
+ --border: 0 0% 0%;
523
+ --input: 0 0% 0%;
524
+ --ring: 0 0% 0%;
525
+
526
+ --sidebar-background: 0 0% 100%;
527
+ --sidebar-foreground: 0 0% 0%;
528
+ --sidebar-primary: 0 0% 0%;
529
+ --sidebar-primary-foreground: 0 0% 100%;
530
+ --sidebar-accent: 0 0% 96%;
531
+ --sidebar-accent-foreground: 0 0% 0%;
532
+ --sidebar-border: 0 0% 0%;
533
+ --sidebar-ring: 0 0% 0%;
534
+ }
535
+
536
+ `;
537
+ }
538
+ if (config.themeModes.includes("high-contrast-dark")) {
539
+ css += ` /* High Contrast Dark Mode */
540
+ .dark.high-contrast {
541
+ --background: 0 0% 0%;
542
+ --foreground: 0 0% 100%;
543
+
544
+ --card: 0 0% 0%;
545
+ --card-foreground: 0 0% 100%;
546
+
547
+ --popover: 0 0% 0%;
548
+ --popover-foreground: 0 0% 100%;
549
+
550
+ --primary: 0 0% 100%;
551
+ --primary-foreground: 0 0% 0%;
552
+
553
+ --secondary: 0 0% 15%;
554
+ --secondary-foreground: 0 0% 100%;
555
+
556
+ --muted: 0 0% 15%;
557
+ --muted-foreground: 0 0% 75%;
558
+
559
+ --accent: 0 0% 15%;
560
+ --accent-foreground: 0 0% 100%;
561
+
562
+ --destructive: 0 100% 60%;
563
+ --destructive-foreground: 0 0% 0%;
564
+
565
+ --border: 0 0% 100%;
566
+ --input: 0 0% 100%;
567
+ --ring: 0 0% 100%;
568
+
569
+ --sidebar-background: 0 0% 0%;
570
+ --sidebar-foreground: 0 0% 100%;
571
+ --sidebar-primary: 0 0% 100%;
572
+ --sidebar-primary-foreground: 0 0% 0%;
573
+ --sidebar-accent: 0 0% 15%;
574
+ --sidebar-accent-foreground: 0 0% 100%;
575
+ --sidebar-border: 0 0% 100%;
576
+ --sidebar-ring: 0 0% 100%;
577
+ }
578
+
579
+ `;
580
+ }
581
+ if (config.enableFontScaling) {
582
+ css += ` /* Font scaling classes */
583
+ .font-scale-small {
584
+ font-size: calc(var(--font-scale, 1) * 1rem);
585
+ }
586
+
587
+ .font-scale-medium {
588
+ font-size: calc(var(--font-scale, 1) * 1rem);
589
+ }
590
+
591
+ .font-scale-large {
592
+ font-size: calc(var(--font-scale, 1) * 1rem);
593
+ }
594
+ `;
595
+ }
596
+ css += `}
597
+
598
+ `;
599
+ css += `@layer base {
600
+ * {
601
+ @apply border-border;
602
+ }
603
+
604
+ body {
605
+ @apply bg-background text-foreground;`;
606
+ if (config.enableFontScaling) {
607
+ css += `
608
+ font-size: calc(1rem * var(--font-scale, 1));`;
609
+ }
610
+ css += `
611
+ }
612
+
613
+ `;
614
+ if (config.enableRTL) {
615
+ css += ` /* RTL Support */
616
+ .rtl {
617
+ direction: rtl;
618
+ }
619
+
620
+ .rtl .text-left {
621
+ text-align: right;
622
+ }
623
+
624
+ .rtl .text-right {
625
+ text-align: left;
626
+ }
627
+
628
+ .rtl .ml-2 {
629
+ margin-left: 0;
630
+ margin-right: 0.5rem;
631
+ }
632
+
633
+ .rtl .mr-2 {
634
+ margin-right: 0;
635
+ margin-left: 0.5rem;
636
+ }
637
+
638
+ .rtl .pl-3 {
639
+ padding-left: 0;
640
+ padding-right: 0.75rem;
641
+ }
642
+
643
+ .rtl .pr-3 {
644
+ padding-right: 0;
645
+ padding-left: 0.75rem;
646
+ }
647
+
648
+ `;
649
+ }
650
+ if (config.enableFontScaling) {
651
+ css += ` /* Scale all text elements */
652
+ h1, h2, h3, h4, h5, h6, p, span, div, button, input, textarea, label, a {
653
+ font-size: calc(1em * var(--font-scale, 1));
654
+ }
655
+
656
+ `;
657
+ }
658
+ css += ` /* Heading font family + size hierarchy */
659
+ h1, h2, h3, h4, h5, h6 {
660
+ font-family: "Circular 20", Circular, ui-sans-serif, system-ui, sans-serif;
661
+ font-weight: 700;
662
+ }
663
+ `;
664
+ if (config.enableFontScaling) {
665
+ css += ` h1 { font-size: calc(2.25rem * var(--font-scale, 1)); }
666
+ h2 { font-size: calc(1.875rem * var(--font-scale, 1)); }
667
+ h3 { font-size: calc(1.5rem * var(--font-scale, 1)); }
668
+ h4 { font-size: calc(1.25rem * var(--font-scale, 1)); }
669
+ h5 { font-size: calc(1.125rem * var(--font-scale, 1)); }
670
+ h6 { font-size: calc(1rem * var(--font-scale, 1)); }
671
+
672
+ /* Scale text utility classes */
673
+ .text-sm { font-size: calc(0.875rem * var(--font-scale, 1)); }
674
+ .text-xs { font-size: calc(0.75rem * var(--font-scale, 1)); }
675
+ .text-lg { font-size: calc(1.125rem * var(--font-scale, 1)); }
676
+ .text-xl { font-size: calc(1.25rem * var(--font-scale, 1)); }
677
+ .text-2xl { font-size: calc(1.5rem * var(--font-scale, 1)); }
678
+ .text-3xl { font-size: calc(1.875rem * var(--font-scale, 1)); }
679
+ `;
680
+ }
681
+ css += `
682
+ pre {
683
+ @apply text-left;
684
+ }
685
+ }
686
+ `;
687
+ return css;
688
+ }
689
+
690
+ // src/generators/config.ts
691
+ function generateTsConfig() {
692
+ return {
693
+ compilerOptions: {
694
+ target: "ES2020",
695
+ useDefineForClassFields: true,
696
+ lib: ["ES2020", "DOM", "DOM.Iterable"],
697
+ module: "ESNext",
698
+ skipLibCheck: true,
699
+ moduleResolution: "bundler",
700
+ allowImportingTsExtensions: true,
701
+ resolveJsonModule: true,
702
+ isolatedModules: true,
703
+ noEmit: true,
704
+ jsx: "react-jsx",
705
+ strict: true,
706
+ noUnusedLocals: true,
707
+ noUnusedParameters: true,
708
+ noFallthroughCasesInSwitch: true,
709
+ baseUrl: ".",
710
+ paths: {
711
+ "@/*": ["./src/*"]
712
+ }
713
+ },
714
+ include: ["src"],
715
+ references: [{ path: "./tsconfig.node.json" }]
716
+ };
717
+ }
718
+ function generateComponentsJson() {
719
+ return {
720
+ "$schema": "https://ui.shadcn.com/schema.json",
721
+ style: "default",
722
+ rsc: false,
723
+ tsx: true,
724
+ tailwind: {
725
+ config: "tailwind.config.ts",
726
+ css: "src/index.css",
727
+ baseColor: "slate",
728
+ cssVariables: true,
729
+ prefix: ""
730
+ },
731
+ aliases: {
732
+ components: "@/components",
733
+ utils: "@/lib/utils",
734
+ ui: "@/components/ui",
735
+ lib: "@/lib",
736
+ hooks: "@/hooks"
737
+ }
738
+ };
739
+ }
740
+
741
+ // src/generators/components.ts
742
+ import path from "path";
743
+ import fs from "fs-extra";
744
+ var componentMappings = {
745
+ core: [
746
+ "button",
747
+ "card",
748
+ "dialog",
749
+ "dropdown-menu",
750
+ "input",
751
+ "separator",
752
+ "scroll-area",
753
+ "avatar",
754
+ "aspect-ratio",
755
+ "collapsible",
756
+ "accordion",
757
+ "badge",
758
+ "skeleton",
759
+ "tooltip",
760
+ "popover"
761
+ ],
762
+ forms: [
763
+ "checkbox",
764
+ "form",
765
+ "input",
766
+ "label",
767
+ "radio-group",
768
+ "select",
769
+ "slider",
770
+ "switch",
771
+ "textarea",
772
+ "input-otp"
773
+ ],
774
+ feedback: [
775
+ "alert",
776
+ "alert-dialog",
777
+ "progress",
778
+ "toast",
779
+ "toaster",
780
+ "sonner"
781
+ ],
782
+ navigation: [
783
+ "breadcrumb",
784
+ "navigation-menu",
785
+ "menubar",
786
+ "context-menu",
787
+ "pagination",
788
+ "tabs",
789
+ "toggle",
790
+ "toggle-group"
791
+ ],
792
+ data: [
793
+ "calendar",
794
+ "table"
795
+ ],
796
+ advanced: [
797
+ "carousel",
798
+ "command",
799
+ "drawer",
800
+ "hover-card",
801
+ "resizable",
802
+ "sheet"
803
+ ],
804
+ charts: [
805
+ "chart"
806
+ ]
807
+ };
808
+ async function generateComponents(projectPath, config) {
809
+ await generateUtilFiles(projectPath);
810
+ const componentsToGenerate = /* @__PURE__ */ new Set();
811
+ for (const feature of config.features) {
812
+ const components = componentMappings[feature] || [];
813
+ components.forEach((c) => componentsToGenerate.add(c));
814
+ }
815
+ for (const component of componentsToGenerate) {
816
+ await generateComponentFile(projectPath, component);
817
+ }
818
+ await generateComponentIndex(projectPath, Array.from(componentsToGenerate));
819
+ }
820
+ async function generateUtilFiles(projectPath) {
821
+ const utilsContent = `import { type ClassValue, clsx } from "clsx";
822
+ import { twMerge } from "tailwind-merge";
823
+
824
+ export function cn(...inputs: ClassValue[]) {
825
+ return twMerge(clsx(inputs));
826
+ }
827
+ `;
828
+ await fs.writeFile(path.join(projectPath, "src/lib/utils.ts"), utilsContent);
829
+ const useMobileContent = `import * as React from "react";
830
+
831
+ const MOBILE_BREAKPOINT = 768;
832
+
833
+ export function useIsMobile() {
834
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);
835
+
836
+ React.useEffect(() => {
837
+ const mql = window.matchMedia(\`(max-width: \${MOBILE_BREAKPOINT - 1}px)\`);
838
+ const onChange = () => {
839
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
840
+ };
841
+ mql.addEventListener("change", onChange);
842
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
843
+ return () => mql.removeEventListener("change", onChange);
844
+ }, []);
845
+
846
+ return !!isMobile;
847
+ }
848
+ `;
849
+ await fs.writeFile(path.join(projectPath, "src/hooks/use-mobile.tsx"), useMobileContent);
850
+ const useToastContent = `import * as React from "react";
851
+
852
+ const TOAST_LIMIT = 1;
853
+ const TOAST_REMOVE_DELAY = 1000000;
854
+
855
+ type ToasterToast = {
856
+ id: string;
857
+ title?: React.ReactNode;
858
+ description?: React.ReactNode;
859
+ action?: React.ReactNode;
860
+ variant?: "default" | "destructive";
861
+ };
862
+
863
+ type State = {
864
+ toasts: ToasterToast[];
865
+ };
866
+
867
+ let count = 0;
868
+ function genId() {
869
+ count = (count + 1) % Number.MAX_SAFE_INTEGER;
870
+ return count.toString();
871
+ }
872
+
873
+ const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
874
+
875
+ const addToRemoveQueue = (toastId: string) => {
876
+ if (toastTimeouts.has(toastId)) {
877
+ return;
878
+ }
879
+
880
+ const timeout = setTimeout(() => {
881
+ toastTimeouts.delete(toastId);
882
+ dispatch({
883
+ type: "REMOVE_TOAST",
884
+ toastId: toastId,
885
+ });
886
+ }, TOAST_REMOVE_DELAY);
887
+
888
+ toastTimeouts.set(toastId, timeout);
889
+ };
890
+
891
+ type Action =
892
+ | { type: "ADD_TOAST"; toast: ToasterToast }
893
+ | { type: "UPDATE_TOAST"; toast: Partial<ToasterToast> }
894
+ | { type: "DISMISS_TOAST"; toastId?: string }
895
+ | { type: "REMOVE_TOAST"; toastId?: string };
896
+
897
+ const listeners: Array<(state: State) => void> = [];
898
+
899
+ let memoryState: State = { toasts: [] };
900
+
901
+ function dispatch(action: Action) {
902
+ memoryState = reducer(memoryState, action);
903
+ listeners.forEach((listener) => {
904
+ listener(memoryState);
905
+ });
906
+ }
907
+
908
+ function reducer(state: State, action: Action): State {
909
+ switch (action.type) {
910
+ case "ADD_TOAST":
911
+ return {
912
+ ...state,
913
+ toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
914
+ };
915
+
916
+ case "UPDATE_TOAST":
917
+ return {
918
+ ...state,
919
+ toasts: state.toasts.map((t) =>
920
+ t.id === action.toast.id ? { ...t, ...action.toast } : t
921
+ ),
922
+ };
923
+
924
+ case "DISMISS_TOAST": {
925
+ const { toastId } = action;
926
+ if (toastId) {
927
+ addToRemoveQueue(toastId);
928
+ } else {
929
+ state.toasts.forEach((toast) => {
930
+ addToRemoveQueue(toast.id);
931
+ });
932
+ }
933
+ return state;
934
+ }
935
+ case "REMOVE_TOAST":
936
+ if (action.toastId === undefined) {
937
+ return { ...state, toasts: [] };
938
+ }
939
+ return {
940
+ ...state,
941
+ toasts: state.toasts.filter((t) => t.id !== action.toastId),
942
+ };
943
+ }
944
+ }
945
+
946
+ function toast({ ...props }: Omit<ToasterToast, "id">) {
947
+ const id = genId();
948
+ dispatch({
949
+ type: "ADD_TOAST",
950
+ toast: { ...props, id },
951
+ });
952
+ return {
953
+ id,
954
+ dismiss: () => dispatch({ type: "DISMISS_TOAST", toastId: id }),
955
+ update: (props: Partial<ToasterToast>) =>
956
+ dispatch({ type: "UPDATE_TOAST", toast: { ...props, id } }),
957
+ };
958
+ }
959
+
960
+ function useToast() {
961
+ const [state, setState] = React.useState<State>(memoryState);
962
+
963
+ React.useEffect(() => {
964
+ listeners.push(setState);
965
+ return () => {
966
+ const index = listeners.indexOf(setState);
967
+ if (index > -1) {
968
+ listeners.splice(index, 1);
969
+ }
970
+ };
971
+ }, [state]);
972
+
973
+ return {
974
+ ...state,
975
+ toast,
976
+ dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
977
+ };
978
+ }
979
+
980
+ export { useToast, toast };
981
+ `;
982
+ await fs.writeFile(path.join(projectPath, "src/hooks/use-toast.ts"), useToastContent);
983
+ }
984
+ async function generateComponentFile(projectPath, componentName) {
985
+ const content = getComponentTemplate(componentName);
986
+ if (content) {
987
+ const fileName = `${componentName}.tsx`;
988
+ await fs.writeFile(path.join(projectPath, "src/components/ui", fileName), content);
989
+ }
990
+ }
991
+ async function generateComponentIndex(projectPath, components) {
992
+ const exports = components.map((c) => `export * from "./${c}";`).join("\n");
993
+ await fs.writeFile(path.join(projectPath, "src/components/ui/index.ts"), exports + "\n");
994
+ }
995
+ function getComponentTemplate(name) {
996
+ const templates = {
997
+ button: `import * as React from "react";
998
+ import { Slot } from "@radix-ui/react-slot";
999
+ import { cva, type VariantProps } from "class-variance-authority";
1000
+ import { cn } from "@/lib/utils";
1001
+
1002
+ const buttonVariants = cva(
1003
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 min-w-6 min-h-6",
1004
+ {
1005
+ variants: {
1006
+ variant: {
1007
+ default: "bg-blue-600 text-white hover:bg-blue-700",
1008
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
1009
+ "outline-destructive": "border border-input bg-background text-destructive-text hover:bg-destructive/10 hover:text-destructive-text",
1010
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
1011
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
1012
+ tertiary: "hover:bg-accent hover:text-accent-foreground",
1013
+ ghost: "hover:bg-accent hover:text-accent-foreground",
1014
+ success: "bg-green-600 text-white hover:bg-green-700",
1015
+ cautionary: "bg-orange-500 text-white hover:bg-orange-600",
1016
+ link: "text-primary underline-offset-4 hover:underline",
1017
+ },
1018
+ size: {
1019
+ default: "h-10 px-4 py-2",
1020
+ sm: "h-9 rounded-md px-3",
1021
+ lg: "h-11 rounded-md px-8",
1022
+ icon: "h-11 w-11",
1023
+ },
1024
+ },
1025
+ defaultVariants: {
1026
+ variant: "default",
1027
+ size: "default",
1028
+ },
1029
+ }
1030
+ );
1031
+
1032
+ export interface ButtonProps
1033
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
1034
+ VariantProps<typeof buttonVariants> {
1035
+ asChild?: boolean;
1036
+ }
1037
+
1038
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
1039
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
1040
+ const Comp = asChild ? Slot : "button";
1041
+ return (
1042
+ <Comp
1043
+ className={cn(buttonVariants({ variant, size, className }))}
1044
+ ref={ref}
1045
+ {...props}
1046
+ />
1047
+ );
1048
+ }
1049
+ );
1050
+ Button.displayName = "Button";
1051
+
1052
+ export { Button, buttonVariants };
1053
+ `,
1054
+ card: `import * as React from "react";
1055
+ import { cn } from "@/lib/utils";
1056
+
1057
+ const Card = React.forwardRef<
1058
+ HTMLDivElement,
1059
+ React.HTMLAttributes<HTMLDivElement>
1060
+ >(({ className, ...props }, ref) => (
1061
+ <div
1062
+ ref={ref}
1063
+ className={cn(
1064
+ "rounded-lg border bg-card text-card-foreground shadow-sm",
1065
+ className
1066
+ )}
1067
+ {...props}
1068
+ />
1069
+ ));
1070
+ Card.displayName = "Card";
1071
+
1072
+ const CardHeader = React.forwardRef<
1073
+ HTMLDivElement,
1074
+ React.HTMLAttributes<HTMLDivElement>
1075
+ >(({ className, ...props }, ref) => (
1076
+ <div
1077
+ ref={ref}
1078
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
1079
+ {...props}
1080
+ />
1081
+ ));
1082
+ CardHeader.displayName = "CardHeader";
1083
+
1084
+ const CardTitle = React.forwardRef<
1085
+ HTMLParagraphElement,
1086
+ React.HTMLAttributes<HTMLHeadingElement>
1087
+ >(({ className, ...props }, ref) => (
1088
+ <h3
1089
+ ref={ref}
1090
+ className={cn(
1091
+ "text-2xl font-semibold leading-none tracking-tight",
1092
+ className
1093
+ )}
1094
+ {...props}
1095
+ />
1096
+ ));
1097
+ CardTitle.displayName = "CardTitle";
1098
+
1099
+ const CardDescription = React.forwardRef<
1100
+ HTMLParagraphElement,
1101
+ React.HTMLAttributes<HTMLParagraphElement>
1102
+ >(({ className, ...props }, ref) => (
1103
+ <p
1104
+ ref={ref}
1105
+ className={cn("text-sm text-muted-foreground", className)}
1106
+ {...props}
1107
+ />
1108
+ ));
1109
+ CardDescription.displayName = "CardDescription";
1110
+
1111
+ const CardContent = React.forwardRef<
1112
+ HTMLDivElement,
1113
+ React.HTMLAttributes<HTMLDivElement>
1114
+ >(({ className, ...props }, ref) => (
1115
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
1116
+ ));
1117
+ CardContent.displayName = "CardContent";
1118
+
1119
+ const CardFooter = React.forwardRef<
1120
+ HTMLDivElement,
1121
+ React.HTMLAttributes<HTMLDivElement>
1122
+ >(({ className, ...props }, ref) => (
1123
+ <div
1124
+ ref={ref}
1125
+ className={cn("flex items-center p-6 pt-0", className)}
1126
+ {...props}
1127
+ />
1128
+ ));
1129
+ CardFooter.displayName = "CardFooter";
1130
+
1131
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
1132
+ `,
1133
+ input: `import * as React from "react";
1134
+ import { cn } from "@/lib/utils";
1135
+
1136
+ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
1137
+ ({ className, type, ...props }, ref) => {
1138
+ return (
1139
+ <input
1140
+ type={type}
1141
+ className={cn(
1142
+ "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
1143
+ className
1144
+ )}
1145
+ ref={ref}
1146
+ {...props}
1147
+ />
1148
+ );
1149
+ }
1150
+ );
1151
+ Input.displayName = "Input";
1152
+
1153
+ export { Input };
1154
+ `,
1155
+ label: `import * as React from "react";
1156
+ import * as LabelPrimitive from "@radix-ui/react-label";
1157
+ import { cva, type VariantProps } from "class-variance-authority";
1158
+ import { cn } from "@/lib/utils";
1159
+
1160
+ const labelVariants = cva(
1161
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
1162
+ );
1163
+
1164
+ const Label = React.forwardRef<
1165
+ React.ElementRef<typeof LabelPrimitive.Root>,
1166
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
1167
+ VariantProps<typeof labelVariants>
1168
+ >(({ className, ...props }, ref) => (
1169
+ <LabelPrimitive.Root
1170
+ ref={ref}
1171
+ className={cn(labelVariants(), className)}
1172
+ {...props}
1173
+ />
1174
+ ));
1175
+ Label.displayName = LabelPrimitive.Root.displayName;
1176
+
1177
+ export { Label };
1178
+ `,
1179
+ badge: `import * as React from "react";
1180
+ import { cva, type VariantProps } from "class-variance-authority";
1181
+ import { cn } from "@/lib/utils";
1182
+
1183
+ const badgeVariants = cva(
1184
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
1185
+ {
1186
+ variants: {
1187
+ variant: {
1188
+ default:
1189
+ "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
1190
+ secondary:
1191
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
1192
+ destructive:
1193
+ "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
1194
+ outline: "text-foreground",
1195
+ },
1196
+ },
1197
+ defaultVariants: {
1198
+ variant: "default",
1199
+ },
1200
+ }
1201
+ );
1202
+
1203
+ export interface BadgeProps
1204
+ extends React.HTMLAttributes<HTMLDivElement>,
1205
+ VariantProps<typeof badgeVariants> {}
1206
+
1207
+ function Badge({ className, variant, ...props }: BadgeProps) {
1208
+ return (
1209
+ <div className={cn(badgeVariants({ variant }), className)} {...props} />
1210
+ );
1211
+ }
1212
+
1213
+ export { Badge, badgeVariants };
1214
+ `,
1215
+ separator: `import * as React from "react";
1216
+ import * as SeparatorPrimitive from "@radix-ui/react-separator";
1217
+ import { cn } from "@/lib/utils";
1218
+
1219
+ const Separator = React.forwardRef<
1220
+ React.ElementRef<typeof SeparatorPrimitive.Root>,
1221
+ React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
1222
+ >(
1223
+ (
1224
+ { className, orientation = "horizontal", decorative = true, ...props },
1225
+ ref
1226
+ ) => (
1227
+ <SeparatorPrimitive.Root
1228
+ ref={ref}
1229
+ decorative={decorative}
1230
+ orientation={orientation}
1231
+ className={cn(
1232
+ "shrink-0 bg-border",
1233
+ orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
1234
+ className
1235
+ )}
1236
+ {...props}
1237
+ />
1238
+ )
1239
+ );
1240
+ Separator.displayName = SeparatorPrimitive.Root.displayName;
1241
+
1242
+ export { Separator };
1243
+ `,
1244
+ skeleton: `import { cn } from "@/lib/utils";
1245
+
1246
+ function Skeleton({
1247
+ className,
1248
+ ...props
1249
+ }: React.HTMLAttributes<HTMLDivElement>) {
1250
+ return (
1251
+ <div
1252
+ className={cn("animate-pulse rounded-md bg-muted", className)}
1253
+ {...props}
1254
+ />
1255
+ );
1256
+ }
1257
+
1258
+ export { Skeleton };
1259
+ `
1260
+ };
1261
+ return templates[name] || null;
1262
+ }
1263
+
1264
+ // src/generators/app.ts
1265
+ import path2 from "path";
1266
+ import fs2 from "fs-extra";
1267
+ async function generateAppFiles(projectPath, config) {
1268
+ const mainContent = `import { StrictMode } from "react";
1269
+ import { createRoot } from "react-dom/client";
1270
+ import { BrowserRouter } from "react-router-dom";
1271
+ import App from "./App.tsx";
1272
+ import "./index.css";
1273
+
1274
+ createRoot(document.getElementById("root")!).render(
1275
+ <StrictMode>
1276
+ <BrowserRouter>
1277
+ <App />
1278
+ </BrowserRouter>
1279
+ </StrictMode>
1280
+ );
1281
+ `;
1282
+ await fs2.writeFile(path2.join(projectPath, "src/main.tsx"), mainContent);
1283
+ const appContent = generateAppComponent(config);
1284
+ await fs2.writeFile(path2.join(projectPath, "src/App.tsx"), appContent);
1285
+ const homeContent = generateHomePage(config);
1286
+ await fs2.writeFile(path2.join(projectPath, "src/pages/Home.tsx"), homeContent);
1287
+ if (config.themeModes.includes("dark")) {
1288
+ await generateThemeProvider(projectPath);
1289
+ }
1290
+ const tsconfigNode = {
1291
+ compilerOptions: {
1292
+ composite: true,
1293
+ skipLibCheck: true,
1294
+ module: "ESNext",
1295
+ moduleResolution: "bundler",
1296
+ allowSyntheticDefaultImports: true,
1297
+ strict: true
1298
+ },
1299
+ include: ["vite.config.ts"]
1300
+ };
1301
+ await fs2.writeJson(path2.join(projectPath, "tsconfig.node.json"), tsconfigNode, { spaces: 2 });
1302
+ const viteSvg = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFBD4F"></stop><stop offset="100%" stop-color="#FF980E"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>`;
1303
+ await fs2.writeFile(path2.join(projectPath, "public/vite.svg"), viteSvg);
1304
+ }
1305
+ function generateAppComponent(config) {
1306
+ const hasDarkMode = config.themeModes.includes("dark");
1307
+ if (hasDarkMode) {
1308
+ return `import { Routes, Route } from "react-router-dom";
1309
+ import { ThemeProvider } from "./contexts/ThemeProvider";
1310
+ import { Toaster } from "sonner";
1311
+ import Home from "./pages/Home";
1312
+
1313
+ function App() {
1314
+ return (
1315
+ <ThemeProvider defaultTheme="system" storageKey="waypoint-theme">
1316
+ <Routes>
1317
+ <Route path="/" element={<Home />} />
1318
+ </Routes>
1319
+ <Toaster />
1320
+ </ThemeProvider>
1321
+ );
1322
+ }
1323
+
1324
+ export default App;
1325
+ `;
1326
+ }
1327
+ return `import { Routes, Route } from "react-router-dom";
1328
+ import { Toaster } from "sonner";
1329
+ import Home from "./pages/Home";
1330
+
1331
+ function App() {
1332
+ return (
1333
+ <>
1334
+ <Routes>
1335
+ <Route path="/" element={<Home />} />
1336
+ </Routes>
1337
+ <Toaster />
1338
+ </>
1339
+ );
1340
+ }
1341
+
1342
+ export default App;
1343
+ `;
1344
+ }
1345
+ function generateHomePage(config) {
1346
+ return `import { Button } from "@/components/ui/button";
1347
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
1348
+
1349
+ export default function Home() {
1350
+ return (
1351
+ <div className="min-h-screen bg-background flex items-center justify-center p-4">
1352
+ <Card className="w-full max-w-md">
1353
+ <CardHeader className="text-center">
1354
+ <CardTitle className="text-3xl font-bold">
1355
+ Welcome to ${config.projectName}
1356
+ </CardTitle>
1357
+ <CardDescription>
1358
+ Built with Waypoint 2.0 Design System
1359
+ </CardDescription>
1360
+ </CardHeader>
1361
+ <CardContent className="space-y-4">
1362
+ <p className="text-muted-foreground text-center">
1363
+ Your project is ready! Start building something amazing.
1364
+ </p>
1365
+ <div className="flex gap-2 justify-center">
1366
+ <Button>Get Started</Button>
1367
+ <Button variant="outline">Documentation</Button>
1368
+ </div>
1369
+ </CardContent>
1370
+ </Card>
1371
+ </div>
1372
+ );
1373
+ }
1374
+ `;
1375
+ }
1376
+ async function generateThemeProvider(projectPath) {
1377
+ const themeProviderContent = `import { createContext, useContext, useEffect, useState } from "react";
1378
+
1379
+ type Theme = "dark" | "light" | "system";
1380
+
1381
+ type ThemeProviderProps = {
1382
+ children: React.ReactNode;
1383
+ defaultTheme?: Theme;
1384
+ storageKey?: string;
1385
+ };
1386
+
1387
+ type ThemeProviderState = {
1388
+ theme: Theme;
1389
+ setTheme: (theme: Theme) => void;
1390
+ };
1391
+
1392
+ const initialState: ThemeProviderState = {
1393
+ theme: "system",
1394
+ setTheme: () => null,
1395
+ };
1396
+
1397
+ const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
1398
+
1399
+ export function ThemeProvider({
1400
+ children,
1401
+ defaultTheme = "system",
1402
+ storageKey = "vite-ui-theme",
1403
+ ...props
1404
+ }: ThemeProviderProps) {
1405
+ const [theme, setTheme] = useState<Theme>(
1406
+ () => (localStorage.getItem(storageKey) as Theme) || defaultTheme
1407
+ );
1408
+
1409
+ useEffect(() => {
1410
+ const root = window.document.documentElement;
1411
+
1412
+ root.classList.remove("light", "dark");
1413
+
1414
+ if (theme === "system") {
1415
+ const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
1416
+ .matches
1417
+ ? "dark"
1418
+ : "light";
1419
+
1420
+ root.classList.add(systemTheme);
1421
+ return;
1422
+ }
1423
+
1424
+ root.classList.add(theme);
1425
+ }, [theme]);
1426
+
1427
+ const value = {
1428
+ theme,
1429
+ setTheme: (theme: Theme) => {
1430
+ localStorage.setItem(storageKey, theme);
1431
+ setTheme(theme);
1432
+ },
1433
+ };
1434
+
1435
+ return (
1436
+ <ThemeProviderContext.Provider {...props} value={value}>
1437
+ {children}
1438
+ </ThemeProviderContext.Provider>
1439
+ );
1440
+ }
1441
+
1442
+ export const useTheme = () => {
1443
+ const context = useContext(ThemeProviderContext);
1444
+
1445
+ if (context === undefined)
1446
+ throw new Error("useTheme must be used within a ThemeProvider");
1447
+
1448
+ return context;
1449
+ };
1450
+ `;
1451
+ await fs2.writeFile(path2.join(projectPath, "src/contexts/ThemeProvider.tsx"), themeProviderContent);
1452
+ }
1453
+
1454
+ // src/generators/readme.ts
1455
+ function generateReadme(config) {
1456
+ const themeModesList = config.themeModes.map((m) => {
1457
+ switch (m) {
1458
+ case "light":
1459
+ return "Light Mode";
1460
+ case "dark":
1461
+ return "Dark Mode";
1462
+ case "high-contrast-light":
1463
+ return "High Contrast Light";
1464
+ case "high-contrast-dark":
1465
+ return "High Contrast Dark";
1466
+ default:
1467
+ return m;
1468
+ }
1469
+ }).join(", ");
1470
+ const featuresList = config.features.map((f) => {
1471
+ switch (f) {
1472
+ case "core":
1473
+ return "Core UI Components";
1474
+ case "forms":
1475
+ return "Form Components";
1476
+ case "feedback":
1477
+ return "Feedback Components";
1478
+ case "navigation":
1479
+ return "Navigation Components";
1480
+ case "data":
1481
+ return "Data Display Components";
1482
+ case "advanced":
1483
+ return "Advanced Components";
1484
+ case "charts":
1485
+ return "Chart Components";
1486
+ default:
1487
+ return f;
1488
+ }
1489
+ }).join(", ");
1490
+ return `# ${config.projectName}
1491
+
1492
+ ${config.description}
1493
+
1494
+ Built with [Waypoint 2.0 Design System](https://waypoint-2-dot-0.lovable.app).
1495
+
1496
+ ## Features
1497
+
1498
+ - **Theme Modes:** ${themeModesList}
1499
+ - **Theme Preset:** ${config.themePreset.charAt(0).toUpperCase() + config.themePreset.slice(1)}
1500
+ - **Components:** ${featuresList}
1501
+ ${config.enableRTL ? "- **RTL Support:** Enabled" : ""}
1502
+ ${config.enableFontScaling ? "- **Font Scaling:** Enabled for accessibility" : ""}
1503
+ ${config.includeAuth ? "- **Authentication:** Scaffolding included" : ""}
1504
+ ${config.includePageTemplates ? "- **Page Templates:** 27+ responsive layouts" : ""}
1505
+ ${config.includeComposer ? "- **Composer:** Visual page builder tool" : ""}
1506
+
1507
+ ## Getting Started
1508
+
1509
+ \`\`\`bash
1510
+ # Install dependencies
1511
+ npm install
1512
+
1513
+ # Start development server
1514
+ npm run dev
1515
+
1516
+ # Build for production
1517
+ npm run build
1518
+ \`\`\`
1519
+
1520
+ ## Project Structure
1521
+
1522
+ \`\`\`
1523
+ ${config.projectName}/
1524
+ \u251C\u2500\u2500 public/
1525
+ \u2502 \u2514\u2500\u2500 vite.svg
1526
+ \u251C\u2500\u2500 src/
1527
+ \u2502 \u251C\u2500\u2500 components/
1528
+ \u2502 \u2502 \u2514\u2500\u2500 ui/ # Waypoint UI components
1529
+ \u2502 \u251C\u2500\u2500 contexts/ # React contexts (theme, etc.)
1530
+ \u2502 \u251C\u2500\u2500 hooks/ # Custom React hooks
1531
+ \u2502 \u251C\u2500\u2500 lib/ # Utilities
1532
+ \u2502 \u251C\u2500\u2500 pages/ # Page components
1533
+ \u2502 \u251C\u2500\u2500 App.tsx
1534
+ \u2502 \u251C\u2500\u2500 main.tsx
1535
+ \u2502 \u2514\u2500\u2500 index.css # Tailwind + design tokens
1536
+ \u251C\u2500\u2500 index.html
1537
+ \u251C\u2500\u2500 package.json
1538
+ \u251C\u2500\u2500 tailwind.config.ts
1539
+ \u251C\u2500\u2500 vite.config.ts
1540
+ \u2514\u2500\u2500 tsconfig.json
1541
+ \`\`\`
1542
+
1543
+ ## Customization
1544
+
1545
+ ### Theme Colors
1546
+
1547
+ Edit the CSS variables in \`src/index.css\` to customize your color palette:
1548
+
1549
+ \`\`\`css
1550
+ :root {
1551
+ --primary: 222.2 47.4% 11.2%;
1552
+ --secondary: 210 40% 96.1%;
1553
+ /* ... other tokens */
1554
+ }
1555
+ \`\`\`
1556
+
1557
+ ### Adding Components
1558
+
1559
+ Use the shadcn/ui CLI to add more components:
1560
+
1561
+ \`\`\`bash
1562
+ npx shadcn-ui@latest add [component-name]
1563
+ \`\`\`
1564
+
1565
+ ## Documentation
1566
+
1567
+ - [Waypoint 2.0 Design System](https://waypoint-2-dot-0.lovable.app)
1568
+ - [Component Library](https://waypoint-2-dot-0.lovable.app/components)
1569
+ - [Tailwind CSS](https://tailwindcss.com/docs)
1570
+ - [Radix UI](https://www.radix-ui.com/docs)
1571
+
1572
+ ## License
1573
+
1574
+ MIT
1575
+ `;
1576
+ }
1577
+
1578
+ // src/scaffolder.ts
1579
+ async function scaffoldProject(config) {
1580
+ const projectPath = path3.resolve(process.cwd(), config.projectName);
1581
+ if (await fs3.pathExists(projectPath)) {
1582
+ throw new Error(`Directory "${config.projectName}" already exists`);
1583
+ }
1584
+ const spinner = ora("Creating project structure...").start();
1585
+ try {
1586
+ await fs3.ensureDir(projectPath);
1587
+ await createDirectoryStructure(projectPath);
1588
+ spinner.text = "Generating configuration files...";
1589
+ await generateConfigFiles(projectPath, config);
1590
+ spinner.text = "Generating styles...";
1591
+ await generateStyleFiles(projectPath, config);
1592
+ spinner.text = "Generating components...";
1593
+ await generateComponents(projectPath, config);
1594
+ spinner.text = "Generating application files...";
1595
+ await generateAppFiles(projectPath, config);
1596
+ spinner.text = "Generating documentation...";
1597
+ await generateDocs(projectPath, config);
1598
+ spinner.succeed("Project created successfully!");
1599
+ } catch (error) {
1600
+ spinner.fail("Failed to create project");
1601
+ await fs3.remove(projectPath);
1602
+ throw error;
1603
+ }
1604
+ }
1605
+ async function createDirectoryStructure(projectPath) {
1606
+ const directories = [
1607
+ "src",
1608
+ "src/components",
1609
+ "src/components/ui",
1610
+ "src/lib",
1611
+ "src/hooks",
1612
+ "src/contexts",
1613
+ "src/pages",
1614
+ "src/assets",
1615
+ "public"
1616
+ ];
1617
+ for (const dir of directories) {
1618
+ await fs3.ensureDir(path3.join(projectPath, dir));
1619
+ }
1620
+ }
1621
+ async function generateConfigFiles(projectPath, config) {
1622
+ const packageJson = generatePackageJson(config);
1623
+ await fs3.writeJson(path3.join(projectPath, "package.json"), packageJson, { spaces: 2 });
1624
+ const tailwindConfig = generateTailwindConfig(config);
1625
+ await fs3.writeFile(path3.join(projectPath, "tailwind.config.ts"), tailwindConfig);
1626
+ const viteConfig = generateViteConfig();
1627
+ await fs3.writeFile(path3.join(projectPath, "vite.config.ts"), viteConfig);
1628
+ const tsConfig = generateTsConfig();
1629
+ await fs3.writeJson(path3.join(projectPath, "tsconfig.json"), tsConfig, { spaces: 2 });
1630
+ const componentsJson = generateComponentsJson();
1631
+ await fs3.writeJson(path3.join(projectPath, "components.json"), componentsJson, { spaces: 2 });
1632
+ const postcssConfig = `export default {
1633
+ plugins: {
1634
+ tailwindcss: {},
1635
+ autoprefixer: {},
1636
+ },
1637
+ };
1638
+ `;
1639
+ await fs3.writeFile(path3.join(projectPath, "postcss.config.js"), postcssConfig);
1640
+ const gitignore = `# Dependencies
1641
+ node_modules
1642
+ .pnp
1643
+ .pnp.js
1644
+
1645
+ # Build
1646
+ dist
1647
+ dist-ssr
1648
+ *.local
1649
+
1650
+ # IDE
1651
+ .vscode/*
1652
+ !.vscode/extensions.json
1653
+ .idea
1654
+ *.suo
1655
+ *.ntvs*
1656
+ *.njsproj
1657
+ *.sln
1658
+ *.sw?
1659
+
1660
+ # Environment
1661
+ .env
1662
+ .env.local
1663
+ .env.*.local
1664
+
1665
+ # Logs
1666
+ npm-debug.log*
1667
+ yarn-debug.log*
1668
+ yarn-error.log*
1669
+ pnpm-debug.log*
1670
+
1671
+ # OS
1672
+ .DS_Store
1673
+ Thumbs.db
1674
+ `;
1675
+ await fs3.writeFile(path3.join(projectPath, ".gitignore"), gitignore);
1676
+ const indexHtml = `<!DOCTYPE html>
1677
+ <html lang="en">
1678
+ <head>
1679
+ <meta charset="UTF-8" />
1680
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
1681
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1682
+ <title>${config.projectName}</title>
1683
+ <meta name="description" content="${config.description}" />
1684
+ </head>
1685
+ <body>
1686
+ <div id="root"></div>
1687
+ <script type="module" src="/src/main.tsx"></script>
1688
+ </body>
1689
+ </html>
1690
+ `;
1691
+ await fs3.writeFile(path3.join(projectPath, "index.html"), indexHtml);
1692
+ }
1693
+ async function generateStyleFiles(projectPath, config) {
1694
+ const indexCSS = generateIndexCSS(config);
1695
+ await fs3.writeFile(path3.join(projectPath, "src/index.css"), indexCSS);
1696
+ }
1697
+ async function generateDocs(projectPath, config) {
1698
+ const readme = generateReadme(config);
1699
+ await fs3.writeFile(path3.join(projectPath, "README.md"), readme);
1700
+ }
1701
+
1702
+ // src/utils/post-setup.ts
1703
+ import { exec } from "child_process";
1704
+ import { promisify } from "util";
1705
+ import ora2 from "ora";
1706
+ import chalk2 from "chalk";
1707
+ var execAsync = promisify(exec);
1708
+ async function installDependencies(projectName) {
1709
+ const spinner = ora2("Installing dependencies...").start();
1710
+ try {
1711
+ await execAsync("npm install", { cwd: projectName });
1712
+ spinner.succeed("Dependencies installed");
1713
+ } catch (error) {
1714
+ spinner.fail("Failed to install dependencies");
1715
+ console.log(chalk2.yellow(" Run `npm install` manually to install dependencies"));
1716
+ }
1717
+ }
1718
+ async function initializeGit(projectName) {
1719
+ const spinner = ora2("Initializing git repository...").start();
1720
+ try {
1721
+ await execAsync("git init", { cwd: projectName });
1722
+ await execAsync("git add .", { cwd: projectName });
1723
+ await execAsync('git commit -m "Initial commit from create-waypoint-app"', { cwd: projectName });
1724
+ spinner.succeed("Git repository initialized");
1725
+ } catch (error) {
1726
+ spinner.fail("Failed to initialize git");
1727
+ console.log(chalk2.yellow(" Run `git init` manually to initialize the repository"));
1728
+ }
1729
+ }
1730
+
1731
+ // src/utils/messages.ts
1732
+ import chalk3 from "chalk";
1733
+ function printWelcome() {
1734
+ console.log();
1735
+ console.log(chalk3.cyan.bold(" \u2566 \u2566\u250C\u2500\u2510\u252C \u252C\u250C\u2500\u2510\u250C\u2500\u2510\u252C\u250C\u2510\u250C\u250C\u252C\u2510 \u250C\u2500\u2510 \u250C\u2500\u2510"));
1736
+ console.log(chalk3.cyan.bold(" \u2551\u2551\u2551\u251C\u2500\u2524\u2514\u252C\u2518\u251C\u2500\u2518\u2502 \u2502\u2502\u2502\u2502\u2502 \u2502 \u250C\u2500\u2518 \u2502 \u2502"));
1737
+ console.log(chalk3.cyan.bold(" \u255A\u2569\u255D\u2534 \u2534 \u2534 \u2534 \u2514\u2500\u2518\u2534\u2518\u2514\u2518 \u2534 \u2514\u2500\u2518o\u2514\u2500\u2518"));
1738
+ console.log();
1739
+ console.log(chalk3.white(" Create beautiful, accessible React applications"));
1740
+ console.log(chalk3.dim(" with the Waypoint 2.0 Design System"));
1741
+ console.log();
1742
+ }
1743
+ function printSuccess(config) {
1744
+ console.log();
1745
+ console.log(chalk3.green.bold(" \u2713 Success!") + ` Created ${chalk3.cyan(config.projectName)}`);
1746
+ console.log();
1747
+ console.log(" Next steps:");
1748
+ console.log();
1749
+ console.log(chalk3.cyan(` cd ${config.projectName}`));
1750
+ console.log(chalk3.cyan(" npm run dev"));
1751
+ console.log();
1752
+ console.log(" Documentation:");
1753
+ console.log(chalk3.dim(" https://waypoint-2-dot-0.lovable.app"));
1754
+ console.log();
1755
+ console.log(chalk3.dim(` Theme modes: ${config.themeModes.join(", ")}`));
1756
+ console.log(chalk3.dim(` Components: ${config.features.length} groups included`));
1757
+ if (config.enableRTL) console.log(chalk3.dim(" RTL support: enabled"));
1758
+ console.log();
1759
+ }
1760
+
1761
+ // src/index.ts
1762
+ async function createProject() {
1763
+ program.name("create-waypoint-app").description("Scaffold a new Waypoint 2.0 design system project").argument("[project-name]", "Name of the project").option("-t, --template <template>", "Use a template: minimal, full, dashboard, marketing, ecommerce").option("--no-install", "Skip dependency installation").option("--no-git", "Skip git initialization").option("-y, --yes", "Use default options (skip prompts)").parse();
1764
+ const options = program.opts();
1765
+ const args = program.args;
1766
+ printWelcome();
1767
+ let config;
1768
+ if (options.yes) {
1769
+ config = getDefaultConfig(args[0] || "waypoint-app");
1770
+ } else {
1771
+ config = await gatherProjectConfig(args[0]);
1772
+ }
1773
+ console.log();
1774
+ console.log(chalk4.cyan(" Creating your Waypoint 2.0 project..."));
1775
+ console.log();
1776
+ try {
1777
+ await scaffoldProject(config);
1778
+ if (options.install !== false) {
1779
+ await installDependencies(config.projectName);
1780
+ }
1781
+ if (options.git !== false) {
1782
+ await initializeGit(config.projectName);
1783
+ }
1784
+ printSuccess(config);
1785
+ } catch (error) {
1786
+ console.error(chalk4.red(" Error creating project:"), error);
1787
+ process.exit(1);
1788
+ }
1789
+ }
1790
+ function getDefaultConfig(projectName) {
1791
+ return {
1792
+ projectName,
1793
+ description: "A Waypoint 2.0 design system project",
1794
+ themeModes: ["light", "dark"],
1795
+ themePreset: "default",
1796
+ enableRTL: false,
1797
+ enableFontScaling: true,
1798
+ features: ["core", "forms", "feedback", "navigation"],
1799
+ includeAuth: false,
1800
+ includePageTemplates: false,
1801
+ includeComposer: false
1802
+ };
1803
+ }
1804
+ export {
1805
+ createProject
1806
+ };