popeye-cli 1.4.7 → 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 (214) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +264 -63
  3. package/dist/adapters/gemini.d.ts +1 -0
  4. package/dist/adapters/gemini.d.ts.map +1 -1
  5. package/dist/adapters/gemini.js +9 -4
  6. package/dist/adapters/gemini.js.map +1 -1
  7. package/dist/adapters/grok.d.ts +1 -0
  8. package/dist/adapters/grok.d.ts.map +1 -1
  9. package/dist/adapters/grok.js +9 -4
  10. package/dist/adapters/grok.js.map +1 -1
  11. package/dist/adapters/openai.d.ts +1 -1
  12. package/dist/adapters/openai.d.ts.map +1 -1
  13. package/dist/adapters/openai.js +35 -9
  14. package/dist/adapters/openai.js.map +1 -1
  15. package/dist/cli/commands/create.d.ts.map +1 -1
  16. package/dist/cli/commands/create.js +54 -4
  17. package/dist/cli/commands/create.js.map +1 -1
  18. package/dist/cli/interactive.d.ts +29 -0
  19. package/dist/cli/interactive.d.ts.map +1 -1
  20. package/dist/cli/interactive.js +132 -7
  21. package/dist/cli/interactive.js.map +1 -1
  22. package/dist/generators/all.d.ts +8 -2
  23. package/dist/generators/all.d.ts.map +1 -1
  24. package/dist/generators/all.js +37 -316
  25. package/dist/generators/all.js.map +1 -1
  26. package/dist/generators/doc-parser.d.ts +64 -0
  27. package/dist/generators/doc-parser.d.ts.map +1 -0
  28. package/dist/generators/doc-parser.js +407 -0
  29. package/dist/generators/doc-parser.js.map +1 -0
  30. package/dist/generators/frontend-design-analyzer.d.ts +30 -0
  31. package/dist/generators/frontend-design-analyzer.d.ts.map +1 -0
  32. package/dist/generators/frontend-design-analyzer.js +208 -0
  33. package/dist/generators/frontend-design-analyzer.js.map +1 -0
  34. package/dist/generators/shared-packages.d.ts +45 -0
  35. package/dist/generators/shared-packages.d.ts.map +1 -0
  36. package/dist/generators/shared-packages.js +456 -0
  37. package/dist/generators/shared-packages.js.map +1 -0
  38. package/dist/generators/templates/index.d.ts +8 -0
  39. package/dist/generators/templates/index.d.ts.map +1 -1
  40. package/dist/generators/templates/index.js +8 -0
  41. package/dist/generators/templates/index.js.map +1 -1
  42. package/dist/generators/templates/website-components.d.ts +33 -0
  43. package/dist/generators/templates/website-components.d.ts.map +1 -0
  44. package/dist/generators/templates/website-components.js +303 -0
  45. package/dist/generators/templates/website-components.js.map +1 -0
  46. package/dist/generators/templates/website-config.d.ts +55 -0
  47. package/dist/generators/templates/website-config.d.ts.map +1 -0
  48. package/dist/generators/templates/website-config.js +425 -0
  49. package/dist/generators/templates/website-config.js.map +1 -0
  50. package/dist/generators/templates/website-conversion.d.ts +27 -0
  51. package/dist/generators/templates/website-conversion.d.ts.map +1 -0
  52. package/dist/generators/templates/website-conversion.js +326 -0
  53. package/dist/generators/templates/website-conversion.js.map +1 -0
  54. package/dist/generators/templates/website-landing.d.ts +24 -0
  55. package/dist/generators/templates/website-landing.d.ts.map +1 -0
  56. package/dist/generators/templates/website-landing.js +276 -0
  57. package/dist/generators/templates/website-landing.js.map +1 -0
  58. package/dist/generators/templates/website-layout.d.ts +42 -0
  59. package/dist/generators/templates/website-layout.d.ts.map +1 -0
  60. package/dist/generators/templates/website-layout.js +408 -0
  61. package/dist/generators/templates/website-layout.js.map +1 -0
  62. package/dist/generators/templates/website-pricing.d.ts +11 -0
  63. package/dist/generators/templates/website-pricing.d.ts.map +1 -0
  64. package/dist/generators/templates/website-pricing.js +313 -0
  65. package/dist/generators/templates/website-pricing.js.map +1 -0
  66. package/dist/generators/templates/website-sections.d.ts +102 -0
  67. package/dist/generators/templates/website-sections.d.ts.map +1 -0
  68. package/dist/generators/templates/website-sections.js +444 -0
  69. package/dist/generators/templates/website-sections.js.map +1 -0
  70. package/dist/generators/templates/website-seo.d.ts +76 -0
  71. package/dist/generators/templates/website-seo.d.ts.map +1 -0
  72. package/dist/generators/templates/website-seo.js +326 -0
  73. package/dist/generators/templates/website-seo.js.map +1 -0
  74. package/dist/generators/templates/website.d.ts +10 -83
  75. package/dist/generators/templates/website.d.ts.map +1 -1
  76. package/dist/generators/templates/website.js +12 -875
  77. package/dist/generators/templates/website.js.map +1 -1
  78. package/dist/generators/website-content-scanner.d.ts +37 -0
  79. package/dist/generators/website-content-scanner.d.ts.map +1 -0
  80. package/dist/generators/website-content-scanner.js +165 -0
  81. package/dist/generators/website-content-scanner.js.map +1 -0
  82. package/dist/generators/website-context.d.ts +119 -0
  83. package/dist/generators/website-context.d.ts.map +1 -0
  84. package/dist/generators/website-context.js +350 -0
  85. package/dist/generators/website-context.js.map +1 -0
  86. package/dist/generators/website-debug.d.ts +68 -0
  87. package/dist/generators/website-debug.d.ts.map +1 -0
  88. package/dist/generators/website-debug.js +93 -0
  89. package/dist/generators/website-debug.js.map +1 -0
  90. package/dist/generators/website.d.ts +5 -0
  91. package/dist/generators/website.d.ts.map +1 -1
  92. package/dist/generators/website.js +136 -11
  93. package/dist/generators/website.js.map +1 -1
  94. package/dist/generators/workspace-root.d.ts +27 -0
  95. package/dist/generators/workspace-root.d.ts.map +1 -0
  96. package/dist/generators/workspace-root.js +100 -0
  97. package/dist/generators/workspace-root.js.map +1 -0
  98. package/dist/state/index.d.ts +35 -0
  99. package/dist/state/index.d.ts.map +1 -1
  100. package/dist/state/index.js +40 -0
  101. package/dist/state/index.js.map +1 -1
  102. package/dist/types/consensus.d.ts +3 -0
  103. package/dist/types/consensus.d.ts.map +1 -1
  104. package/dist/types/consensus.js +1 -0
  105. package/dist/types/consensus.js.map +1 -1
  106. package/dist/types/website-strategy.d.ts +263 -0
  107. package/dist/types/website-strategy.d.ts.map +1 -0
  108. package/dist/types/website-strategy.js +105 -0
  109. package/dist/types/website-strategy.js.map +1 -0
  110. package/dist/types/workflow.d.ts +21 -0
  111. package/dist/types/workflow.d.ts.map +1 -1
  112. package/dist/types/workflow.js +8 -0
  113. package/dist/types/workflow.js.map +1 -1
  114. package/dist/upgrade/handlers.d.ts +15 -0
  115. package/dist/upgrade/handlers.d.ts.map +1 -1
  116. package/dist/upgrade/handlers.js +52 -0
  117. package/dist/upgrade/handlers.js.map +1 -1
  118. package/dist/workflow/auto-fix-bundler.d.ts +37 -0
  119. package/dist/workflow/auto-fix-bundler.d.ts.map +1 -0
  120. package/dist/workflow/auto-fix-bundler.js +320 -0
  121. package/dist/workflow/auto-fix-bundler.js.map +1 -0
  122. package/dist/workflow/auto-fix.d.ts.map +1 -1
  123. package/dist/workflow/auto-fix.js +10 -3
  124. package/dist/workflow/auto-fix.js.map +1 -1
  125. package/dist/workflow/consensus.d.ts.map +1 -1
  126. package/dist/workflow/consensus.js +2 -0
  127. package/dist/workflow/consensus.js.map +1 -1
  128. package/dist/workflow/execution-mode.d.ts.map +1 -1
  129. package/dist/workflow/execution-mode.js +18 -0
  130. package/dist/workflow/execution-mode.js.map +1 -1
  131. package/dist/workflow/index.d.ts +4 -0
  132. package/dist/workflow/index.d.ts.map +1 -1
  133. package/dist/workflow/index.js +37 -0
  134. package/dist/workflow/index.js.map +1 -1
  135. package/dist/workflow/overview.d.ts +89 -0
  136. package/dist/workflow/overview.d.ts.map +1 -0
  137. package/dist/workflow/overview.js +358 -0
  138. package/dist/workflow/overview.js.map +1 -0
  139. package/dist/workflow/plan-mode.d.ts +6 -4
  140. package/dist/workflow/plan-mode.d.ts.map +1 -1
  141. package/dist/workflow/plan-mode.js +148 -6
  142. package/dist/workflow/plan-mode.js.map +1 -1
  143. package/dist/workflow/website-strategy.d.ts +79 -0
  144. package/dist/workflow/website-strategy.d.ts.map +1 -0
  145. package/dist/workflow/website-strategy.js +310 -0
  146. package/dist/workflow/website-strategy.js.map +1 -0
  147. package/dist/workflow/website-updater.d.ts +17 -0
  148. package/dist/workflow/website-updater.d.ts.map +1 -0
  149. package/dist/workflow/website-updater.js +116 -0
  150. package/dist/workflow/website-updater.js.map +1 -0
  151. package/dist/workflow/workflow-logger.d.ts +1 -1
  152. package/dist/workflow/workflow-logger.d.ts.map +1 -1
  153. package/dist/workflow/workflow-logger.js.map +1 -1
  154. package/package.json +1 -1
  155. package/src/adapters/gemini.ts +10 -4
  156. package/src/adapters/grok.ts +10 -4
  157. package/src/adapters/openai.ts +38 -6
  158. package/src/cli/commands/create.ts +58 -4
  159. package/src/cli/interactive.ts +143 -7
  160. package/src/generators/all.ts +49 -332
  161. package/src/generators/doc-parser.ts +449 -0
  162. package/src/generators/frontend-design-analyzer.ts +261 -0
  163. package/src/generators/shared-packages.ts +500 -0
  164. package/src/generators/templates/index.ts +8 -0
  165. package/src/generators/templates/website-components.ts +330 -0
  166. package/src/generators/templates/website-config.ts +444 -0
  167. package/src/generators/templates/website-conversion.ts +341 -0
  168. package/src/generators/templates/website-landing.ts +331 -0
  169. package/src/generators/templates/website-layout.ts +443 -0
  170. package/src/generators/templates/website-pricing.ts +330 -0
  171. package/src/generators/templates/website-sections.ts +541 -0
  172. package/src/generators/templates/website-seo.ts +370 -0
  173. package/src/generators/templates/website.ts +38 -905
  174. package/src/generators/website-content-scanner.ts +208 -0
  175. package/src/generators/website-context.ts +493 -0
  176. package/src/generators/website-debug.ts +130 -0
  177. package/src/generators/website.ts +178 -20
  178. package/src/generators/workspace-root.ts +113 -0
  179. package/src/state/index.ts +56 -0
  180. package/src/types/consensus.ts +3 -0
  181. package/src/types/website-strategy.ts +243 -0
  182. package/src/types/workflow.ts +21 -0
  183. package/src/upgrade/handlers.ts +65 -0
  184. package/src/workflow/auto-fix-bundler.ts +392 -0
  185. package/src/workflow/auto-fix.ts +11 -3
  186. package/src/workflow/consensus.ts +2 -0
  187. package/src/workflow/execution-mode.ts +21 -0
  188. package/src/workflow/index.ts +37 -0
  189. package/src/workflow/overview.ts +475 -0
  190. package/src/workflow/plan-mode.ts +193 -8
  191. package/src/workflow/website-strategy.ts +379 -0
  192. package/src/workflow/website-updater.ts +142 -0
  193. package/src/workflow/workflow-logger.ts +1 -0
  194. package/tests/adapters/persona-switching.test.ts +63 -0
  195. package/tests/cli/project-naming.test.ts +136 -0
  196. package/tests/generators/doc-parser.test.ts +121 -0
  197. package/tests/generators/frontend-design-analyzer.test.ts +90 -0
  198. package/tests/generators/quality-gate.test.ts +183 -0
  199. package/tests/generators/shared-packages.test.ts +83 -0
  200. package/tests/generators/website-components.test.ts +159 -0
  201. package/tests/generators/website-config.test.ts +84 -0
  202. package/tests/generators/website-content-scanner.test.ts +181 -0
  203. package/tests/generators/website-context.test.ts +331 -0
  204. package/tests/generators/website-debug.test.ts +77 -0
  205. package/tests/generators/website-landing.test.ts +188 -0
  206. package/tests/generators/website-pricing.test.ts +98 -0
  207. package/tests/generators/website-sections.test.ts +245 -0
  208. package/tests/generators/website-seo-quality.test.ts +246 -0
  209. package/tests/generators/workspace-root.test.ts +105 -0
  210. package/tests/upgrade/handlers.test.ts +162 -0
  211. package/tests/workflow/auto-fix-bundler.test.ts +242 -0
  212. package/tests/workflow/overview.test.ts +392 -0
  213. package/tests/workflow/plan-mode.test.ts +111 -1
  214. package/tests/workflow/website-strategy.test.ts +246 -0
@@ -0,0 +1,330 @@
1
+ /**
2
+ * Shared website component templates
3
+ * Generates Header, Footer, and Navigation components with
4
+ * strategy-driven content, logo support, and mobile responsiveness
5
+ */
6
+
7
+ import type { WebsiteContentContext } from '../website-context.js';
8
+ import type { WebsiteStrategyDocument, NavItem, FooterSection } from '../../types/website-strategy.js';
9
+
10
+ /**
11
+ * Escape a string for safe use inside JSX template literals
12
+ */
13
+ function escapeJsx(str: string): string {
14
+ return str
15
+ .replace(/\\/g, '\\\\')
16
+ .replace(/'/g, "\\'")
17
+ .replace(/`/g, '\\`')
18
+ .replace(/\$/g, '\\$');
19
+ }
20
+
21
+ /**
22
+ * Generate website header component with logo, navigation, and CTA
23
+ *
24
+ * @param projectName - Project name for fallback display
25
+ * @param context - Optional content context
26
+ * @param strategy - Optional strategy for navigation and CTA
27
+ * @returns Header component source code
28
+ */
29
+ export function generateWebsiteHeader(
30
+ projectName: string,
31
+ context?: WebsiteContentContext,
32
+ strategy?: WebsiteStrategyDocument
33
+ ): string {
34
+ const displayName = context?.productName || projectName
35
+ .split('-')
36
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
37
+ .join(' ');
38
+
39
+ const hasLogo = !!(context?.brandAssets?.logoOutputPath || context?.brand?.logoPath);
40
+ // Reason: Next.js serves public/ at root, so public/brand/logo.svg -> /brand/logo.svg
41
+ const logoPath = context?.brandAssets?.logoOutputPath
42
+ ? `/${context.brandAssets.logoOutputPath.replace(/^public\//, '')}`
43
+ : '/brand/logo.svg';
44
+
45
+ // Build nav items from strategy or defaults
46
+ const navItems = strategy?.siteArchitecture.navigation || [
47
+ { label: 'Features', href: '/#features' },
48
+ { label: 'Pricing', href: '/pricing' },
49
+ { label: 'Docs', href: '/docs' },
50
+ { label: 'Blog', href: '/blog' },
51
+ ];
52
+
53
+ const navItemsStr = navItems
54
+ .map(item => ` { label: '${escapeJsx(item.label)}', href: '${escapeJsx(item.href)}' }`)
55
+ .join(',\n');
56
+
57
+ // CTA from strategy or default
58
+ const ctaText = strategy?.conversionStrategy.primaryCta.text || 'Get Started';
59
+ const ctaHref = strategy?.conversionStrategy.primaryCta.href || '/pricing';
60
+
61
+ // Logo rendering: Image if available, product initial circle if not
62
+ const initialLetter = displayName.charAt(0).toUpperCase();
63
+ const logoBlock = hasLogo
64
+ ? `<Image src="${logoPath}" alt="${escapeJsx(displayName)}" width={32} height={32} className="h-8 w-auto" />`
65
+ : `<div className="flex items-center gap-2">
66
+ <div className="flex h-8 w-8 items-center justify-center rounded-full bg-primary-600 text-sm font-bold text-white">${initialLetter}</div>
67
+ <span className="text-lg font-bold text-foreground">${escapeJsx(displayName)}</span>
68
+ </div>`;
69
+
70
+ return `'use client';
71
+
72
+ import { useState } from 'react';
73
+ import Link from 'next/link';
74
+ ${hasLogo ? "import Image from 'next/image';" : ''}
75
+
76
+ const NAV_ITEMS = [
77
+ ${navItemsStr}
78
+ ];
79
+
80
+ /**
81
+ * Site header with logo, navigation links, mobile menu, and CTA
82
+ */
83
+ export default function Header() {
84
+ const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
85
+
86
+ return (
87
+ <header className="sticky top-0 z-50 border-b border-gray-200 bg-white/80 backdrop-blur-sm">
88
+ <nav className="container flex h-16 items-center justify-between">
89
+ {/* Logo */}
90
+ <Link href="/" className="flex items-center gap-2">
91
+ ${logoBlock}
92
+ </Link>
93
+
94
+ {/* Desktop Navigation */}
95
+ <div className="hidden items-center gap-8 md:flex">
96
+ {NAV_ITEMS.map((item) => (
97
+ <Link
98
+ key={item.href}
99
+ href={item.href}
100
+ className="text-sm font-medium text-gray-700 hover:text-primary-600 transition-colors"
101
+ >
102
+ {item.label}
103
+ </Link>
104
+ ))}
105
+ <Link
106
+ href="${escapeJsx(ctaHref)}"
107
+ className="rounded-md bg-primary-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-primary-500 transition-colors"
108
+ >
109
+ ${escapeJsx(ctaText)}
110
+ </Link>
111
+ </div>
112
+
113
+ {/* Mobile Menu Button */}
114
+ <button
115
+ type="button"
116
+ className="md:hidden rounded-md p-2 text-gray-700"
117
+ onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
118
+ aria-label={mobileMenuOpen ? 'Close menu' : 'Open menu'}
119
+ aria-expanded={mobileMenuOpen}
120
+ >
121
+ <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
122
+ {mobileMenuOpen ? (
123
+ <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
124
+ ) : (
125
+ <path strokeLinecap="round" strokeLinejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
126
+ )}
127
+ </svg>
128
+ </button>
129
+ </nav>
130
+
131
+ {/* Mobile Menu */}
132
+ {mobileMenuOpen && (
133
+ <div className="border-t border-gray-200 bg-white px-4 py-4 md:hidden">
134
+ <div className="flex flex-col gap-4">
135
+ {NAV_ITEMS.map((item) => (
136
+ <Link
137
+ key={item.href}
138
+ href={item.href}
139
+ className="text-sm font-medium text-gray-700 hover:text-primary-600"
140
+ onClick={() => setMobileMenuOpen(false)}
141
+ >
142
+ {item.label}
143
+ </Link>
144
+ ))}
145
+ <Link
146
+ href="${escapeJsx(ctaHref)}"
147
+ className="rounded-md bg-primary-600 px-4 py-2 text-center text-sm font-semibold text-white"
148
+ onClick={() => setMobileMenuOpen(false)}
149
+ >
150
+ ${escapeJsx(ctaText)}
151
+ </Link>
152
+ </div>
153
+ </div>
154
+ )}
155
+ </header>
156
+ );
157
+ }
158
+ `;
159
+ }
160
+
161
+ /**
162
+ * Generate website footer component with multi-column sections
163
+ *
164
+ * @param projectName - Project name for copyright
165
+ * @param context - Optional content context
166
+ * @param strategy - Optional strategy for footer sections
167
+ * @returns Footer component source code
168
+ */
169
+ export function generateWebsiteFooter(
170
+ projectName: string,
171
+ context?: WebsiteContentContext,
172
+ strategy?: WebsiteStrategyDocument
173
+ ): string {
174
+ const displayName = context?.productName || projectName
175
+ .split('-')
176
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
177
+ .join(' ');
178
+
179
+ // Build footer sections from strategy or defaults
180
+ const sections: FooterSection[] = strategy?.siteArchitecture.footerSections || [
181
+ {
182
+ title: 'Product',
183
+ links: [
184
+ { label: 'Features', href: '/#features' },
185
+ { label: 'Pricing', href: '/pricing' },
186
+ { label: 'Documentation', href: '/docs' },
187
+ ],
188
+ },
189
+ {
190
+ title: 'Resources',
191
+ links: [
192
+ { label: 'Blog', href: '/blog' },
193
+ { label: 'Support', href: '/contact' },
194
+ ],
195
+ },
196
+ {
197
+ title: 'Legal',
198
+ links: [
199
+ { label: 'Privacy Policy', href: '/privacy' },
200
+ { label: 'Terms of Service', href: '/terms' },
201
+ ],
202
+ },
203
+ ];
204
+
205
+ const sectionsStr = sections
206
+ .map(section => {
207
+ const linksStr = section.links
208
+ .map(link => ` { label: '${escapeJsx(link.label)}', href: '${escapeJsx(link.href)}' }`)
209
+ .join(',\n');
210
+ return ` {\n title: '${escapeJsx(section.title)}',\n links: [\n${linksStr}\n ],\n }`;
211
+ })
212
+ .join(',\n');
213
+
214
+ return `import Link from 'next/link';
215
+
216
+ const FOOTER_SECTIONS = [
217
+ ${sectionsStr}
218
+ ];
219
+
220
+ /**
221
+ * Site footer with multi-column link sections and copyright
222
+ */
223
+ export default function Footer() {
224
+ return (
225
+ <footer className="border-t border-border bg-muted/50">
226
+ <div className="container py-12">
227
+ <div className="grid grid-cols-2 gap-8 md:grid-cols-${Math.min(sections.length + 1, 4)}">
228
+ {/* Brand column */}
229
+ <div className="col-span-2 md:col-span-1">
230
+ <Link href="/" className="text-lg font-bold text-foreground">
231
+ ${escapeJsx(displayName)}
232
+ </Link>
233
+ <p className="mt-2 text-sm text-muted-foreground">
234
+ ${context?.tagline ? escapeJsx(context.tagline) : 'Build something amazing.'}
235
+ </p>
236
+ {/* Newsletter */}
237
+ <form className="mt-6" onSubmit={(e) => e.preventDefault()}>
238
+ <label htmlFor="newsletter-email" className="text-sm font-medium text-foreground">
239
+ Stay updated
240
+ </label>
241
+ <div className="mt-2 flex gap-2">
242
+ <input
243
+ id="newsletter-email"
244
+ type="email"
245
+ placeholder="you@example.com"
246
+ className="flex-1 rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary-600"
247
+ />
248
+ <button
249
+ type="submit"
250
+ className="rounded-lg bg-primary-600 px-4 py-2 text-sm font-medium text-white hover:bg-primary-500 transition-colors"
251
+ >
252
+ Subscribe
253
+ </button>
254
+ </div>
255
+ </form>
256
+ </div>
257
+
258
+ {/* Link columns */}
259
+ {FOOTER_SECTIONS.map((section) => (
260
+ <div key={section.title}>
261
+ <h3 className="text-sm font-semibold text-foreground">{section.title}</h3>
262
+ <ul className="mt-4 space-y-2">
263
+ {section.links.map((link) => (
264
+ <li key={link.href}>
265
+ <Link
266
+ href={link.href}
267
+ className="text-sm text-muted-foreground hover:text-primary-600 transition-colors"
268
+ >
269
+ {link.label}
270
+ </Link>
271
+ </li>
272
+ ))}
273
+ </ul>
274
+ </div>
275
+ ))}
276
+ </div>
277
+
278
+ <div className="mt-12 border-t border-border pt-8">
279
+ <p className="text-center text-sm text-muted-foreground">
280
+ &copy; {new Date().getFullYear()} ${escapeJsx(displayName)}. All rights reserved.
281
+ </p>
282
+ </div>
283
+ </div>
284
+ </footer>
285
+ );
286
+ }
287
+ `;
288
+ }
289
+
290
+ /**
291
+ * Generate navigation config module
292
+ *
293
+ * @param strategy - Optional strategy for navigation items
294
+ * @returns Navigation config source code
295
+ */
296
+ export function generateWebsiteNavigation(
297
+ strategy?: WebsiteStrategyDocument
298
+ ): string {
299
+ const navItems: NavItem[] = strategy?.siteArchitecture.navigation || [
300
+ { label: 'Features', href: '/#features' },
301
+ { label: 'Pricing', href: '/pricing' },
302
+ { label: 'Docs', href: '/docs' },
303
+ { label: 'Blog', href: '/blog' },
304
+ ];
305
+
306
+ const itemsStr = navItems
307
+ .map(item => {
308
+ const childrenStr = item.children && item.children.length > 0
309
+ ? `,\n children: [\n${item.children.map(c => ` { label: '${escapeJsx(c.label)}', href: '${escapeJsx(c.href)}' }`).join(',\n')}\n ]`
310
+ : '';
311
+ return ` { label: '${escapeJsx(item.label)}', href: '${escapeJsx(item.href)}'${childrenStr} }`;
312
+ })
313
+ .join(',\n');
314
+
315
+ return `/**
316
+ * Navigation configuration
317
+ * Exported for use in Header and mobile navigation components
318
+ */
319
+
320
+ export interface NavItem {
321
+ label: string;
322
+ href: string;
323
+ children?: NavItem[];
324
+ }
325
+
326
+ export const NAV_ITEMS: NavItem[] = [
327
+ ${itemsStr}
328
+ ];
329
+ `;
330
+ }