@zenithbuild/cli 0.4.2
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/LICENSE +21 -0
- package/README.md +40 -0
- package/bin/zen-build.ts +2 -0
- package/bin/zen-dev.ts +2 -0
- package/bin/zen-preview.ts +2 -0
- package/bin/zenith.ts +2 -0
- package/package.json +63 -0
- package/src/commands/add.ts +37 -0
- package/src/commands/build.ts +36 -0
- package/src/commands/create.ts +702 -0
- package/src/commands/dev.ts +467 -0
- package/src/commands/index.ts +112 -0
- package/src/commands/preview.ts +62 -0
- package/src/commands/remove.ts +33 -0
- package/src/index.ts +10 -0
- package/src/main.ts +101 -0
- package/src/utils/branding.ts +178 -0
- package/src/utils/logger.ts +46 -0
- package/src/utils/plugin-manager.ts +114 -0
- package/src/utils/project.ts +77 -0
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zenithbuild/cli - Create Command
|
|
3
|
+
*
|
|
4
|
+
* Scaffolds a new Zenith application with interactive prompts,
|
|
5
|
+
* branded visuals, and optional configuration generation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs'
|
|
9
|
+
import path from 'path'
|
|
10
|
+
import { execSync } from 'child_process'
|
|
11
|
+
import readline from 'readline'
|
|
12
|
+
import * as brand from '../utils/branding'
|
|
13
|
+
|
|
14
|
+
// Types for project options
|
|
15
|
+
interface ProjectOptions {
|
|
16
|
+
name: string
|
|
17
|
+
directory: 'app' | 'src'
|
|
18
|
+
eslint: boolean
|
|
19
|
+
prettier: boolean
|
|
20
|
+
pathAlias: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Interactive readline prompt helper
|
|
25
|
+
*/
|
|
26
|
+
async function prompt(question: string, defaultValue?: string): Promise<string> {
|
|
27
|
+
const rl = readline.createInterface({
|
|
28
|
+
input: process.stdin,
|
|
29
|
+
output: process.stdout
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const displayQuestion = defaultValue
|
|
33
|
+
? `${question} ${brand.dim(`(${defaultValue})`)}: `
|
|
34
|
+
: `${question}: `
|
|
35
|
+
|
|
36
|
+
return new Promise((resolve) => {
|
|
37
|
+
rl.question(displayQuestion, (answer) => {
|
|
38
|
+
rl.close()
|
|
39
|
+
resolve(answer.trim() || defaultValue || '')
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Yes/No prompt helper
|
|
46
|
+
*/
|
|
47
|
+
async function confirm(question: string, defaultYes: boolean = true): Promise<boolean> {
|
|
48
|
+
const hint = defaultYes ? 'Y/n' : 'y/N'
|
|
49
|
+
const answer = await prompt(`${question} ${brand.dim(`(${hint})`)}`)
|
|
50
|
+
|
|
51
|
+
if (!answer) return defaultYes
|
|
52
|
+
return answer.toLowerCase().startsWith('y')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Main create command
|
|
57
|
+
*/
|
|
58
|
+
export async function create(appName?: string): Promise<void> {
|
|
59
|
+
// Show branded intro
|
|
60
|
+
await brand.showIntro()
|
|
61
|
+
brand.header('Create a new Zenith app')
|
|
62
|
+
|
|
63
|
+
// Gather project options
|
|
64
|
+
const options = await gatherOptions(appName)
|
|
65
|
+
|
|
66
|
+
console.log('')
|
|
67
|
+
const spinner = new brand.Spinner('Creating project structure...')
|
|
68
|
+
spinner.start()
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
// Create project
|
|
72
|
+
await createProject(options)
|
|
73
|
+
spinner.succeed('Project structure created')
|
|
74
|
+
|
|
75
|
+
// Always generate configs (tsconfig.json is required)
|
|
76
|
+
const configSpinner = new brand.Spinner('Generating configurations...')
|
|
77
|
+
configSpinner.start()
|
|
78
|
+
await generateConfigs(options)
|
|
79
|
+
configSpinner.succeed('Configurations generated')
|
|
80
|
+
|
|
81
|
+
// Install dependencies
|
|
82
|
+
const installSpinner = new brand.Spinner('Installing dependencies...')
|
|
83
|
+
installSpinner.start()
|
|
84
|
+
|
|
85
|
+
const targetDir = path.resolve(process.cwd(), options.name)
|
|
86
|
+
process.chdir(targetDir)
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
execSync('bun install', { stdio: 'pipe' })
|
|
90
|
+
installSpinner.succeed('Dependencies installed')
|
|
91
|
+
} catch {
|
|
92
|
+
try {
|
|
93
|
+
execSync('npm install', { stdio: 'pipe' })
|
|
94
|
+
installSpinner.succeed('Dependencies installed')
|
|
95
|
+
} catch {
|
|
96
|
+
installSpinner.fail('Could not install dependencies automatically')
|
|
97
|
+
brand.warn('Run "bun install" or "npm install" manually')
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Show success message
|
|
102
|
+
brand.showNextSteps(options.name)
|
|
103
|
+
|
|
104
|
+
} catch (err: unknown) {
|
|
105
|
+
spinner.fail('Failed to create project')
|
|
106
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
107
|
+
brand.error(message)
|
|
108
|
+
process.exit(1)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Gather all project options through interactive prompts
|
|
114
|
+
*/
|
|
115
|
+
async function gatherOptions(providedName?: string): Promise<ProjectOptions> {
|
|
116
|
+
// Project name
|
|
117
|
+
let name = providedName
|
|
118
|
+
if (!name) {
|
|
119
|
+
name = await prompt(brand.highlight('Project name'))
|
|
120
|
+
if (!name) {
|
|
121
|
+
brand.error('Project name is required')
|
|
122
|
+
process.exit(1)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const targetDir = path.resolve(process.cwd(), name)
|
|
127
|
+
if (fs.existsSync(targetDir)) {
|
|
128
|
+
brand.error(`Directory "${name}" already exists`)
|
|
129
|
+
process.exit(1)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
console.log('')
|
|
133
|
+
brand.info(`Creating ${brand.bold(name)} in ${brand.dim(targetDir)}`)
|
|
134
|
+
console.log('')
|
|
135
|
+
|
|
136
|
+
// Directory structure
|
|
137
|
+
const useSrc = await confirm('Use src/ directory instead of app/?', false)
|
|
138
|
+
const directory = useSrc ? 'src' : 'app'
|
|
139
|
+
|
|
140
|
+
// ESLint
|
|
141
|
+
const eslint = await confirm('Add ESLint for code linting?', true)
|
|
142
|
+
|
|
143
|
+
// Prettier
|
|
144
|
+
const prettier = await confirm('Add Prettier for code formatting?', true)
|
|
145
|
+
|
|
146
|
+
// TypeScript path aliases
|
|
147
|
+
const pathAlias = await confirm('Add TypeScript path alias (@/*)?', true)
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
name,
|
|
151
|
+
directory,
|
|
152
|
+
eslint,
|
|
153
|
+
prettier,
|
|
154
|
+
pathAlias
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Create the project directory structure and files
|
|
160
|
+
*/
|
|
161
|
+
async function createProject(options: ProjectOptions): Promise<void> {
|
|
162
|
+
const targetDir = path.resolve(process.cwd(), options.name)
|
|
163
|
+
const baseDir = options.directory
|
|
164
|
+
const appDir = path.join(targetDir, baseDir)
|
|
165
|
+
|
|
166
|
+
// Create directories
|
|
167
|
+
fs.mkdirSync(targetDir, { recursive: true })
|
|
168
|
+
fs.mkdirSync(path.join(appDir, 'pages'), { recursive: true })
|
|
169
|
+
fs.mkdirSync(path.join(appDir, 'layouts'), { recursive: true })
|
|
170
|
+
fs.mkdirSync(path.join(appDir, 'components'), { recursive: true })
|
|
171
|
+
fs.mkdirSync(path.join(appDir, 'styles'), { recursive: true }) // Create styles inside appDir
|
|
172
|
+
|
|
173
|
+
// package.json
|
|
174
|
+
const pkg: Record<string, unknown> = {
|
|
175
|
+
name: options.name,
|
|
176
|
+
version: '0.1.0',
|
|
177
|
+
private: true,
|
|
178
|
+
type: 'module',
|
|
179
|
+
scripts: {
|
|
180
|
+
dev: 'zen-dev',
|
|
181
|
+
build: 'zen-build',
|
|
182
|
+
preview: 'zen-preview',
|
|
183
|
+
test: 'bun test'
|
|
184
|
+
},
|
|
185
|
+
dependencies: {
|
|
186
|
+
'@zenithbuild/core': '^0.1.0'
|
|
187
|
+
},
|
|
188
|
+
devDependencies: {
|
|
189
|
+
'@types/bun': 'latest'
|
|
190
|
+
} as Record<string, string>
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Add optional dev dependencies
|
|
194
|
+
const devDeps = pkg.devDependencies as Record<string, string>
|
|
195
|
+
if (options.eslint) {
|
|
196
|
+
devDeps['eslint'] = '^8.0.0'
|
|
197
|
+
devDeps['@typescript-eslint/eslint-plugin'] = '^6.0.0'
|
|
198
|
+
devDeps['@typescript-eslint/parser'] = '^6.0.0'
|
|
199
|
+
pkg.scripts = { ...(pkg.scripts as object), lint: 'eslint .' }
|
|
200
|
+
}
|
|
201
|
+
if (options.prettier) {
|
|
202
|
+
devDeps['prettier'] = '^3.0.0'
|
|
203
|
+
pkg.scripts = { ...(pkg.scripts as object), format: 'prettier --write .' }
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
fs.writeFileSync(
|
|
207
|
+
path.join(targetDir, 'package.json'),
|
|
208
|
+
JSON.stringify(pkg, null, 4)
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
// index.zen
|
|
212
|
+
fs.writeFileSync(
|
|
213
|
+
path.join(targetDir, baseDir, 'pages', 'index.zen'),
|
|
214
|
+
generateIndexPage()
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
// DefaultLayout.zen
|
|
218
|
+
fs.writeFileSync(
|
|
219
|
+
path.join(targetDir, baseDir, 'layouts', 'DefaultLayout.zen'),
|
|
220
|
+
generateDefaultLayout()
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
// global.css
|
|
224
|
+
fs.writeFileSync(
|
|
225
|
+
path.join(appDir, 'styles', 'global.css'),
|
|
226
|
+
generateGlobalCSS()
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
// .gitignore
|
|
230
|
+
fs.writeFileSync(
|
|
231
|
+
path.join(targetDir, '.gitignore'),
|
|
232
|
+
generateGitignore()
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Generate configuration files based on options
|
|
238
|
+
*/
|
|
239
|
+
async function generateConfigs(options: ProjectOptions): Promise<void> {
|
|
240
|
+
const targetDir = path.resolve(process.cwd(), options.name)
|
|
241
|
+
|
|
242
|
+
// tsconfig.json
|
|
243
|
+
const tsconfig: Record<string, unknown> = {
|
|
244
|
+
compilerOptions: {
|
|
245
|
+
target: 'ESNext',
|
|
246
|
+
module: 'ESNext',
|
|
247
|
+
moduleResolution: 'bundler',
|
|
248
|
+
strict: true,
|
|
249
|
+
esModuleInterop: true,
|
|
250
|
+
skipLibCheck: true,
|
|
251
|
+
forceConsistentCasingInFileNames: true,
|
|
252
|
+
resolveJsonModule: true,
|
|
253
|
+
declaration: true,
|
|
254
|
+
declarationMap: true,
|
|
255
|
+
noEmit: true
|
|
256
|
+
},
|
|
257
|
+
include: [options.directory + '/**/*', '*.ts'],
|
|
258
|
+
exclude: ['node_modules', 'dist']
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (options.pathAlias) {
|
|
262
|
+
(tsconfig.compilerOptions as Record<string, unknown>).baseUrl = '.'
|
|
263
|
+
; (tsconfig.compilerOptions as Record<string, unknown>).paths = {
|
|
264
|
+
'@/*': [`./${options.directory}/*`]
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
fs.writeFileSync(
|
|
269
|
+
path.join(targetDir, 'tsconfig.json'),
|
|
270
|
+
JSON.stringify(tsconfig, null, 4)
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
// ESLint config
|
|
274
|
+
if (options.eslint) {
|
|
275
|
+
const eslintConfig = {
|
|
276
|
+
root: true,
|
|
277
|
+
parser: '@typescript-eslint/parser',
|
|
278
|
+
plugins: ['@typescript-eslint'],
|
|
279
|
+
extends: [
|
|
280
|
+
'eslint:recommended',
|
|
281
|
+
'plugin:@typescript-eslint/recommended'
|
|
282
|
+
],
|
|
283
|
+
env: {
|
|
284
|
+
browser: true,
|
|
285
|
+
node: true,
|
|
286
|
+
es2022: true
|
|
287
|
+
},
|
|
288
|
+
rules: {
|
|
289
|
+
'@typescript-eslint/no-unused-vars': 'warn',
|
|
290
|
+
'@typescript-eslint/no-explicit-any': 'warn'
|
|
291
|
+
},
|
|
292
|
+
ignorePatterns: ['dist', 'node_modules']
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
fs.writeFileSync(
|
|
296
|
+
path.join(targetDir, '.eslintrc.json'),
|
|
297
|
+
JSON.stringify(eslintConfig, null, 4)
|
|
298
|
+
)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Prettier config
|
|
302
|
+
if (options.prettier) {
|
|
303
|
+
const prettierConfig = {
|
|
304
|
+
semi: false,
|
|
305
|
+
singleQuote: true,
|
|
306
|
+
tabWidth: 4,
|
|
307
|
+
trailingComma: 'es5',
|
|
308
|
+
printWidth: 100
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
fs.writeFileSync(
|
|
312
|
+
path.join(targetDir, '.prettierrc'),
|
|
313
|
+
JSON.stringify(prettierConfig, null, 4)
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
fs.writeFileSync(
|
|
317
|
+
path.join(targetDir, '.prettierignore'),
|
|
318
|
+
'dist\nnode_modules\nbun.lock\n'
|
|
319
|
+
)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Template generators
|
|
324
|
+
function generateIndexPage(): string {
|
|
325
|
+
return `<script setup="ts">
|
|
326
|
+
state count = 0
|
|
327
|
+
|
|
328
|
+
function increment() {
|
|
329
|
+
count = count + 1
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function decrement() {
|
|
333
|
+
count = count - 1
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
zenOnMount(() => {
|
|
337
|
+
console.log('🚀 Zenith app mounted!')
|
|
338
|
+
})
|
|
339
|
+
</script>
|
|
340
|
+
|
|
341
|
+
<DefaultLayout title="Zenith App">
|
|
342
|
+
<main>
|
|
343
|
+
<div class="hero">
|
|
344
|
+
<h1>Welcome to <span class="brand">Zenith</span></h1>
|
|
345
|
+
<p class="tagline">The Modern Reactive Web Framework</p>
|
|
346
|
+
</div>
|
|
347
|
+
|
|
348
|
+
<div class="counter-card">
|
|
349
|
+
<h2>Interactive Counter</h2>
|
|
350
|
+
<p class="count">{count}</p>
|
|
351
|
+
<div class="buttons">
|
|
352
|
+
<button onclick="decrement" class="btn-secondary">−</button>
|
|
353
|
+
<button onclick="increment" class="btn-primary">+</button>
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
356
|
+
|
|
357
|
+
<div class="features">
|
|
358
|
+
<div class="feature">
|
|
359
|
+
<span class="icon">⚡</span>
|
|
360
|
+
<h3>Reactive State</h3>
|
|
361
|
+
<p>Built-in state management with automatic DOM updates</p>
|
|
362
|
+
</div>
|
|
363
|
+
<div class="feature">
|
|
364
|
+
<span class="icon">🎯</span>
|
|
365
|
+
<h3>Zero Config</h3>
|
|
366
|
+
<p>Works immediately with no build step required</p>
|
|
367
|
+
</div>
|
|
368
|
+
<div class="feature">
|
|
369
|
+
<span class="icon">🔥</span>
|
|
370
|
+
<h3>Hot Reload</h3>
|
|
371
|
+
<p>Instant updates during development</p>
|
|
372
|
+
</div>
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
</DefaultLayout>
|
|
376
|
+
|
|
377
|
+
<style>
|
|
378
|
+
main {
|
|
379
|
+
max-width: 900px;
|
|
380
|
+
margin: 0 auto;
|
|
381
|
+
padding: 3rem 2rem;
|
|
382
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
383
|
+
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
|
|
384
|
+
color: #f1f5f9;
|
|
385
|
+
min-height: 100vh;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.hero {
|
|
389
|
+
text-align: center;
|
|
390
|
+
margin-bottom: 3rem;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
h1 {
|
|
394
|
+
font-size: 3rem;
|
|
395
|
+
font-weight: 700;
|
|
396
|
+
margin-bottom: 0.5rem;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.brand {
|
|
400
|
+
background: linear-gradient(135deg, #3b82f6, #06b6d4);
|
|
401
|
+
-webkit-background-clip: text;
|
|
402
|
+
-webkit-text-fill-color: transparent;
|
|
403
|
+
background-clip: text;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.tagline {
|
|
407
|
+
color: #94a3b8;
|
|
408
|
+
font-size: 1.25rem;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.counter-card {
|
|
412
|
+
background: rgba(255, 255, 255, 0.05);
|
|
413
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
414
|
+
border-radius: 16px;
|
|
415
|
+
padding: 2rem;
|
|
416
|
+
text-align: center;
|
|
417
|
+
margin-bottom: 3rem;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.counter-card h2 {
|
|
421
|
+
color: #e2e8f0;
|
|
422
|
+
margin-bottom: 1rem;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.count {
|
|
426
|
+
font-size: 4rem;
|
|
427
|
+
font-weight: 700;
|
|
428
|
+
color: #3b82f6;
|
|
429
|
+
margin: 1rem 0;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.buttons {
|
|
433
|
+
display: flex;
|
|
434
|
+
gap: 1rem;
|
|
435
|
+
justify-content: center;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
button {
|
|
439
|
+
font-size: 1.5rem;
|
|
440
|
+
width: 60px;
|
|
441
|
+
height: 60px;
|
|
442
|
+
border: none;
|
|
443
|
+
border-radius: 12px;
|
|
444
|
+
cursor: pointer;
|
|
445
|
+
transition: all 0.2s ease;
|
|
446
|
+
font-weight: 600;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.btn-primary {
|
|
450
|
+
background: linear-gradient(135deg, #3b82f6, #2563eb);
|
|
451
|
+
color: white;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.btn-primary:hover {
|
|
455
|
+
transform: translateY(-2px);
|
|
456
|
+
box-shadow: 0 8px 20px rgba(59, 130, 246, 0.4);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.btn-secondary {
|
|
460
|
+
background: rgba(255, 255, 255, 0.1);
|
|
461
|
+
color: #e2e8f0;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.btn-secondary:hover {
|
|
465
|
+
background: rgba(255, 255, 255, 0.2);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.features {
|
|
469
|
+
display: grid;
|
|
470
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
471
|
+
gap: 1.5rem;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.feature {
|
|
475
|
+
background: rgba(255, 255, 255, 0.03);
|
|
476
|
+
border: 1px solid rgba(255, 255, 255, 0.06);
|
|
477
|
+
border-radius: 12px;
|
|
478
|
+
padding: 1.5rem;
|
|
479
|
+
text-align: center;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.icon {
|
|
483
|
+
font-size: 2rem;
|
|
484
|
+
display: block;
|
|
485
|
+
margin-bottom: 0.75rem;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.feature h3 {
|
|
489
|
+
color: #e2e8f0;
|
|
490
|
+
margin-bottom: 0.5rem;
|
|
491
|
+
font-size: 1.1rem;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.feature p {
|
|
495
|
+
color: #94a3b8;
|
|
496
|
+
font-size: 0.9rem;
|
|
497
|
+
line-height: 1.5;
|
|
498
|
+
}
|
|
499
|
+
</style>
|
|
500
|
+
`
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function generateDefaultLayout(): string {
|
|
504
|
+
return `<script setup="ts">
|
|
505
|
+
// interface Props { title?: string; lang?: string }
|
|
506
|
+
|
|
507
|
+
zenEffect(() => {
|
|
508
|
+
document.title = title || 'Zenith App'
|
|
509
|
+
})
|
|
510
|
+
|
|
511
|
+
zenOnMount(() => {
|
|
512
|
+
console.log(\`[Layout] Mounted with title: \${title || 'Zenith App'}\`)
|
|
513
|
+
})
|
|
514
|
+
</script>
|
|
515
|
+
|
|
516
|
+
<html lang={lang || 'en'}>
|
|
517
|
+
<head>
|
|
518
|
+
<meta charset="UTF-8">
|
|
519
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
520
|
+
<link rel="stylesheet" href="/styles/global.css">
|
|
521
|
+
</head>
|
|
522
|
+
<body>
|
|
523
|
+
<div class="layout">
|
|
524
|
+
<header class="header">
|
|
525
|
+
<nav class="nav">
|
|
526
|
+
<a href="/" class="logo">⚡ Zenith</a>
|
|
527
|
+
<div class="nav-links">
|
|
528
|
+
<a href="/">Home</a>
|
|
529
|
+
<a href="/docs">Docs</a>
|
|
530
|
+
<a href="https://github.com/zenithbuild/zenith" target="_blank">GitHub</a>
|
|
531
|
+
</div>
|
|
532
|
+
</nav>
|
|
533
|
+
</header>
|
|
534
|
+
|
|
535
|
+
<main class="content">
|
|
536
|
+
<slot />
|
|
537
|
+
</main>
|
|
538
|
+
|
|
539
|
+
<footer class="footer">
|
|
540
|
+
<p>Built with ⚡ Zenith Framework</p>
|
|
541
|
+
</footer>
|
|
542
|
+
</div>
|
|
543
|
+
</body>
|
|
544
|
+
</html>
|
|
545
|
+
|
|
546
|
+
<style>
|
|
547
|
+
.layout {
|
|
548
|
+
min-height: 100vh;
|
|
549
|
+
display: flex;
|
|
550
|
+
flex-direction: column;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.header {
|
|
554
|
+
background: rgba(15, 23, 42, 0.95);
|
|
555
|
+
backdrop-filter: blur(10px);
|
|
556
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
557
|
+
position: sticky;
|
|
558
|
+
top: 0;
|
|
559
|
+
z-index: 100;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
.nav {
|
|
563
|
+
max-width: 1200px;
|
|
564
|
+
margin: 0 auto;
|
|
565
|
+
padding: 1rem 2rem;
|
|
566
|
+
display: flex;
|
|
567
|
+
justify-content: space-between;
|
|
568
|
+
align-items: center;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.logo {
|
|
572
|
+
font-size: 1.25rem;
|
|
573
|
+
font-weight: 700;
|
|
574
|
+
color: #3b82f6;
|
|
575
|
+
text-decoration: none;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.nav-links {
|
|
579
|
+
display: flex;
|
|
580
|
+
gap: 2rem;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
.nav-links a {
|
|
584
|
+
color: #94a3b8;
|
|
585
|
+
text-decoration: none;
|
|
586
|
+
transition: color 0.2s;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
.nav-links a:hover {
|
|
590
|
+
color: #f1f5f9;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
.content {
|
|
594
|
+
flex: 1;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.footer {
|
|
598
|
+
background: #0f172a;
|
|
599
|
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
600
|
+
padding: 2rem;
|
|
601
|
+
text-align: center;
|
|
602
|
+
color: #64748b;
|
|
603
|
+
}
|
|
604
|
+
</style>
|
|
605
|
+
`
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function generateGlobalCSS(): string {
|
|
609
|
+
return `/* Zenith Global Styles */
|
|
610
|
+
|
|
611
|
+
/* CSS Reset */
|
|
612
|
+
*, *::before, *::after {
|
|
613
|
+
box-sizing: border-box;
|
|
614
|
+
margin: 0;
|
|
615
|
+
padding: 0;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
html {
|
|
619
|
+
font-size: 16px;
|
|
620
|
+
-webkit-font-smoothing: antialiased;
|
|
621
|
+
-moz-osx-font-smoothing: grayscale;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
body {
|
|
625
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
626
|
+
line-height: 1.6;
|
|
627
|
+
background: #0f172a;
|
|
628
|
+
color: #f1f5f9;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
a {
|
|
632
|
+
color: #3b82f6;
|
|
633
|
+
text-decoration: none;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
a:hover {
|
|
637
|
+
text-decoration: underline;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
img, video {
|
|
641
|
+
max-width: 100%;
|
|
642
|
+
height: auto;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
button, input, select, textarea {
|
|
646
|
+
font: inherit;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/* Utility Classes */
|
|
650
|
+
.container {
|
|
651
|
+
max-width: 1200px;
|
|
652
|
+
margin: 0 auto;
|
|
653
|
+
padding: 0 1rem;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
.text-center { text-align: center; }
|
|
657
|
+
.text-muted { color: #94a3b8; }
|
|
658
|
+
|
|
659
|
+
/* Zenith Brand Colors */
|
|
660
|
+
:root {
|
|
661
|
+
--zen-primary: #3b82f6;
|
|
662
|
+
--zen-secondary: #06b6d4;
|
|
663
|
+
--zen-bg: #0f172a;
|
|
664
|
+
--zen-surface: #1e293b;
|
|
665
|
+
--zen-text: #f1f5f9;
|
|
666
|
+
--zen-muted: #94a3b8;
|
|
667
|
+
}
|
|
668
|
+
`
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
function generateGitignore(): string {
|
|
672
|
+
return `# Dependencies
|
|
673
|
+
node_modules/
|
|
674
|
+
bun.lock
|
|
675
|
+
|
|
676
|
+
# Build output
|
|
677
|
+
dist/
|
|
678
|
+
.cache/
|
|
679
|
+
|
|
680
|
+
# IDE
|
|
681
|
+
.idea/
|
|
682
|
+
.vscode/
|
|
683
|
+
*.swp
|
|
684
|
+
*.swo
|
|
685
|
+
|
|
686
|
+
# OS
|
|
687
|
+
.DS_Store
|
|
688
|
+
Thumbs.db
|
|
689
|
+
|
|
690
|
+
# Environment
|
|
691
|
+
.env
|
|
692
|
+
.env.local
|
|
693
|
+
.env.*.local
|
|
694
|
+
|
|
695
|
+
# Logs
|
|
696
|
+
*.log
|
|
697
|
+
npm-debug.log*
|
|
698
|
+
|
|
699
|
+
# Testing
|
|
700
|
+
coverage/
|
|
701
|
+
`
|
|
702
|
+
}
|