@zenithbuild/core 0.1.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +24 -40
  3. package/bin/zen-build.ts +2 -0
  4. package/bin/zen-dev.ts +2 -0
  5. package/bin/zen-preview.ts +2 -0
  6. package/bin/zenith.ts +2 -0
  7. package/cli/commands/add.ts +37 -0
  8. package/cli/commands/build.ts +37 -0
  9. package/cli/commands/create.ts +702 -0
  10. package/cli/commands/dev.ts +197 -0
  11. package/cli/commands/index.ts +112 -0
  12. package/cli/commands/preview.ts +62 -0
  13. package/cli/commands/remove.ts +33 -0
  14. package/cli/index.ts +10 -0
  15. package/cli/main.ts +101 -0
  16. package/cli/utils/branding.ts +153 -0
  17. package/cli/utils/logger.ts +40 -0
  18. package/cli/utils/plugin-manager.ts +114 -0
  19. package/cli/utils/project.ts +71 -0
  20. package/compiler/build-analyzer.ts +122 -0
  21. package/compiler/discovery/layouts.ts +61 -0
  22. package/compiler/index.ts +40 -24
  23. package/compiler/ir/types.ts +1 -0
  24. package/compiler/parse/parseScript.ts +29 -5
  25. package/compiler/parse/parseTemplate.ts +96 -58
  26. package/compiler/parse/scriptAnalysis.ts +77 -0
  27. package/compiler/runtime/dataExposure.ts +49 -31
  28. package/compiler/runtime/generateDOM.ts +18 -17
  29. package/compiler/runtime/generateHydrationBundle.ts +24 -5
  30. package/compiler/runtime/transformIR.ts +140 -49
  31. package/compiler/runtime/wrapExpressionWithLoop.ts +11 -11
  32. package/compiler/spa-build.ts +70 -153
  33. package/compiler/ssg-build.ts +412 -0
  34. package/compiler/transform/layoutProcessor.ts +132 -0
  35. package/compiler/transform/transformNode.ts +19 -19
  36. package/dist/cli.js +11648 -0
  37. package/dist/zen-build.js +11659 -0
  38. package/dist/zen-dev.js +11659 -0
  39. package/dist/zen-preview.js +11659 -0
  40. package/dist/zenith.js +11659 -0
  41. package/package.json +22 -2
  42. package/runtime/bundle-generator.ts +416 -0
  43. package/runtime/client-runtime.ts +532 -0
  44. package/.eslintignore +0 -15
  45. package/.gitattributes +0 -2
  46. package/.github/ISSUE_TEMPLATE/compiler-errors-for-invalid-state-declarations.md +0 -25
  47. package/.github/ISSUE_TEMPLATE/new_ticket.yaml +0 -34
  48. package/.github/pull_request_template.md +0 -15
  49. package/.github/workflows/discord-changelog.yml +0 -141
  50. package/.github/workflows/discord-notify.yml +0 -242
  51. package/.github/workflows/discord-version.yml +0 -195
  52. package/.prettierignore +0 -13
  53. package/.prettierrc +0 -21
  54. package/.zen.d.ts +0 -15
  55. package/app/components/Button.zen +0 -46
  56. package/app/components/Link.zen +0 -11
  57. package/app/favicon.ico +0 -0
  58. package/app/layouts/Main.zen +0 -59
  59. package/app/pages/about.zen +0 -23
  60. package/app/pages/blog/[id].zen +0 -53
  61. package/app/pages/blog/index.zen +0 -32
  62. package/app/pages/dynamic-dx.zen +0 -712
  63. package/app/pages/dynamic-primitives.zen +0 -453
  64. package/app/pages/index.zen +0 -154
  65. package/app/pages/navigation-demo.zen +0 -229
  66. package/app/pages/posts/[...slug].zen +0 -61
  67. package/app/pages/primitives-demo.zen +0 -273
  68. package/assets/logos/0E3B5DDD-605C-4839-BB2E-DFCA8ADC9604.PNG +0 -0
  69. package/assets/logos/760971E5-79A1-44F9-90B9-925DF30F4278.PNG +0 -0
  70. package/assets/logos/8A06ED80-9ED2-4689-BCBD-13B2E95EE8E4.JPG +0 -0
  71. package/assets/logos/C691FF58-ED13-4E8D-B6A3-02E835849340.PNG +0 -0
  72. package/assets/logos/C691FF58-ED13-4E8D-B6A3-02E835849340.svg +0 -601
  73. package/assets/logos/README.md +0 -54
  74. package/assets/logos/zen.icns +0 -0
  75. package/bun.lock +0 -39
  76. package/compiler/legacy/binding.ts +0 -254
  77. package/compiler/legacy/bindings.ts +0 -338
  78. package/compiler/legacy/component-process.ts +0 -1208
  79. package/compiler/legacy/component.ts +0 -301
  80. package/compiler/legacy/event.ts +0 -50
  81. package/compiler/legacy/expression.ts +0 -1149
  82. package/compiler/legacy/mutation.ts +0 -280
  83. package/compiler/legacy/parse.ts +0 -299
  84. package/compiler/legacy/split.ts +0 -608
  85. package/compiler/legacy/types.ts +0 -32
  86. package/docs/COMMENTS.md +0 -111
  87. package/docs/COMMITS.md +0 -36
  88. package/docs/CONTRIBUTING.md +0 -116
  89. package/docs/STYLEGUIDE.md +0 -62
  90. package/scripts/webhook-proxy.ts +0 -213
@@ -0,0 +1,197 @@
1
+ /**
2
+ * @zenith/cli - Dev Command
3
+ *
4
+ * Starts the development server with in-memory compilation and hot reload
5
+ */
6
+
7
+ import path from 'path'
8
+ import fs from 'fs'
9
+ import { serve } from 'bun'
10
+ import { requireProject } from '../utils/project'
11
+ import * as logger from '../utils/logger'
12
+ import { compileZenSource } from '../../compiler/index'
13
+ import { discoverLayouts } from '../../compiler/discovery/layouts'
14
+ import { processLayout } from '../../compiler/transform/layoutProcessor'
15
+ import { generateRouteDefinition } from '../../router/manifest'
16
+ import { generateBundleJS } from '../../runtime/bundle-generator'
17
+
18
+ export interface DevOptions {
19
+ port?: number
20
+ }
21
+
22
+ interface CompiledPage {
23
+ html: string
24
+ script: string
25
+ styles: string[]
26
+ route: string
27
+ lastModified: number
28
+ }
29
+
30
+ const pageCache = new Map<string, CompiledPage>()
31
+
32
+ export async function dev(options: DevOptions = {}): Promise<void> {
33
+ const project = requireProject()
34
+ const port = options.port || parseInt(process.env.PORT || '3000', 10)
35
+
36
+ // Support both app/ and src/ directory structures
37
+ const appDir = project.root
38
+ const pagesDir = project.pagesDir
39
+
40
+ logger.header('Zenith Dev Server')
41
+ logger.log(`Project: ${project.root}`)
42
+ logger.log(`Pages: ${project.pagesDir}`)
43
+
44
+ // File extensions that should be served as static assets
45
+ const STATIC_EXTENSIONS = new Set([
46
+ '.js', '.css', '.ico', '.png', '.jpg', '.jpeg', '.gif', '.svg',
47
+ '.webp', '.woff', '.woff2', '.ttf', '.eot', '.json', '.map'
48
+ ])
49
+
50
+ /**
51
+ * Generate the shared runtime JavaScript
52
+ */
53
+ function generateRuntimeJS(): string {
54
+ return generateBundleJS()
55
+ }
56
+
57
+ /**
58
+ * Compile a .zen page in memory
59
+ */
60
+ function compilePageInMemory(pagePath: string): CompiledPage | null {
61
+ try {
62
+ const layoutsDir = path.join(pagesDir, '../layouts')
63
+ const layouts = discoverLayouts(layoutsDir)
64
+
65
+ const source = fs.readFileSync(pagePath, 'utf-8')
66
+
67
+ // Find suitable layout
68
+ let processedSource = source
69
+ let layoutToUse = layouts.get('DefaultLayout')
70
+
71
+ if (layoutToUse) {
72
+ processedSource = processLayout(source, layoutToUse)
73
+ }
74
+
75
+ const result = compileZenSource(processedSource, pagePath)
76
+
77
+ if (!result.finalized) {
78
+ throw new Error('Compilation failed: No finalized output')
79
+ }
80
+
81
+ const routeDef = generateRouteDefinition(pagePath, pagesDir)
82
+
83
+ return {
84
+ html: result.finalized.html,
85
+ script: result.finalized.js,
86
+ styles: result.finalized.styles,
87
+ route: routeDef.path,
88
+ lastModified: Date.now()
89
+ }
90
+ } catch (error: any) {
91
+ logger.error(`Compilation error for ${pagePath}: ${error.message}`)
92
+ return null
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Generate full HTML page from compiled output
98
+ */
99
+ function generateDevHTML(page: CompiledPage): string {
100
+ const runtimeTag = `<script src="/runtime.js"></script>`
101
+ const scriptTag = `<script>\n${page.script}\n</script>`
102
+ const allScripts = `${runtimeTag}\n${scriptTag}`
103
+
104
+ if (page.html.includes('</body>')) {
105
+ return page.html.replace('</body>', `${allScripts}\n</body>`)
106
+ }
107
+
108
+ return `${page.html}\n${allScripts}`
109
+ }
110
+
111
+ /**
112
+ * Find .zen page file for a given route
113
+ */
114
+ function findPageForRoute(route: string): string | null {
115
+ const exactPath = path.join(pagesDir, route === '/' ? 'index.zen' : `${route.slice(1)}.zen`)
116
+ if (fs.existsSync(exactPath)) return exactPath
117
+
118
+ const indexPath = path.join(pagesDir, route === '/' ? 'index.zen' : `${route.slice(1)}/index.zen`)
119
+ if (fs.existsSync(indexPath)) return indexPath
120
+
121
+ return null
122
+ }
123
+
124
+ const cachedRuntimeJS = generateRuntimeJS()
125
+
126
+ const server = serve({
127
+ port,
128
+ async fetch(req) {
129
+ const url = new URL(req.url)
130
+ const pathname = url.pathname
131
+ const ext = path.extname(pathname).toLowerCase()
132
+
133
+ if (pathname === '/runtime.js' || pathname === '/assets/bundle.js') {
134
+ return new Response(cachedRuntimeJS, {
135
+ headers: {
136
+ 'Content-Type': 'application/javascript; charset=utf-8',
137
+ 'Cache-Control': 'no-cache'
138
+ }
139
+ })
140
+ }
141
+
142
+ if (pathname === '/assets/styles.css' || pathname === '/styles/global.css' || pathname === '/app/styles/global.css') {
143
+ const globalCssPath = path.join(pagesDir, '../styles/global.css')
144
+ if (fs.existsSync(globalCssPath)) {
145
+ const css = fs.readFileSync(globalCssPath, 'utf-8')
146
+ return new Response(css, {
147
+ headers: { 'Content-Type': 'text/css; charset=utf-8' }
148
+ })
149
+ }
150
+ }
151
+
152
+ if (STATIC_EXTENSIONS.has(ext)) {
153
+ const publicPath = path.join(pagesDir, '../public', pathname)
154
+ const distPath = path.join(pagesDir, '../dist', pathname)
155
+ const appRelativePath = path.join(pagesDir, '..', pathname)
156
+
157
+ for (const filePath of [publicPath, distPath, appRelativePath]) {
158
+ const file = Bun.file(filePath)
159
+ if (await file.exists()) {
160
+ return new Response(file)
161
+ }
162
+ }
163
+ return new Response('Not found', { status: 404 })
164
+ }
165
+
166
+ const pagePath = findPageForRoute(pathname)
167
+ if (pagePath) {
168
+ let cached = pageCache.get(pagePath)
169
+ const stat = fs.statSync(pagePath)
170
+
171
+ if (!cached || stat.mtimeMs > cached.lastModified) {
172
+ const compiled = compilePageInMemory(pagePath)
173
+ if (compiled) {
174
+ pageCache.set(pagePath, compiled)
175
+ cached = compiled
176
+ }
177
+ }
178
+
179
+ if (cached) {
180
+ const html = generateDevHTML(cached)
181
+ return new Response(html, {
182
+ headers: { 'Content-Type': 'text/html; charset=utf-8' }
183
+ })
184
+ }
185
+ }
186
+
187
+ return new Response('Not Found', { status: 404 })
188
+ }
189
+ })
190
+
191
+ logger.success(`Server running at http://localhost:${server.port}`)
192
+ logger.info('• In-memory compilation active')
193
+ logger.info('• Auto-recompile on file changes')
194
+ logger.info('Press Ctrl+C to stop')
195
+
196
+ await new Promise(() => { })
197
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * @zenith/cli - Command Registry
3
+ *
4
+ * Central registry for all CLI commands
5
+ */
6
+
7
+ import { dev, type DevOptions } from './dev'
8
+ import { preview, type PreviewOptions } from './preview'
9
+ import { build, type BuildOptions } from './build'
10
+ import { add, type AddOptions } from './add'
11
+ import { remove } from './remove'
12
+ import { create } from './create'
13
+ import * as logger from '../utils/logger'
14
+
15
+ export interface Command {
16
+ name: string
17
+ description: string
18
+ usage: string
19
+ run: (args: string[], options: Record<string, string>) => Promise<void>
20
+ }
21
+
22
+ export const commands: Command[] = [
23
+ {
24
+ name: 'create',
25
+ description: 'Create a new Zenith project',
26
+ usage: 'zenith create [project-name]',
27
+ async run(args) {
28
+ const projectName = args[0]
29
+ await create(projectName)
30
+ }
31
+ },
32
+ {
33
+ name: 'dev',
34
+ description: 'Start development server',
35
+ usage: 'zenith dev [--port <port>]',
36
+ async run(args, options) {
37
+ const opts: DevOptions = {}
38
+ if (options.port) opts.port = parseInt(options.port, 10)
39
+ await dev(opts)
40
+ }
41
+ },
42
+ {
43
+ name: 'preview',
44
+ description: 'Preview production build',
45
+ usage: 'zenith preview [--port <port>]',
46
+ async run(args, options) {
47
+ const opts: PreviewOptions = {}
48
+ if (options.port) opts.port = parseInt(options.port, 10)
49
+ await preview(opts)
50
+ }
51
+ },
52
+ {
53
+ name: 'build',
54
+ description: 'Build for production',
55
+ usage: 'zenith build [--outDir <dir>]',
56
+ async run(args, options) {
57
+ const opts: BuildOptions = {}
58
+ if (options.outDir) opts.outDir = options.outDir
59
+ await build(opts)
60
+ }
61
+ },
62
+ {
63
+ name: 'add',
64
+ description: 'Add a plugin',
65
+ usage: 'zenith add <plugin>',
66
+ async run(args) {
67
+ const pluginName = args[0]
68
+ if (!pluginName) {
69
+ logger.error('Plugin name required')
70
+ process.exit(1)
71
+ }
72
+ await add(pluginName)
73
+ }
74
+ },
75
+ {
76
+ name: 'remove',
77
+ description: 'Remove a plugin',
78
+ usage: 'zenith remove <plugin>',
79
+ async run(args) {
80
+ const pluginName = args[0]
81
+ if (!pluginName) {
82
+ logger.error('Plugin name required')
83
+ process.exit(1)
84
+ }
85
+ await remove(pluginName)
86
+ }
87
+ }
88
+ ]
89
+
90
+ // Placeholder commands for future expansion
91
+ export const placeholderCommands = ['test', 'export', 'deploy']
92
+
93
+ export function getCommand(name: string): Command | undefined {
94
+ return commands.find(c => c.name === name)
95
+ }
96
+
97
+ export function showHelp(): void {
98
+ logger.header('Zenith CLI')
99
+ console.log('Usage: zenith <command> [options]\n')
100
+ console.log('Commands:')
101
+
102
+ for (const cmd of commands) {
103
+ console.log(` ${cmd.name.padEnd(12)} ${cmd.description}`)
104
+ }
105
+
106
+ console.log('\nComing soon:')
107
+ for (const cmd of placeholderCommands) {
108
+ console.log(` ${cmd.padEnd(12)} (not yet implemented)`)
109
+ }
110
+
111
+ console.log('\nRun `zenith <command> --help` for command-specific help.')
112
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @zenith/cli - Preview Command
3
+ *
4
+ * Serves the production build from the distribution directory.
5
+ */
6
+
7
+ import path from 'path'
8
+ import { serve } from 'bun'
9
+ import { requireProject } from '../utils/project'
10
+ import * as logger from '../utils/logger'
11
+
12
+ export interface PreviewOptions {
13
+ port?: number
14
+ }
15
+
16
+ export async function preview(options: PreviewOptions = {}): Promise<void> {
17
+ const project = requireProject()
18
+ const distDir = project.distDir
19
+ const port = options.port || parseInt(process.env.PORT || '4173', 10)
20
+
21
+ logger.header('Zenith Preview Server')
22
+ logger.log(`Serving: ${distDir}`)
23
+
24
+ // File extensions that should be served as static assets
25
+ const STATIC_EXTENSIONS = new Set([
26
+ '.js', '.css', '.ico', '.png', '.jpg', '.jpeg', '.gif', '.svg',
27
+ '.webp', '.woff', '.woff2', '.ttf', '.eot', '.json', '.map'
28
+ ])
29
+
30
+ const server = serve({
31
+ port,
32
+ async fetch(req) {
33
+ const url = new URL(req.url)
34
+ const pathname = url.pathname
35
+ const ext = path.extname(pathname).toLowerCase()
36
+
37
+ if (STATIC_EXTENSIONS.has(ext)) {
38
+ const filePath = path.join(distDir, pathname)
39
+ const file = Bun.file(filePath)
40
+ if (await file.exists()) {
41
+ return new Response(file)
42
+ }
43
+ return new Response('Not found', { status: 404 })
44
+ }
45
+
46
+ const indexPath = path.join(distDir, 'index.html')
47
+ const indexFile = Bun.file(indexPath)
48
+ if (await indexFile.exists()) {
49
+ return new Response(indexFile, {
50
+ headers: { 'Content-Type': 'text/html; charset=utf-8' }
51
+ })
52
+ }
53
+
54
+ return new Response('No production build found. Run `zenith build` first.', { status: 500 })
55
+ }
56
+ })
57
+
58
+ logger.success(`Preview server running at http://localhost:${server.port}`)
59
+ logger.info('Press Ctrl+C to stop')
60
+
61
+ await new Promise(() => { })
62
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @zenith/cli - Remove Command
3
+ *
4
+ * Removes a plugin from the project registry
5
+ */
6
+
7
+ import { requireProject } from '../utils/project'
8
+ import { removePlugin, hasPlugin } from '../utils/plugin-manager'
9
+ import * as logger from '../utils/logger'
10
+
11
+ export async function remove(pluginName: string): Promise<void> {
12
+ requireProject()
13
+
14
+ logger.header('Remove Plugin')
15
+
16
+ if (!pluginName) {
17
+ logger.error('Plugin name required. Usage: zenith remove <plugin>')
18
+ process.exit(1)
19
+ }
20
+
21
+ if (!hasPlugin(pluginName)) {
22
+ logger.warn(`Plugin "${pluginName}" is not registered`)
23
+ return
24
+ }
25
+
26
+ const success = removePlugin(pluginName)
27
+
28
+ if (success) {
29
+ logger.info(`Plugin "${pluginName}" has been unregistered.`)
30
+ logger.info('Note: You may want to remove the package manually:')
31
+ logger.log(` bun remove @zenith/plugin-${pluginName}`)
32
+ }
33
+ }
package/cli/index.ts ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @zenith/cli
3
+ *
4
+ * CLI for Zenith framework - dev server, build tools, and plugin management
5
+ */
6
+
7
+ export * from './commands/index'
8
+ export * from './utils/logger'
9
+ export * from './utils/project'
10
+ export * from './utils/plugin-manager'
package/cli/main.ts ADDED
@@ -0,0 +1,101 @@
1
+ /**
2
+ * @zenith/cli - Shared CLI Execution Logic
3
+ */
4
+
5
+ import process from 'node:process'
6
+ import { getCommand, showHelp, placeholderCommands } from './commands/index'
7
+ import * as logger from './utils/logger'
8
+ import { execSync } from 'node:child_process'
9
+
10
+ /**
11
+ * Check if Bun is available in the environment
12
+ */
13
+ function checkBun() {
14
+ try {
15
+ execSync('bun --version', { stdio: 'pipe' })
16
+ return true
17
+ } catch {
18
+ return false
19
+ }
20
+ }
21
+
22
+ export interface CLIOptions {
23
+ defaultCommand?: string
24
+ }
25
+
26
+ /**
27
+ * Main CLI execution entry point
28
+ */
29
+ export async function runCLI(options: CLIOptions = {}) {
30
+ // 1. Check for Bun
31
+ if (!checkBun()) {
32
+ logger.error('Bun is required to run Zenith.')
33
+ logger.info('Please install Bun: https://bun.sh/install')
34
+ process.exit(1)
35
+ }
36
+
37
+ const args = process.argv.slice(2)
38
+ const VERSION = '0.3.0'
39
+
40
+ // 2. Handle global version flag
41
+ if (args.includes('--version') || args.includes('-v')) {
42
+ console.log(`Zenith CLI v${VERSION}`)
43
+ process.exit(0)
44
+ }
45
+
46
+ // Determine command name: either from args or default (for aliases)
47
+ let commandName = args[0]
48
+ let commandArgs = args.slice(1)
49
+
50
+ if (options.defaultCommand) {
51
+ if (!commandName || commandName.startsWith('-')) {
52
+ commandName = options.defaultCommand
53
+ commandArgs = args
54
+ }
55
+ }
56
+
57
+ // Handle help
58
+ if (!commandName || ((commandName === '--help' || commandName === '-h') && !options.defaultCommand)) {
59
+ showHelp()
60
+ process.exit(0)
61
+ }
62
+
63
+ // Parse options (--key value format) for internal use if needed
64
+ const cliOptions: Record<string, string> = {}
65
+ for (let i = 0; i < commandArgs.length; i++) {
66
+ const arg = commandArgs[i]!
67
+ if (arg.startsWith('--')) {
68
+ const key = arg.slice(2)
69
+ const value = commandArgs[i + 1]
70
+ if (value && !value.startsWith('--')) {
71
+ cliOptions[key] = value
72
+ i++
73
+ } else {
74
+ cliOptions[key] = 'true'
75
+ }
76
+ }
77
+ }
78
+
79
+ // Check for placeholder commands
80
+ if (placeholderCommands.includes(commandName)) {
81
+ logger.warn(`Command "${commandName}" is not yet implemented.`)
82
+ logger.info('This feature is planned for a future release.')
83
+ process.exit(0)
84
+ }
85
+
86
+ const command = getCommand(commandName)
87
+
88
+ if (!command) {
89
+ logger.error(`Unknown command: ${commandName}`)
90
+ showHelp()
91
+ process.exit(1)
92
+ }
93
+
94
+ try {
95
+ await command!.run(commandArgs, cliOptions)
96
+ } catch (err: unknown) {
97
+ const message = err instanceof Error ? err.message : String(err)
98
+ logger.error(message)
99
+ process.exit(1)
100
+ }
101
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Zenith CLI Branding
3
+ *
4
+ * ASCII art logo, colors, animations, and styled output
5
+ */
6
+
7
+ import pc from 'picocolors'
8
+
9
+ // Brand colors
10
+ export const colors = {
11
+ primary: pc.blue,
12
+ secondary: pc.cyan,
13
+ success: pc.green,
14
+ warning: pc.yellow,
15
+ error: pc.red,
16
+ muted: pc.gray,
17
+ bold: pc.bold,
18
+ dim: pc.dim
19
+ }
20
+
21
+ // ASCII Zenith logo
22
+ export const LOGO = `
23
+ ${pc.cyan('╔═══════════════════════════════════════════════════════════╗')}
24
+ ${pc.cyan('║')} ${pc.cyan('║')}
25
+ ${pc.cyan('║')} ${pc.bold(pc.blue('███████╗'))}${pc.bold(pc.cyan('███████╗'))}${pc.bold(pc.blue('███╗ ██╗'))}${pc.bold(pc.cyan('██╗'))}${pc.bold(pc.blue('████████╗'))}${pc.bold(pc.cyan('██╗ ██╗'))} ${pc.cyan('║')}
26
+ ${pc.cyan('║')} ${pc.bold(pc.blue('╚══███╔╝'))}${pc.bold(pc.cyan('██╔════╝'))}${pc.bold(pc.blue('████╗ ██║'))}${pc.bold(pc.cyan('██║'))}${pc.bold(pc.blue('╚══██╔══╝'))}${pc.bold(pc.cyan('██║ ██║'))} ${pc.cyan('║')}
27
+ ${pc.cyan('║')} ${pc.bold(pc.blue(' ███╔╝ '))}${pc.bold(pc.cyan('█████╗ '))}${pc.bold(pc.blue('██╔██╗ ██║'))}${pc.bold(pc.cyan('██║'))}${pc.bold(pc.blue(' ██║ '))}${pc.bold(pc.cyan('███████║'))} ${pc.cyan('║')}
28
+ ${pc.cyan('║')} ${pc.bold(pc.blue(' ███╔╝ '))}${pc.bold(pc.cyan('██╔══╝ '))}${pc.bold(pc.blue('██║╚██╗██║'))}${pc.bold(pc.cyan('██║'))}${pc.bold(pc.blue(' ██║ '))}${pc.bold(pc.cyan('██╔══██║'))} ${pc.cyan('║')}
29
+ ${pc.cyan('║')} ${pc.bold(pc.blue('███████╗'))}${pc.bold(pc.cyan('███████╗'))}${pc.bold(pc.blue('██║ ╚████║'))}${pc.bold(pc.cyan('██║'))}${pc.bold(pc.blue(' ██║ '))}${pc.bold(pc.cyan('██║ ██║'))} ${pc.cyan('║')}
30
+ ${pc.cyan('║')} ${pc.bold(pc.blue('╚══════╝'))}${pc.bold(pc.cyan('╚══════╝'))}${pc.bold(pc.blue('╚═╝ ╚═══╝'))}${pc.bold(pc.cyan('╚═╝'))}${pc.bold(pc.blue(' ╚═╝ '))}${pc.bold(pc.cyan('╚═╝ ╚═╝'))} ${pc.cyan('║')}
31
+ ${pc.cyan('║')} ${pc.cyan('║')}
32
+ ${pc.cyan('║')} ${pc.dim('The Modern Reactive Web Framework')} ${pc.cyan('║')}
33
+ ${pc.cyan('║')} ${pc.cyan('║')}
34
+ ${pc.cyan('╚═══════════════════════════════════════════════════════════╝')}
35
+ `
36
+
37
+ // Compact logo for smaller spaces
38
+ export const LOGO_COMPACT = `
39
+ ${pc.bold(pc.blue('⚡'))} ${pc.bold(pc.cyan('ZENITH'))} ${pc.dim('- Modern Reactive Framework')}
40
+ `
41
+
42
+ // Spinner frames for animations
43
+ const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
44
+
45
+ export class Spinner {
46
+ private interval: ReturnType<typeof setInterval> | null = null
47
+ private frameIndex = 0
48
+ private message: string
49
+
50
+ constructor(message: string) {
51
+ this.message = message
52
+ }
53
+
54
+ start() {
55
+ this.interval = setInterval(() => {
56
+ process.stdout.write(`\r${pc.cyan(spinnerFrames[this.frameIndex])} ${this.message}`)
57
+ this.frameIndex = (this.frameIndex + 1) % spinnerFrames.length
58
+ }, 80)
59
+ }
60
+
61
+ stop(finalMessage?: string) {
62
+ if (this.interval) {
63
+ clearInterval(this.interval)
64
+ this.interval = null
65
+ }
66
+ process.stdout.write('\r' + ' '.repeat(this.message.length + 5) + '\r')
67
+ if (finalMessage) {
68
+ console.log(finalMessage)
69
+ }
70
+ }
71
+
72
+ succeed(message: string) {
73
+ this.stop(`${pc.green('✓')} ${message}`)
74
+ }
75
+
76
+ fail(message: string) {
77
+ this.stop(`${pc.red('✗')} ${message}`)
78
+ }
79
+ }
80
+
81
+ // Styled output functions
82
+ export function showLogo() {
83
+ console.log(LOGO)
84
+ }
85
+
86
+ export function showCompactLogo() {
87
+ console.log(LOGO_COMPACT)
88
+ }
89
+
90
+ export function header(text: string) {
91
+ console.log(`\n${pc.bold(pc.cyan('▸'))} ${pc.bold(text)}\n`)
92
+ }
93
+
94
+ export function success(text: string) {
95
+ console.log(`${pc.green('✓')} ${text}`)
96
+ }
97
+
98
+ export function error(text: string) {
99
+ console.log(`${pc.red('✗')} ${text}`)
100
+ }
101
+
102
+ export function warn(text: string) {
103
+ console.log(`${pc.yellow('⚠')} ${text}`)
104
+ }
105
+
106
+ export function info(text: string) {
107
+ console.log(`${pc.blue('ℹ')} ${text}`)
108
+ }
109
+
110
+ export function step(num: number, text: string) {
111
+ console.log(`${pc.dim(`[${num}]`)} ${text}`)
112
+ }
113
+
114
+ export function highlight(text: string): string {
115
+ return pc.cyan(text)
116
+ }
117
+
118
+ export function dim(text: string): string {
119
+ return pc.dim(text)
120
+ }
121
+
122
+ export function bold(text: string): string {
123
+ return pc.bold(text)
124
+ }
125
+
126
+ // Animated intro (optional)
127
+ export async function showIntro() {
128
+ console.clear()
129
+ showLogo()
130
+ await sleep(300)
131
+ }
132
+
133
+ function sleep(ms: number): Promise<void> {
134
+ return new Promise(resolve => setTimeout(resolve, ms))
135
+ }
136
+
137
+ // Next steps box
138
+ export function showNextSteps(projectName: string) {
139
+ console.log(`
140
+ ${pc.cyan('┌─────────────────────────────────────────────────────────┐')}
141
+ ${pc.cyan('│')} ${pc.cyan('│')}
142
+ ${pc.cyan('│')} ${pc.green('✨')} ${pc.bold('Your Zenith app is ready!')} ${pc.cyan('│')}
143
+ ${pc.cyan('│')} ${pc.cyan('│')}
144
+ ${pc.cyan('│')} ${pc.dim('Next steps:')} ${pc.cyan('│')}
145
+ ${pc.cyan('│')} ${pc.cyan('│')}
146
+ ${pc.cyan('│')} ${pc.cyan('$')} ${pc.bold(`cd ${projectName}`)}${' '.repeat(Math.max(0, 40 - projectName.length))}${pc.cyan('│')}
147
+ ${pc.cyan('│')} ${pc.cyan('$')} ${pc.bold('bun run dev')} ${pc.cyan('│')}
148
+ ${pc.cyan('│')} ${pc.cyan('│')}
149
+ ${pc.cyan('│')} ${pc.dim('Then open')} ${pc.underline(pc.blue('http://localhost:3000'))} ${pc.cyan('│')}
150
+ ${pc.cyan('│')} ${pc.cyan('│')}
151
+ ${pc.cyan('└─────────────────────────────────────────────────────────┘')}
152
+ `)
153
+ }