create-flexireact 1.0.1 → 1.2.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/index.js +1311 -233
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -74,11 +74,11 @@ ${c.green} ╰─────────────────────
|
|
|
74
74
|
const TEMPLATES = {
|
|
75
75
|
default: {
|
|
76
76
|
name: 'Default',
|
|
77
|
-
description: '
|
|
77
|
+
description: 'Premium template with modern UI, animations & dark mode',
|
|
78
78
|
},
|
|
79
79
|
'flexi-ui': {
|
|
80
|
-
name: '
|
|
81
|
-
description: '
|
|
80
|
+
name: 'Flexi UI',
|
|
81
|
+
description: 'Showcase template with @flexireact/flexi-ui components',
|
|
82
82
|
},
|
|
83
83
|
minimal: {
|
|
84
84
|
name: 'Minimal',
|
|
@@ -106,10 +106,6 @@ function info(msg) {
|
|
|
106
106
|
console.log(` ${c.cyan}ℹ${c.reset} ${msg}`);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
function step(num, total, msg) {
|
|
110
|
-
console.log(` ${c.dim}[${num}/${total}]${c.reset} ${msg}`);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
109
|
// Spinner
|
|
114
110
|
class Spinner {
|
|
115
111
|
constructor(message) {
|
|
@@ -184,26 +180,6 @@ async function select(question, options) {
|
|
|
184
180
|
});
|
|
185
181
|
}
|
|
186
182
|
|
|
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
183
|
// Check if directory is empty
|
|
208
184
|
function isDirEmpty(dir) {
|
|
209
185
|
if (!fs.existsSync(dir)) return true;
|
|
@@ -211,7 +187,7 @@ function isDirEmpty(dir) {
|
|
|
211
187
|
}
|
|
212
188
|
|
|
213
189
|
// ============================================================================
|
|
214
|
-
// Template Files
|
|
190
|
+
// Template Files
|
|
215
191
|
// ============================================================================
|
|
216
192
|
|
|
217
193
|
const TEMPLATE_FILES = {
|
|
@@ -224,21 +200,24 @@ const TEMPLATE_FILES = {
|
|
|
224
200
|
dev: "npm run css && flexireact dev",
|
|
225
201
|
build: "npm run css && flexireact build",
|
|
226
202
|
start: "flexireact start",
|
|
227
|
-
css: "npx tailwindcss -i ./app/styles/
|
|
203
|
+
css: "npx tailwindcss -i ./app/styles/globals.css -o ./public/styles.css --minify"
|
|
228
204
|
},
|
|
229
205
|
dependencies: {
|
|
230
|
-
react: "^18.2.0",
|
|
206
|
+
"react": "^18.2.0",
|
|
231
207
|
"react-dom": "^18.2.0",
|
|
232
208
|
"@flexireact/core": "^1.0.0",
|
|
233
|
-
|
|
209
|
+
"framer-motion": "^11.0.0",
|
|
210
|
+
"lucide-react": "^0.400.0",
|
|
211
|
+
"clsx": "^2.1.0",
|
|
212
|
+
"tailwind-merge": "^2.2.0"
|
|
234
213
|
},
|
|
235
214
|
devDependencies: {
|
|
236
215
|
"@types/react": "^18.2.0",
|
|
237
216
|
"@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"
|
|
217
|
+
"typescript": "^5.3.0",
|
|
218
|
+
"tailwindcss": "^3.4.0",
|
|
219
|
+
"postcss": "^8.4.32",
|
|
220
|
+
"autoprefixer": "^10.4.16"
|
|
242
221
|
}
|
|
243
222
|
}, null, 2),
|
|
244
223
|
|
|
@@ -265,8 +244,7 @@ const TEMPLATE_FILES = {
|
|
|
265
244
|
exclude: ["node_modules", ".flexi"]
|
|
266
245
|
}, null, 2),
|
|
267
246
|
|
|
268
|
-
'tailwind.config.js': (
|
|
269
|
-
${template === 'flexi-ui' ? "const { flexiUIPlugin } = require('@flexireact/flexi-ui/tailwind');\n" : ''}
|
|
247
|
+
'tailwind.config.js': () => `/** @type {import('tailwindcss').Config} */
|
|
270
248
|
module.exports = {
|
|
271
249
|
darkMode: 'class',
|
|
272
250
|
content: [
|
|
@@ -274,16 +252,71 @@ module.exports = {
|
|
|
274
252
|
'./pages/**/*.{js,ts,jsx,tsx}',
|
|
275
253
|
'./components/**/*.{js,ts,jsx,tsx}',
|
|
276
254
|
'./layouts/**/*.{js,ts,jsx,tsx}',
|
|
277
|
-
${template === 'flexi-ui' ? "'./node_modules/@flexireact/flexi-ui/dist/**/*.js'," : ''}
|
|
278
255
|
],
|
|
279
256
|
theme: {
|
|
280
257
|
extend: {
|
|
281
258
|
fontFamily: {
|
|
282
259
|
sans: ['Inter', 'system-ui', 'sans-serif'],
|
|
283
260
|
},
|
|
261
|
+
colors: {
|
|
262
|
+
border: 'hsl(240 3.7% 15.9%)',
|
|
263
|
+
input: 'hsl(240 3.7% 15.9%)',
|
|
264
|
+
ring: 'hsl(142.1 76.2% 36.3%)',
|
|
265
|
+
background: 'hsl(240 10% 3.9%)',
|
|
266
|
+
foreground: 'hsl(0 0% 98%)',
|
|
267
|
+
primary: {
|
|
268
|
+
DEFAULT: 'hsl(142.1 76.2% 36.3%)',
|
|
269
|
+
foreground: 'hsl(144.9 80.4% 10%)',
|
|
270
|
+
},
|
|
271
|
+
secondary: {
|
|
272
|
+
DEFAULT: 'hsl(240 3.7% 15.9%)',
|
|
273
|
+
foreground: 'hsl(0 0% 98%)',
|
|
274
|
+
},
|
|
275
|
+
muted: {
|
|
276
|
+
DEFAULT: 'hsl(240 3.7% 15.9%)',
|
|
277
|
+
foreground: 'hsl(240 5% 64.9%)',
|
|
278
|
+
},
|
|
279
|
+
accent: {
|
|
280
|
+
DEFAULT: 'hsl(240 3.7% 15.9%)',
|
|
281
|
+
foreground: 'hsl(0 0% 98%)',
|
|
282
|
+
},
|
|
283
|
+
card: {
|
|
284
|
+
DEFAULT: 'hsl(240 10% 3.9%)',
|
|
285
|
+
foreground: 'hsl(0 0% 98%)',
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
borderRadius: {
|
|
289
|
+
lg: '0.75rem',
|
|
290
|
+
md: '0.5rem',
|
|
291
|
+
sm: '0.25rem',
|
|
292
|
+
},
|
|
293
|
+
animation: {
|
|
294
|
+
'fade-in': 'fadeIn 0.5s ease-out',
|
|
295
|
+
'fade-up': 'fadeUp 0.5s ease-out',
|
|
296
|
+
'scale-in': 'scaleIn 0.3s ease-out',
|
|
297
|
+
'glow': 'glow 2s ease-in-out infinite alternate',
|
|
298
|
+
},
|
|
299
|
+
keyframes: {
|
|
300
|
+
fadeIn: {
|
|
301
|
+
'0%': { opacity: '0' },
|
|
302
|
+
'100%': { opacity: '1' },
|
|
303
|
+
},
|
|
304
|
+
fadeUp: {
|
|
305
|
+
'0%': { opacity: '0', transform: 'translateY(20px)' },
|
|
306
|
+
'100%': { opacity: '1', transform: 'translateY(0)' },
|
|
307
|
+
},
|
|
308
|
+
scaleIn: {
|
|
309
|
+
'0%': { opacity: '0', transform: 'scale(0.95)' },
|
|
310
|
+
'100%': { opacity: '1', transform: 'scale(1)' },
|
|
311
|
+
},
|
|
312
|
+
glow: {
|
|
313
|
+
'0%': { boxShadow: '0 0 20px rgba(16, 185, 129, 0.2)' },
|
|
314
|
+
'100%': { boxShadow: '0 0 40px rgba(16, 185, 129, 0.4)' },
|
|
315
|
+
},
|
|
316
|
+
},
|
|
284
317
|
},
|
|
285
318
|
},
|
|
286
|
-
plugins: [
|
|
319
|
+
plugins: [],
|
|
287
320
|
};
|
|
288
321
|
`,
|
|
289
322
|
|
|
@@ -295,126 +328,398 @@ module.exports = {
|
|
|
295
328
|
};
|
|
296
329
|
`,
|
|
297
330
|
|
|
298
|
-
'flexireact.config.js': (
|
|
331
|
+
'flexireact.config.js': () => `/** @type {import('@flexireact/core').Config} */
|
|
299
332
|
export default {
|
|
300
|
-
// Styles to include
|
|
301
333
|
styles: [
|
|
302
334
|
'/styles.css',
|
|
303
|
-
'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap'
|
|
335
|
+
'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap'
|
|
304
336
|
],
|
|
305
|
-
|
|
306
|
-
// Favicon
|
|
307
337
|
favicon: '/favicon.svg',
|
|
308
|
-
|
|
309
|
-
// Server options
|
|
310
338
|
server: {
|
|
311
339
|
port: 3000
|
|
312
340
|
},
|
|
313
|
-
|
|
314
|
-
// Islands (partial hydration)
|
|
315
341
|
islands: {
|
|
316
342
|
enabled: true
|
|
317
343
|
}
|
|
318
344
|
};
|
|
319
345
|
`,
|
|
320
346
|
|
|
321
|
-
|
|
347
|
+
// ============================================================================
|
|
348
|
+
// Components
|
|
349
|
+
// ============================================================================
|
|
322
350
|
|
|
323
|
-
|
|
351
|
+
'components/ui/button.tsx': () => `import React from 'react';
|
|
352
|
+
import { cn } from '../../lib/utils';
|
|
353
|
+
|
|
354
|
+
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
355
|
+
variant?: 'default' | 'outline' | 'ghost';
|
|
356
|
+
size?: 'default' | 'sm' | 'lg';
|
|
357
|
+
children: React.ReactNode;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export function Button({
|
|
361
|
+
className,
|
|
362
|
+
variant = 'default',
|
|
363
|
+
size = 'default',
|
|
364
|
+
children,
|
|
365
|
+
...props
|
|
366
|
+
}: ButtonProps) {
|
|
324
367
|
return (
|
|
325
|
-
<
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
368
|
+
<button
|
|
369
|
+
className={cn(
|
|
370
|
+
'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',
|
|
371
|
+
{
|
|
372
|
+
'bg-primary text-primary-foreground hover:bg-primary/90 shadow-lg shadow-primary/25': variant === 'default',
|
|
373
|
+
'border border-border bg-transparent hover:bg-secondary hover:text-foreground': variant === 'outline',
|
|
374
|
+
'hover:bg-secondary hover:text-foreground': variant === 'ghost',
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
'h-10 px-4 py-2 text-sm': size === 'default',
|
|
378
|
+
'h-9 px-3 text-sm': size === 'sm',
|
|
379
|
+
'h-12 px-8 text-base': size === 'lg',
|
|
380
|
+
},
|
|
381
|
+
className
|
|
382
|
+
)}
|
|
383
|
+
{...props}
|
|
384
|
+
>
|
|
385
|
+
{children}
|
|
386
|
+
</button>
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
`,
|
|
339
390
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
391
|
+
'components/ui/card.tsx': () => `import React from 'react';
|
|
392
|
+
import { cn } from '../../lib/utils';
|
|
393
|
+
|
|
394
|
+
interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
395
|
+
children: React.ReactNode;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export function Card({ className, children, ...props }: CardProps) {
|
|
399
|
+
return (
|
|
400
|
+
<div
|
|
401
|
+
className={cn(
|
|
402
|
+
'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',
|
|
403
|
+
className
|
|
404
|
+
)}
|
|
405
|
+
{...props}
|
|
406
|
+
>
|
|
407
|
+
{children}
|
|
349
408
|
</div>
|
|
350
409
|
);
|
|
351
410
|
}
|
|
352
|
-
` : `import React from 'react';
|
|
353
411
|
|
|
354
|
-
export
|
|
412
|
+
export function CardHeader({ className, children, ...props }: CardProps) {
|
|
355
413
|
return (
|
|
356
|
-
<div className=
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
414
|
+
<div className={cn('flex flex-col space-y-1.5 p-6', className)} {...props}>
|
|
415
|
+
{children}
|
|
416
|
+
</div>
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export function CardTitle({ className, children, ...props }: CardProps) {
|
|
421
|
+
return (
|
|
422
|
+
<h3 className={cn('text-lg font-semibold leading-none tracking-tight', className)} {...props}>
|
|
423
|
+
{children}
|
|
424
|
+
</h3>
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export function CardDescription({ className, children, ...props }: CardProps) {
|
|
429
|
+
return (
|
|
430
|
+
<p className={cn('text-sm text-muted-foreground', className)} {...props}>
|
|
431
|
+
{children}
|
|
432
|
+
</p>
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export function CardContent({ className, children, ...props }: CardProps) {
|
|
437
|
+
return (
|
|
438
|
+
<div className={cn('p-6 pt-0', className)} {...props}>
|
|
439
|
+
{children}
|
|
440
|
+
</div>
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
`,
|
|
444
|
+
|
|
445
|
+
'components/ui/badge.tsx': () => `import React from 'react';
|
|
446
|
+
import { cn } from '../../lib/utils';
|
|
447
|
+
|
|
448
|
+
interface BadgeProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
449
|
+
variant?: 'default' | 'secondary' | 'outline';
|
|
450
|
+
children: React.ReactNode;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export function Badge({ className, variant = 'default', children, ...props }: BadgeProps) {
|
|
454
|
+
return (
|
|
455
|
+
<div
|
|
456
|
+
className={cn(
|
|
457
|
+
'inline-flex items-center rounded-full px-3 py-1 text-xs font-medium transition-colors',
|
|
458
|
+
{
|
|
459
|
+
'bg-primary/10 text-primary border border-primary/20': variant === 'default',
|
|
460
|
+
'bg-secondary text-secondary-foreground': variant === 'secondary',
|
|
461
|
+
'border border-border text-foreground': variant === 'outline',
|
|
462
|
+
},
|
|
463
|
+
className
|
|
464
|
+
)}
|
|
465
|
+
{...props}
|
|
466
|
+
>
|
|
467
|
+
{children}
|
|
468
|
+
</div>
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
`,
|
|
472
|
+
|
|
473
|
+
'components/Navbar.tsx': () => `import React from 'react';
|
|
474
|
+
import { Button } from './ui/button';
|
|
475
|
+
|
|
476
|
+
export function Navbar() {
|
|
477
|
+
return (
|
|
478
|
+
<header className="sticky top-0 z-50 w-full border-b border-border/40 bg-background/80 backdrop-blur-xl">
|
|
479
|
+
<nav className="container mx-auto flex h-16 max-w-6xl items-center justify-between px-4">
|
|
480
|
+
<a href="/" className="flex items-center gap-2 transition-opacity hover:opacity-80">
|
|
481
|
+
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-primary to-emerald-400">
|
|
482
|
+
<svg className="h-4 w-4 text-primary-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
|
483
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
|
484
|
+
</svg>
|
|
485
|
+
</div>
|
|
486
|
+
<span className="text-lg font-bold">FlexiReact</span>
|
|
487
|
+
</a>
|
|
370
488
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
489
|
+
<div className="flex items-center gap-2">
|
|
490
|
+
<Button variant="ghost" size="sm" asChild>
|
|
491
|
+
<a href="https://github.com/flexireact/flexireact">Docs</a>
|
|
492
|
+
</Button>
|
|
493
|
+
<Button variant="ghost" size="sm" asChild>
|
|
494
|
+
<a href="https://github.com/flexireact/flexireact" className="flex items-center gap-2">
|
|
495
|
+
<svg className="h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
|
|
496
|
+
<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"/>
|
|
497
|
+
</svg>
|
|
498
|
+
GitHub
|
|
499
|
+
</a>
|
|
500
|
+
</Button>
|
|
501
|
+
</div>
|
|
502
|
+
</nav>
|
|
503
|
+
</header>
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
`,
|
|
507
|
+
|
|
508
|
+
'components/Hero.tsx': () => `import React from 'react';
|
|
509
|
+
import { Button } from './ui/button';
|
|
510
|
+
import { Badge } from './ui/badge';
|
|
376
511
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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>
|
|
512
|
+
export function Hero() {
|
|
513
|
+
return (
|
|
514
|
+
<section className="relative overflow-hidden">
|
|
515
|
+
{/* Gradient Background */}
|
|
516
|
+
<div className="absolute inset-0 -z-10">
|
|
517
|
+
<div className="absolute left-1/2 top-0 -translate-x-1/2 -translate-y-1/2">
|
|
518
|
+
<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" />
|
|
519
|
+
</div>
|
|
520
|
+
<div className="absolute right-0 top-1/2 -translate-y-1/2">
|
|
521
|
+
<div className="h-[400px] w-[400px] rounded-full bg-gradient-to-l from-primary/10 to-transparent blur-3xl" />
|
|
391
522
|
</div>
|
|
523
|
+
</div>
|
|
392
524
|
|
|
393
|
-
|
|
394
|
-
<div className="
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
<
|
|
398
|
-
|
|
525
|
+
<div className="container mx-auto max-w-6xl px-4 py-24 sm:py-32 lg:py-40">
|
|
526
|
+
<div className="flex flex-col items-center text-center">
|
|
527
|
+
{/* Badge */}
|
|
528
|
+
<Badge className="mb-6 animate-fade-in">
|
|
529
|
+
<span className="mr-1">⚡</span> The Modern React Framework
|
|
530
|
+
</Badge>
|
|
531
|
+
|
|
532
|
+
{/* Title */}
|
|
533
|
+
<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">
|
|
534
|
+
Build{' '}
|
|
535
|
+
<span className="bg-gradient-to-r from-primary via-emerald-400 to-cyan-400 bg-clip-text text-transparent">
|
|
536
|
+
blazing fast
|
|
537
|
+
</span>{' '}
|
|
538
|
+
web apps
|
|
539
|
+
</h1>
|
|
540
|
+
|
|
541
|
+
{/* Subtitle */}
|
|
542
|
+
<p className="mb-10 max-w-2xl animate-fade-up text-lg text-muted-foreground sm:text-xl" style={{ animationDelay: '0.1s' }}>
|
|
543
|
+
A modern React framework with TypeScript, Tailwind CSS, SSR, SSG,
|
|
544
|
+
Islands architecture, and file-based routing. Ship faster.
|
|
545
|
+
</p>
|
|
546
|
+
|
|
547
|
+
{/* CTA Buttons */}
|
|
548
|
+
<div className="flex flex-wrap items-center justify-center gap-4 animate-fade-up" style={{ animationDelay: '0.2s' }}>
|
|
549
|
+
<Button size="lg" className="gap-2">
|
|
550
|
+
Start Building
|
|
551
|
+
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
552
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M13 7l5 5m0 0l-5 5m5-5H6" />
|
|
553
|
+
</svg>
|
|
554
|
+
</Button>
|
|
555
|
+
<Button variant="outline" size="lg" asChild>
|
|
556
|
+
<a href="https://github.com/flexireact/flexireact" className="gap-2">
|
|
557
|
+
<svg className="h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
|
|
558
|
+
<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"/>
|
|
559
|
+
</svg>
|
|
560
|
+
GitHub
|
|
561
|
+
</a>
|
|
562
|
+
</Button>
|
|
399
563
|
</div>
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
<
|
|
564
|
+
|
|
565
|
+
{/* Code Preview */}
|
|
566
|
+
<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' }}>
|
|
567
|
+
<div className="flex items-center gap-2 border-b border-border pb-3">
|
|
568
|
+
<div className="h-3 w-3 rounded-full bg-red-500/80" />
|
|
569
|
+
<div className="h-3 w-3 rounded-full bg-yellow-500/80" />
|
|
570
|
+
<div className="h-3 w-3 rounded-full bg-green-500/80" />
|
|
571
|
+
<span className="ml-2 text-xs text-muted-foreground">terminal</span>
|
|
572
|
+
</div>
|
|
573
|
+
<pre className="mt-4 overflow-x-auto text-left text-sm">
|
|
574
|
+
<code className="text-muted-foreground">
|
|
575
|
+
<span className="text-muted-foreground/60">$</span>{' '}
|
|
576
|
+
<span className="text-primary">npx</span> create-flexireact@latest my-app{'\n'}
|
|
577
|
+
<span className="text-muted-foreground/60">$</span>{' '}
|
|
578
|
+
<span className="text-primary">cd</span> my-app{'\n'}
|
|
579
|
+
<span className="text-muted-foreground/60">$</span>{' '}
|
|
580
|
+
<span className="text-primary">npm</span> run dev{'\n'}
|
|
581
|
+
{'\n'}
|
|
582
|
+
<span className="text-emerald-400">✓</span> Ready in <span className="text-primary">38ms</span>
|
|
583
|
+
</code>
|
|
584
|
+
</pre>
|
|
404
585
|
</div>
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
586
|
+
</div>
|
|
587
|
+
</div>
|
|
588
|
+
</section>
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
`,
|
|
592
|
+
|
|
593
|
+
'components/Features.tsx': () => `import React from 'react';
|
|
594
|
+
import { Card, CardHeader, CardTitle, CardDescription } from './ui/card';
|
|
595
|
+
|
|
596
|
+
const features = [
|
|
597
|
+
{
|
|
598
|
+
icon: (
|
|
599
|
+
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
600
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
|
601
|
+
</svg>
|
|
602
|
+
),
|
|
603
|
+
title: 'Lightning Fast',
|
|
604
|
+
description: 'Powered by esbuild for instant builds and sub-second hot module replacement.',
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
icon: (
|
|
608
|
+
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
609
|
+
<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" />
|
|
610
|
+
</svg>
|
|
611
|
+
),
|
|
612
|
+
title: 'File-based Routing',
|
|
613
|
+
description: 'Create a file in pages/, get a route automatically. Simple and intuitive.',
|
|
614
|
+
},
|
|
615
|
+
{
|
|
616
|
+
icon: (
|
|
617
|
+
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
618
|
+
<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" />
|
|
619
|
+
</svg>
|
|
620
|
+
),
|
|
621
|
+
title: 'Islands Architecture',
|
|
622
|
+
description: 'Partial hydration for minimal JavaScript. Only hydrate what needs interactivity.',
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
icon: (
|
|
626
|
+
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
627
|
+
<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" />
|
|
628
|
+
</svg>
|
|
629
|
+
),
|
|
630
|
+
title: 'SSR & SSG',
|
|
631
|
+
description: 'Server-side rendering and static generation out of the box. SEO friendly.',
|
|
632
|
+
},
|
|
633
|
+
];
|
|
634
|
+
|
|
635
|
+
export function Features() {
|
|
636
|
+
return (
|
|
637
|
+
<section className="container mx-auto max-w-6xl px-4 py-24">
|
|
638
|
+
<div className="mb-12 text-center">
|
|
639
|
+
<h2 className="mb-4 text-3xl font-bold tracking-tight sm:text-4xl">
|
|
640
|
+
Everything you need
|
|
641
|
+
</h2>
|
|
642
|
+
<p className="mx-auto max-w-2xl text-muted-foreground">
|
|
643
|
+
A complete toolkit for building modern web applications with React.
|
|
644
|
+
</p>
|
|
645
|
+
</div>
|
|
646
|
+
|
|
647
|
+
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
|
648
|
+
{features.map((feature, index) => (
|
|
649
|
+
<Card key={index} className="group cursor-default">
|
|
650
|
+
<CardHeader>
|
|
651
|
+
<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">
|
|
652
|
+
{feature.icon}
|
|
653
|
+
</div>
|
|
654
|
+
<CardTitle>{feature.title}</CardTitle>
|
|
655
|
+
<CardDescription>{feature.description}</CardDescription>
|
|
656
|
+
</CardHeader>
|
|
657
|
+
</Card>
|
|
658
|
+
))}
|
|
659
|
+
</div>
|
|
660
|
+
</section>
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
`,
|
|
664
|
+
|
|
665
|
+
'components/Footer.tsx': () => `import React from 'react';
|
|
666
|
+
|
|
667
|
+
export function Footer() {
|
|
668
|
+
return (
|
|
669
|
+
<footer className="border-t border-border">
|
|
670
|
+
<div className="container mx-auto max-w-6xl px-4 py-8">
|
|
671
|
+
<div className="flex flex-col items-center justify-between gap-4 sm:flex-row">
|
|
672
|
+
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
673
|
+
<span>Built with</span>
|
|
674
|
+
<span className="text-red-500">❤️</span>
|
|
675
|
+
<span>using</span>
|
|
676
|
+
<a href="https://github.com/flexireact/flexireact" className="font-medium text-foreground hover:text-primary transition-colors">
|
|
677
|
+
FlexiReact
|
|
678
|
+
</a>
|
|
679
|
+
</div>
|
|
680
|
+
<div className="flex items-center gap-4 text-sm text-muted-foreground">
|
|
681
|
+
<a href="https://github.com/flexireact/flexireact" className="hover:text-foreground transition-colors">
|
|
682
|
+
GitHub
|
|
683
|
+
</a>
|
|
684
|
+
<a href="https://github.com/flexireact/flexireact" className="hover:text-foreground transition-colors">
|
|
685
|
+
Documentation
|
|
686
|
+
</a>
|
|
409
687
|
</div>
|
|
410
688
|
</div>
|
|
411
689
|
</div>
|
|
412
|
-
</
|
|
690
|
+
</footer>
|
|
413
691
|
);
|
|
414
692
|
}
|
|
415
693
|
`,
|
|
416
694
|
|
|
417
|
-
'
|
|
695
|
+
'components/index.ts': () => `export { Navbar } from './Navbar';
|
|
696
|
+
export { Hero } from './Hero';
|
|
697
|
+
export { Features } from './Features';
|
|
698
|
+
export { Footer } from './Footer';
|
|
699
|
+
export { Button } from './ui/button';
|
|
700
|
+
export { Card, CardHeader, CardTitle, CardDescription, CardContent } from './ui/card';
|
|
701
|
+
export { Badge } from './ui/badge';
|
|
702
|
+
`,
|
|
703
|
+
|
|
704
|
+
// ============================================================================
|
|
705
|
+
// Lib
|
|
706
|
+
// ============================================================================
|
|
707
|
+
|
|
708
|
+
'lib/utils.ts': () => `import { clsx, type ClassValue } from 'clsx';
|
|
709
|
+
import { twMerge } from 'tailwind-merge';
|
|
710
|
+
|
|
711
|
+
export function cn(...inputs: ClassValue[]) {
|
|
712
|
+
return twMerge(clsx(inputs));
|
|
713
|
+
}
|
|
714
|
+
`,
|
|
715
|
+
|
|
716
|
+
// ============================================================================
|
|
717
|
+
// Pages & Layouts
|
|
718
|
+
// ============================================================================
|
|
719
|
+
|
|
720
|
+
'layouts/root.tsx': () => `import React from 'react';
|
|
721
|
+
import { Navbar } from '../components/Navbar';
|
|
722
|
+
import { Footer } from '../components/Footer';
|
|
418
723
|
|
|
419
724
|
interface LayoutProps {
|
|
420
725
|
children: React.ReactNode;
|
|
@@ -422,31 +727,722 @@ interface LayoutProps {
|
|
|
422
727
|
|
|
423
728
|
export default function RootLayout({ children }: LayoutProps) {
|
|
424
729
|
return (
|
|
425
|
-
<div className="min-h-screen
|
|
426
|
-
<
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
730
|
+
<div className="relative min-h-screen bg-background text-foreground antialiased">
|
|
731
|
+
<Navbar />
|
|
732
|
+
<main>{children}</main>
|
|
733
|
+
<Footer />
|
|
734
|
+
</div>
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
`,
|
|
738
|
+
|
|
739
|
+
'pages/index.tsx': () => `import React from 'react';
|
|
740
|
+
import { Hero } from '../components/Hero';
|
|
741
|
+
import { Features } from '../components/Features';
|
|
742
|
+
|
|
743
|
+
export default function HomePage() {
|
|
744
|
+
return (
|
|
745
|
+
<>
|
|
746
|
+
<Hero />
|
|
747
|
+
<Features />
|
|
748
|
+
</>
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
`,
|
|
752
|
+
|
|
753
|
+
// ============================================================================
|
|
754
|
+
// Styles
|
|
755
|
+
// ============================================================================
|
|
756
|
+
|
|
757
|
+
'app/styles/globals.css': () => `@tailwind base;
|
|
758
|
+
@tailwind components;
|
|
759
|
+
@tailwind utilities;
|
|
760
|
+
|
|
761
|
+
@layer base {
|
|
762
|
+
:root {
|
|
763
|
+
--background: 240 10% 3.9%;
|
|
764
|
+
--foreground: 0 0% 98%;
|
|
765
|
+
--card: 240 10% 3.9%;
|
|
766
|
+
--card-foreground: 0 0% 98%;
|
|
767
|
+
--popover: 240 10% 3.9%;
|
|
768
|
+
--popover-foreground: 0 0% 98%;
|
|
769
|
+
--primary: 142.1 76.2% 36.3%;
|
|
770
|
+
--primary-foreground: 144.9 80.4% 10%;
|
|
771
|
+
--secondary: 240 3.7% 15.9%;
|
|
772
|
+
--secondary-foreground: 0 0% 98%;
|
|
773
|
+
--muted: 240 3.7% 15.9%;
|
|
774
|
+
--muted-foreground: 240 5% 64.9%;
|
|
775
|
+
--accent: 240 3.7% 15.9%;
|
|
776
|
+
--accent-foreground: 0 0% 98%;
|
|
777
|
+
--destructive: 0 62.8% 30.6%;
|
|
778
|
+
--destructive-foreground: 0 0% 98%;
|
|
779
|
+
--border: 240 3.7% 15.9%;
|
|
780
|
+
--input: 240 3.7% 15.9%;
|
|
781
|
+
--ring: 142.1 76.2% 36.3%;
|
|
782
|
+
--radius: 0.75rem;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
* {
|
|
786
|
+
border-color: hsl(var(--border));
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
html {
|
|
790
|
+
scroll-behavior: smooth;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
body {
|
|
794
|
+
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
|
795
|
+
background-color: hsl(var(--background));
|
|
796
|
+
color: hsl(var(--foreground));
|
|
797
|
+
min-height: 100vh;
|
|
798
|
+
-webkit-font-smoothing: antialiased;
|
|
799
|
+
-moz-osx-font-smoothing: grayscale;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
@layer utilities {
|
|
804
|
+
.text-balance {
|
|
805
|
+
text-wrap: balance;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
`,
|
|
809
|
+
|
|
810
|
+
// ============================================================================
|
|
811
|
+
// Public Assets
|
|
812
|
+
// ============================================================================
|
|
813
|
+
|
|
814
|
+
'public/.gitkeep': () => '',
|
|
815
|
+
|
|
816
|
+
'public/favicon.svg': () => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
817
|
+
<defs>
|
|
818
|
+
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
819
|
+
<stop offset="0%" style="stop-color:#10b981"/>
|
|
820
|
+
<stop offset="100%" style="stop-color:#06b6d4"/>
|
|
821
|
+
</linearGradient>
|
|
822
|
+
</defs>
|
|
823
|
+
<rect width="100" height="100" rx="20" fill="#0a0a0a"/>
|
|
824
|
+
<path d="M50 20L30 55h15v25l20-35H50V20z" fill="url(#grad)"/>
|
|
825
|
+
</svg>`,
|
|
826
|
+
};
|
|
827
|
+
|
|
828
|
+
// Minimal template files
|
|
829
|
+
const MINIMAL_FILES = {
|
|
830
|
+
'package.json': (name) => JSON.stringify({
|
|
831
|
+
name: name,
|
|
832
|
+
version: "1.0.0",
|
|
833
|
+
private: true,
|
|
834
|
+
type: "module",
|
|
835
|
+
scripts: {
|
|
836
|
+
dev: "flexireact dev",
|
|
837
|
+
build: "flexireact build",
|
|
838
|
+
start: "flexireact start"
|
|
839
|
+
},
|
|
840
|
+
dependencies: {
|
|
841
|
+
"react": "^18.2.0",
|
|
842
|
+
"react-dom": "^18.2.0",
|
|
843
|
+
"@flexireact/core": "^1.0.0"
|
|
844
|
+
},
|
|
845
|
+
devDependencies: {
|
|
846
|
+
"@types/react": "^18.2.0",
|
|
847
|
+
"@types/react-dom": "^18.2.0",
|
|
848
|
+
"typescript": "^5.3.0"
|
|
849
|
+
}
|
|
850
|
+
}, null, 2),
|
|
851
|
+
|
|
852
|
+
'tsconfig.json': TEMPLATE_FILES['tsconfig.json'],
|
|
853
|
+
|
|
854
|
+
'flexireact.config.js': () => `export default {
|
|
855
|
+
server: { port: 3000 }
|
|
856
|
+
};
|
|
857
|
+
`,
|
|
858
|
+
|
|
859
|
+
'pages/index.tsx': () => `import React from 'react';
|
|
860
|
+
|
|
861
|
+
export default function HomePage() {
|
|
862
|
+
return (
|
|
863
|
+
<div style={{ padding: '2rem', fontFamily: 'system-ui' }}>
|
|
864
|
+
<h1>Welcome to FlexiReact</h1>
|
|
865
|
+
<p>Edit pages/index.tsx to get started.</p>
|
|
866
|
+
</div>
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
`,
|
|
870
|
+
|
|
871
|
+
'public/.gitkeep': () => '',
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
// ============================================================================
|
|
875
|
+
// Flexi UI Template Files
|
|
876
|
+
// ============================================================================
|
|
877
|
+
|
|
878
|
+
const FLEXI_UI_FILES = {
|
|
879
|
+
'package.json': (name) => JSON.stringify({
|
|
880
|
+
name: name,
|
|
881
|
+
version: "1.0.0",
|
|
882
|
+
private: true,
|
|
883
|
+
type: "module",
|
|
884
|
+
scripts: {
|
|
885
|
+
dev: "npm run css && flexireact dev",
|
|
886
|
+
build: "npm run css && flexireact build",
|
|
887
|
+
start: "flexireact start",
|
|
888
|
+
css: "npx tailwindcss -i ./app/styles/globals.css -o ./public/styles.css --minify"
|
|
889
|
+
},
|
|
890
|
+
dependencies: {
|
|
891
|
+
"react": "^18.2.0",
|
|
892
|
+
"react-dom": "^18.2.0",
|
|
893
|
+
"@flexireact/core": "^1.0.0",
|
|
894
|
+
"@flexireact/flexi-ui": "^1.0.0",
|
|
895
|
+
"lucide-react": "^0.400.0"
|
|
896
|
+
},
|
|
897
|
+
devDependencies: {
|
|
898
|
+
"@types/react": "^18.2.0",
|
|
899
|
+
"@types/react-dom": "^18.2.0",
|
|
900
|
+
"typescript": "^5.3.0",
|
|
901
|
+
"tailwindcss": "^3.4.0",
|
|
902
|
+
"postcss": "^8.4.32",
|
|
903
|
+
"autoprefixer": "^10.4.16"
|
|
904
|
+
}
|
|
905
|
+
}, null, 2),
|
|
906
|
+
|
|
907
|
+
'tsconfig.json': TEMPLATE_FILES['tsconfig.json'],
|
|
908
|
+
|
|
909
|
+
'tailwind.config.js': () => `/** @type {import('tailwindcss').Config} */
|
|
910
|
+
const { flexiUIPlugin } = require('@flexireact/flexi-ui/tailwind');
|
|
911
|
+
|
|
912
|
+
module.exports = {
|
|
913
|
+
darkMode: 'class',
|
|
914
|
+
content: [
|
|
915
|
+
'./app/**/*.{js,ts,jsx,tsx}',
|
|
916
|
+
'./pages/**/*.{js,ts,jsx,tsx}',
|
|
917
|
+
'./components/**/*.{js,ts,jsx,tsx}',
|
|
918
|
+
'./layouts/**/*.{js,ts,jsx,tsx}',
|
|
919
|
+
'./node_modules/@flexireact/flexi-ui/dist/**/*.js',
|
|
920
|
+
],
|
|
921
|
+
theme: {
|
|
922
|
+
extend: {
|
|
923
|
+
fontFamily: {
|
|
924
|
+
sans: ['Inter', 'system-ui', 'sans-serif'],
|
|
925
|
+
},
|
|
926
|
+
animation: {
|
|
927
|
+
'fade-in': 'fadeIn 0.6s ease-out forwards',
|
|
928
|
+
'fade-up': 'fadeUp 0.6s ease-out forwards',
|
|
929
|
+
'scale-in': 'scaleIn 0.4s ease-out forwards',
|
|
930
|
+
'glow-pulse': 'glowPulse 3s ease-in-out infinite',
|
|
931
|
+
},
|
|
932
|
+
keyframes: {
|
|
933
|
+
fadeIn: {
|
|
934
|
+
'0%': { opacity: '0' },
|
|
935
|
+
'100%': { opacity: '1' },
|
|
936
|
+
},
|
|
937
|
+
fadeUp: {
|
|
938
|
+
'0%': { opacity: '0', transform: 'translateY(30px)' },
|
|
939
|
+
'100%': { opacity: '1', transform: 'translateY(0)' },
|
|
940
|
+
},
|
|
941
|
+
scaleIn: {
|
|
942
|
+
'0%': { opacity: '0', transform: 'scale(0.9)' },
|
|
943
|
+
'100%': { opacity: '1', transform: 'scale(1)' },
|
|
944
|
+
},
|
|
945
|
+
glowPulse: {
|
|
946
|
+
'0%, 100%': { boxShadow: '0 0 20px rgba(0, 255, 156, 0.15)' },
|
|
947
|
+
'50%': { boxShadow: '0 0 40px rgba(0, 255, 156, 0.3)' },
|
|
948
|
+
},
|
|
949
|
+
},
|
|
950
|
+
},
|
|
951
|
+
},
|
|
952
|
+
plugins: [flexiUIPlugin],
|
|
953
|
+
};
|
|
954
|
+
`,
|
|
955
|
+
|
|
956
|
+
'postcss.config.js': () => `module.exports = {
|
|
957
|
+
plugins: {
|
|
958
|
+
tailwindcss: {},
|
|
959
|
+
autoprefixer: {},
|
|
960
|
+
},
|
|
961
|
+
};
|
|
962
|
+
`,
|
|
963
|
+
|
|
964
|
+
'flexireact.config.js': () => `/** @type {import('@flexireact/core').Config} */
|
|
965
|
+
export default {
|
|
966
|
+
styles: [
|
|
967
|
+
'/styles.css',
|
|
968
|
+
'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap'
|
|
969
|
+
],
|
|
970
|
+
favicon: '/favicon.svg',
|
|
971
|
+
server: {
|
|
972
|
+
port: 3000
|
|
973
|
+
},
|
|
974
|
+
islands: {
|
|
975
|
+
enabled: true
|
|
976
|
+
}
|
|
977
|
+
};
|
|
978
|
+
`,
|
|
979
|
+
|
|
980
|
+
// ============================================================================
|
|
981
|
+
// Components
|
|
982
|
+
// ============================================================================
|
|
983
|
+
|
|
984
|
+
'components/Hero.tsx': () => `import React from 'react';
|
|
985
|
+
import { Button, Badge, Card } from '@flexireact/flexi-ui';
|
|
986
|
+
import { Zap, Github, ArrowRight, Sparkles } from 'lucide-react';
|
|
987
|
+
|
|
988
|
+
export function Hero() {
|
|
989
|
+
return (
|
|
990
|
+
<section className="relative min-h-screen flex items-center justify-center overflow-hidden">
|
|
991
|
+
{/* Radial Gradient Background */}
|
|
992
|
+
<div className="absolute inset-0 -z-10">
|
|
993
|
+
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[800px] rounded-full bg-gradient-radial from-[#00FF9C]/20 via-[#00FF9C]/5 to-transparent blur-3xl" />
|
|
994
|
+
<div className="absolute top-0 right-0 w-[600px] h-[600px] rounded-full bg-gradient-radial from-cyan-500/10 to-transparent blur-3xl" />
|
|
995
|
+
<div className="absolute bottom-0 left-0 w-[400px] h-[400px] rounded-full bg-gradient-radial from-emerald-500/10 to-transparent blur-3xl" />
|
|
996
|
+
</div>
|
|
997
|
+
|
|
998
|
+
{/* Grid Pattern Overlay */}
|
|
999
|
+
<div className="absolute inset-0 -z-10 bg-[linear-gradient(rgba(255,255,255,0.02)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.02)_1px,transparent_1px)] bg-[size:64px_64px]" />
|
|
1000
|
+
|
|
1001
|
+
<div className="container mx-auto px-6 py-32 max-w-6xl">
|
|
1002
|
+
<div className="flex flex-col items-center text-center">
|
|
1003
|
+
{/* Badge */}
|
|
1004
|
+
<div className="animate-fade-in" style={{ animationDelay: '0.1s' }}>
|
|
1005
|
+
<Badge variant="success" className="mb-8 px-4 py-2 text-sm">
|
|
1006
|
+
<Sparkles className="w-4 h-4 mr-2" />
|
|
1007
|
+
The Modern React Framework
|
|
1008
|
+
</Badge>
|
|
1009
|
+
</div>
|
|
1010
|
+
|
|
1011
|
+
{/* Title */}
|
|
1012
|
+
<h1 className="text-5xl sm:text-6xl md:text-7xl lg:text-8xl font-black tracking-tight mb-8 animate-fade-up" style={{ animationDelay: '0.2s' }}>
|
|
1013
|
+
Build{' '}
|
|
1014
|
+
<span className="text-transparent bg-clip-text bg-gradient-to-r from-[#00FF9C] via-emerald-400 to-cyan-400">
|
|
1015
|
+
beautiful
|
|
1016
|
+
</span>
|
|
1017
|
+
<br />
|
|
1018
|
+
apps faster
|
|
1019
|
+
</h1>
|
|
1020
|
+
|
|
1021
|
+
{/* Subtitle */}
|
|
1022
|
+
<p className="text-xl md:text-2xl text-[#94a3b8] max-w-2xl mb-12 leading-relaxed animate-fade-up" style={{ animationDelay: '0.3s' }}>
|
|
1023
|
+
Flexi UI is a stunning component library with neon emerald accents,
|
|
1024
|
+
dark-first design, and seamless React integration.
|
|
1025
|
+
</p>
|
|
1026
|
+
|
|
1027
|
+
{/* CTA Buttons */}
|
|
1028
|
+
<div className="flex flex-wrap items-center justify-center gap-4 mb-20 animate-fade-up" style={{ animationDelay: '0.4s' }}>
|
|
1029
|
+
<Button size="lg" className="gap-2 text-base px-8 py-4 h-auto">
|
|
1030
|
+
Get Started
|
|
1031
|
+
<ArrowRight className="w-5 h-5" />
|
|
1032
|
+
</Button>
|
|
1033
|
+
<Button variant="outline" size="lg" className="gap-2 text-base px-8 py-4 h-auto">
|
|
1034
|
+
<Github className="w-5 h-5" />
|
|
1035
|
+
GitHub
|
|
1036
|
+
</Button>
|
|
1037
|
+
</div>
|
|
1038
|
+
|
|
1039
|
+
{/* Terminal Preview */}
|
|
1040
|
+
<Card className="w-full max-w-2xl animate-scale-in animate-glow-pulse" style={{ animationDelay: '0.5s' }}>
|
|
1041
|
+
<div className="p-6">
|
|
1042
|
+
<div className="flex items-center gap-2 mb-4">
|
|
1043
|
+
<div className="w-3 h-3 rounded-full bg-red-500/80" />
|
|
1044
|
+
<div className="w-3 h-3 rounded-full bg-yellow-500/80" />
|
|
1045
|
+
<div className="w-3 h-3 rounded-full bg-green-500/80" />
|
|
1046
|
+
<span className="ml-3 text-xs text-[#64748b]">terminal</span>
|
|
1047
|
+
</div>
|
|
1048
|
+
<pre className="text-left text-sm md:text-base font-mono">
|
|
1049
|
+
<code>
|
|
1050
|
+
<span className="text-[#64748b]">$</span>{' '}
|
|
1051
|
+
<span className="text-[#00FF9C]">npm</span> install @flexireact/flexi-ui{'\n'}
|
|
1052
|
+
<span className="text-[#64748b]">$</span>{' '}
|
|
1053
|
+
<span className="text-[#00FF9C]">npx</span> create-flexireact my-app{'\n'}
|
|
1054
|
+
{'\n'}
|
|
1055
|
+
<span className="text-[#00FF9C]">✓</span> <span className="text-[#f8fafc]">Ready in</span> <span className="text-[#00FF9C]">38ms</span>
|
|
1056
|
+
</code>
|
|
1057
|
+
</pre>
|
|
1058
|
+
</div>
|
|
1059
|
+
</Card>
|
|
1060
|
+
</div>
|
|
1061
|
+
</div>
|
|
1062
|
+
</section>
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
1065
|
+
`,
|
|
1066
|
+
|
|
1067
|
+
'components/Features.tsx': () => `import React from 'react';
|
|
1068
|
+
import { Card, Badge } from '@flexireact/flexi-ui';
|
|
1069
|
+
import { Zap, Folder, Sparkles, Server, Palette, Shield } from 'lucide-react';
|
|
1070
|
+
|
|
1071
|
+
const features = [
|
|
1072
|
+
{
|
|
1073
|
+
icon: Zap,
|
|
1074
|
+
title: 'Lightning Fast',
|
|
1075
|
+
description: 'Powered by esbuild for instant builds and sub-second hot module replacement.',
|
|
1076
|
+
badge: 'Performance',
|
|
1077
|
+
},
|
|
1078
|
+
{
|
|
1079
|
+
icon: Folder,
|
|
1080
|
+
title: 'File-based Routing',
|
|
1081
|
+
description: 'Create a file in pages/, get a route automatically. Simple and intuitive.',
|
|
1082
|
+
badge: 'DX',
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
icon: Sparkles,
|
|
1086
|
+
title: 'Islands Architecture',
|
|
1087
|
+
description: 'Partial hydration for minimal JavaScript. Only hydrate what needs interactivity.',
|
|
1088
|
+
badge: 'Modern',
|
|
1089
|
+
},
|
|
1090
|
+
{
|
|
1091
|
+
icon: Server,
|
|
1092
|
+
title: 'SSR & SSG',
|
|
1093
|
+
description: 'Server-side rendering and static generation out of the box. SEO friendly.',
|
|
1094
|
+
badge: 'SEO',
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
icon: Palette,
|
|
1098
|
+
title: 'Beautiful Design',
|
|
1099
|
+
description: 'Neon emerald accents with dark-first design. Stunning out of the box.',
|
|
1100
|
+
badge: 'UI',
|
|
1101
|
+
},
|
|
1102
|
+
{
|
|
1103
|
+
icon: Shield,
|
|
1104
|
+
title: 'Type Safe',
|
|
1105
|
+
description: 'Full TypeScript support with strict type checking and excellent DX.',
|
|
1106
|
+
badge: 'TypeScript',
|
|
1107
|
+
},
|
|
1108
|
+
];
|
|
1109
|
+
|
|
1110
|
+
export function Features() {
|
|
1111
|
+
return (
|
|
1112
|
+
<section className="py-32 px-6">
|
|
1113
|
+
<div className="container mx-auto max-w-6xl">
|
|
1114
|
+
{/* Section Header */}
|
|
1115
|
+
<div className="text-center mb-20">
|
|
1116
|
+
<Badge variant="outline" className="mb-6">Features</Badge>
|
|
1117
|
+
<h2 className="text-4xl md:text-5xl font-bold tracking-tight mb-6">
|
|
1118
|
+
Everything you need
|
|
1119
|
+
</h2>
|
|
1120
|
+
<p className="text-xl text-[#94a3b8] max-w-2xl mx-auto">
|
|
1121
|
+
A complete toolkit for building modern web applications with React and Flexi UI.
|
|
1122
|
+
</p>
|
|
1123
|
+
</div>
|
|
1124
|
+
|
|
1125
|
+
{/* Feature Grid */}
|
|
1126
|
+
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
1127
|
+
{features.map((feature, index) => (
|
|
1128
|
+
<Card
|
|
1129
|
+
key={index}
|
|
1130
|
+
className="group p-8 transition-all duration-300 hover:border-[#00FF9C]/50 hover:shadow-[0_0_30px_rgba(0,255,156,0.1)] cursor-default animate-fade-up"
|
|
1131
|
+
style={{ animationDelay: \`\${index * 0.1}s\` }}
|
|
1132
|
+
>
|
|
1133
|
+
<div className="flex items-start gap-4">
|
|
1134
|
+
<div className="p-3 rounded-xl bg-[#00FF9C]/10 text-[#00FF9C] group-hover:bg-[#00FF9C] group-hover:text-black transition-all duration-300">
|
|
1135
|
+
<feature.icon className="w-6 h-6" />
|
|
1136
|
+
</div>
|
|
1137
|
+
<div className="flex-1">
|
|
1138
|
+
<div className="flex items-center gap-2 mb-2">
|
|
1139
|
+
<h3 className="text-lg font-semibold">{feature.title}</h3>
|
|
1140
|
+
</div>
|
|
1141
|
+
<p className="text-[#94a3b8] text-sm leading-relaxed">
|
|
1142
|
+
{feature.description}
|
|
1143
|
+
</p>
|
|
1144
|
+
</div>
|
|
1145
|
+
</div>
|
|
1146
|
+
</Card>
|
|
1147
|
+
))}
|
|
1148
|
+
</div>
|
|
1149
|
+
</div>
|
|
1150
|
+
</section>
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1153
|
+
`,
|
|
1154
|
+
|
|
1155
|
+
'components/Showcase.tsx': () => `import React from 'react';
|
|
1156
|
+
import {
|
|
1157
|
+
Button,
|
|
1158
|
+
Card,
|
|
1159
|
+
Badge,
|
|
1160
|
+
Input,
|
|
1161
|
+
Checkbox,
|
|
1162
|
+
Switch,
|
|
1163
|
+
Progress,
|
|
1164
|
+
Spinner,
|
|
1165
|
+
Avatar,
|
|
1166
|
+
Separator
|
|
1167
|
+
} from '@flexireact/flexi-ui';
|
|
1168
|
+
import { Mail, Lock, User, Search, Heart, Star, Check } from 'lucide-react';
|
|
1169
|
+
|
|
1170
|
+
export function Showcase() {
|
|
1171
|
+
return (
|
|
1172
|
+
<section className="py-32 px-6 relative">
|
|
1173
|
+
{/* Background Gradient */}
|
|
1174
|
+
<div className="absolute inset-0 -z-10">
|
|
1175
|
+
<div className="absolute top-1/2 left-1/4 w-[500px] h-[500px] rounded-full bg-gradient-radial from-[#00FF9C]/10 to-transparent blur-3xl" />
|
|
1176
|
+
</div>
|
|
1177
|
+
|
|
1178
|
+
<div className="container mx-auto max-w-6xl">
|
|
1179
|
+
{/* Section Header */}
|
|
1180
|
+
<div className="text-center mb-20">
|
|
1181
|
+
<Badge variant="success" className="mb-6">Components</Badge>
|
|
1182
|
+
<h2 className="text-4xl md:text-5xl font-bold tracking-tight mb-6">
|
|
1183
|
+
Beautiful by default
|
|
1184
|
+
</h2>
|
|
1185
|
+
<p className="text-xl text-[#94a3b8] max-w-2xl mx-auto">
|
|
1186
|
+
23+ components designed with attention to detail. Dark mode first, accessible, and customizable.
|
|
1187
|
+
</p>
|
|
1188
|
+
</div>
|
|
1189
|
+
|
|
1190
|
+
{/* Component Showcase Grid */}
|
|
1191
|
+
<div className="grid lg:grid-cols-2 gap-8">
|
|
1192
|
+
{/* Buttons Card */}
|
|
1193
|
+
<Card className="p-8">
|
|
1194
|
+
<h3 className="text-lg font-semibold mb-6 flex items-center gap-2">
|
|
1195
|
+
<span className="w-2 h-2 rounded-full bg-[#00FF9C]" />
|
|
1196
|
+
Buttons
|
|
1197
|
+
</h3>
|
|
1198
|
+
<div className="flex flex-wrap gap-3">
|
|
1199
|
+
<Button>Primary</Button>
|
|
1200
|
+
<Button variant="secondary">Secondary</Button>
|
|
1201
|
+
<Button variant="outline">Outline</Button>
|
|
1202
|
+
<Button variant="ghost">Ghost</Button>
|
|
1203
|
+
<Button variant="danger">Danger</Button>
|
|
1204
|
+
<Button variant="link">Link</Button>
|
|
1205
|
+
</div>
|
|
1206
|
+
<Separator className="my-6" />
|
|
1207
|
+
<div className="flex flex-wrap gap-3">
|
|
1208
|
+
<Button size="sm">Small</Button>
|
|
1209
|
+
<Button size="md">Medium</Button>
|
|
1210
|
+
<Button size="lg">Large</Button>
|
|
1211
|
+
</div>
|
|
1212
|
+
<Separator className="my-6" />
|
|
1213
|
+
<div className="flex flex-wrap gap-3">
|
|
1214
|
+
<Button loading>Loading</Button>
|
|
1215
|
+
<Button disabled>Disabled</Button>
|
|
1216
|
+
<Button className="gap-2">
|
|
1217
|
+
<Heart className="w-4 h-4" /> With Icon
|
|
1218
|
+
</Button>
|
|
1219
|
+
</div>
|
|
1220
|
+
</Card>
|
|
1221
|
+
|
|
1222
|
+
{/* Inputs Card */}
|
|
1223
|
+
<Card className="p-8">
|
|
1224
|
+
<h3 className="text-lg font-semibold mb-6 flex items-center gap-2">
|
|
1225
|
+
<span className="w-2 h-2 rounded-full bg-[#00FF9C]" />
|
|
1226
|
+
Inputs
|
|
1227
|
+
</h3>
|
|
1228
|
+
<div className="space-y-4">
|
|
1229
|
+
<Input
|
|
1230
|
+
label="Email"
|
|
1231
|
+
placeholder="you@example.com"
|
|
1232
|
+
type="email"
|
|
1233
|
+
/>
|
|
1234
|
+
<Input
|
|
1235
|
+
label="Password"
|
|
1236
|
+
placeholder="Enter password"
|
|
1237
|
+
type="password"
|
|
1238
|
+
/>
|
|
1239
|
+
<Input
|
|
1240
|
+
label="Search"
|
|
1241
|
+
placeholder="Search..."
|
|
1242
|
+
/>
|
|
1243
|
+
<Input
|
|
1244
|
+
label="With Error"
|
|
1245
|
+
placeholder="Invalid input"
|
|
1246
|
+
error
|
|
1247
|
+
helperText="This field is required"
|
|
1248
|
+
/>
|
|
1249
|
+
</div>
|
|
1250
|
+
</Card>
|
|
1251
|
+
|
|
1252
|
+
{/* Badges Card */}
|
|
1253
|
+
<Card className="p-8">
|
|
1254
|
+
<h3 className="text-lg font-semibold mb-6 flex items-center gap-2">
|
|
1255
|
+
<span className="w-2 h-2 rounded-full bg-[#00FF9C]" />
|
|
1256
|
+
Badges & Status
|
|
1257
|
+
</h3>
|
|
1258
|
+
<div className="flex flex-wrap gap-3 mb-6">
|
|
1259
|
+
<Badge>Default</Badge>
|
|
1260
|
+
<Badge variant="success">Success</Badge>
|
|
1261
|
+
<Badge variant="warning">Warning</Badge>
|
|
1262
|
+
<Badge variant="danger">Danger</Badge>
|
|
1263
|
+
<Badge variant="outline">Outline</Badge>
|
|
1264
|
+
</div>
|
|
1265
|
+
<Separator className="my-6" />
|
|
1266
|
+
<h4 className="text-sm font-medium mb-4 text-[#94a3b8]">Progress</h4>
|
|
1267
|
+
<div className="space-y-4">
|
|
1268
|
+
<Progress value={25} />
|
|
1269
|
+
<Progress value={50} />
|
|
1270
|
+
<Progress value={75} />
|
|
1271
|
+
<Progress value={100} />
|
|
1272
|
+
</div>
|
|
1273
|
+
</Card>
|
|
1274
|
+
|
|
1275
|
+
{/* Form Controls Card */}
|
|
1276
|
+
<Card className="p-8">
|
|
1277
|
+
<h3 className="text-lg font-semibold mb-6 flex items-center gap-2">
|
|
1278
|
+
<span className="w-2 h-2 rounded-full bg-[#00FF9C]" />
|
|
1279
|
+
Form Controls
|
|
1280
|
+
</h3>
|
|
1281
|
+
<div className="space-y-6">
|
|
1282
|
+
<div className="flex items-center gap-4">
|
|
1283
|
+
<Checkbox id="terms" />
|
|
1284
|
+
<label htmlFor="terms" className="text-sm">Accept terms and conditions</label>
|
|
1285
|
+
</div>
|
|
1286
|
+
<div className="flex items-center gap-4">
|
|
1287
|
+
<Checkbox id="newsletter" defaultChecked />
|
|
1288
|
+
<label htmlFor="newsletter" className="text-sm">Subscribe to newsletter</label>
|
|
1289
|
+
</div>
|
|
1290
|
+
<Separator />
|
|
1291
|
+
<div className="flex items-center justify-between">
|
|
1292
|
+
<span className="text-sm">Dark Mode</span>
|
|
1293
|
+
<Switch defaultChecked />
|
|
1294
|
+
</div>
|
|
1295
|
+
<div className="flex items-center justify-between">
|
|
1296
|
+
<span className="text-sm">Notifications</span>
|
|
1297
|
+
<Switch />
|
|
1298
|
+
</div>
|
|
1299
|
+
<Separator />
|
|
1300
|
+
<div className="flex items-center gap-4">
|
|
1301
|
+
<Spinner size="sm" />
|
|
1302
|
+
<Spinner />
|
|
1303
|
+
<Spinner size="lg" />
|
|
1304
|
+
</div>
|
|
1305
|
+
</div>
|
|
1306
|
+
</Card>
|
|
1307
|
+
</div>
|
|
1308
|
+
|
|
1309
|
+
{/* Avatars Row */}
|
|
1310
|
+
<Card className="p-8 mt-8">
|
|
1311
|
+
<h3 className="text-lg font-semibold mb-6 flex items-center gap-2">
|
|
1312
|
+
<span className="w-2 h-2 rounded-full bg-[#00FF9C]" />
|
|
1313
|
+
Avatars
|
|
1314
|
+
</h3>
|
|
1315
|
+
<div className="flex items-center gap-4 flex-wrap">
|
|
1316
|
+
<Avatar size="sm" fallback="JD" />
|
|
1317
|
+
<Avatar fallback="AB" />
|
|
1318
|
+
<Avatar size="lg" fallback="CD" />
|
|
1319
|
+
<Avatar size="xl" fallback="EF" />
|
|
1320
|
+
<Separator orientation="vertical" className="h-12 mx-4" />
|
|
1321
|
+
<div className="flex -space-x-3">
|
|
1322
|
+
<Avatar fallback="A" className="ring-2 ring-[#0a0a0a]" />
|
|
1323
|
+
<Avatar fallback="B" className="ring-2 ring-[#0a0a0a]" />
|
|
1324
|
+
<Avatar fallback="C" className="ring-2 ring-[#0a0a0a]" />
|
|
1325
|
+
<Avatar fallback="+5" className="ring-2 ring-[#0a0a0a]" />
|
|
1326
|
+
</div>
|
|
1327
|
+
</div>
|
|
1328
|
+
</Card>
|
|
1329
|
+
</div>
|
|
1330
|
+
</section>
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1333
|
+
`,
|
|
1334
|
+
|
|
1335
|
+
'components/Footer.tsx': () => `import React from 'react';
|
|
1336
|
+
import { Separator } from '@flexireact/flexi-ui';
|
|
1337
|
+
import { Github, Twitter, Heart } from 'lucide-react';
|
|
1338
|
+
|
|
1339
|
+
export function Footer() {
|
|
1340
|
+
return (
|
|
1341
|
+
<footer className="border-t border-[#1e293b]">
|
|
1342
|
+
<div className="container mx-auto max-w-6xl px-6 py-12">
|
|
1343
|
+
<div className="flex flex-col md:flex-row items-center justify-between gap-6">
|
|
1344
|
+
{/* Logo & Copyright */}
|
|
1345
|
+
<div className="flex items-center gap-3">
|
|
1346
|
+
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-[#00FF9C] to-emerald-400 flex items-center justify-center">
|
|
430
1347
|
<span className="text-black font-bold text-sm">F</span>
|
|
431
1348
|
</div>
|
|
432
|
-
<span className="
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
1349
|
+
<span className="text-[#94a3b8] text-sm">
|
|
1350
|
+
Built with <Heart className="w-4 h-4 inline text-red-500 mx-1" /> using{' '}
|
|
1351
|
+
<a href="https://github.com/flexireact/flexi-ui" className="text-[#00FF9C] hover:underline">
|
|
1352
|
+
Flexi UI
|
|
1353
|
+
</a>
|
|
1354
|
+
</span>
|
|
437
1355
|
</div>
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
1356
|
+
|
|
1357
|
+
{/* Links */}
|
|
1358
|
+
<div className="flex items-center gap-6">
|
|
1359
|
+
<a
|
|
1360
|
+
href="https://github.com/flexireact/flexi-ui"
|
|
1361
|
+
className="text-[#94a3b8] hover:text-white transition-colors flex items-center gap-2 text-sm"
|
|
1362
|
+
>
|
|
1363
|
+
<Github className="w-4 h-4" />
|
|
1364
|
+
GitHub
|
|
1365
|
+
</a>
|
|
1366
|
+
<a
|
|
1367
|
+
href="https://github.com/flexireact/flexireact"
|
|
1368
|
+
className="text-[#94a3b8] hover:text-white transition-colors text-sm"
|
|
1369
|
+
>
|
|
1370
|
+
Documentation
|
|
1371
|
+
</a>
|
|
1372
|
+
<a
|
|
1373
|
+
href="https://github.com/flexireact/flexi-ui"
|
|
1374
|
+
className="text-[#94a3b8] hover:text-white transition-colors text-sm"
|
|
1375
|
+
>
|
|
1376
|
+
Components
|
|
1377
|
+
</a>
|
|
1378
|
+
</div>
|
|
1379
|
+
</div>
|
|
1380
|
+
</div>
|
|
1381
|
+
</footer>
|
|
447
1382
|
);
|
|
448
1383
|
}
|
|
449
|
-
|
|
1384
|
+
`,
|
|
1385
|
+
|
|
1386
|
+
'components/Navbar.tsx': () => `import React from 'react';
|
|
1387
|
+
import { Button, Badge } from '@flexireact/flexi-ui';
|
|
1388
|
+
import { Github, Menu } from 'lucide-react';
|
|
1389
|
+
|
|
1390
|
+
export function Navbar() {
|
|
1391
|
+
return (
|
|
1392
|
+
<header className="fixed top-0 left-0 right-0 z-50 border-b border-[#1e293b]/50 bg-[#0a0a0a]/80 backdrop-blur-xl">
|
|
1393
|
+
<nav className="container mx-auto max-w-6xl px-6 h-16 flex items-center justify-between">
|
|
1394
|
+
{/* Logo */}
|
|
1395
|
+
<a href="/" className="flex items-center gap-3 group">
|
|
1396
|
+
<div className="w-9 h-9 rounded-xl bg-gradient-to-br from-[#00FF9C] to-emerald-400 flex items-center justify-center shadow-lg shadow-[#00FF9C]/20 group-hover:shadow-[#00FF9C]/40 transition-shadow">
|
|
1397
|
+
<span className="text-black font-black text-lg">F</span>
|
|
1398
|
+
</div>
|
|
1399
|
+
<span className="text-lg font-bold">Flexi UI</span>
|
|
1400
|
+
<Badge variant="outline" className="hidden sm:flex text-xs">v1.0</Badge>
|
|
1401
|
+
</a>
|
|
1402
|
+
|
|
1403
|
+
{/* Desktop Nav */}
|
|
1404
|
+
<div className="hidden md:flex items-center gap-2">
|
|
1405
|
+
<Button variant="ghost" size="sm">
|
|
1406
|
+
<a href="https://github.com/flexireact/flexi-ui">Docs</a>
|
|
1407
|
+
</Button>
|
|
1408
|
+
<Button variant="ghost" size="sm">
|
|
1409
|
+
<a href="https://github.com/flexireact/flexi-ui">Components</a>
|
|
1410
|
+
</Button>
|
|
1411
|
+
<Button variant="ghost" size="sm">
|
|
1412
|
+
<a href="https://github.com/flexireact/flexireact">FlexiReact</a>
|
|
1413
|
+
</Button>
|
|
1414
|
+
<div className="w-px h-6 bg-[#1e293b] mx-2" />
|
|
1415
|
+
<Button variant="outline" size="sm" className="gap-2">
|
|
1416
|
+
<Github className="w-4 h-4" />
|
|
1417
|
+
<a href="https://github.com/flexireact/flexi-ui">GitHub</a>
|
|
1418
|
+
</Button>
|
|
1419
|
+
</div>
|
|
1420
|
+
|
|
1421
|
+
{/* Mobile Menu Button */}
|
|
1422
|
+
<Button variant="ghost" size="sm" className="md:hidden">
|
|
1423
|
+
<Menu className="w-5 h-5" />
|
|
1424
|
+
</Button>
|
|
1425
|
+
</nav>
|
|
1426
|
+
</header>
|
|
1427
|
+
);
|
|
1428
|
+
}
|
|
1429
|
+
`,
|
|
1430
|
+
|
|
1431
|
+
'components/index.ts': () => `export { Hero } from './Hero';
|
|
1432
|
+
export { Features } from './Features';
|
|
1433
|
+
export { Showcase } from './Showcase';
|
|
1434
|
+
export { Footer } from './Footer';
|
|
1435
|
+
export { Navbar } from './Navbar';
|
|
1436
|
+
`,
|
|
1437
|
+
|
|
1438
|
+
// ============================================================================
|
|
1439
|
+
// Pages & Layouts
|
|
1440
|
+
// ============================================================================
|
|
1441
|
+
|
|
1442
|
+
'layouts/root.tsx': () => `import React from 'react';
|
|
1443
|
+
import { ThemeProvider } from '@flexireact/flexi-ui';
|
|
1444
|
+
import { Navbar } from '../components/Navbar';
|
|
1445
|
+
import { Footer } from '../components/Footer';
|
|
450
1446
|
|
|
451
1447
|
interface LayoutProps {
|
|
452
1448
|
children: React.ReactNode;
|
|
@@ -454,72 +1450,163 @@ interface LayoutProps {
|
|
|
454
1450
|
|
|
455
1451
|
export default function RootLayout({ children }: LayoutProps) {
|
|
456
1452
|
return (
|
|
457
|
-
<
|
|
458
|
-
<
|
|
459
|
-
<
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
1453
|
+
<ThemeProvider defaultTheme="dark">
|
|
1454
|
+
<div className="min-h-screen bg-[#0a0a0a] text-white antialiased">
|
|
1455
|
+
<Navbar />
|
|
1456
|
+
<main className="pt-16">{children}</main>
|
|
1457
|
+
<Footer />
|
|
1458
|
+
</div>
|
|
1459
|
+
</ThemeProvider>
|
|
1460
|
+
);
|
|
1461
|
+
}
|
|
1462
|
+
`,
|
|
1463
|
+
|
|
1464
|
+
'pages/index.tsx': () => `import React from 'react';
|
|
1465
|
+
import { Hero } from '../components/Hero';
|
|
1466
|
+
import { Features } from '../components/Features';
|
|
1467
|
+
import { Showcase } from '../components/Showcase';
|
|
1468
|
+
|
|
1469
|
+
export default function HomePage() {
|
|
1470
|
+
return (
|
|
1471
|
+
<>
|
|
1472
|
+
<Hero />
|
|
1473
|
+
<Features />
|
|
1474
|
+
<Showcase />
|
|
1475
|
+
</>
|
|
479
1476
|
);
|
|
480
1477
|
}
|
|
481
1478
|
`,
|
|
482
1479
|
|
|
483
|
-
|
|
1480
|
+
// ============================================================================
|
|
1481
|
+
// Styles
|
|
1482
|
+
// ============================================================================
|
|
1483
|
+
|
|
1484
|
+
'app/styles/globals.css': () => `@tailwind base;
|
|
484
1485
|
@tailwind components;
|
|
485
1486
|
@tailwind utilities;
|
|
486
1487
|
|
|
487
1488
|
@layer base {
|
|
488
1489
|
:root {
|
|
489
|
-
--flexi-bg: #
|
|
490
|
-
--flexi-fg: #
|
|
491
|
-
--flexi-
|
|
492
|
-
--flexi-
|
|
493
|
-
--flexi-
|
|
494
|
-
--flexi-
|
|
495
|
-
--flexi-
|
|
1490
|
+
--flexi-bg: #0a0a0a;
|
|
1491
|
+
--flexi-fg: #fafafa;
|
|
1492
|
+
--flexi-primary: #00FF9C;
|
|
1493
|
+
--flexi-primary-fg: #000000;
|
|
1494
|
+
--flexi-muted: #94a3b8;
|
|
1495
|
+
--flexi-border: #1e293b;
|
|
1496
|
+
--flexi-card: #0f0f0f;
|
|
496
1497
|
}
|
|
497
|
-
|
|
1498
|
+
|
|
1499
|
+
* {
|
|
1500
|
+
border-color: var(--flexi-border);
|
|
1501
|
+
}
|
|
1502
|
+
|
|
498
1503
|
html {
|
|
499
1504
|
scroll-behavior: smooth;
|
|
500
1505
|
}
|
|
501
|
-
|
|
1506
|
+
|
|
502
1507
|
body {
|
|
503
|
-
font-family: 'Inter', system-ui, sans-serif;
|
|
504
|
-
background-color:
|
|
505
|
-
color:
|
|
1508
|
+
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
|
1509
|
+
background-color: var(--flexi-bg);
|
|
1510
|
+
color: var(--flexi-fg);
|
|
506
1511
|
min-height: 100vh;
|
|
1512
|
+
-webkit-font-smoothing: antialiased;
|
|
1513
|
+
-moz-osx-font-smoothing: grayscale;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
::selection {
|
|
1517
|
+
background-color: rgba(0, 255, 156, 0.3);
|
|
1518
|
+
color: white;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
@layer utilities {
|
|
1523
|
+
.bg-gradient-radial {
|
|
1524
|
+
background: radial-gradient(circle, var(--tw-gradient-stops));
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
.text-balance {
|
|
1528
|
+
text-wrap: balance;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
/* Custom scrollbar */
|
|
1532
|
+
::-webkit-scrollbar {
|
|
1533
|
+
width: 8px;
|
|
1534
|
+
height: 8px;
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
::-webkit-scrollbar-track {
|
|
1538
|
+
background: #0a0a0a;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
::-webkit-scrollbar-thumb {
|
|
1542
|
+
background: #1e293b;
|
|
1543
|
+
border-radius: 4px;
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
::-webkit-scrollbar-thumb:hover {
|
|
1547
|
+
background: #334155;
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
/* Animation utilities */
|
|
1552
|
+
.animate-fade-in {
|
|
1553
|
+
animation: fadeIn 0.6s ease-out forwards;
|
|
1554
|
+
opacity: 0;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
.animate-fade-up {
|
|
1558
|
+
animation: fadeUp 0.6s ease-out forwards;
|
|
1559
|
+
opacity: 0;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
.animate-scale-in {
|
|
1563
|
+
animation: scaleIn 0.4s ease-out forwards;
|
|
1564
|
+
opacity: 0;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
@keyframes fadeIn {
|
|
1568
|
+
from { opacity: 0; }
|
|
1569
|
+
to { opacity: 1; }
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
@keyframes fadeUp {
|
|
1573
|
+
from {
|
|
1574
|
+
opacity: 0;
|
|
1575
|
+
transform: translateY(30px);
|
|
1576
|
+
}
|
|
1577
|
+
to {
|
|
1578
|
+
opacity: 1;
|
|
1579
|
+
transform: translateY(0);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
@keyframes scaleIn {
|
|
1584
|
+
from {
|
|
1585
|
+
opacity: 0;
|
|
1586
|
+
transform: scale(0.9);
|
|
1587
|
+
}
|
|
1588
|
+
to {
|
|
1589
|
+
opacity: 1;
|
|
1590
|
+
transform: scale(1);
|
|
507
1591
|
}
|
|
508
1592
|
}
|
|
509
1593
|
`,
|
|
510
1594
|
|
|
1595
|
+
// ============================================================================
|
|
1596
|
+
// Public Assets
|
|
1597
|
+
// ============================================================================
|
|
1598
|
+
|
|
511
1599
|
'public/.gitkeep': () => '',
|
|
512
1600
|
|
|
513
1601
|
'public/favicon.svg': () => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
514
1602
|
<defs>
|
|
515
1603
|
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
516
|
-
<stop offset="0%" style="stop-color:#
|
|
517
|
-
<stop offset="100%" style="stop-color:#
|
|
1604
|
+
<stop offset="0%" style="stop-color:#00FF9C"/>
|
|
1605
|
+
<stop offset="100%" style="stop-color:#10b981"/>
|
|
518
1606
|
</linearGradient>
|
|
519
1607
|
</defs>
|
|
520
|
-
<rect width="100" height="100" rx="20" fill="#
|
|
521
|
-
<
|
|
522
|
-
<circle cx="65" cy="65" r="8" fill="url(#grad)"/>
|
|
1608
|
+
<rect width="100" height="100" rx="20" fill="#0a0a0a"/>
|
|
1609
|
+
<text x="50" y="68" font-family="system-ui" font-size="50" font-weight="900" fill="url(#grad)" text-anchor="middle">F</text>
|
|
523
1610
|
</svg>`,
|
|
524
1611
|
};
|
|
525
1612
|
|
|
@@ -548,50 +1635,67 @@ async function main() {
|
|
|
548
1635
|
|
|
549
1636
|
// Check if directory exists
|
|
550
1637
|
if (fs.existsSync(projectPath) && !isDirEmpty(projectPath)) {
|
|
551
|
-
error(`Directory
|
|
1638
|
+
error(`Directory ${projectName} already exists and is not empty`);
|
|
552
1639
|
process.exit(1);
|
|
553
1640
|
}
|
|
554
1641
|
|
|
555
|
-
console.log('');
|
|
556
|
-
|
|
557
1642
|
// Select template
|
|
1643
|
+
console.log('');
|
|
558
1644
|
const templateOptions = Object.entries(TEMPLATES).map(([key, value]) => ({
|
|
559
1645
|
key,
|
|
560
1646
|
...value,
|
|
561
1647
|
}));
|
|
562
1648
|
|
|
563
1649
|
const selectedTemplate = await select('Select a template:', templateOptions);
|
|
564
|
-
const
|
|
1650
|
+
const templateKey = selectedTemplate.key;
|
|
565
1651
|
|
|
566
1652
|
console.log('');
|
|
567
|
-
|
|
1653
|
+
log(`Creating project in ${c.cyan}${projectPath}${c.reset}`);
|
|
568
1654
|
console.log('');
|
|
569
1655
|
|
|
570
1656
|
// Create project directory
|
|
571
1657
|
const spinner1 = new Spinner('Creating project structure...');
|
|
572
1658
|
spinner1.start();
|
|
573
|
-
|
|
1659
|
+
|
|
574
1660
|
try {
|
|
575
1661
|
fs.mkdirSync(projectPath, { recursive: true });
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
1662
|
+
|
|
1663
|
+
// Create subdirectories
|
|
1664
|
+
let dirs;
|
|
1665
|
+
if (templateKey === 'minimal') {
|
|
1666
|
+
dirs = ['pages', 'public'];
|
|
1667
|
+
} else if (templateKey === 'flexi-ui') {
|
|
1668
|
+
dirs = ['pages', 'public', 'components', 'layouts', 'app/styles'];
|
|
1669
|
+
} else {
|
|
1670
|
+
dirs = ['pages', 'public', 'components', 'components/ui', 'layouts', 'app/styles', 'lib'];
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
for (const dir of dirs) {
|
|
1674
|
+
fs.mkdirSync(path.join(projectPath, dir), { recursive: true });
|
|
1675
|
+
}
|
|
581
1676
|
|
|
582
1677
|
spinner1.stop(true);
|
|
583
1678
|
} catch (err) {
|
|
584
1679
|
spinner1.stop(false);
|
|
585
|
-
error(`Failed to create
|
|
1680
|
+
error(`Failed to create project structure: ${err.message}`);
|
|
586
1681
|
process.exit(1);
|
|
587
1682
|
}
|
|
588
1683
|
|
|
589
1684
|
// Write template files
|
|
590
1685
|
const spinner2 = new Spinner('Writing template files...');
|
|
591
1686
|
spinner2.start();
|
|
592
|
-
|
|
1687
|
+
|
|
593
1688
|
try {
|
|
594
|
-
|
|
1689
|
+
let files;
|
|
1690
|
+
if (templateKey === 'minimal') {
|
|
1691
|
+
files = MINIMAL_FILES;
|
|
1692
|
+
} else if (templateKey === 'flexi-ui') {
|
|
1693
|
+
files = FLEXI_UI_FILES;
|
|
1694
|
+
} else {
|
|
1695
|
+
files = TEMPLATE_FILES;
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
for (const [filePath, contentFn] of Object.entries(files)) {
|
|
595
1699
|
const fullPath = path.join(projectPath, filePath);
|
|
596
1700
|
const dir = path.dirname(fullPath);
|
|
597
1701
|
|
|
@@ -599,51 +1703,25 @@ async function main() {
|
|
|
599
1703
|
fs.mkdirSync(dir, { recursive: true });
|
|
600
1704
|
}
|
|
601
1705
|
|
|
602
|
-
const content =
|
|
1706
|
+
const content = contentFn(projectName, templateKey);
|
|
603
1707
|
fs.writeFileSync(fullPath, content);
|
|
604
1708
|
}
|
|
605
1709
|
|
|
606
1710
|
spinner2.stop(true);
|
|
607
1711
|
} catch (err) {
|
|
608
1712
|
spinner2.stop(false);
|
|
609
|
-
error(`Failed to write files: ${err.message}`);
|
|
1713
|
+
error(`Failed to write template files: ${err.message}`);
|
|
610
1714
|
process.exit(1);
|
|
611
1715
|
}
|
|
612
1716
|
|
|
613
|
-
// Create
|
|
1717
|
+
// Create README
|
|
614
1718
|
const spinner3 = new Spinner('Creating configuration files...');
|
|
615
1719
|
spinner3.start();
|
|
616
|
-
|
|
1720
|
+
|
|
617
1721
|
try {
|
|
618
|
-
|
|
619
|
-
node_modules/
|
|
620
|
-
|
|
621
|
-
# Build
|
|
622
|
-
.flexi/
|
|
623
|
-
dist/
|
|
624
|
-
build/
|
|
1722
|
+
const readmeContent = `# ${projectName}
|
|
625
1723
|
|
|
626
|
-
|
|
627
|
-
.env
|
|
628
|
-
.env.local
|
|
629
|
-
.env.*.local
|
|
630
|
-
|
|
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.
|
|
1724
|
+
A modern web application built with [FlexiReact](https://github.com/flexireact/flexireact).
|
|
647
1725
|
|
|
648
1726
|
## Getting Started
|
|
649
1727
|
|
|
@@ -657,9 +1735,9 @@ Open [http://localhost:3000](http://localhost:3000) in your browser.
|
|
|
657
1735
|
## Learn More
|
|
658
1736
|
|
|
659
1737
|
- [FlexiReact Documentation](https://github.com/flexireact/flexireact)
|
|
660
|
-
|
|
661
|
-
`);
|
|
1738
|
+
`;
|
|
662
1739
|
|
|
1740
|
+
fs.writeFileSync(path.join(projectPath, 'README.md'), readmeContent);
|
|
663
1741
|
spinner3.stop(true);
|
|
664
1742
|
} catch (err) {
|
|
665
1743
|
spinner3.stop(false);
|
|
@@ -667,7 +1745,7 @@ Open [http://localhost:3000](http://localhost:3000) in your browser.
|
|
|
667
1745
|
process.exit(1);
|
|
668
1746
|
}
|
|
669
1747
|
|
|
670
|
-
//
|
|
1748
|
+
// Success message
|
|
671
1749
|
console.log(SUCCESS_BANNER(projectName));
|
|
672
1750
|
}
|
|
673
1751
|
|