create-flexireact 1.0.1 → 1.1.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.
Files changed (2) hide show
  1. package/index.js +561 -238
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -74,11 +74,7 @@ ${c.green} ╰─────────────────────
74
74
  const TEMPLATES = {
75
75
  default: {
76
76
  name: 'Default',
77
- description: 'Basic FlexiReact app with TypeScript and Tailwind',
78
- },
79
- 'flexi-ui': {
80
- name: 'FlexiUI',
81
- description: 'FlexiReact with FlexiUI component library',
77
+ description: 'Premium template with modern UI, animations & dark mode',
82
78
  },
83
79
  minimal: {
84
80
  name: 'Minimal',
@@ -106,10 +102,6 @@ function info(msg) {
106
102
  console.log(` ${c.cyan}ℹ${c.reset} ${msg}`);
107
103
  }
108
104
 
109
- function step(num, total, msg) {
110
- console.log(` ${c.dim}[${num}/${total}]${c.reset} ${msg}`);
111
- }
112
-
113
105
  // Spinner
114
106
  class Spinner {
115
107
  constructor(message) {
@@ -184,26 +176,6 @@ async function select(question, options) {
184
176
  });
185
177
  }
186
178
 
187
- // Copy directory recursively
188
- function copyDir(src, dest) {
189
- if (!fs.existsSync(dest)) {
190
- fs.mkdirSync(dest, { recursive: true });
191
- }
192
-
193
- const entries = fs.readdirSync(src, { withFileTypes: true });
194
-
195
- for (const entry of entries) {
196
- const srcPath = path.join(src, entry.name);
197
- const destPath = path.join(dest, entry.name);
198
-
199
- if (entry.isDirectory()) {
200
- copyDir(srcPath, destPath);
201
- } else {
202
- fs.copyFileSync(srcPath, destPath);
203
- }
204
- }
205
- }
206
-
207
179
  // Check if directory is empty
208
180
  function isDirEmpty(dir) {
209
181
  if (!fs.existsSync(dir)) return true;
@@ -211,7 +183,7 @@ function isDirEmpty(dir) {
211
183
  }
212
184
 
213
185
  // ============================================================================
214
- // Template Files (Inline for npm package)
186
+ // Template Files
215
187
  // ============================================================================
216
188
 
217
189
  const TEMPLATE_FILES = {
@@ -224,21 +196,24 @@ const TEMPLATE_FILES = {
224
196
  dev: "npm run css && flexireact dev",
225
197
  build: "npm run css && flexireact build",
226
198
  start: "flexireact start",
227
- css: "npx tailwindcss -i ./app/styles/input.css -o ./public/styles.css --minify"
199
+ css: "npx tailwindcss -i ./app/styles/globals.css -o ./public/styles.css --minify"
228
200
  },
229
201
  dependencies: {
230
- react: "^18.2.0",
202
+ "react": "^18.2.0",
231
203
  "react-dom": "^18.2.0",
232
204
  "@flexireact/core": "^1.0.0",
233
- ...(template === 'flexi-ui' && { "@flexireact/flexi-ui": "^1.0.0" })
205
+ "framer-motion": "^11.0.0",
206
+ "lucide-react": "^0.400.0",
207
+ "clsx": "^2.1.0",
208
+ "tailwind-merge": "^2.2.0"
234
209
  },
235
210
  devDependencies: {
236
211
  "@types/react": "^18.2.0",
237
212
  "@types/react-dom": "^18.2.0",
238
- typescript: "^5.3.0",
239
- tailwindcss: "^3.4.0",
240
- postcss: "^8.4.32",
241
- autoprefixer: "^10.4.16"
213
+ "typescript": "^5.3.0",
214
+ "tailwindcss": "^3.4.0",
215
+ "postcss": "^8.4.32",
216
+ "autoprefixer": "^10.4.16"
242
217
  }
243
218
  }, null, 2),
244
219
 
@@ -265,8 +240,7 @@ const TEMPLATE_FILES = {
265
240
  exclude: ["node_modules", ".flexi"]
266
241
  }, null, 2),
267
242
 
268
- 'tailwind.config.js': (name, template) => `/** @type {import('tailwindcss').Config} */
269
- ${template === 'flexi-ui' ? "const { flexiUIPlugin } = require('@flexireact/flexi-ui/tailwind');\n" : ''}
243
+ 'tailwind.config.js': () => `/** @type {import('tailwindcss').Config} */
270
244
  module.exports = {
271
245
  darkMode: 'class',
272
246
  content: [
@@ -274,16 +248,71 @@ module.exports = {
274
248
  './pages/**/*.{js,ts,jsx,tsx}',
275
249
  './components/**/*.{js,ts,jsx,tsx}',
276
250
  './layouts/**/*.{js,ts,jsx,tsx}',
277
- ${template === 'flexi-ui' ? "'./node_modules/@flexireact/flexi-ui/dist/**/*.js'," : ''}
278
251
  ],
279
252
  theme: {
280
253
  extend: {
281
254
  fontFamily: {
282
255
  sans: ['Inter', 'system-ui', 'sans-serif'],
283
256
  },
257
+ colors: {
258
+ border: 'hsl(240 3.7% 15.9%)',
259
+ input: 'hsl(240 3.7% 15.9%)',
260
+ ring: 'hsl(142.1 76.2% 36.3%)',
261
+ background: 'hsl(240 10% 3.9%)',
262
+ foreground: 'hsl(0 0% 98%)',
263
+ primary: {
264
+ DEFAULT: 'hsl(142.1 76.2% 36.3%)',
265
+ foreground: 'hsl(144.9 80.4% 10%)',
266
+ },
267
+ secondary: {
268
+ DEFAULT: 'hsl(240 3.7% 15.9%)',
269
+ foreground: 'hsl(0 0% 98%)',
270
+ },
271
+ muted: {
272
+ DEFAULT: 'hsl(240 3.7% 15.9%)',
273
+ foreground: 'hsl(240 5% 64.9%)',
274
+ },
275
+ accent: {
276
+ DEFAULT: 'hsl(240 3.7% 15.9%)',
277
+ foreground: 'hsl(0 0% 98%)',
278
+ },
279
+ card: {
280
+ DEFAULT: 'hsl(240 10% 3.9%)',
281
+ foreground: 'hsl(0 0% 98%)',
282
+ },
283
+ },
284
+ borderRadius: {
285
+ lg: '0.75rem',
286
+ md: '0.5rem',
287
+ sm: '0.25rem',
288
+ },
289
+ animation: {
290
+ 'fade-in': 'fadeIn 0.5s ease-out',
291
+ 'fade-up': 'fadeUp 0.5s ease-out',
292
+ 'scale-in': 'scaleIn 0.3s ease-out',
293
+ 'glow': 'glow 2s ease-in-out infinite alternate',
294
+ },
295
+ keyframes: {
296
+ fadeIn: {
297
+ '0%': { opacity: '0' },
298
+ '100%': { opacity: '1' },
299
+ },
300
+ fadeUp: {
301
+ '0%': { opacity: '0', transform: 'translateY(20px)' },
302
+ '100%': { opacity: '1', transform: 'translateY(0)' },
303
+ },
304
+ scaleIn: {
305
+ '0%': { opacity: '0', transform: 'scale(0.95)' },
306
+ '100%': { opacity: '1', transform: 'scale(1)' },
307
+ },
308
+ glow: {
309
+ '0%': { boxShadow: '0 0 20px rgba(16, 185, 129, 0.2)' },
310
+ '100%': { boxShadow: '0 0 40px rgba(16, 185, 129, 0.4)' },
311
+ },
312
+ },
284
313
  },
285
314
  },
286
- plugins: [${template === 'flexi-ui' ? 'flexiUIPlugin' : ''}],
315
+ plugins: [],
287
316
  };
288
317
  `,
289
318
 
@@ -295,158 +324,398 @@ module.exports = {
295
324
  };
296
325
  `,
297
326
 
298
- 'flexireact.config.js': (name, template) => `/** @type {import('@flexireact/core').Config} */
327
+ 'flexireact.config.js': () => `/** @type {import('@flexireact/core').Config} */
299
328
  export default {
300
- // Styles to include
301
329
  styles: [
302
330
  '/styles.css',
303
- 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap'
331
+ 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap'
304
332
  ],
305
-
306
- // Favicon
307
333
  favicon: '/favicon.svg',
308
-
309
- // Server options
310
334
  server: {
311
335
  port: 3000
312
336
  },
313
-
314
- // Islands (partial hydration)
315
337
  islands: {
316
338
  enabled: true
317
339
  }
318
340
  };
319
341
  `,
320
342
 
321
- 'pages/index.tsx': (name, template) => template === 'flexi-ui' ? `import React from 'react';
343
+ // ============================================================================
344
+ // Components
345
+ // ============================================================================
322
346
 
323
- export default function HomePage() {
347
+ 'components/ui/button.tsx': () => `import React from 'react';
348
+ import { cn } from '../../lib/utils';
349
+
350
+ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
351
+ variant?: 'default' | 'outline' | 'ghost';
352
+ size?: 'default' | 'sm' | 'lg';
353
+ children: React.ReactNode;
354
+ }
355
+
356
+ export function Button({
357
+ className,
358
+ variant = 'default',
359
+ size = 'default',
360
+ children,
361
+ ...props
362
+ }: ButtonProps) {
324
363
  return (
325
- <div className="py-20">
326
- <div className="container mx-auto px-4 text-center">
327
- <span className="inline-block px-4 py-1.5 text-sm font-medium rounded-full mb-6" style={{ backgroundColor: 'rgba(0,255,156,0.1)', color: '#00FF9C', border: '1px solid rgba(0,255,156,0.3)' }}>
328
- ✨ Welcome to FlexiReact
329
- </span>
330
-
331
- <h1 className="text-4xl md:text-6xl font-bold mb-6">
332
- Build amazing apps with{' '}
333
- <span style={{ color: '#00FF9C' }}>FlexiReact</span>
334
- </h1>
335
-
336
- <p className="text-lg opacity-70 mb-8 max-w-2xl mx-auto">
337
- The modern React framework with TypeScript, Tailwind CSS, SSR, and Islands architecture.
338
- </p>
364
+ <button
365
+ className={cn(
366
+ 'inline-flex items-center justify-center rounded-lg font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
367
+ {
368
+ 'bg-primary text-primary-foreground hover:bg-primary/90 shadow-lg shadow-primary/25': variant === 'default',
369
+ 'border border-border bg-transparent hover:bg-secondary hover:text-foreground': variant === 'outline',
370
+ 'hover:bg-secondary hover:text-foreground': variant === 'ghost',
371
+ },
372
+ {
373
+ 'h-10 px-4 py-2 text-sm': size === 'default',
374
+ 'h-9 px-3 text-sm': size === 'sm',
375
+ 'h-12 px-8 text-base': size === 'lg',
376
+ },
377
+ className
378
+ )}
379
+ {...props}
380
+ >
381
+ {children}
382
+ </button>
383
+ );
384
+ }
385
+ `,
339
386
 
340
- <div className="flex gap-4 justify-center">
341
- <a href="/docs" className="px-6 py-3 font-medium rounded-xl text-black" style={{ backgroundColor: '#00FF9C' }}>
342
- Get Started →
343
- </a>
344
- <a href="https://github.com/flexireact/flexireact" className="px-6 py-3 font-medium rounded-xl border border-[var(--flexi-border)] hover:bg-[var(--flexi-bg-muted)] transition-colors">
345
- GitHub
346
- </a>
347
- </div>
348
- </div>
387
+ 'components/ui/card.tsx': () => `import React from 'react';
388
+ import { cn } from '../../lib/utils';
389
+
390
+ interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
391
+ children: React.ReactNode;
392
+ }
393
+
394
+ export function Card({ className, children, ...props }: CardProps) {
395
+ return (
396
+ <div
397
+ className={cn(
398
+ 'rounded-xl border border-border bg-card text-card-foreground shadow-sm transition-all duration-300 hover:border-primary/50 hover:shadow-lg hover:shadow-primary/5',
399
+ className
400
+ )}
401
+ {...props}
402
+ >
403
+ {children}
349
404
  </div>
350
405
  );
351
406
  }
352
- ` : `import React from 'react';
353
407
 
354
- export default function HomePage() {
408
+ export function CardHeader({ className, children, ...props }: CardProps) {
355
409
  return (
356
- <div className="py-20 px-4">
357
- <div className="max-w-4xl mx-auto text-center">
358
- {/* Badge */}
359
- <span className="inline-block px-4 py-2 text-sm font-medium rounded-full mb-8 bg-emerald-500/10 text-emerald-400 border border-emerald-500/30">
360
- ⚡ The Modern React Framework
361
- </span>
362
-
363
- {/* Title */}
364
- <h1 className="text-5xl md:text-7xl font-extrabold mb-6 text-white">
365
- Build amazing apps with{' '}
366
- <span className="text-transparent bg-clip-text bg-gradient-to-r from-emerald-400 to-cyan-400">
367
- FlexiReact
368
- </span>
369
- </h1>
410
+ <div className={cn('flex flex-col space-y-1.5 p-6', className)} {...props}>
411
+ {children}
412
+ </div>
413
+ );
414
+ }
415
+
416
+ export function CardTitle({ className, children, ...props }: CardProps) {
417
+ return (
418
+ <h3 className={cn('text-lg font-semibold leading-none tracking-tight', className)} {...props}>
419
+ {children}
420
+ </h3>
421
+ );
422
+ }
423
+
424
+ export function CardDescription({ className, children, ...props }: CardProps) {
425
+ return (
426
+ <p className={cn('text-sm text-muted-foreground', className)} {...props}>
427
+ {children}
428
+ </p>
429
+ );
430
+ }
431
+
432
+ export function CardContent({ className, children, ...props }: CardProps) {
433
+ return (
434
+ <div className={cn('p-6 pt-0', className)} {...props}>
435
+ {children}
436
+ </div>
437
+ );
438
+ }
439
+ `,
440
+
441
+ 'components/ui/badge.tsx': () => `import React from 'react';
442
+ import { cn } from '../../lib/utils';
443
+
444
+ interface BadgeProps extends React.HTMLAttributes<HTMLDivElement> {
445
+ variant?: 'default' | 'secondary' | 'outline';
446
+ children: React.ReactNode;
447
+ }
448
+
449
+ export function Badge({ className, variant = 'default', children, ...props }: BadgeProps) {
450
+ return (
451
+ <div
452
+ className={cn(
453
+ 'inline-flex items-center rounded-full px-3 py-1 text-xs font-medium transition-colors',
454
+ {
455
+ 'bg-primary/10 text-primary border border-primary/20': variant === 'default',
456
+ 'bg-secondary text-secondary-foreground': variant === 'secondary',
457
+ 'border border-border text-foreground': variant === 'outline',
458
+ },
459
+ className
460
+ )}
461
+ {...props}
462
+ >
463
+ {children}
464
+ </div>
465
+ );
466
+ }
467
+ `,
468
+
469
+ 'components/Navbar.tsx': () => `import React from 'react';
470
+ import { Button } from './ui/button';
471
+
472
+ export function Navbar() {
473
+ return (
474
+ <header className="sticky top-0 z-50 w-full border-b border-border/40 bg-background/80 backdrop-blur-xl">
475
+ <nav className="container mx-auto flex h-16 max-w-6xl items-center justify-between px-4">
476
+ <a href="/" className="flex items-center gap-2 transition-opacity hover:opacity-80">
477
+ <div className="flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-primary to-emerald-400">
478
+ <svg className="h-4 w-4 text-primary-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
479
+ <path strokeLinecap="round" strokeLinejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
480
+ </svg>
481
+ </div>
482
+ <span className="text-lg font-bold">FlexiReact</span>
483
+ </a>
370
484
 
371
- {/* Subtitle */}
372
- <p className="text-xl text-slate-400 mb-10 max-w-2xl mx-auto leading-relaxed">
373
- A blazing-fast React framework with TypeScript, Tailwind CSS, SSR, SSG,
374
- Islands architecture, and file-based routing.
375
- </p>
485
+ <div className="flex items-center gap-2">
486
+ <Button variant="ghost" size="sm" asChild>
487
+ <a href="https://github.com/flexireact/flexireact">Docs</a>
488
+ </Button>
489
+ <Button variant="ghost" size="sm" asChild>
490
+ <a href="https://github.com/flexireact/flexireact" className="flex items-center gap-2">
491
+ <svg className="h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
492
+ <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
493
+ </svg>
494
+ GitHub
495
+ </a>
496
+ </Button>
497
+ </div>
498
+ </nav>
499
+ </header>
500
+ );
501
+ }
502
+ `,
503
+
504
+ 'components/Hero.tsx': () => `import React from 'react';
505
+ import { Button } from './ui/button';
506
+ import { Badge } from './ui/badge';
376
507
 
377
- {/* Buttons */}
378
- <div className="flex flex-wrap gap-4 justify-center mb-16">
379
- <a
380
- href="https://github.com/flexireact/flexireact"
381
- className="px-8 py-4 bg-emerald-500 text-black font-semibold rounded-xl hover:bg-emerald-400 transition-all shadow-lg shadow-emerald-500/25"
382
- >
383
- Get Started
384
- </a>
385
- <a
386
- href="https://github.com/flexireact/flexireact"
387
- className="px-8 py-4 bg-slate-800 text-white font-semibold rounded-xl border border-slate-700 hover:bg-slate-700 transition-all"
388
- >
389
- GitHub
390
- </a>
508
+ export function Hero() {
509
+ return (
510
+ <section className="relative overflow-hidden">
511
+ {/* Gradient Background */}
512
+ <div className="absolute inset-0 -z-10">
513
+ <div className="absolute left-1/2 top-0 -translate-x-1/2 -translate-y-1/2">
514
+ <div className="h-[600px] w-[600px] rounded-full bg-gradient-to-r from-primary/20 via-emerald-500/10 to-cyan-500/20 blur-3xl" />
391
515
  </div>
516
+ <div className="absolute right-0 top-1/2 -translate-y-1/2">
517
+ <div className="h-[400px] w-[400px] rounded-full bg-gradient-to-l from-primary/10 to-transparent blur-3xl" />
518
+ </div>
519
+ </div>
392
520
 
393
- {/* Features */}
394
- <div className="grid md:grid-cols-3 gap-6 text-left">
395
- <div className="p-6 rounded-2xl bg-slate-800/50 border border-slate-700">
396
- <div className="text-3xl mb-3">⚡</div>
397
- <h3 className="text-lg font-semibold text-white mb-2">Lightning Fast</h3>
398
- <p className="text-slate-400 text-sm">Powered by esbuild for instant builds and sub-second HMR.</p>
399
- </div>
400
- <div className="p-6 rounded-2xl bg-slate-800/50 border border-slate-700">
401
- <div className="text-3xl mb-3">🏝️</div>
402
- <h3 className="text-lg font-semibold text-white mb-2">Islands Architecture</h3>
403
- <p className="text-slate-400 text-sm">Partial hydration for minimal JavaScript and maximum performance.</p>
521
+ <div className="container mx-auto max-w-6xl px-4 py-24 sm:py-32 lg:py-40">
522
+ <div className="flex flex-col items-center text-center">
523
+ {/* Badge */}
524
+ <Badge className="mb-6 animate-fade-in">
525
+ <span className="mr-1">⚡</span> The Modern React Framework
526
+ </Badge>
527
+
528
+ {/* Title */}
529
+ <h1 className="mb-6 max-w-4xl animate-fade-up text-4xl font-extrabold tracking-tight sm:text-5xl md:text-6xl lg:text-7xl">
530
+ Build{' '}
531
+ <span className="bg-gradient-to-r from-primary via-emerald-400 to-cyan-400 bg-clip-text text-transparent">
532
+ blazing fast
533
+ </span>{' '}
534
+ web apps
535
+ </h1>
536
+
537
+ {/* Subtitle */}
538
+ <p className="mb-10 max-w-2xl animate-fade-up text-lg text-muted-foreground sm:text-xl" style={{ animationDelay: '0.1s' }}>
539
+ A modern React framework with TypeScript, Tailwind CSS, SSR, SSG,
540
+ Islands architecture, and file-based routing. Ship faster.
541
+ </p>
542
+
543
+ {/* CTA Buttons */}
544
+ <div className="flex flex-wrap items-center justify-center gap-4 animate-fade-up" style={{ animationDelay: '0.2s' }}>
545
+ <Button size="lg" className="gap-2">
546
+ Start Building
547
+ <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
548
+ <path strokeLinecap="round" strokeLinejoin="round" d="M13 7l5 5m0 0l-5 5m5-5H6" />
549
+ </svg>
550
+ </Button>
551
+ <Button variant="outline" size="lg" asChild>
552
+ <a href="https://github.com/flexireact/flexireact" className="gap-2">
553
+ <svg className="h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
554
+ <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
555
+ </svg>
556
+ GitHub
557
+ </a>
558
+ </Button>
404
559
  </div>
405
- <div className="p-6 rounded-2xl bg-slate-800/50 border border-slate-700">
406
- <div className="text-3xl mb-3">📁</div>
407
- <h3 className="text-lg font-semibold text-white mb-2">File-based Routing</h3>
408
- <p className="text-slate-400 text-sm">Create a file in pages/, get a route automatically.</p>
560
+
561
+ {/* Code Preview */}
562
+ <div className="mt-16 w-full max-w-2xl animate-fade-up rounded-xl border border-border bg-card/50 p-4 backdrop-blur-sm" style={{ animationDelay: '0.3s' }}>
563
+ <div className="flex items-center gap-2 border-b border-border pb-3">
564
+ <div className="h-3 w-3 rounded-full bg-red-500/80" />
565
+ <div className="h-3 w-3 rounded-full bg-yellow-500/80" />
566
+ <div className="h-3 w-3 rounded-full bg-green-500/80" />
567
+ <span className="ml-2 text-xs text-muted-foreground">terminal</span>
568
+ </div>
569
+ <pre className="mt-4 overflow-x-auto text-left text-sm">
570
+ <code className="text-muted-foreground">
571
+ <span className="text-muted-foreground/60">$</span>{' '}
572
+ <span className="text-primary">npx</span> create-flexireact@latest my-app{'\n'}
573
+ <span className="text-muted-foreground/60">$</span>{' '}
574
+ <span className="text-primary">cd</span> my-app{'\n'}
575
+ <span className="text-muted-foreground/60">$</span>{' '}
576
+ <span className="text-primary">npm</span> run dev{'\n'}
577
+ {'\n'}
578
+ <span className="text-emerald-400">✓</span> Ready in <span className="text-primary">38ms</span>
579
+ </code>
580
+ </pre>
409
581
  </div>
410
582
  </div>
411
583
  </div>
412
- </div>
584
+ </section>
413
585
  );
414
586
  }
415
587
  `,
416
588
 
417
- 'layouts/root.tsx': (name, template) => template === 'flexi-ui' ? `import React from 'react';
589
+ 'components/Features.tsx': () => `import React from 'react';
590
+ import { Card, CardHeader, CardTitle, CardDescription } from './ui/card';
591
+
592
+ const features = [
593
+ {
594
+ icon: (
595
+ <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
596
+ <path strokeLinecap="round" strokeLinejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
597
+ </svg>
598
+ ),
599
+ title: 'Lightning Fast',
600
+ description: 'Powered by esbuild for instant builds and sub-second hot module replacement.',
601
+ },
602
+ {
603
+ icon: (
604
+ <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
605
+ <path strokeLinecap="round" strokeLinejoin="round" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
606
+ </svg>
607
+ ),
608
+ title: 'File-based Routing',
609
+ description: 'Create a file in pages/, get a route automatically. Simple and intuitive.',
610
+ },
611
+ {
612
+ icon: (
613
+ <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
614
+ <path strokeLinecap="round" strokeLinejoin="round" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z" />
615
+ </svg>
616
+ ),
617
+ title: 'Islands Architecture',
618
+ description: 'Partial hydration for minimal JavaScript. Only hydrate what needs interactivity.',
619
+ },
620
+ {
621
+ icon: (
622
+ <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
623
+ <path strokeLinecap="round" strokeLinejoin="round" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01" />
624
+ </svg>
625
+ ),
626
+ title: 'SSR & SSG',
627
+ description: 'Server-side rendering and static generation out of the box. SEO friendly.',
628
+ },
629
+ ];
418
630
 
419
- interface LayoutProps {
420
- children: React.ReactNode;
631
+ export function Features() {
632
+ return (
633
+ <section className="container mx-auto max-w-6xl px-4 py-24">
634
+ <div className="mb-12 text-center">
635
+ <h2 className="mb-4 text-3xl font-bold tracking-tight sm:text-4xl">
636
+ Everything you need
637
+ </h2>
638
+ <p className="mx-auto max-w-2xl text-muted-foreground">
639
+ A complete toolkit for building modern web applications with React.
640
+ </p>
641
+ </div>
642
+
643
+ <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
644
+ {features.map((feature, index) => (
645
+ <Card key={index} className="group cursor-default">
646
+ <CardHeader>
647
+ <div className="mb-3 inline-flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10 text-primary transition-colors group-hover:bg-primary group-hover:text-primary-foreground">
648
+ {feature.icon}
649
+ </div>
650
+ <CardTitle>{feature.title}</CardTitle>
651
+ <CardDescription>{feature.description}</CardDescription>
652
+ </CardHeader>
653
+ </Card>
654
+ ))}
655
+ </div>
656
+ </section>
657
+ );
421
658
  }
659
+ `,
422
660
 
423
- export default function RootLayout({ children }: LayoutProps) {
661
+ 'components/Footer.tsx': () => `import React from 'react';
662
+
663
+ export function Footer() {
424
664
  return (
425
- <div className="min-h-screen flex flex-col" style={{ backgroundColor: 'var(--flexi-bg)', color: 'var(--flexi-fg)' }}>
426
- <header className="sticky top-0 z-50 w-full border-b border-[var(--flexi-border)] backdrop-blur-sm" style={{ backgroundColor: 'rgba(2, 6, 23, 0.8)' }}>
427
- <nav className="container mx-auto px-4 h-16 flex items-center justify-between">
428
- <a href="/" className="flex items-center gap-2">
429
- <div className="w-8 h-8 rounded-xl flex items-center justify-center" style={{ background: 'linear-gradient(135deg, #00FF9C, #00CC7D)' }}>
430
- <span className="text-black font-bold text-sm">F</span>
431
- </div>
432
- <span className="font-bold text-xl">FlexiReact</span>
433
- </a>
434
- <div className="flex items-center gap-4">
435
- <a href="/" className="text-sm opacity-70 hover:opacity-100">Home</a>
436
- <a href="/about" className="text-sm opacity-70 hover:opacity-100">About</a>
665
+ <footer className="border-t border-border">
666
+ <div className="container mx-auto max-w-6xl px-4 py-8">
667
+ <div className="flex flex-col items-center justify-between gap-4 sm:flex-row">
668
+ <div className="flex items-center gap-2 text-sm text-muted-foreground">
669
+ <span>Built with</span>
670
+ <span className="text-red-500">❤️</span>
671
+ <span>using</span>
672
+ <a href="https://github.com/flexireact/flexireact" className="font-medium text-foreground hover:text-primary transition-colors">
673
+ FlexiReact
674
+ </a>
437
675
  </div>
438
- </nav>
439
- </header>
440
- <main className="flex-1">
441
- {children}
442
- </main>
443
- <footer className="border-t border-[var(--flexi-border)] py-8 text-center text-sm opacity-70">
444
- Built with FlexiReact
445
- </footer>
446
- </div>
676
+ <div className="flex items-center gap-4 text-sm text-muted-foreground">
677
+ <a href="https://github.com/flexireact/flexireact" className="hover:text-foreground transition-colors">
678
+ GitHub
679
+ </a>
680
+ <a href="https://github.com/flexireact/flexireact" className="hover:text-foreground transition-colors">
681
+ Documentation
682
+ </a>
683
+ </div>
684
+ </div>
685
+ </div>
686
+ </footer>
447
687
  );
448
688
  }
449
- ` : `import React from 'react';
689
+ `,
690
+
691
+ 'components/index.ts': () => `export { Navbar } from './Navbar';
692
+ export { Hero } from './Hero';
693
+ export { Features } from './Features';
694
+ export { Footer } from './Footer';
695
+ export { Button } from './ui/button';
696
+ export { Card, CardHeader, CardTitle, CardDescription, CardContent } from './ui/card';
697
+ export { Badge } from './ui/badge';
698
+ `,
699
+
700
+ // ============================================================================
701
+ // Lib
702
+ // ============================================================================
703
+
704
+ 'lib/utils.ts': () => `import { clsx, type ClassValue } from 'clsx';
705
+ import { twMerge } from 'tailwind-merge';
706
+
707
+ export function cn(...inputs: ClassValue[]) {
708
+ return twMerge(clsx(inputs));
709
+ }
710
+ `,
711
+
712
+ // ============================================================================
713
+ // Pages & Layouts
714
+ // ============================================================================
715
+
716
+ 'layouts/root.tsx': () => `import React from 'react';
717
+ import { Navbar } from '../components/Navbar';
718
+ import { Footer } from '../components/Footer';
450
719
 
451
720
  interface LayoutProps {
452
721
  children: React.ReactNode;
@@ -454,60 +723,90 @@ interface LayoutProps {
454
723
 
455
724
  export default function RootLayout({ children }: LayoutProps) {
456
725
  return (
457
- <div className="min-h-screen bg-slate-900 text-white">
458
- <header className="sticky top-0 z-50 border-b border-slate-700 bg-slate-900/80 backdrop-blur-sm">
459
- <nav className="container mx-auto px-4 h-16 flex items-center justify-between">
460
- <a href="/" className="flex items-center gap-2 font-bold text-xl">
461
- <span className="text-emerald-400">⚡</span> FlexiReact
462
- </a>
463
- <div className="flex items-center gap-6">
464
- <a href="/" className="text-sm text-slate-400 hover:text-white">Home</a>
465
- <a href="/about" className="text-sm text-slate-400 hover:text-white">About</a>
466
- <a href="https://github.com/flexireact/flexireact" className="text-sm px-4 py-2 bg-emerald-500 text-black rounded-lg font-medium hover:bg-emerald-400">
467
- GitHub
468
- </a>
469
- </div>
470
- </nav>
471
- </header>
472
- <main className="flex-1">
473
- {children}
474
- </main>
475
- <footer className="border-t border-slate-700 py-8 text-center text-sm text-slate-500">
476
- Built with ❤️ using FlexiReact
477
- </footer>
726
+ <div className="relative min-h-screen bg-background text-foreground antialiased">
727
+ <Navbar />
728
+ <main>{children}</main>
729
+ <Footer />
478
730
  </div>
479
731
  );
480
732
  }
481
733
  `,
482
734
 
483
- 'app/styles/input.css': () => `@tailwind base;
735
+ 'pages/index.tsx': () => `import React from 'react';
736
+ import { Hero } from '../components/Hero';
737
+ import { Features } from '../components/Features';
738
+
739
+ export default function HomePage() {
740
+ return (
741
+ <>
742
+ <Hero />
743
+ <Features />
744
+ </>
745
+ );
746
+ }
747
+ `,
748
+
749
+ // ============================================================================
750
+ // Styles
751
+ // ============================================================================
752
+
753
+ 'app/styles/globals.css': () => `@tailwind base;
484
754
  @tailwind components;
485
755
  @tailwind utilities;
486
756
 
487
757
  @layer base {
488
758
  :root {
489
- --flexi-bg: #0f172a;
490
- --flexi-fg: #f8fafc;
491
- --flexi-bg-subtle: #1e293b;
492
- --flexi-bg-muted: #334155;
493
- --flexi-border: #475569;
494
- --flexi-fg-muted: #94a3b8;
495
- --flexi-primary: #10b981;
759
+ --background: 240 10% 3.9%;
760
+ --foreground: 0 0% 98%;
761
+ --card: 240 10% 3.9%;
762
+ --card-foreground: 0 0% 98%;
763
+ --popover: 240 10% 3.9%;
764
+ --popover-foreground: 0 0% 98%;
765
+ --primary: 142.1 76.2% 36.3%;
766
+ --primary-foreground: 144.9 80.4% 10%;
767
+ --secondary: 240 3.7% 15.9%;
768
+ --secondary-foreground: 0 0% 98%;
769
+ --muted: 240 3.7% 15.9%;
770
+ --muted-foreground: 240 5% 64.9%;
771
+ --accent: 240 3.7% 15.9%;
772
+ --accent-foreground: 0 0% 98%;
773
+ --destructive: 0 62.8% 30.6%;
774
+ --destructive-foreground: 0 0% 98%;
775
+ --border: 240 3.7% 15.9%;
776
+ --input: 240 3.7% 15.9%;
777
+ --ring: 142.1 76.2% 36.3%;
778
+ --radius: 0.75rem;
496
779
  }
497
-
780
+
781
+ * {
782
+ border-color: hsl(var(--border));
783
+ }
784
+
498
785
  html {
499
786
  scroll-behavior: smooth;
500
787
  }
501
-
788
+
502
789
  body {
503
- font-family: 'Inter', system-ui, sans-serif;
504
- background-color: #0f172a;
505
- color: #f8fafc;
790
+ font-family: 'Inter', system-ui, -apple-system, sans-serif;
791
+ background-color: hsl(var(--background));
792
+ color: hsl(var(--foreground));
506
793
  min-height: 100vh;
794
+ -webkit-font-smoothing: antialiased;
795
+ -moz-osx-font-smoothing: grayscale;
796
+ }
797
+ }
798
+
799
+ @layer utilities {
800
+ .text-balance {
801
+ text-wrap: balance;
507
802
  }
508
803
  }
509
804
  `,
510
805
 
806
+ // ============================================================================
807
+ // Public Assets
808
+ // ============================================================================
809
+
511
810
  'public/.gitkeep': () => '',
512
811
 
513
812
  'public/favicon.svg': () => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
@@ -517,12 +816,57 @@ export default function RootLayout({ children }: LayoutProps) {
517
816
  <stop offset="100%" style="stop-color:#06b6d4"/>
518
817
  </linearGradient>
519
818
  </defs>
520
- <rect width="100" height="100" rx="20" fill="#0f172a"/>
521
- <path d="M25 70V30h30v10H37v8h15v10H37v12H25z" fill="url(#grad)"/>
522
- <circle cx="65" cy="65" r="8" fill="url(#grad)"/>
819
+ <rect width="100" height="100" rx="20" fill="#0a0a0a"/>
820
+ <path d="M50 20L30 55h15v25l20-35H50V20z" fill="url(#grad)"/>
523
821
  </svg>`,
524
822
  };
525
823
 
824
+ // Minimal template files
825
+ const MINIMAL_FILES = {
826
+ 'package.json': (name) => JSON.stringify({
827
+ name: name,
828
+ version: "1.0.0",
829
+ private: true,
830
+ type: "module",
831
+ scripts: {
832
+ dev: "flexireact dev",
833
+ build: "flexireact build",
834
+ start: "flexireact start"
835
+ },
836
+ dependencies: {
837
+ "react": "^18.2.0",
838
+ "react-dom": "^18.2.0",
839
+ "@flexireact/core": "^1.0.0"
840
+ },
841
+ devDependencies: {
842
+ "@types/react": "^18.2.0",
843
+ "@types/react-dom": "^18.2.0",
844
+ "typescript": "^5.3.0"
845
+ }
846
+ }, null, 2),
847
+
848
+ 'tsconfig.json': TEMPLATE_FILES['tsconfig.json'],
849
+
850
+ 'flexireact.config.js': () => `export default {
851
+ server: { port: 3000 }
852
+ };
853
+ `,
854
+
855
+ 'pages/index.tsx': () => `import React from 'react';
856
+
857
+ export default function HomePage() {
858
+ return (
859
+ <div style={{ padding: '2rem', fontFamily: 'system-ui' }}>
860
+ <h1>Welcome to FlexiReact</h1>
861
+ <p>Edit pages/index.tsx to get started.</p>
862
+ </div>
863
+ );
864
+ }
865
+ `,
866
+
867
+ 'public/.gitkeep': () => '',
868
+ };
869
+
526
870
  // ============================================================================
527
871
  // Main
528
872
  // ============================================================================
@@ -548,50 +892,55 @@ async function main() {
548
892
 
549
893
  // Check if directory exists
550
894
  if (fs.existsSync(projectPath) && !isDirEmpty(projectPath)) {
551
- error(`Directory "${projectName}" already exists and is not empty`);
895
+ error(`Directory ${projectName} already exists and is not empty`);
552
896
  process.exit(1);
553
897
  }
554
898
 
555
- console.log('');
556
-
557
899
  // Select template
900
+ console.log('');
558
901
  const templateOptions = Object.entries(TEMPLATES).map(([key, value]) => ({
559
902
  key,
560
903
  ...value,
561
904
  }));
562
905
 
563
906
  const selectedTemplate = await select('Select a template:', templateOptions);
564
- const template = selectedTemplate.key;
907
+ const templateKey = selectedTemplate.key;
565
908
 
566
909
  console.log('');
567
- console.log(` ${c.dim}Creating project in${c.reset} ${c.cyan}${projectPath}${c.reset}`);
910
+ log(`Creating project in ${c.cyan}${projectPath}${c.reset}`);
568
911
  console.log('');
569
912
 
570
913
  // Create project directory
571
914
  const spinner1 = new Spinner('Creating project structure...');
572
915
  spinner1.start();
573
-
916
+
574
917
  try {
575
918
  fs.mkdirSync(projectPath, { recursive: true });
576
- fs.mkdirSync(path.join(projectPath, 'pages'), { recursive: true });
577
- fs.mkdirSync(path.join(projectPath, 'layouts'), { recursive: true });
578
- fs.mkdirSync(path.join(projectPath, 'app', 'styles'), { recursive: true });
579
- fs.mkdirSync(path.join(projectPath, 'public'), { recursive: true });
580
- fs.mkdirSync(path.join(projectPath, 'components'), { recursive: true });
919
+
920
+ // Create subdirectories
921
+ const dirs = templateKey === 'minimal'
922
+ ? ['pages', 'public']
923
+ : ['pages', 'public', 'components', 'components/ui', 'layouts', 'app/styles', 'lib'];
924
+
925
+ for (const dir of dirs) {
926
+ fs.mkdirSync(path.join(projectPath, dir), { recursive: true });
927
+ }
581
928
 
582
929
  spinner1.stop(true);
583
930
  } catch (err) {
584
931
  spinner1.stop(false);
585
- error(`Failed to create directory: ${err.message}`);
932
+ error(`Failed to create project structure: ${err.message}`);
586
933
  process.exit(1);
587
934
  }
588
935
 
589
936
  // Write template files
590
937
  const spinner2 = new Spinner('Writing template files...');
591
938
  spinner2.start();
592
-
939
+
593
940
  try {
594
- for (const [filePath, generator] of Object.entries(TEMPLATE_FILES)) {
941
+ const files = templateKey === 'minimal' ? MINIMAL_FILES : TEMPLATE_FILES;
942
+
943
+ for (const [filePath, contentFn] of Object.entries(files)) {
595
944
  const fullPath = path.join(projectPath, filePath);
596
945
  const dir = path.dirname(fullPath);
597
946
 
@@ -599,51 +948,25 @@ async function main() {
599
948
  fs.mkdirSync(dir, { recursive: true });
600
949
  }
601
950
 
602
- const content = generator(projectName, template);
951
+ const content = contentFn(projectName, templateKey);
603
952
  fs.writeFileSync(fullPath, content);
604
953
  }
605
954
 
606
955
  spinner2.stop(true);
607
956
  } catch (err) {
608
957
  spinner2.stop(false);
609
- error(`Failed to write files: ${err.message}`);
958
+ error(`Failed to write template files: ${err.message}`);
610
959
  process.exit(1);
611
960
  }
612
961
 
613
- // Create .gitignore
962
+ // Create README
614
963
  const spinner3 = new Spinner('Creating configuration files...');
615
964
  spinner3.start();
616
-
965
+
617
966
  try {
618
- fs.writeFileSync(path.join(projectPath, '.gitignore'), `# Dependencies
619
- node_modules/
620
-
621
- # Build
622
- .flexi/
623
- dist/
624
- build/
625
-
626
- # Environment
627
- .env
628
- .env.local
629
- .env.*.local
967
+ const readmeContent = `# ${projectName}
630
968
 
631
- # IDE
632
- .vscode/
633
- .idea/
634
-
635
- # OS
636
- .DS_Store
637
- Thumbs.db
638
-
639
- # Logs
640
- *.log
641
- npm-debug.log*
642
- `);
643
-
644
- fs.writeFileSync(path.join(projectPath, 'README.md'), `# ${projectName}
645
-
646
- A FlexiReact application.
969
+ A modern web application built with [FlexiReact](https://github.com/flexireact/flexireact).
647
970
 
648
971
  ## Getting Started
649
972
 
@@ -657,9 +980,9 @@ Open [http://localhost:3000](http://localhost:3000) in your browser.
657
980
  ## Learn More
658
981
 
659
982
  - [FlexiReact Documentation](https://github.com/flexireact/flexireact)
660
- - [FlexiUI Components](https://github.com/flexireact/flexi-ui)
661
- `);
983
+ `;
662
984
 
985
+ fs.writeFileSync(path.join(projectPath, 'README.md'), readmeContent);
663
986
  spinner3.stop(true);
664
987
  } catch (err) {
665
988
  spinner3.stop(false);
@@ -667,7 +990,7 @@ Open [http://localhost:3000](http://localhost:3000) in your browser.
667
990
  process.exit(1);
668
991
  }
669
992
 
670
- // Done!
993
+ // Success message
671
994
  console.log(SUCCESS_BANNER(projectName));
672
995
  }
673
996
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-flexireact",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Create FlexiReact apps with one command - The Modern React Framework",
5
5
  "author": "FlexiReact Team",
6
6
  "license": "MIT",