create-flexireact 1.3.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -15
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +175 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/app-router.d.ts +6 -0
- package/dist/templates/app-router.d.ts.map +1 -0
- package/dist/templates/app-router.js +211 -0
- package/dist/templates/app-router.js.map +1 -0
- package/dist/templates/default.d.ts +12 -0
- package/dist/templates/default.d.ts.map +1 -0
- package/dist/templates/default.js +558 -0
- package/dist/templates/default.js.map +1 -0
- package/dist/templates/index.d.ts +12 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +34 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/minimal.d.ts +6 -0
- package/dist/templates/minimal.d.ts.map +1 -0
- package/dist/templates/minimal.js +71 -0
- package/dist/templates/minimal.js.map +1 -0
- package/dist/ui.d.ts +18 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +47 -0
- package/dist/ui.js.map +1 -0
- package/package.json +15 -4
- package/index.js +0 -1653
package/index.js
DELETED
|
@@ -1,1653 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* create-flexireact
|
|
5
|
-
* Create FlexiReact apps with one command
|
|
6
|
-
*
|
|
7
|
-
* Usage: npx create-flexireact@latest my-app
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import fs from 'fs';
|
|
11
|
-
import path from 'path';
|
|
12
|
-
import { execSync, spawn } from 'child_process';
|
|
13
|
-
import readline from 'readline';
|
|
14
|
-
import { fileURLToPath } from 'url';
|
|
15
|
-
|
|
16
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
-
const __dirname = path.dirname(__filename);
|
|
18
|
-
|
|
19
|
-
// ============================================================================
|
|
20
|
-
// Colors & Styling
|
|
21
|
-
// ============================================================================
|
|
22
|
-
|
|
23
|
-
const c = {
|
|
24
|
-
reset: '\x1b[0m',
|
|
25
|
-
bold: '\x1b[1m',
|
|
26
|
-
dim: '\x1b[2m',
|
|
27
|
-
green: '\x1b[32m',
|
|
28
|
-
cyan: '\x1b[36m',
|
|
29
|
-
yellow: '\x1b[33m',
|
|
30
|
-
red: '\x1b[31m',
|
|
31
|
-
magenta: '\x1b[35m',
|
|
32
|
-
white: '\x1b[37m',
|
|
33
|
-
bgGreen: '\x1b[42m',
|
|
34
|
-
bgCyan: '\x1b[46m',
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// ============================================================================
|
|
38
|
-
// ASCII Art & Branding
|
|
39
|
-
// ============================================================================
|
|
40
|
-
|
|
41
|
-
const BANNER = `
|
|
42
|
-
${c.green} ╭─────────────────────────────────────────────────╮${c.reset}
|
|
43
|
-
${c.green} │${c.reset} ${c.green}│${c.reset}
|
|
44
|
-
${c.green} │${c.reset} ${c.green}⚡${c.reset} ${c.bold}${c.white}C R E A T E - F L E X I R E A C T${c.reset} ${c.green}│${c.reset}
|
|
45
|
-
${c.green} │${c.reset} ${c.green}│${c.reset}
|
|
46
|
-
${c.green} │${c.reset} ${c.dim}The Modern React Framework${c.reset} ${c.green}│${c.reset}
|
|
47
|
-
${c.green} │${c.reset} ${c.dim}TypeScript • Tailwind • SSR • Islands${c.reset} ${c.green}│${c.reset}
|
|
48
|
-
${c.green} │${c.reset} ${c.green}│${c.reset}
|
|
49
|
-
${c.green} ╰─────────────────────────────────────────────────╯${c.reset}
|
|
50
|
-
`;
|
|
51
|
-
|
|
52
|
-
const SUCCESS_BANNER = (projectName) => `
|
|
53
|
-
${c.green} ╭─────────────────────────────────────────────────╮${c.reset}
|
|
54
|
-
${c.green} │${c.reset} ${c.green}│${c.reset}
|
|
55
|
-
${c.green} │${c.reset} ${c.green}✓${c.reset} ${c.bold}Project created successfully!${c.reset} ${c.green}│${c.reset}
|
|
56
|
-
${c.green} │${c.reset} ${c.green}│${c.reset}
|
|
57
|
-
${c.green} ╰─────────────────────────────────────────────────╯${c.reset}
|
|
58
|
-
|
|
59
|
-
${c.dim}Next steps:${c.reset}
|
|
60
|
-
|
|
61
|
-
${c.cyan}cd${c.reset} ${projectName}
|
|
62
|
-
${c.cyan}npm${c.reset} install
|
|
63
|
-
${c.cyan}npm${c.reset} run dev
|
|
64
|
-
|
|
65
|
-
${c.dim}Then open${c.reset} ${c.cyan}http://localhost:3000${c.reset}
|
|
66
|
-
|
|
67
|
-
${c.dim}Documentation:${c.reset} ${c.cyan}https://github.com/flexireact/flexireact${c.reset}
|
|
68
|
-
`;
|
|
69
|
-
|
|
70
|
-
// ============================================================================
|
|
71
|
-
// Templates
|
|
72
|
-
// ============================================================================
|
|
73
|
-
|
|
74
|
-
const TEMPLATES = {
|
|
75
|
-
default: {
|
|
76
|
-
name: 'Default',
|
|
77
|
-
description: 'Premium template with modern UI, animations & dark mode',
|
|
78
|
-
},
|
|
79
|
-
'flexi-ui': {
|
|
80
|
-
name: 'Flexi UI',
|
|
81
|
-
description: 'Showcase template with @flexireact/flexi-ui components',
|
|
82
|
-
},
|
|
83
|
-
minimal: {
|
|
84
|
-
name: 'Minimal',
|
|
85
|
-
description: 'Bare minimum FlexiReact setup',
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
// ============================================================================
|
|
90
|
-
// Utilities
|
|
91
|
-
// ============================================================================
|
|
92
|
-
|
|
93
|
-
function log(msg) {
|
|
94
|
-
console.log(` ${msg}`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function success(msg) {
|
|
98
|
-
console.log(` ${c.green}✓${c.reset} ${msg}`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function error(msg) {
|
|
102
|
-
console.log(` ${c.red}✗${c.reset} ${c.red}${msg}${c.reset}`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function info(msg) {
|
|
106
|
-
console.log(` ${c.cyan}ℹ${c.reset} ${msg}`);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Spinner
|
|
110
|
-
class Spinner {
|
|
111
|
-
constructor(message) {
|
|
112
|
-
this.message = message;
|
|
113
|
-
this.frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
114
|
-
this.current = 0;
|
|
115
|
-
this.interval = null;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
start() {
|
|
119
|
-
process.stdout.write(` ${this.frames[0]} ${this.message}`);
|
|
120
|
-
this.interval = setInterval(() => {
|
|
121
|
-
this.current = (this.current + 1) % this.frames.length;
|
|
122
|
-
process.stdout.clearLine(0);
|
|
123
|
-
process.stdout.cursorTo(0);
|
|
124
|
-
process.stdout.write(` ${c.cyan}${this.frames[this.current]}${c.reset} ${this.message}`);
|
|
125
|
-
}, 80);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
stop(success = true) {
|
|
129
|
-
clearInterval(this.interval);
|
|
130
|
-
process.stdout.clearLine(0);
|
|
131
|
-
process.stdout.cursorTo(0);
|
|
132
|
-
const icon = success ? `${c.green}✓${c.reset}` : `${c.red}✗${c.reset}`;
|
|
133
|
-
console.log(` ${icon} ${this.message}`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Prompt
|
|
138
|
-
async function prompt(question, defaultValue = '') {
|
|
139
|
-
const rl = readline.createInterface({
|
|
140
|
-
input: process.stdin,
|
|
141
|
-
output: process.stdout,
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
return new Promise((resolve) => {
|
|
145
|
-
const defaultStr = defaultValue ? ` ${c.dim}(${defaultValue})${c.reset}` : '';
|
|
146
|
-
rl.question(` ${c.cyan}?${c.reset} ${question}${defaultStr}: `, (answer) => {
|
|
147
|
-
rl.close();
|
|
148
|
-
resolve(answer.trim() || defaultValue);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Select
|
|
154
|
-
async function select(question, options) {
|
|
155
|
-
console.log(` ${c.cyan}?${c.reset} ${question}`);
|
|
156
|
-
console.log('');
|
|
157
|
-
|
|
158
|
-
options.forEach((opt, i) => {
|
|
159
|
-
console.log(` ${c.cyan}${i + 1}.${c.reset} ${c.bold}${opt.name}${c.reset}`);
|
|
160
|
-
console.log(` ${c.dim}${opt.description}${c.reset}`);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
console.log('');
|
|
164
|
-
|
|
165
|
-
const rl = readline.createInterface({
|
|
166
|
-
input: process.stdin,
|
|
167
|
-
output: process.stdout,
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
return new Promise((resolve) => {
|
|
171
|
-
rl.question(` ${c.dim}Enter number (1-${options.length}):${c.reset} `, (answer) => {
|
|
172
|
-
rl.close();
|
|
173
|
-
const index = parseInt(answer) - 1;
|
|
174
|
-
if (index >= 0 && index < options.length) {
|
|
175
|
-
resolve(options[index]);
|
|
176
|
-
} else {
|
|
177
|
-
resolve(options[0]);
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Check if directory is empty
|
|
184
|
-
function isDirEmpty(dir) {
|
|
185
|
-
if (!fs.existsSync(dir)) return true;
|
|
186
|
-
return fs.readdirSync(dir).length === 0;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// ============================================================================
|
|
190
|
-
// Template Files
|
|
191
|
-
// ============================================================================
|
|
192
|
-
|
|
193
|
-
const TEMPLATE_FILES = {
|
|
194
|
-
'package.json': (name, template) => JSON.stringify({
|
|
195
|
-
name: name,
|
|
196
|
-
version: "1.0.0",
|
|
197
|
-
private: true,
|
|
198
|
-
type: "module",
|
|
199
|
-
scripts: {
|
|
200
|
-
dev: "npm run css && flexireact dev",
|
|
201
|
-
build: "npm run css && flexireact build",
|
|
202
|
-
start: "flexireact start",
|
|
203
|
-
css: "npx tailwindcss -i ./app/styles/globals.css -o ./public/styles.css --minify"
|
|
204
|
-
},
|
|
205
|
-
dependencies: {
|
|
206
|
-
"react": "^18.2.0",
|
|
207
|
-
"react-dom": "^18.2.0",
|
|
208
|
-
"@flexireact/core": "^1.0.0",
|
|
209
|
-
"framer-motion": "^11.0.0",
|
|
210
|
-
"lucide-react": "^0.400.0",
|
|
211
|
-
"clsx": "^2.1.0",
|
|
212
|
-
"tailwind-merge": "^2.2.0"
|
|
213
|
-
},
|
|
214
|
-
devDependencies: {
|
|
215
|
-
"@types/react": "^18.2.0",
|
|
216
|
-
"@types/react-dom": "^18.2.0",
|
|
217
|
-
"typescript": "^5.3.0",
|
|
218
|
-
"tailwindcss": "^3.4.0",
|
|
219
|
-
"postcss": "^8.4.32",
|
|
220
|
-
"autoprefixer": "^10.4.16"
|
|
221
|
-
}
|
|
222
|
-
}, null, 2),
|
|
223
|
-
|
|
224
|
-
'tsconfig.json': () => JSON.stringify({
|
|
225
|
-
compilerOptions: {
|
|
226
|
-
target: "ES2020",
|
|
227
|
-
lib: ["DOM", "DOM.Iterable", "ES2020"],
|
|
228
|
-
module: "ESNext",
|
|
229
|
-
moduleResolution: "bundler",
|
|
230
|
-
jsx: "react-jsx",
|
|
231
|
-
strict: true,
|
|
232
|
-
skipLibCheck: true,
|
|
233
|
-
esModuleInterop: true,
|
|
234
|
-
allowSyntheticDefaultImports: true,
|
|
235
|
-
resolveJsonModule: true,
|
|
236
|
-
isolatedModules: true,
|
|
237
|
-
noEmit: true,
|
|
238
|
-
baseUrl: ".",
|
|
239
|
-
paths: {
|
|
240
|
-
"@/*": ["./*"]
|
|
241
|
-
}
|
|
242
|
-
},
|
|
243
|
-
include: ["**/*.ts", "**/*.tsx"],
|
|
244
|
-
exclude: ["node_modules", ".flexi"]
|
|
245
|
-
}, null, 2),
|
|
246
|
-
|
|
247
|
-
'tailwind.config.js': () => `/** @type {import('tailwindcss').Config} */
|
|
248
|
-
module.exports = {
|
|
249
|
-
darkMode: 'class',
|
|
250
|
-
content: [
|
|
251
|
-
'./app/**/*.{js,ts,jsx,tsx}',
|
|
252
|
-
'./pages/**/*.{js,ts,jsx,tsx}',
|
|
253
|
-
'./components/**/*.{js,ts,jsx,tsx}',
|
|
254
|
-
'./layouts/**/*.{js,ts,jsx,tsx}',
|
|
255
|
-
],
|
|
256
|
-
theme: {
|
|
257
|
-
extend: {
|
|
258
|
-
fontFamily: {
|
|
259
|
-
sans: ['Inter', 'system-ui', 'sans-serif'],
|
|
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
|
-
},
|
|
317
|
-
},
|
|
318
|
-
},
|
|
319
|
-
plugins: [],
|
|
320
|
-
};
|
|
321
|
-
`,
|
|
322
|
-
|
|
323
|
-
'postcss.config.js': () => `module.exports = {
|
|
324
|
-
plugins: {
|
|
325
|
-
tailwindcss: {},
|
|
326
|
-
autoprefixer: {},
|
|
327
|
-
},
|
|
328
|
-
};
|
|
329
|
-
`,
|
|
330
|
-
|
|
331
|
-
'flexireact.config.js': () => `/** @type {import('@flexireact/core').Config} */
|
|
332
|
-
export default {
|
|
333
|
-
styles: [
|
|
334
|
-
'/styles.css',
|
|
335
|
-
'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap'
|
|
336
|
-
],
|
|
337
|
-
favicon: '/favicon.svg',
|
|
338
|
-
server: {
|
|
339
|
-
port: 3000
|
|
340
|
-
},
|
|
341
|
-
islands: {
|
|
342
|
-
enabled: true
|
|
343
|
-
}
|
|
344
|
-
};
|
|
345
|
-
`,
|
|
346
|
-
|
|
347
|
-
// ============================================================================
|
|
348
|
-
// Components
|
|
349
|
-
// ============================================================================
|
|
350
|
-
|
|
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
|
-
const baseStyles = 'inline-flex items-center justify-center rounded-lg ' +
|
|
361
|
-
'font-medium transition-all duration-200 focus-visible:outline-none ' +
|
|
362
|
-
'focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50';
|
|
363
|
-
|
|
364
|
-
const variants = {
|
|
365
|
-
default: 'bg-primary text-black hover:bg-primary/90',
|
|
366
|
-
outline: 'border border-border bg-transparent hover:bg-secondary',
|
|
367
|
-
ghost: 'hover:bg-secondary hover:text-foreground',
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
const sizes = {
|
|
371
|
-
default: 'h-10 px-4 py-2 text-sm',
|
|
372
|
-
sm: 'h-9 px-3 text-sm',
|
|
373
|
-
lg: 'h-12 px-8 text-base',
|
|
374
|
-
};
|
|
375
|
-
|
|
376
|
-
export function Button({
|
|
377
|
-
className,
|
|
378
|
-
variant = 'default',
|
|
379
|
-
size = 'default',
|
|
380
|
-
children,
|
|
381
|
-
...props
|
|
382
|
-
}: ButtonProps) {
|
|
383
|
-
return (
|
|
384
|
-
<button
|
|
385
|
-
className={cn(baseStyles, variants[variant], sizes[size], className)}
|
|
386
|
-
{...props}
|
|
387
|
-
>
|
|
388
|
-
{children}
|
|
389
|
-
</button>
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
|
-
`,
|
|
393
|
-
|
|
394
|
-
'components/ui/card.tsx': () => `import React from 'react';
|
|
395
|
-
import { cn } from '../../lib/utils';
|
|
396
|
-
|
|
397
|
-
interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
398
|
-
children: React.ReactNode;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
const cardStyles = 'rounded-xl border border-border bg-card ' +
|
|
402
|
-
'text-card-foreground shadow-sm transition-all duration-300 ' +
|
|
403
|
-
'hover:border-primary/50';
|
|
404
|
-
|
|
405
|
-
export function Card({ className, children, ...props }: CardProps) {
|
|
406
|
-
return (
|
|
407
|
-
<div className={cn(cardStyles, className)} {...props}>
|
|
408
|
-
{children}
|
|
409
|
-
</div>
|
|
410
|
-
);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
export function CardHeader({ className, children, ...props }: CardProps) {
|
|
414
|
-
return (
|
|
415
|
-
<div className={cn('flex flex-col space-y-1.5 p-6', className)} {...props}>
|
|
416
|
-
{children}
|
|
417
|
-
</div>
|
|
418
|
-
);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
export function CardTitle({ className, children, ...props }: CardProps) {
|
|
422
|
-
return (
|
|
423
|
-
<h3 className={cn('text-lg font-semibold', className)} {...props}>
|
|
424
|
-
{children}
|
|
425
|
-
</h3>
|
|
426
|
-
);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
export function CardDescription({ className, children, ...props }: CardProps) {
|
|
430
|
-
return (
|
|
431
|
-
<p className={cn('text-sm text-muted-foreground', className)} {...props}>
|
|
432
|
-
{children}
|
|
433
|
-
</p>
|
|
434
|
-
);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
export function CardContent({ className, children, ...props }: CardProps) {
|
|
438
|
-
return (
|
|
439
|
-
<div className={cn('p-6 pt-0', className)} {...props}>
|
|
440
|
-
{children}
|
|
441
|
-
</div>
|
|
442
|
-
);
|
|
443
|
-
}
|
|
444
|
-
`,
|
|
445
|
-
|
|
446
|
-
'components/ui/badge.tsx': () => `import React from 'react';
|
|
447
|
-
import { cn } from '../../lib/utils';
|
|
448
|
-
|
|
449
|
-
interface BadgeProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
450
|
-
variant?: 'default' | 'secondary' | 'outline';
|
|
451
|
-
children: React.ReactNode;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
export function Badge({ className, variant = 'default', children, ...props }: BadgeProps) {
|
|
455
|
-
return (
|
|
456
|
-
<div
|
|
457
|
-
className={cn(
|
|
458
|
-
'inline-flex items-center rounded-full px-3 py-1 text-xs font-medium transition-colors',
|
|
459
|
-
{
|
|
460
|
-
'bg-primary/10 text-primary border border-primary/20': variant === 'default',
|
|
461
|
-
'bg-secondary text-secondary-foreground': variant === 'secondary',
|
|
462
|
-
'border border-border text-foreground': variant === 'outline',
|
|
463
|
-
},
|
|
464
|
-
className
|
|
465
|
-
)}
|
|
466
|
-
{...props}
|
|
467
|
-
>
|
|
468
|
-
{children}
|
|
469
|
-
</div>
|
|
470
|
-
);
|
|
471
|
-
}
|
|
472
|
-
`,
|
|
473
|
-
|
|
474
|
-
'components/Navbar.tsx': () => `import React from 'react';
|
|
475
|
-
|
|
476
|
-
export function Navbar() {
|
|
477
|
-
return (
|
|
478
|
-
<header className="navbar">
|
|
479
|
-
<nav className="navbar-inner">
|
|
480
|
-
<a href="/" className="logo">
|
|
481
|
-
<div className="logo-icon">⚡</div>
|
|
482
|
-
<span className="logo-text">FlexiReact</span>
|
|
483
|
-
</a>
|
|
484
|
-
<div className="nav-links">
|
|
485
|
-
<a href="https://github.com/flexireact/flexireact">Docs</a>
|
|
486
|
-
<a href="https://github.com/flexireact/flexireact">GitHub</a>
|
|
487
|
-
</div>
|
|
488
|
-
</nav>
|
|
489
|
-
</header>
|
|
490
|
-
);
|
|
491
|
-
}
|
|
492
|
-
`,
|
|
493
|
-
|
|
494
|
-
'components/Hero.tsx': () => `import React from 'react';
|
|
495
|
-
|
|
496
|
-
export function Hero() {
|
|
497
|
-
return (
|
|
498
|
-
<section className="hero">
|
|
499
|
-
<div className="hero-glow" />
|
|
500
|
-
<div className="hero-content">
|
|
501
|
-
<span className="hero-badge">⚡ The Modern React Framework</span>
|
|
502
|
-
<h1 className="hero-title">
|
|
503
|
-
Build <span className="gradient-text">blazing fast</span> web apps
|
|
504
|
-
</h1>
|
|
505
|
-
<p className="hero-subtitle">
|
|
506
|
-
A modern React framework with TypeScript, Tailwind CSS,
|
|
507
|
-
SSR, SSG, Islands architecture, and file-based routing.
|
|
508
|
-
</p>
|
|
509
|
-
<div className="hero-buttons">
|
|
510
|
-
<a href="https://github.com/flexireact/flexireact" className="btn-primary">
|
|
511
|
-
Get Started →
|
|
512
|
-
</a>
|
|
513
|
-
<a href="https://github.com/flexireact/flexireact" className="btn-outline">
|
|
514
|
-
GitHub
|
|
515
|
-
</a>
|
|
516
|
-
</div>
|
|
517
|
-
<div className="terminal">
|
|
518
|
-
<div className="terminal-header">
|
|
519
|
-
<span className="dot red"></span>
|
|
520
|
-
<span className="dot yellow"></span>
|
|
521
|
-
<span className="dot green"></span>
|
|
522
|
-
</div>
|
|
523
|
-
<pre className="terminal-body">
|
|
524
|
-
<span className="dim">$</span> <span className="green">npx</span> create-flexireact my-app
|
|
525
|
-
<span className="dim">$</span> <span className="green">cd</span> my-app
|
|
526
|
-
<span className="dim">$</span> <span className="green">npm</span> run dev
|
|
527
|
-
|
|
528
|
-
<span className="green">✓</span> Ready in 38ms
|
|
529
|
-
</pre>
|
|
530
|
-
</div>
|
|
531
|
-
</div>
|
|
532
|
-
</section>
|
|
533
|
-
);
|
|
534
|
-
}
|
|
535
|
-
`,
|
|
536
|
-
|
|
537
|
-
'components/Features.tsx': () => `import React from 'react';
|
|
538
|
-
|
|
539
|
-
const features = [
|
|
540
|
-
{ icon: '⚡', title: 'Lightning Fast', desc: 'Powered by esbuild for instant builds.' },
|
|
541
|
-
{ icon: '📁', title: 'File Routing', desc: 'Create a file, get a route automatically.' },
|
|
542
|
-
{ icon: '🏝️', title: 'Islands', desc: 'Partial hydration for minimal JavaScript.' },
|
|
543
|
-
{ icon: '🚀', title: 'SSR & SSG', desc: 'Server rendering out of the box.' },
|
|
544
|
-
];
|
|
545
|
-
|
|
546
|
-
export function Features() {
|
|
547
|
-
return (
|
|
548
|
-
<section className="features">
|
|
549
|
-
<h2 className="features-title">Everything you need</h2>
|
|
550
|
-
<p className="features-subtitle">
|
|
551
|
-
A complete toolkit for building modern web apps.
|
|
552
|
-
</p>
|
|
553
|
-
<div className="features-grid">
|
|
554
|
-
{features.map((f, i) => (
|
|
555
|
-
<div key={i} className="feature-card">
|
|
556
|
-
<span className="feature-icon">{f.icon}</span>
|
|
557
|
-
<h3 className="feature-title">{f.title}</h3>
|
|
558
|
-
<p className="feature-desc">{f.desc}</p>
|
|
559
|
-
</div>
|
|
560
|
-
))}
|
|
561
|
-
</div>
|
|
562
|
-
</section>
|
|
563
|
-
);
|
|
564
|
-
}
|
|
565
|
-
`,
|
|
566
|
-
|
|
567
|
-
'components/Footer.tsx': () => `import React from 'react';
|
|
568
|
-
|
|
569
|
-
export function Footer() {
|
|
570
|
-
return (
|
|
571
|
-
<footer className="footer">
|
|
572
|
-
<div className="footer-inner">
|
|
573
|
-
<span>Built with ❤️ using FlexiReact</span>
|
|
574
|
-
<div className="footer-links">
|
|
575
|
-
<a href="https://github.com/flexireact/flexireact">GitHub</a>
|
|
576
|
-
<a href="https://github.com/flexireact/flexireact">Docs</a>
|
|
577
|
-
</div>
|
|
578
|
-
</div>
|
|
579
|
-
</footer>
|
|
580
|
-
);
|
|
581
|
-
}
|
|
582
|
-
`,
|
|
583
|
-
|
|
584
|
-
'components/index.ts': () => `export { Navbar } from './Navbar';
|
|
585
|
-
export { Hero } from './Hero';
|
|
586
|
-
export { Features } from './Features';
|
|
587
|
-
export { Footer } from './Footer';
|
|
588
|
-
export { Button } from './ui/button';
|
|
589
|
-
export { Card, CardHeader, CardTitle, CardDescription, CardContent } from './ui/card';
|
|
590
|
-
export { Badge } from './ui/badge';
|
|
591
|
-
`,
|
|
592
|
-
|
|
593
|
-
// ============================================================================
|
|
594
|
-
// Lib
|
|
595
|
-
// ============================================================================
|
|
596
|
-
|
|
597
|
-
'lib/utils.ts': () => `import { clsx, type ClassValue } from 'clsx';
|
|
598
|
-
import { twMerge } from 'tailwind-merge';
|
|
599
|
-
|
|
600
|
-
export function cn(...inputs: ClassValue[]) {
|
|
601
|
-
return twMerge(clsx(inputs));
|
|
602
|
-
}
|
|
603
|
-
`,
|
|
604
|
-
|
|
605
|
-
// ============================================================================
|
|
606
|
-
// Pages & Layouts
|
|
607
|
-
// ============================================================================
|
|
608
|
-
|
|
609
|
-
'layouts/root.tsx': () => `import React from 'react';
|
|
610
|
-
import { Navbar } from '../components/Navbar';
|
|
611
|
-
import { Footer } from '../components/Footer';
|
|
612
|
-
|
|
613
|
-
interface LayoutProps {
|
|
614
|
-
children: React.ReactNode;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
export default function RootLayout({ children }: LayoutProps) {
|
|
618
|
-
return (
|
|
619
|
-
<div className="app">
|
|
620
|
-
<Navbar />
|
|
621
|
-
<main>{children}</main>
|
|
622
|
-
<Footer />
|
|
623
|
-
</div>
|
|
624
|
-
);
|
|
625
|
-
}
|
|
626
|
-
`,
|
|
627
|
-
|
|
628
|
-
'pages/index.tsx': () => `import React from 'react';
|
|
629
|
-
import { Hero } from '../components/Hero';
|
|
630
|
-
import { Features } from '../components/Features';
|
|
631
|
-
|
|
632
|
-
export default function HomePage() {
|
|
633
|
-
return (
|
|
634
|
-
<>
|
|
635
|
-
<Hero />
|
|
636
|
-
<Features />
|
|
637
|
-
</>
|
|
638
|
-
);
|
|
639
|
-
}
|
|
640
|
-
`,
|
|
641
|
-
|
|
642
|
-
// ============================================================================
|
|
643
|
-
// Styles
|
|
644
|
-
// ============================================================================
|
|
645
|
-
|
|
646
|
-
'app/styles/globals.css': () => `/* FlexiReact Default Template Styles */
|
|
647
|
-
|
|
648
|
-
:root {
|
|
649
|
-
--bg: #0a0a0a;
|
|
650
|
-
--fg: #fafafa;
|
|
651
|
-
--primary: #10b981;
|
|
652
|
-
--primary-light: #34d399;
|
|
653
|
-
--accent: #06b6d4;
|
|
654
|
-
--muted: #71717a;
|
|
655
|
-
--border: #27272a;
|
|
656
|
-
--card: #18181b;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
660
|
-
|
|
661
|
-
html { scroll-behavior: smooth; }
|
|
662
|
-
|
|
663
|
-
body {
|
|
664
|
-
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
|
665
|
-
background: var(--bg);
|
|
666
|
-
color: var(--fg);
|
|
667
|
-
min-height: 100vh;
|
|
668
|
-
-webkit-font-smoothing: antialiased;
|
|
669
|
-
line-height: 1.6;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
a { color: inherit; text-decoration: none; transition: color 0.2s; }
|
|
673
|
-
a:hover { color: var(--primary); }
|
|
674
|
-
|
|
675
|
-
/* Layout */
|
|
676
|
-
.app { min-height: 100vh; }
|
|
677
|
-
|
|
678
|
-
/* Navbar */
|
|
679
|
-
.navbar {
|
|
680
|
-
position: sticky;
|
|
681
|
-
top: 0;
|
|
682
|
-
z-index: 50;
|
|
683
|
-
border-bottom: 1px solid var(--border);
|
|
684
|
-
background: rgba(10,10,10,0.85);
|
|
685
|
-
backdrop-filter: blur(12px);
|
|
686
|
-
}
|
|
687
|
-
.navbar-inner {
|
|
688
|
-
max-width: 1200px;
|
|
689
|
-
margin: 0 auto;
|
|
690
|
-
padding: 1rem 1.5rem;
|
|
691
|
-
display: flex;
|
|
692
|
-
justify-content: space-between;
|
|
693
|
-
align-items: center;
|
|
694
|
-
}
|
|
695
|
-
.logo { display: flex; align-items: center; gap: 0.5rem; }
|
|
696
|
-
.logo-icon {
|
|
697
|
-
width: 32px;
|
|
698
|
-
height: 32px;
|
|
699
|
-
background: linear-gradient(135deg, var(--primary), var(--accent));
|
|
700
|
-
border-radius: 8px;
|
|
701
|
-
display: flex;
|
|
702
|
-
align-items: center;
|
|
703
|
-
justify-content: center;
|
|
704
|
-
font-size: 1rem;
|
|
705
|
-
}
|
|
706
|
-
.logo-text { font-weight: 700; font-size: 1.125rem; }
|
|
707
|
-
.nav-links { display: flex; gap: 2rem; }
|
|
708
|
-
.nav-links a { color: var(--muted); font-size: 0.9375rem; font-weight: 500; }
|
|
709
|
-
.nav-links a:hover { color: var(--fg); }
|
|
710
|
-
|
|
711
|
-
/* Hero */
|
|
712
|
-
.hero {
|
|
713
|
-
position: relative;
|
|
714
|
-
min-height: 90vh;
|
|
715
|
-
display: flex;
|
|
716
|
-
align-items: center;
|
|
717
|
-
justify-content: center;
|
|
718
|
-
overflow: hidden;
|
|
719
|
-
padding: 4rem 1.5rem;
|
|
720
|
-
}
|
|
721
|
-
.hero-glow {
|
|
722
|
-
position: absolute;
|
|
723
|
-
top: 50%;
|
|
724
|
-
left: 50%;
|
|
725
|
-
transform: translate(-50%, -50%);
|
|
726
|
-
width: 700px;
|
|
727
|
-
height: 700px;
|
|
728
|
-
background: radial-gradient(circle, rgba(16,185,129,0.12), transparent 70%);
|
|
729
|
-
pointer-events: none;
|
|
730
|
-
}
|
|
731
|
-
.hero-content {
|
|
732
|
-
position: relative;
|
|
733
|
-
z-index: 1;
|
|
734
|
-
text-align: center;
|
|
735
|
-
padding: 2rem;
|
|
736
|
-
max-width: 800px;
|
|
737
|
-
}
|
|
738
|
-
.hero-badge {
|
|
739
|
-
display: inline-flex;
|
|
740
|
-
align-items: center;
|
|
741
|
-
gap: 0.5rem;
|
|
742
|
-
padding: 0.5rem 1.25rem;
|
|
743
|
-
background: rgba(16,185,129,0.1);
|
|
744
|
-
border: 1px solid rgba(16,185,129,0.2);
|
|
745
|
-
border-radius: 9999px;
|
|
746
|
-
font-size: 0.875rem;
|
|
747
|
-
font-weight: 500;
|
|
748
|
-
color: var(--primary);
|
|
749
|
-
margin-bottom: 2rem;
|
|
750
|
-
}
|
|
751
|
-
.hero-title {
|
|
752
|
-
font-size: clamp(2.5rem, 7vw, 4.5rem);
|
|
753
|
-
font-weight: 800;
|
|
754
|
-
line-height: 1.1;
|
|
755
|
-
letter-spacing: -0.02em;
|
|
756
|
-
margin-bottom: 1.5rem;
|
|
757
|
-
}
|
|
758
|
-
.gradient-text {
|
|
759
|
-
background: linear-gradient(135deg, var(--primary), var(--accent));
|
|
760
|
-
-webkit-background-clip: text;
|
|
761
|
-
-webkit-text-fill-color: transparent;
|
|
762
|
-
background-clip: text;
|
|
763
|
-
}
|
|
764
|
-
.hero-subtitle {
|
|
765
|
-
font-size: 1.125rem;
|
|
766
|
-
color: var(--muted);
|
|
767
|
-
line-height: 1.7;
|
|
768
|
-
max-width: 600px;
|
|
769
|
-
margin: 0 auto 2.5rem;
|
|
770
|
-
}
|
|
771
|
-
.hero-buttons {
|
|
772
|
-
display: flex;
|
|
773
|
-
gap: 1rem;
|
|
774
|
-
justify-content: center;
|
|
775
|
-
flex-wrap: wrap;
|
|
776
|
-
margin-bottom: 3rem;
|
|
777
|
-
}
|
|
778
|
-
.btn-primary {
|
|
779
|
-
display: inline-flex;
|
|
780
|
-
align-items: center;
|
|
781
|
-
gap: 0.5rem;
|
|
782
|
-
padding: 0.875rem 2rem;
|
|
783
|
-
background: linear-gradient(135deg, var(--primary), var(--accent));
|
|
784
|
-
color: #000;
|
|
785
|
-
font-weight: 600;
|
|
786
|
-
border-radius: 10px;
|
|
787
|
-
transition: all 0.2s;
|
|
788
|
-
}
|
|
789
|
-
.btn-primary:hover {
|
|
790
|
-
transform: translateY(-2px);
|
|
791
|
-
box-shadow: 0 10px 30px rgba(16,185,129,0.3);
|
|
792
|
-
color: #000;
|
|
793
|
-
}
|
|
794
|
-
.btn-outline {
|
|
795
|
-
display: inline-flex;
|
|
796
|
-
align-items: center;
|
|
797
|
-
gap: 0.5rem;
|
|
798
|
-
padding: 0.875rem 2rem;
|
|
799
|
-
border: 1px solid var(--border);
|
|
800
|
-
border-radius: 10px;
|
|
801
|
-
font-weight: 600;
|
|
802
|
-
transition: all 0.2s;
|
|
803
|
-
}
|
|
804
|
-
.btn-outline:hover {
|
|
805
|
-
border-color: var(--primary);
|
|
806
|
-
background: rgba(16,185,129,0.1);
|
|
807
|
-
color: var(--fg);
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
/* Terminal */
|
|
811
|
-
.terminal {
|
|
812
|
-
background: var(--card);
|
|
813
|
-
border: 1px solid var(--border);
|
|
814
|
-
border-radius: 12px;
|
|
815
|
-
max-width: 500px;
|
|
816
|
-
margin: 0 auto;
|
|
817
|
-
overflow: hidden;
|
|
818
|
-
text-align: left;
|
|
819
|
-
}
|
|
820
|
-
.terminal-header {
|
|
821
|
-
padding: 0.875rem 1rem;
|
|
822
|
-
border-bottom: 1px solid var(--border);
|
|
823
|
-
display: flex;
|
|
824
|
-
gap: 0.5rem;
|
|
825
|
-
background: rgba(255,255,255,0.02);
|
|
826
|
-
}
|
|
827
|
-
.dot { width: 12px; height: 12px; border-radius: 50%; }
|
|
828
|
-
.dot.red { background: #ef4444; }
|
|
829
|
-
.dot.yellow { background: #eab308; }
|
|
830
|
-
.dot.green { background: #22c55e; }
|
|
831
|
-
.terminal-body {
|
|
832
|
-
padding: 1.25rem;
|
|
833
|
-
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
|
834
|
-
font-size: 0.875rem;
|
|
835
|
-
line-height: 1.8;
|
|
836
|
-
}
|
|
837
|
-
.dim { color: var(--muted); }
|
|
838
|
-
.green { color: var(--primary); }
|
|
839
|
-
|
|
840
|
-
/* Features */
|
|
841
|
-
.features {
|
|
842
|
-
padding: 6rem 1.5rem;
|
|
843
|
-
max-width: 1200px;
|
|
844
|
-
margin: 0 auto;
|
|
845
|
-
}
|
|
846
|
-
.features-title {
|
|
847
|
-
font-size: 2rem;
|
|
848
|
-
font-weight: 700;
|
|
849
|
-
text-align: center;
|
|
850
|
-
margin-bottom: 0.75rem;
|
|
851
|
-
}
|
|
852
|
-
.features-subtitle {
|
|
853
|
-
text-align: center;
|
|
854
|
-
color: var(--muted);
|
|
855
|
-
margin-bottom: 3rem;
|
|
856
|
-
font-size: 1.0625rem;
|
|
857
|
-
}
|
|
858
|
-
.features-grid {
|
|
859
|
-
display: grid;
|
|
860
|
-
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
|
861
|
-
gap: 1.5rem;
|
|
862
|
-
}
|
|
863
|
-
.feature-card {
|
|
864
|
-
background: var(--card);
|
|
865
|
-
border: 1px solid var(--border);
|
|
866
|
-
border-radius: 16px;
|
|
867
|
-
padding: 1.75rem;
|
|
868
|
-
transition: all 0.3s;
|
|
869
|
-
}
|
|
870
|
-
.feature-card:hover {
|
|
871
|
-
border-color: var(--primary);
|
|
872
|
-
transform: translateY(-4px);
|
|
873
|
-
}
|
|
874
|
-
.feature-icon {
|
|
875
|
-
font-size: 2rem;
|
|
876
|
-
margin-bottom: 1rem;
|
|
877
|
-
display: block;
|
|
878
|
-
width: 48px;
|
|
879
|
-
height: 48px;
|
|
880
|
-
display: flex;
|
|
881
|
-
align-items: center;
|
|
882
|
-
justify-content: center;
|
|
883
|
-
background: rgba(16,185,129,0.1);
|
|
884
|
-
border-radius: 12px;
|
|
885
|
-
}
|
|
886
|
-
.feature-title { font-weight: 600; font-size: 1.0625rem; margin-bottom: 0.5rem; }
|
|
887
|
-
.feature-desc { color: var(--muted); font-size: 0.9375rem; line-height: 1.6; }
|
|
888
|
-
|
|
889
|
-
/* Footer */
|
|
890
|
-
.footer {
|
|
891
|
-
border-top: 1px solid var(--border);
|
|
892
|
-
padding: 2rem 1.5rem;
|
|
893
|
-
margin-top: 2rem;
|
|
894
|
-
}
|
|
895
|
-
.footer-inner {
|
|
896
|
-
max-width: 1200px;
|
|
897
|
-
margin: 0 auto;
|
|
898
|
-
display: flex;
|
|
899
|
-
justify-content: space-between;
|
|
900
|
-
align-items: center;
|
|
901
|
-
flex-wrap: wrap;
|
|
902
|
-
gap: 1rem;
|
|
903
|
-
font-size: 0.875rem;
|
|
904
|
-
color: var(--muted);
|
|
905
|
-
}
|
|
906
|
-
.footer-links { display: flex; gap: 1.5rem; }
|
|
907
|
-
.footer-links a:hover { color: var(--primary); }
|
|
908
|
-
|
|
909
|
-
/* Responsive */
|
|
910
|
-
@media (max-width: 768px) {
|
|
911
|
-
.nav-links { display: none; }
|
|
912
|
-
.hero { min-height: auto; padding: 6rem 1.5rem 4rem; }
|
|
913
|
-
.hero-buttons { flex-direction: column; align-items: center; }
|
|
914
|
-
.btn-primary, .btn-outline { width: 100%; justify-content: center; }
|
|
915
|
-
.footer-inner { flex-direction: column; text-align: center; }
|
|
916
|
-
}
|
|
917
|
-
`,
|
|
918
|
-
|
|
919
|
-
// ============================================================================
|
|
920
|
-
// Public Assets
|
|
921
|
-
// ============================================================================
|
|
922
|
-
|
|
923
|
-
'public/.gitkeep': () => '',
|
|
924
|
-
|
|
925
|
-
'public/favicon.svg': () => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
926
|
-
<defs>
|
|
927
|
-
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
928
|
-
<stop offset="0%" style="stop-color:#10b981"/>
|
|
929
|
-
<stop offset="100%" style="stop-color:#06b6d4"/>
|
|
930
|
-
</linearGradient>
|
|
931
|
-
</defs>
|
|
932
|
-
<rect width="100" height="100" rx="20" fill="#0a0a0a"/>
|
|
933
|
-
<path d="M50 20L30 55h15v25l20-35H50V20z" fill="url(#grad)"/>
|
|
934
|
-
</svg>`,
|
|
935
|
-
};
|
|
936
|
-
|
|
937
|
-
// Minimal template files
|
|
938
|
-
const MINIMAL_FILES = {
|
|
939
|
-
'package.json': (name) => JSON.stringify({
|
|
940
|
-
name: name,
|
|
941
|
-
version: "1.0.0",
|
|
942
|
-
private: true,
|
|
943
|
-
type: "module",
|
|
944
|
-
scripts: {
|
|
945
|
-
dev: "flexireact dev",
|
|
946
|
-
build: "flexireact build",
|
|
947
|
-
start: "flexireact start"
|
|
948
|
-
},
|
|
949
|
-
dependencies: {
|
|
950
|
-
"react": "^18.2.0",
|
|
951
|
-
"react-dom": "^18.2.0",
|
|
952
|
-
"@flexireact/core": "^1.0.0"
|
|
953
|
-
},
|
|
954
|
-
devDependencies: {
|
|
955
|
-
"@types/react": "^18.2.0",
|
|
956
|
-
"@types/react-dom": "^18.2.0",
|
|
957
|
-
"typescript": "^5.3.0"
|
|
958
|
-
}
|
|
959
|
-
}, null, 2),
|
|
960
|
-
|
|
961
|
-
'tsconfig.json': TEMPLATE_FILES['tsconfig.json'],
|
|
962
|
-
|
|
963
|
-
'flexireact.config.js': () => `export default {
|
|
964
|
-
server: { port: 3000 }
|
|
965
|
-
};
|
|
966
|
-
`,
|
|
967
|
-
|
|
968
|
-
'pages/index.tsx': () => `import React from 'react';
|
|
969
|
-
|
|
970
|
-
export default function HomePage() {
|
|
971
|
-
return (
|
|
972
|
-
<div style={{ padding: '2rem', fontFamily: 'system-ui' }}>
|
|
973
|
-
<h1>Welcome to FlexiReact</h1>
|
|
974
|
-
<p>Edit pages/index.tsx to get started.</p>
|
|
975
|
-
</div>
|
|
976
|
-
);
|
|
977
|
-
}
|
|
978
|
-
`,
|
|
979
|
-
|
|
980
|
-
'public/.gitkeep': () => '',
|
|
981
|
-
};
|
|
982
|
-
|
|
983
|
-
// ============================================================================
|
|
984
|
-
// Flexi UI Template Files
|
|
985
|
-
// ============================================================================
|
|
986
|
-
|
|
987
|
-
const FLEXI_UI_FILES = {
|
|
988
|
-
'package.json': (name) => JSON.stringify({
|
|
989
|
-
name: name,
|
|
990
|
-
version: "1.0.0",
|
|
991
|
-
private: true,
|
|
992
|
-
type: "module",
|
|
993
|
-
scripts: {
|
|
994
|
-
dev: "npm run css && flexireact dev",
|
|
995
|
-
build: "npm run css && flexireact build",
|
|
996
|
-
start: "flexireact start",
|
|
997
|
-
css: "npx tailwindcss -i ./app/styles/globals.css -o ./public/styles.css --minify"
|
|
998
|
-
},
|
|
999
|
-
dependencies: {
|
|
1000
|
-
"react": "^18.2.0",
|
|
1001
|
-
"react-dom": "^18.2.0",
|
|
1002
|
-
"@flexireact/core": "^1.0.0",
|
|
1003
|
-
"@flexireact/flexi-ui": "^1.0.0",
|
|
1004
|
-
"lucide-react": "^0.400.0"
|
|
1005
|
-
},
|
|
1006
|
-
devDependencies: {
|
|
1007
|
-
"@types/react": "^18.2.0",
|
|
1008
|
-
"@types/react-dom": "^18.2.0",
|
|
1009
|
-
"typescript": "^5.3.0",
|
|
1010
|
-
"tailwindcss": "^3.4.0",
|
|
1011
|
-
"postcss": "^8.4.32",
|
|
1012
|
-
"autoprefixer": "^10.4.16"
|
|
1013
|
-
}
|
|
1014
|
-
}, null, 2),
|
|
1015
|
-
|
|
1016
|
-
'tsconfig.json': TEMPLATE_FILES['tsconfig.json'],
|
|
1017
|
-
|
|
1018
|
-
'tailwind.config.js': () => `/** @type {import('tailwindcss').Config} */
|
|
1019
|
-
const { flexiUIPlugin } = require('@flexireact/flexi-ui/tailwind');
|
|
1020
|
-
|
|
1021
|
-
module.exports = {
|
|
1022
|
-
darkMode: 'class',
|
|
1023
|
-
content: [
|
|
1024
|
-
'./app/**/*.{js,ts,jsx,tsx}',
|
|
1025
|
-
'./pages/**/*.{js,ts,jsx,tsx}',
|
|
1026
|
-
'./components/**/*.{js,ts,jsx,tsx}',
|
|
1027
|
-
'./layouts/**/*.{js,ts,jsx,tsx}',
|
|
1028
|
-
'./node_modules/@flexireact/flexi-ui/dist/**/*.js',
|
|
1029
|
-
],
|
|
1030
|
-
theme: {
|
|
1031
|
-
extend: {
|
|
1032
|
-
fontFamily: {
|
|
1033
|
-
sans: ['Inter', 'system-ui', 'sans-serif'],
|
|
1034
|
-
},
|
|
1035
|
-
animation: {
|
|
1036
|
-
'fade-in': 'fadeIn 0.6s ease-out forwards',
|
|
1037
|
-
'fade-up': 'fadeUp 0.6s ease-out forwards',
|
|
1038
|
-
'scale-in': 'scaleIn 0.4s ease-out forwards',
|
|
1039
|
-
'glow-pulse': 'glowPulse 3s ease-in-out infinite',
|
|
1040
|
-
},
|
|
1041
|
-
keyframes: {
|
|
1042
|
-
fadeIn: {
|
|
1043
|
-
'0%': { opacity: '0' },
|
|
1044
|
-
'100%': { opacity: '1' },
|
|
1045
|
-
},
|
|
1046
|
-
fadeUp: {
|
|
1047
|
-
'0%': { opacity: '0', transform: 'translateY(30px)' },
|
|
1048
|
-
'100%': { opacity: '1', transform: 'translateY(0)' },
|
|
1049
|
-
},
|
|
1050
|
-
scaleIn: {
|
|
1051
|
-
'0%': { opacity: '0', transform: 'scale(0.9)' },
|
|
1052
|
-
'100%': { opacity: '1', transform: 'scale(1)' },
|
|
1053
|
-
},
|
|
1054
|
-
glowPulse: {
|
|
1055
|
-
'0%, 100%': { boxShadow: '0 0 20px rgba(0, 255, 156, 0.15)' },
|
|
1056
|
-
'50%': { boxShadow: '0 0 40px rgba(0, 255, 156, 0.3)' },
|
|
1057
|
-
},
|
|
1058
|
-
},
|
|
1059
|
-
},
|
|
1060
|
-
},
|
|
1061
|
-
plugins: [flexiUIPlugin],
|
|
1062
|
-
};
|
|
1063
|
-
`,
|
|
1064
|
-
|
|
1065
|
-
'postcss.config.js': () => `module.exports = {
|
|
1066
|
-
plugins: {
|
|
1067
|
-
tailwindcss: {},
|
|
1068
|
-
autoprefixer: {},
|
|
1069
|
-
},
|
|
1070
|
-
};
|
|
1071
|
-
`,
|
|
1072
|
-
|
|
1073
|
-
'flexireact.config.js': () => `/** @type {import('@flexireact/core').Config} */
|
|
1074
|
-
export default {
|
|
1075
|
-
styles: [
|
|
1076
|
-
'/styles.css',
|
|
1077
|
-
'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap'
|
|
1078
|
-
],
|
|
1079
|
-
favicon: '/favicon.svg',
|
|
1080
|
-
server: {
|
|
1081
|
-
port: 3000
|
|
1082
|
-
},
|
|
1083
|
-
islands: {
|
|
1084
|
-
enabled: true
|
|
1085
|
-
}
|
|
1086
|
-
};
|
|
1087
|
-
`,
|
|
1088
|
-
|
|
1089
|
-
// ============================================================================
|
|
1090
|
-
// Components - Simple Landing Page
|
|
1091
|
-
// ============================================================================
|
|
1092
|
-
|
|
1093
|
-
'components/Hero.tsx': () => `import React from 'react';
|
|
1094
|
-
import { Button, Badge } from '@flexireact/flexi-ui';
|
|
1095
|
-
|
|
1096
|
-
export function Hero() {
|
|
1097
|
-
return (
|
|
1098
|
-
<section className="hero">
|
|
1099
|
-
<div className="hero-glow" />
|
|
1100
|
-
<div className="hero-content">
|
|
1101
|
-
<Badge variant="success" className="mb-6">
|
|
1102
|
-
⚡ Flexi UI Components
|
|
1103
|
-
</Badge>
|
|
1104
|
-
<h1 className="hero-title">
|
|
1105
|
-
Build <span className="gradient-text">beautiful</span> apps
|
|
1106
|
-
</h1>
|
|
1107
|
-
<p className="hero-subtitle">
|
|
1108
|
-
A stunning component library with neon emerald accents,
|
|
1109
|
-
dark-first design, and seamless React integration.
|
|
1110
|
-
</p>
|
|
1111
|
-
<div className="hero-buttons">
|
|
1112
|
-
<Button size="lg">Get Started</Button>
|
|
1113
|
-
<Button variant="outline" size="lg">GitHub</Button>
|
|
1114
|
-
</div>
|
|
1115
|
-
</div>
|
|
1116
|
-
</section>
|
|
1117
|
-
);
|
|
1118
|
-
}
|
|
1119
|
-
`,
|
|
1120
|
-
|
|
1121
|
-
'components/Features.tsx': () => `import React from 'react';
|
|
1122
|
-
import { Card } from '@flexireact/flexi-ui';
|
|
1123
|
-
|
|
1124
|
-
const features = [
|
|
1125
|
-
{ icon: '⚡', title: 'Fast', desc: 'Powered by esbuild.' },
|
|
1126
|
-
{ icon: '📁', title: 'File Routing', desc: 'Automatic routes.' },
|
|
1127
|
-
{ icon: '🏝️', title: 'Islands', desc: 'Partial hydration.' },
|
|
1128
|
-
{ icon: '🎨', title: 'Beautiful', desc: 'Dark mode first.' },
|
|
1129
|
-
];
|
|
1130
|
-
|
|
1131
|
-
export function Features() {
|
|
1132
|
-
return (
|
|
1133
|
-
<section className="features">
|
|
1134
|
-
<h2 className="features-title">Features</h2>
|
|
1135
|
-
<div className="features-grid">
|
|
1136
|
-
{features.map((f, i) => (
|
|
1137
|
-
<Card key={i} className="feature-card">
|
|
1138
|
-
<span className="feature-icon">{f.icon}</span>
|
|
1139
|
-
<h3>{f.title}</h3>
|
|
1140
|
-
<p>{f.desc}</p>
|
|
1141
|
-
</Card>
|
|
1142
|
-
))}
|
|
1143
|
-
</div>
|
|
1144
|
-
</section>
|
|
1145
|
-
);
|
|
1146
|
-
}
|
|
1147
|
-
`,
|
|
1148
|
-
|
|
1149
|
-
'components/Footer.tsx': () => `import React from 'react';
|
|
1150
|
-
|
|
1151
|
-
export function Footer() {
|
|
1152
|
-
return (
|
|
1153
|
-
<footer className="footer">
|
|
1154
|
-
<span>Built with Flexi UI</span>
|
|
1155
|
-
<a href="https://github.com/flexireact/flexi-ui">GitHub</a>
|
|
1156
|
-
</footer>
|
|
1157
|
-
);
|
|
1158
|
-
}
|
|
1159
|
-
`,
|
|
1160
|
-
|
|
1161
|
-
'components/Navbar.tsx': () => `import React from 'react';
|
|
1162
|
-
import { Button, Badge } from '@flexireact/flexi-ui';
|
|
1163
|
-
|
|
1164
|
-
export function Navbar() {
|
|
1165
|
-
return (
|
|
1166
|
-
<header className="navbar">
|
|
1167
|
-
<nav className="navbar-inner">
|
|
1168
|
-
<a href="/" className="logo">
|
|
1169
|
-
<span className="logo-icon">F</span>
|
|
1170
|
-
<span className="logo-text">Flexi UI</span>
|
|
1171
|
-
<Badge variant="outline" className="ml-2">v1.0</Badge>
|
|
1172
|
-
</a>
|
|
1173
|
-
<div className="nav-links">
|
|
1174
|
-
<Button variant="ghost" size="sm">Docs</Button>
|
|
1175
|
-
<Button variant="outline" size="sm">GitHub</Button>
|
|
1176
|
-
</div>
|
|
1177
|
-
</nav>
|
|
1178
|
-
</header>
|
|
1179
|
-
);
|
|
1180
|
-
}
|
|
1181
|
-
`,
|
|
1182
|
-
|
|
1183
|
-
'components/index.ts': () => `export { Hero } from './Hero';
|
|
1184
|
-
export { Features } from './Features';
|
|
1185
|
-
export { Footer } from './Footer';
|
|
1186
|
-
export { Navbar } from './Navbar';
|
|
1187
|
-
`,
|
|
1188
|
-
|
|
1189
|
-
// ============================================================================
|
|
1190
|
-
// Pages & Layouts
|
|
1191
|
-
// ============================================================================
|
|
1192
|
-
|
|
1193
|
-
'layouts/root.tsx': () => `import React from 'react';
|
|
1194
|
-
import { ThemeProvider } from '@flexireact/flexi-ui';
|
|
1195
|
-
import { Navbar } from '../components/Navbar';
|
|
1196
|
-
import { Footer } from '../components/Footer';
|
|
1197
|
-
|
|
1198
|
-
interface LayoutProps {
|
|
1199
|
-
children: React.ReactNode;
|
|
1200
|
-
}
|
|
1201
|
-
|
|
1202
|
-
export default function RootLayout({ children }: LayoutProps) {
|
|
1203
|
-
return (
|
|
1204
|
-
<ThemeProvider defaultTheme="dark">
|
|
1205
|
-
<div className="min-h-screen bg-[#0a0a0a] text-white antialiased">
|
|
1206
|
-
<Navbar />
|
|
1207
|
-
<main className="pt-16">{children}</main>
|
|
1208
|
-
<Footer />
|
|
1209
|
-
</div>
|
|
1210
|
-
</ThemeProvider>
|
|
1211
|
-
);
|
|
1212
|
-
}
|
|
1213
|
-
`,
|
|
1214
|
-
|
|
1215
|
-
'pages/index.tsx': () => `import React from 'react';
|
|
1216
|
-
import { Hero } from '../components/Hero';
|
|
1217
|
-
import { Features } from '../components/Features';
|
|
1218
|
-
|
|
1219
|
-
export default function HomePage() {
|
|
1220
|
-
return (
|
|
1221
|
-
<>
|
|
1222
|
-
<Hero />
|
|
1223
|
-
<Features />
|
|
1224
|
-
</>
|
|
1225
|
-
);
|
|
1226
|
-
}
|
|
1227
|
-
`,
|
|
1228
|
-
|
|
1229
|
-
// ============================================================================
|
|
1230
|
-
// Styles
|
|
1231
|
-
// ============================================================================
|
|
1232
|
-
|
|
1233
|
-
'app/styles/globals.css': () => `/* Flexi UI Template Styles */
|
|
1234
|
-
|
|
1235
|
-
:root {
|
|
1236
|
-
--bg: #0a0a0a;
|
|
1237
|
-
--fg: #fafafa;
|
|
1238
|
-
--primary: #00FF9C;
|
|
1239
|
-
--primary-dark: #10b981;
|
|
1240
|
-
--muted: #94a3b8;
|
|
1241
|
-
--border: #1e293b;
|
|
1242
|
-
--card: #0f0f0f;
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
1246
|
-
|
|
1247
|
-
html { scroll-behavior: smooth; }
|
|
1248
|
-
|
|
1249
|
-
body {
|
|
1250
|
-
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
|
1251
|
-
background: var(--bg);
|
|
1252
|
-
color: var(--fg);
|
|
1253
|
-
min-height: 100vh;
|
|
1254
|
-
-webkit-font-smoothing: antialiased;
|
|
1255
|
-
line-height: 1.6;
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
a { color: inherit; text-decoration: none; transition: color 0.2s; }
|
|
1259
|
-
a:hover { color: var(--primary); }
|
|
1260
|
-
|
|
1261
|
-
/* Buttons */
|
|
1262
|
-
button {
|
|
1263
|
-
font-family: inherit;
|
|
1264
|
-
cursor: pointer;
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
.btn, [class*="btn-"] {
|
|
1268
|
-
display: inline-flex;
|
|
1269
|
-
align-items: center;
|
|
1270
|
-
justify-content: center;
|
|
1271
|
-
gap: 0.5rem;
|
|
1272
|
-
padding: 0.75rem 1.5rem;
|
|
1273
|
-
font-size: 0.9375rem;
|
|
1274
|
-
font-weight: 600;
|
|
1275
|
-
border-radius: 8px;
|
|
1276
|
-
transition: all 0.2s;
|
|
1277
|
-
border: none;
|
|
1278
|
-
cursor: pointer;
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
.btn-primary, button[class*="primary"] {
|
|
1282
|
-
background: linear-gradient(135deg, var(--primary), var(--primary-dark));
|
|
1283
|
-
color: #000;
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
.btn-primary:hover, button[class*="primary"]:hover {
|
|
1287
|
-
transform: translateY(-2px);
|
|
1288
|
-
box-shadow: 0 8px 25px rgba(0, 255, 156, 0.3);
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
.btn-outline, button[class*="outline"] {
|
|
1292
|
-
background: transparent;
|
|
1293
|
-
border: 1px solid var(--border);
|
|
1294
|
-
color: var(--fg);
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
.btn-outline:hover, button[class*="outline"]:hover {
|
|
1298
|
-
border-color: var(--primary);
|
|
1299
|
-
background: rgba(0, 255, 156, 0.1);
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
.btn-ghost, button[class*="ghost"] {
|
|
1303
|
-
background: transparent;
|
|
1304
|
-
border: 1px solid var(--border);
|
|
1305
|
-
color: var(--fg);
|
|
1306
|
-
}
|
|
1307
|
-
|
|
1308
|
-
.btn-ghost:hover, button[class*="ghost"]:hover {
|
|
1309
|
-
background: rgba(255, 255, 255, 0.05);
|
|
1310
|
-
border-color: var(--muted);
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
.btn-sm, button[class*="sm"] {
|
|
1314
|
-
padding: 0.5rem 1rem;
|
|
1315
|
-
font-size: 0.875rem;
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
/* Badge */
|
|
1319
|
-
[class*="badge"], .badge {
|
|
1320
|
-
display: inline-flex;
|
|
1321
|
-
align-items: center;
|
|
1322
|
-
padding: 0.25rem 0.75rem;
|
|
1323
|
-
font-size: 0.75rem;
|
|
1324
|
-
font-weight: 600;
|
|
1325
|
-
border-radius: 9999px;
|
|
1326
|
-
background: rgba(0, 255, 156, 0.1);
|
|
1327
|
-
color: var(--primary);
|
|
1328
|
-
border: 1px solid rgba(0, 255, 156, 0.2);
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
|
-
/* Card */
|
|
1332
|
-
[class*="card"], .card {
|
|
1333
|
-
background: var(--card);
|
|
1334
|
-
border: 1px solid var(--border);
|
|
1335
|
-
border-radius: 12px;
|
|
1336
|
-
transition: all 0.3s;
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
[class*="card"]:hover, .card:hover {
|
|
1340
|
-
border-color: var(--primary);
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
/* Navbar */
|
|
1344
|
-
.navbar {
|
|
1345
|
-
position: fixed;
|
|
1346
|
-
top: 0;
|
|
1347
|
-
left: 0;
|
|
1348
|
-
right: 0;
|
|
1349
|
-
z-index: 50;
|
|
1350
|
-
border-bottom: 1px solid var(--border);
|
|
1351
|
-
background: rgba(10,10,10,0.85);
|
|
1352
|
-
backdrop-filter: blur(12px);
|
|
1353
|
-
}
|
|
1354
|
-
.navbar-inner {
|
|
1355
|
-
max-width: 1200px;
|
|
1356
|
-
margin: 0 auto;
|
|
1357
|
-
padding: 1rem 1.5rem;
|
|
1358
|
-
display: flex;
|
|
1359
|
-
justify-content: space-between;
|
|
1360
|
-
align-items: center;
|
|
1361
|
-
}
|
|
1362
|
-
.logo { display: flex; align-items: center; gap: 0.5rem; }
|
|
1363
|
-
.logo-icon {
|
|
1364
|
-
width: 32px;
|
|
1365
|
-
height: 32px;
|
|
1366
|
-
background: linear-gradient(135deg, var(--primary), var(--primary-dark));
|
|
1367
|
-
border-radius: 8px;
|
|
1368
|
-
display: flex;
|
|
1369
|
-
align-items: center;
|
|
1370
|
-
justify-content: center;
|
|
1371
|
-
font-weight: 900;
|
|
1372
|
-
font-size: 1rem;
|
|
1373
|
-
color: #000;
|
|
1374
|
-
}
|
|
1375
|
-
.logo-text { font-weight: 700; font-size: 1.125rem; }
|
|
1376
|
-
.nav-links { display: flex; gap: 0.75rem; align-items: center; }
|
|
1377
|
-
|
|
1378
|
-
/* Hero */
|
|
1379
|
-
.hero {
|
|
1380
|
-
position: relative;
|
|
1381
|
-
min-height: 100vh;
|
|
1382
|
-
display: flex;
|
|
1383
|
-
align-items: center;
|
|
1384
|
-
justify-content: center;
|
|
1385
|
-
padding: 6rem 1.5rem;
|
|
1386
|
-
overflow: hidden;
|
|
1387
|
-
}
|
|
1388
|
-
.hero-glow {
|
|
1389
|
-
position: absolute;
|
|
1390
|
-
top: 50%;
|
|
1391
|
-
left: 50%;
|
|
1392
|
-
transform: translate(-50%, -50%);
|
|
1393
|
-
width: 700px;
|
|
1394
|
-
height: 700px;
|
|
1395
|
-
background: radial-gradient(circle, rgba(0,255,156,0.12), transparent 70%);
|
|
1396
|
-
pointer-events: none;
|
|
1397
|
-
}
|
|
1398
|
-
.hero-content {
|
|
1399
|
-
position: relative;
|
|
1400
|
-
z-index: 1;
|
|
1401
|
-
text-align: center;
|
|
1402
|
-
padding: 2rem;
|
|
1403
|
-
max-width: 800px;
|
|
1404
|
-
}
|
|
1405
|
-
.hero-title {
|
|
1406
|
-
font-size: clamp(2.5rem, 7vw, 4.5rem);
|
|
1407
|
-
font-weight: 800;
|
|
1408
|
-
line-height: 1.1;
|
|
1409
|
-
letter-spacing: -0.02em;
|
|
1410
|
-
margin-bottom: 1.5rem;
|
|
1411
|
-
}
|
|
1412
|
-
.gradient-text {
|
|
1413
|
-
background: linear-gradient(135deg, var(--primary), var(--primary-dark));
|
|
1414
|
-
-webkit-background-clip: text;
|
|
1415
|
-
-webkit-text-fill-color: transparent;
|
|
1416
|
-
background-clip: text;
|
|
1417
|
-
}
|
|
1418
|
-
.hero-subtitle {
|
|
1419
|
-
font-size: 1.125rem;
|
|
1420
|
-
color: var(--muted);
|
|
1421
|
-
line-height: 1.7;
|
|
1422
|
-
max-width: 600px;
|
|
1423
|
-
margin: 0 auto 2.5rem;
|
|
1424
|
-
}
|
|
1425
|
-
.hero-buttons {
|
|
1426
|
-
display: flex;
|
|
1427
|
-
gap: 1rem;
|
|
1428
|
-
justify-content: center;
|
|
1429
|
-
flex-wrap: wrap;
|
|
1430
|
-
}
|
|
1431
|
-
|
|
1432
|
-
/* Features */
|
|
1433
|
-
.features {
|
|
1434
|
-
padding: 6rem 1.5rem;
|
|
1435
|
-
max-width: 1200px;
|
|
1436
|
-
margin: 0 auto;
|
|
1437
|
-
}
|
|
1438
|
-
.features-title {
|
|
1439
|
-
font-size: 2rem;
|
|
1440
|
-
font-weight: 700;
|
|
1441
|
-
text-align: center;
|
|
1442
|
-
margin-bottom: 3rem;
|
|
1443
|
-
}
|
|
1444
|
-
.features-grid {
|
|
1445
|
-
display: grid;
|
|
1446
|
-
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
1447
|
-
gap: 1.5rem;
|
|
1448
|
-
}
|
|
1449
|
-
.feature-card {
|
|
1450
|
-
padding: 1.75rem;
|
|
1451
|
-
text-align: center;
|
|
1452
|
-
background: var(--card);
|
|
1453
|
-
border: 1px solid var(--border);
|
|
1454
|
-
border-radius: 16px;
|
|
1455
|
-
transition: all 0.3s;
|
|
1456
|
-
}
|
|
1457
|
-
.feature-card:hover {
|
|
1458
|
-
border-color: var(--primary);
|
|
1459
|
-
transform: translateY(-4px);
|
|
1460
|
-
}
|
|
1461
|
-
.feature-icon {
|
|
1462
|
-
font-size: 2rem;
|
|
1463
|
-
margin-bottom: 1rem;
|
|
1464
|
-
display: block;
|
|
1465
|
-
}
|
|
1466
|
-
.feature-card h3 { font-weight: 600; margin-bottom: 0.5rem; }
|
|
1467
|
-
.feature-card p { color: var(--muted); font-size: 0.9375rem; line-height: 1.6; }
|
|
1468
|
-
|
|
1469
|
-
/* Footer */
|
|
1470
|
-
.footer {
|
|
1471
|
-
border-top: 1px solid var(--border);
|
|
1472
|
-
padding: 2rem 1.5rem;
|
|
1473
|
-
display: flex;
|
|
1474
|
-
justify-content: space-between;
|
|
1475
|
-
align-items: center;
|
|
1476
|
-
max-width: 1200px;
|
|
1477
|
-
margin: 0 auto;
|
|
1478
|
-
color: var(--muted);
|
|
1479
|
-
font-size: 0.875rem;
|
|
1480
|
-
}
|
|
1481
|
-
.footer a:hover { color: var(--primary); }
|
|
1482
|
-
|
|
1483
|
-
/* Responsive */
|
|
1484
|
-
@media (max-width: 768px) {
|
|
1485
|
-
.nav-links { display: none; }
|
|
1486
|
-
.hero { min-height: auto; padding: 8rem 1.5rem 4rem; }
|
|
1487
|
-
.hero-buttons { flex-direction: column; align-items: center; }
|
|
1488
|
-
.hero-buttons button, .hero-buttons a { width: 100%; }
|
|
1489
|
-
.footer { flex-direction: column; gap: 1rem; text-align: center; }
|
|
1490
|
-
}
|
|
1491
|
-
`,
|
|
1492
|
-
|
|
1493
|
-
// ============================================================================
|
|
1494
|
-
// Public Assets
|
|
1495
|
-
// ============================================================================
|
|
1496
|
-
|
|
1497
|
-
'public/.gitkeep': () => '',
|
|
1498
|
-
|
|
1499
|
-
'public/favicon.svg': () => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
1500
|
-
<defs>
|
|
1501
|
-
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
1502
|
-
<stop offset="0%" style="stop-color:#00FF9C"/>
|
|
1503
|
-
<stop offset="100%" style="stop-color:#10b981"/>
|
|
1504
|
-
</linearGradient>
|
|
1505
|
-
</defs>
|
|
1506
|
-
<rect width="100" height="100" rx="20" fill="#0a0a0a"/>
|
|
1507
|
-
<text x="50" y="68" font-family="system-ui" font-size="50" font-weight="900" fill="url(#grad)" text-anchor="middle">F</text>
|
|
1508
|
-
</svg>`,
|
|
1509
|
-
};
|
|
1510
|
-
|
|
1511
|
-
// ============================================================================
|
|
1512
|
-
// Main
|
|
1513
|
-
// ============================================================================
|
|
1514
|
-
|
|
1515
|
-
async function main() {
|
|
1516
|
-
console.clear();
|
|
1517
|
-
console.log(BANNER);
|
|
1518
|
-
|
|
1519
|
-
// Get project name from args or prompt
|
|
1520
|
-
let projectName = process.argv[2];
|
|
1521
|
-
|
|
1522
|
-
if (!projectName) {
|
|
1523
|
-
projectName = await prompt('Project name', 'my-flexireact-app');
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
// Validate project name
|
|
1527
|
-
if (!/^[a-zA-Z0-9-_]+$/.test(projectName)) {
|
|
1528
|
-
error('Project name can only contain letters, numbers, dashes, and underscores');
|
|
1529
|
-
process.exit(1);
|
|
1530
|
-
}
|
|
1531
|
-
|
|
1532
|
-
const projectPath = path.resolve(process.cwd(), projectName);
|
|
1533
|
-
|
|
1534
|
-
// Check if directory exists
|
|
1535
|
-
if (fs.existsSync(projectPath) && !isDirEmpty(projectPath)) {
|
|
1536
|
-
error(`Directory ${projectName} already exists and is not empty`);
|
|
1537
|
-
process.exit(1);
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
// Select template
|
|
1541
|
-
console.log('');
|
|
1542
|
-
const templateOptions = Object.entries(TEMPLATES).map(([key, value]) => ({
|
|
1543
|
-
key,
|
|
1544
|
-
...value,
|
|
1545
|
-
}));
|
|
1546
|
-
|
|
1547
|
-
const selectedTemplate = await select('Select a template:', templateOptions);
|
|
1548
|
-
const templateKey = selectedTemplate.key;
|
|
1549
|
-
|
|
1550
|
-
console.log('');
|
|
1551
|
-
log(`Creating project in ${c.cyan}${projectPath}${c.reset}`);
|
|
1552
|
-
console.log('');
|
|
1553
|
-
|
|
1554
|
-
// Create project directory
|
|
1555
|
-
const spinner1 = new Spinner('Creating project structure...');
|
|
1556
|
-
spinner1.start();
|
|
1557
|
-
|
|
1558
|
-
try {
|
|
1559
|
-
fs.mkdirSync(projectPath, { recursive: true });
|
|
1560
|
-
|
|
1561
|
-
// Create subdirectories
|
|
1562
|
-
let dirs;
|
|
1563
|
-
if (templateKey === 'minimal') {
|
|
1564
|
-
dirs = ['pages', 'public'];
|
|
1565
|
-
} else if (templateKey === 'flexi-ui') {
|
|
1566
|
-
dirs = ['pages', 'public', 'components', 'layouts', 'app/styles'];
|
|
1567
|
-
} else {
|
|
1568
|
-
dirs = ['pages', 'public', 'components', 'components/ui', 'layouts', 'app/styles', 'lib'];
|
|
1569
|
-
}
|
|
1570
|
-
|
|
1571
|
-
for (const dir of dirs) {
|
|
1572
|
-
fs.mkdirSync(path.join(projectPath, dir), { recursive: true });
|
|
1573
|
-
}
|
|
1574
|
-
|
|
1575
|
-
spinner1.stop(true);
|
|
1576
|
-
} catch (err) {
|
|
1577
|
-
spinner1.stop(false);
|
|
1578
|
-
error(`Failed to create project structure: ${err.message}`);
|
|
1579
|
-
process.exit(1);
|
|
1580
|
-
}
|
|
1581
|
-
|
|
1582
|
-
// Write template files
|
|
1583
|
-
const spinner2 = new Spinner('Writing template files...');
|
|
1584
|
-
spinner2.start();
|
|
1585
|
-
|
|
1586
|
-
try {
|
|
1587
|
-
let files;
|
|
1588
|
-
if (templateKey === 'minimal') {
|
|
1589
|
-
files = MINIMAL_FILES;
|
|
1590
|
-
} else if (templateKey === 'flexi-ui') {
|
|
1591
|
-
files = FLEXI_UI_FILES;
|
|
1592
|
-
} else {
|
|
1593
|
-
files = TEMPLATE_FILES;
|
|
1594
|
-
}
|
|
1595
|
-
|
|
1596
|
-
for (const [filePath, contentFn] of Object.entries(files)) {
|
|
1597
|
-
const fullPath = path.join(projectPath, filePath);
|
|
1598
|
-
const dir = path.dirname(fullPath);
|
|
1599
|
-
|
|
1600
|
-
if (!fs.existsSync(dir)) {
|
|
1601
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
1602
|
-
}
|
|
1603
|
-
|
|
1604
|
-
const content = contentFn(projectName, templateKey);
|
|
1605
|
-
fs.writeFileSync(fullPath, content);
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
spinner2.stop(true);
|
|
1609
|
-
} catch (err) {
|
|
1610
|
-
spinner2.stop(false);
|
|
1611
|
-
error(`Failed to write template files: ${err.message}`);
|
|
1612
|
-
process.exit(1);
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
// Create README
|
|
1616
|
-
const spinner3 = new Spinner('Creating configuration files...');
|
|
1617
|
-
spinner3.start();
|
|
1618
|
-
|
|
1619
|
-
try {
|
|
1620
|
-
const readmeContent = `# ${projectName}
|
|
1621
|
-
|
|
1622
|
-
A modern web application built with [FlexiReact](https://github.com/flexireact/flexireact).
|
|
1623
|
-
|
|
1624
|
-
## Getting Started
|
|
1625
|
-
|
|
1626
|
-
\`\`\`bash
|
|
1627
|
-
npm install
|
|
1628
|
-
npm run dev
|
|
1629
|
-
\`\`\`
|
|
1630
|
-
|
|
1631
|
-
Open [http://localhost:3000](http://localhost:3000) in your browser.
|
|
1632
|
-
|
|
1633
|
-
## Learn More
|
|
1634
|
-
|
|
1635
|
-
- [FlexiReact Documentation](https://github.com/flexireact/flexireact)
|
|
1636
|
-
`;
|
|
1637
|
-
|
|
1638
|
-
fs.writeFileSync(path.join(projectPath, 'README.md'), readmeContent);
|
|
1639
|
-
spinner3.stop(true);
|
|
1640
|
-
} catch (err) {
|
|
1641
|
-
spinner3.stop(false);
|
|
1642
|
-
error(`Failed to create config files: ${err.message}`);
|
|
1643
|
-
process.exit(1);
|
|
1644
|
-
}
|
|
1645
|
-
|
|
1646
|
-
// Success message
|
|
1647
|
-
console.log(SUCCESS_BANNER(projectName));
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
|
-
main().catch((err) => {
|
|
1651
|
-
error(err.message);
|
|
1652
|
-
process.exit(1);
|
|
1653
|
-
});
|