popeye-cli 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +50 -8
  3. package/dist/cli/commands/create.d.ts.map +1 -1
  4. package/dist/cli/commands/create.js +54 -4
  5. package/dist/cli/commands/create.js.map +1 -1
  6. package/dist/cli/interactive.d.ts +29 -0
  7. package/dist/cli/interactive.d.ts.map +1 -1
  8. package/dist/cli/interactive.js +90 -7
  9. package/dist/cli/interactive.js.map +1 -1
  10. package/dist/generators/all.d.ts +4 -1
  11. package/dist/generators/all.d.ts.map +1 -1
  12. package/dist/generators/all.js +36 -316
  13. package/dist/generators/all.js.map +1 -1
  14. package/dist/generators/doc-parser.d.ts +18 -3
  15. package/dist/generators/doc-parser.d.ts.map +1 -1
  16. package/dist/generators/doc-parser.js +81 -10
  17. package/dist/generators/doc-parser.js.map +1 -1
  18. package/dist/generators/frontend-design-analyzer.d.ts +30 -0
  19. package/dist/generators/frontend-design-analyzer.d.ts.map +1 -0
  20. package/dist/generators/frontend-design-analyzer.js +208 -0
  21. package/dist/generators/frontend-design-analyzer.js.map +1 -0
  22. package/dist/generators/shared-packages.d.ts +45 -0
  23. package/dist/generators/shared-packages.d.ts.map +1 -0
  24. package/dist/generators/shared-packages.js +456 -0
  25. package/dist/generators/shared-packages.js.map +1 -0
  26. package/dist/generators/templates/index.d.ts +4 -0
  27. package/dist/generators/templates/index.d.ts.map +1 -1
  28. package/dist/generators/templates/index.js +4 -0
  29. package/dist/generators/templates/index.js.map +1 -1
  30. package/dist/generators/templates/website-components.d.ts.map +1 -1
  31. package/dist/generators/templates/website-components.js +36 -11
  32. package/dist/generators/templates/website-components.js.map +1 -1
  33. package/dist/generators/templates/website-config.d.ts +15 -1
  34. package/dist/generators/templates/website-config.d.ts.map +1 -1
  35. package/dist/generators/templates/website-config.js +155 -13
  36. package/dist/generators/templates/website-config.js.map +1 -1
  37. package/dist/generators/templates/website-landing.d.ts +24 -0
  38. package/dist/generators/templates/website-landing.d.ts.map +1 -0
  39. package/dist/generators/templates/website-landing.js +276 -0
  40. package/dist/generators/templates/website-landing.js.map +1 -0
  41. package/dist/generators/templates/website-layout.d.ts +42 -0
  42. package/dist/generators/templates/website-layout.d.ts.map +1 -0
  43. package/dist/generators/templates/website-layout.js +408 -0
  44. package/dist/generators/templates/website-layout.js.map +1 -0
  45. package/dist/generators/templates/website-pricing.d.ts +11 -0
  46. package/dist/generators/templates/website-pricing.d.ts.map +1 -0
  47. package/dist/generators/templates/website-pricing.js +313 -0
  48. package/dist/generators/templates/website-pricing.js.map +1 -0
  49. package/dist/generators/templates/website-sections.d.ts +102 -0
  50. package/dist/generators/templates/website-sections.d.ts.map +1 -0
  51. package/dist/generators/templates/website-sections.js +444 -0
  52. package/dist/generators/templates/website-sections.js.map +1 -0
  53. package/dist/generators/templates/website.d.ts +10 -50
  54. package/dist/generators/templates/website.d.ts.map +1 -1
  55. package/dist/generators/templates/website.js +12 -788
  56. package/dist/generators/templates/website.js.map +1 -1
  57. package/dist/generators/website-content-scanner.d.ts +37 -0
  58. package/dist/generators/website-content-scanner.d.ts.map +1 -0
  59. package/dist/generators/website-content-scanner.js +165 -0
  60. package/dist/generators/website-content-scanner.js.map +1 -0
  61. package/dist/generators/website-context.d.ts +38 -2
  62. package/dist/generators/website-context.d.ts.map +1 -1
  63. package/dist/generators/website-context.js +179 -19
  64. package/dist/generators/website-context.js.map +1 -1
  65. package/dist/generators/website-debug.d.ts +68 -0
  66. package/dist/generators/website-debug.d.ts.map +1 -0
  67. package/dist/generators/website-debug.js +93 -0
  68. package/dist/generators/website-debug.js.map +1 -0
  69. package/dist/generators/website.d.ts +2 -0
  70. package/dist/generators/website.d.ts.map +1 -1
  71. package/dist/generators/website.js +66 -4
  72. package/dist/generators/website.js.map +1 -1
  73. package/dist/generators/workspace-root.d.ts +27 -0
  74. package/dist/generators/workspace-root.d.ts.map +1 -0
  75. package/dist/generators/workspace-root.js +100 -0
  76. package/dist/generators/workspace-root.js.map +1 -0
  77. package/dist/state/index.d.ts +8 -0
  78. package/dist/state/index.d.ts.map +1 -1
  79. package/dist/state/index.js +10 -0
  80. package/dist/state/index.js.map +1 -1
  81. package/dist/types/workflow.d.ts +6 -0
  82. package/dist/types/workflow.d.ts.map +1 -1
  83. package/dist/types/workflow.js +2 -0
  84. package/dist/types/workflow.js.map +1 -1
  85. package/dist/upgrade/handlers.d.ts +15 -0
  86. package/dist/upgrade/handlers.d.ts.map +1 -1
  87. package/dist/upgrade/handlers.js +52 -0
  88. package/dist/upgrade/handlers.js.map +1 -1
  89. package/dist/workflow/auto-fix-bundler.d.ts +37 -0
  90. package/dist/workflow/auto-fix-bundler.d.ts.map +1 -0
  91. package/dist/workflow/auto-fix-bundler.js +320 -0
  92. package/dist/workflow/auto-fix-bundler.js.map +1 -0
  93. package/dist/workflow/auto-fix.d.ts.map +1 -1
  94. package/dist/workflow/auto-fix.js +10 -3
  95. package/dist/workflow/auto-fix.js.map +1 -1
  96. package/dist/workflow/index.d.ts +1 -0
  97. package/dist/workflow/index.d.ts.map +1 -1
  98. package/dist/workflow/index.js +12 -0
  99. package/dist/workflow/index.js.map +1 -1
  100. package/dist/workflow/overview.d.ts.map +1 -1
  101. package/dist/workflow/overview.js +4 -0
  102. package/dist/workflow/overview.js.map +1 -1
  103. package/dist/workflow/plan-mode.d.ts +4 -3
  104. package/dist/workflow/plan-mode.d.ts.map +1 -1
  105. package/dist/workflow/plan-mode.js +69 -5
  106. package/dist/workflow/plan-mode.js.map +1 -1
  107. package/dist/workflow/website-strategy.d.ts +9 -0
  108. package/dist/workflow/website-strategy.d.ts.map +1 -1
  109. package/dist/workflow/website-strategy.js +73 -1
  110. package/dist/workflow/website-strategy.js.map +1 -1
  111. package/dist/workflow/website-updater.d.ts.map +1 -1
  112. package/dist/workflow/website-updater.js +15 -4
  113. package/dist/workflow/website-updater.js.map +1 -1
  114. package/package.json +1 -1
  115. package/src/cli/commands/create.ts +58 -4
  116. package/src/cli/interactive.ts +96 -7
  117. package/src/generators/all.ts +44 -332
  118. package/src/generators/doc-parser.ts +87 -10
  119. package/src/generators/frontend-design-analyzer.ts +261 -0
  120. package/src/generators/shared-packages.ts +500 -0
  121. package/src/generators/templates/index.ts +4 -0
  122. package/src/generators/templates/website-components.ts +36 -11
  123. package/src/generators/templates/website-config.ts +166 -13
  124. package/src/generators/templates/website-landing.ts +331 -0
  125. package/src/generators/templates/website-layout.ts +443 -0
  126. package/src/generators/templates/website-pricing.ts +330 -0
  127. package/src/generators/templates/website-sections.ts +541 -0
  128. package/src/generators/templates/website.ts +38 -851
  129. package/src/generators/website-content-scanner.ts +208 -0
  130. package/src/generators/website-context.ts +248 -20
  131. package/src/generators/website-debug.ts +130 -0
  132. package/src/generators/website.ts +71 -3
  133. package/src/generators/workspace-root.ts +113 -0
  134. package/src/state/index.ts +14 -0
  135. package/src/types/workflow.ts +6 -0
  136. package/src/upgrade/handlers.ts +65 -0
  137. package/src/workflow/auto-fix-bundler.ts +392 -0
  138. package/src/workflow/auto-fix.ts +11 -3
  139. package/src/workflow/index.ts +12 -0
  140. package/src/workflow/overview.ts +6 -0
  141. package/src/workflow/plan-mode.ts +81 -7
  142. package/src/workflow/website-strategy.ts +75 -1
  143. package/src/workflow/website-updater.ts +17 -6
  144. package/tests/cli/project-naming.test.ts +136 -0
  145. package/tests/generators/doc-parser.test.ts +121 -0
  146. package/tests/generators/frontend-design-analyzer.test.ts +90 -0
  147. package/tests/generators/quality-gate.test.ts +183 -0
  148. package/tests/generators/shared-packages.test.ts +83 -0
  149. package/tests/generators/website-components.test.ts +1 -1
  150. package/tests/generators/website-config.test.ts +84 -0
  151. package/tests/generators/website-content-scanner.test.ts +181 -0
  152. package/tests/generators/website-context.test.ts +109 -0
  153. package/tests/generators/website-debug.test.ts +77 -0
  154. package/tests/generators/website-landing.test.ts +188 -0
  155. package/tests/generators/website-pricing.test.ts +98 -0
  156. package/tests/generators/website-sections.test.ts +245 -0
  157. package/tests/generators/workspace-root.test.ts +105 -0
  158. package/tests/upgrade/handlers.test.ts +162 -0
  159. package/tests/workflow/auto-fix-bundler.test.ts +242 -0
  160. package/tests/workflow/plan-mode.test.ts +111 -1
  161. package/tests/workflow/website-strategy.test.ts +55 -0
@@ -0,0 +1,443 @@
1
+ /**
2
+ * Website layout and utility page generators
3
+ * Layout, globals CSS, sitemap, robots, docs, blog, readme, spec, test
4
+ */
5
+
6
+ import type { WebsiteContentContext } from '../website-context.js';
7
+
8
+ /**
9
+ * Escape a string for safe use inside JSX template literals
10
+ */
11
+ function escapeJsx(str: string): string {
12
+ return str
13
+ .replace(/\\/g, '\\\\')
14
+ .replace(/'/g, "\\'")
15
+ .replace(/`/g, '\\`')
16
+ .replace(/\$/g, '\\$');
17
+ }
18
+
19
+ /**
20
+ * Convert hex color to HSL string for CSS custom properties
21
+ * Returns format: "H S% L%"
22
+ */
23
+ function hexToHslString(hex: string): string {
24
+ const h = hex.replace('#', '');
25
+ const r = parseInt(h.substring(0, 2), 16) / 255;
26
+ const g = parseInt(h.substring(2, 4), 16) / 255;
27
+ const b = parseInt(h.substring(4, 6), 16) / 255;
28
+
29
+ const max = Math.max(r, g, b);
30
+ const min = Math.min(r, g, b);
31
+ const l = (max + min) / 2;
32
+
33
+ if (max === min) {
34
+ return `0 0% ${Math.round(l * 100)}%`;
35
+ }
36
+
37
+ const d = max - min;
38
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
39
+
40
+ let hue = 0;
41
+ if (max === r) {
42
+ hue = ((g - b) / d + (g < b ? 6 : 0)) / 6;
43
+ } else if (max === g) {
44
+ hue = ((b - r) / d + 2) / 6;
45
+ } else {
46
+ hue = ((r - g) / d + 4) / 6;
47
+ }
48
+
49
+ return `${Math.round(hue * 360)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%`;
50
+ }
51
+
52
+ /**
53
+ * Generate root layout.tsx with metadata
54
+ */
55
+ export function generateWebsiteLayout(
56
+ projectName: string,
57
+ context?: WebsiteContentContext
58
+ ): string {
59
+ const title = projectName
60
+ .split('-')
61
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
62
+ .join(' ');
63
+
64
+ const strategy = context?.strategy;
65
+ const displayName = context?.productName || title;
66
+ const desc = strategy?.messaging.longDescription
67
+ || context?.description
68
+ || `${displayName} - Your modern web application`;
69
+
70
+ const keywords = strategy?.seoStrategy.primaryKeywords
71
+ ? strategy.seoStrategy.primaryKeywords.map(k => `'${escapeJsx(k)}'`).join(', ')
72
+ : `'${projectName}', 'web app', 'nextjs'`;
73
+
74
+ return `import type { Metadata } from 'next';
75
+ import { Inter } from 'next/font/google';
76
+ import './globals.css';
77
+
78
+ const inter = Inter({
79
+ subsets: ['latin'],
80
+ variable: '--font-inter',
81
+ });
82
+
83
+ const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com';
84
+
85
+ export const metadata: Metadata = {
86
+ metadataBase: new URL(BASE_URL),
87
+ title: {
88
+ default: '${escapeJsx(displayName)}',
89
+ template: '%s | ${escapeJsx(displayName)}',
90
+ },
91
+ description: '${escapeJsx(desc)}',
92
+ keywords: [${keywords}],
93
+ authors: [{ name: '${escapeJsx(displayName)} Team' }],
94
+ creator: '${escapeJsx(displayName)}',
95
+ openGraph: {
96
+ type: 'website',
97
+ locale: 'en_US',
98
+ url: BASE_URL,
99
+ siteName: '${escapeJsx(displayName)}',
100
+ title: '${escapeJsx(displayName)}',
101
+ description: '${escapeJsx(desc)}',
102
+ },
103
+ twitter: {
104
+ card: 'summary_large_image',
105
+ title: '${escapeJsx(displayName)}',
106
+ description: '${escapeJsx(desc)}',
107
+ },
108
+ robots: {
109
+ index: true,
110
+ follow: true,
111
+ },
112
+ };
113
+
114
+ export default function RootLayout({
115
+ children,
116
+ }: {
117
+ children: React.ReactNode;
118
+ }) {
119
+ return (
120
+ <html lang="en" className={inter.variable}>
121
+ <body className="min-h-screen bg-background text-foreground antialiased">
122
+ {children}
123
+ </body>
124
+ </html>
125
+ );
126
+ }
127
+ `;
128
+ }
129
+
130
+ /**
131
+ * Generate globals.css with full CSS variable set
132
+ */
133
+ export function generateWebsiteGlobalsCss(
134
+ context?: WebsiteContentContext
135
+ ): string {
136
+ const primaryHsl = context?.brand?.primaryColor
137
+ ? hexToHslString(context.brand.primaryColor)
138
+ : '199 89% 48%';
139
+
140
+ return `@tailwind base;
141
+ @tailwind components;
142
+ @tailwind utilities;
143
+
144
+ @layer base {
145
+ :root {
146
+ --background: 0 0% 100%;
147
+ --foreground: 222.2 84% 4.9%;
148
+ --primary: ${primaryHsl};
149
+ --primary-foreground: 210 40% 98%;
150
+ --muted: 210 40% 96%;
151
+ --muted-foreground: 215 16% 47%;
152
+ --accent: 210 40% 96%;
153
+ --accent-foreground: 222.2 47% 11%;
154
+ --card: 0 0% 100%;
155
+ --card-foreground: 222.2 84% 4.9%;
156
+ --border: 214 32% 91%;
157
+ --ring: ${primaryHsl};
158
+ --radius: 0.5rem;
159
+ }
160
+
161
+ body {
162
+ @apply bg-background text-foreground;
163
+ }
164
+ }
165
+
166
+ html {
167
+ scroll-behavior: smooth;
168
+ }
169
+
170
+ @layer components {
171
+ .container {
172
+ @apply mx-auto max-w-7xl px-4 sm:px-6 lg:px-8;
173
+ }
174
+ }
175
+ `;
176
+ }
177
+
178
+ /**
179
+ * Generate sitemap.ts
180
+ */
181
+ export function generateWebsiteSitemap(projectName: string): string {
182
+ return `import { MetadataRoute } from 'next';
183
+
184
+ export default function sitemap(): MetadataRoute.Sitemap {
185
+ const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com';
186
+
187
+ return [
188
+ {
189
+ url: baseUrl,
190
+ lastModified: new Date(),
191
+ changeFrequency: 'weekly',
192
+ priority: 1,
193
+ },
194
+ {
195
+ url: \`\${baseUrl}/pricing\`,
196
+ lastModified: new Date(),
197
+ changeFrequency: 'monthly',
198
+ priority: 0.8,
199
+ },
200
+ {
201
+ url: \`\${baseUrl}/docs\`,
202
+ lastModified: new Date(),
203
+ changeFrequency: 'weekly',
204
+ priority: 0.8,
205
+ },
206
+ {
207
+ url: \`\${baseUrl}/blog\`,
208
+ lastModified: new Date(),
209
+ changeFrequency: 'daily',
210
+ priority: 0.7,
211
+ },
212
+ ];
213
+ }
214
+ `;
215
+ }
216
+
217
+ /**
218
+ * Generate robots.ts
219
+ */
220
+ export function generateWebsiteRobots(projectName: string): string {
221
+ return `import { MetadataRoute } from 'next';
222
+
223
+ export default function robots(): MetadataRoute.Robots {
224
+ const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com';
225
+
226
+ return {
227
+ rules: [
228
+ {
229
+ userAgent: '*',
230
+ allow: '/',
231
+ disallow: ['/api/', '/admin/'],
232
+ },
233
+ ],
234
+ sitemap: \`\${baseUrl}/sitemap.xml\`,
235
+ };
236
+ }
237
+ `;
238
+ }
239
+
240
+ /**
241
+ * Generate website README
242
+ */
243
+ export function generateWebsiteReadme(projectName: string): string {
244
+ const title = projectName
245
+ .split('-')
246
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
247
+ .join(' ');
248
+
249
+ return `# ${title} Website
250
+
251
+ Next.js marketing website with SEO optimization.
252
+
253
+ ## Getting Started
254
+
255
+ \`\`\`bash
256
+ # Install dependencies
257
+ npm install
258
+
259
+ # Run development server (port 3001)
260
+ npm run dev
261
+
262
+ # Build for production
263
+ npm run build
264
+
265
+ # Run production server
266
+ npm start
267
+ \`\`\`
268
+
269
+ ## SEO Features
270
+
271
+ - Server-side rendering (SSR)
272
+ - Auto-generated sitemap
273
+ - robots.txt configuration
274
+ - OpenGraph and Twitter meta tags
275
+ - Structured data support (Organization + SoftwareApplication + FAQ)
276
+
277
+ ## Project Structure
278
+
279
+ \`\`\`
280
+ src/
281
+ app/
282
+ layout.tsx # Root layout with metadata
283
+ page.tsx # Landing page (10 sections)
284
+ pricing/ # Pricing page with comparison
285
+ docs/ # Documentation
286
+ blog/ # Blog
287
+ sitemap.ts # Auto-generated sitemap
288
+ robots.ts # robots.txt config
289
+ components/ # UI components (Header, Footer, JsonLd)
290
+ lib/ # Utilities
291
+ content/
292
+ blog/ # MDX blog posts
293
+ docs/ # MDX documentation
294
+ \`\`\`
295
+
296
+ ## Development
297
+
298
+ - Port: 3001 (to avoid conflicts with frontend on 5173)
299
+ - API URL: Configure via NEXT_PUBLIC_APP_URL
300
+ `;
301
+ }
302
+
303
+ /**
304
+ * Generate website spec JSON with optional context
305
+ */
306
+ export function generateWebsiteSpec(
307
+ projectName: string,
308
+ context?: WebsiteContentContext
309
+ ): string {
310
+ const title = projectName
311
+ .split('-')
312
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
313
+ .join(' ');
314
+
315
+ const displayName = context?.productName || title;
316
+ const tagline = context?.tagline || context?.description || 'Build something amazing';
317
+ const primaryColor = context?.brand?.primaryColor || '#0ea5e9';
318
+
319
+ return JSON.stringify(
320
+ {
321
+ version: '1.0',
322
+ brand: {
323
+ name: displayName,
324
+ tagline,
325
+ colors: {
326
+ primary: primaryColor,
327
+ secondary: '#64748b',
328
+ accent: '#f59e0b',
329
+ background: '#ffffff',
330
+ foreground: '#0f172a',
331
+ },
332
+ typography: {
333
+ headingFont: 'Inter',
334
+ bodyFont: 'Inter',
335
+ },
336
+ },
337
+ seo: {
338
+ title: displayName,
339
+ description: context?.description || `${displayName} - Your modern web application`,
340
+ keywords: [projectName, 'web app', 'nextjs', 'saas'],
341
+ locale: 'en_US',
342
+ },
343
+ pages: [
344
+ { name: 'Home', path: '/', type: 'landing' },
345
+ { name: 'Pricing', path: '/pricing', type: 'pricing' },
346
+ { name: 'Documentation', path: '/docs', type: 'docs' },
347
+ { name: 'Blog', path: '/blog', type: 'blog' },
348
+ ],
349
+ cta: {
350
+ primary: { text: 'Get Started', href: '/pricing' },
351
+ secondary: { text: 'Learn More', href: '/docs' },
352
+ },
353
+ features: {
354
+ analytics: true,
355
+ newsletter: false,
356
+ mdxBlog: true,
357
+ docsSearch: false,
358
+ },
359
+ },
360
+ null,
361
+ 2
362
+ );
363
+ }
364
+
365
+ /**
366
+ * Generate sample test for website
367
+ */
368
+ export function generateWebsiteTest(projectName: string): string {
369
+ const title = projectName
370
+ .split('-')
371
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
372
+ .join(' ');
373
+
374
+ return `import { describe, it, expect } from 'vitest';
375
+ import { render, screen } from '@testing-library/react';
376
+ import HomePage from '@/app/page';
377
+
378
+ describe('HomePage', () => {
379
+ it('renders the title', () => {
380
+ render(<HomePage />);
381
+ expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent('${title}');
382
+ });
383
+
384
+ it('renders the call-to-action buttons', () => {
385
+ render(<HomePage />);
386
+ expect(screen.getByRole('link', { name: /get started/i })).toBeInTheDocument();
387
+ expect(screen.getByRole('link', { name: /learn more/i })).toBeInTheDocument();
388
+ });
389
+ });
390
+ `;
391
+ }
392
+
393
+ /**
394
+ * Generate docs page placeholder
395
+ */
396
+ export function generateWebsiteDocsPage(): string {
397
+ return `import type { Metadata } from 'next';
398
+
399
+ export const metadata: Metadata = {
400
+ title: 'Documentation',
401
+ description: 'Learn how to use our platform with comprehensive documentation.',
402
+ };
403
+
404
+ export default function DocsPage() {
405
+ return (
406
+ <main className="py-20">
407
+ <div className="container">
408
+ <h1 className="text-4xl font-bold text-foreground">Documentation</h1>
409
+ <p className="mt-4 text-lg text-muted-foreground">
410
+ Documentation coming soon...
411
+ </p>
412
+ </div>
413
+ </main>
414
+ );
415
+ }
416
+ `;
417
+ }
418
+
419
+ /**
420
+ * Generate blog listing page placeholder
421
+ */
422
+ export function generateWebsiteBlogPage(): string {
423
+ return `import type { Metadata } from 'next';
424
+
425
+ export const metadata: Metadata = {
426
+ title: 'Blog',
427
+ description: 'Latest news, updates, and insights from our team.',
428
+ };
429
+
430
+ export default function BlogPage() {
431
+ return (
432
+ <main className="py-20">
433
+ <div className="container">
434
+ <h1 className="text-4xl font-bold text-foreground">Blog</h1>
435
+ <p className="mt-4 text-lg text-muted-foreground">
436
+ Blog posts coming soon...
437
+ </p>
438
+ </div>
439
+ </main>
440
+ );
441
+ }
442
+ `;
443
+ }