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.
- package/CHANGELOG.md +54 -0
- package/README.md +50 -8
- package/dist/cli/commands/create.d.ts.map +1 -1
- package/dist/cli/commands/create.js +54 -4
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/interactive.d.ts +29 -0
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +90 -7
- package/dist/cli/interactive.js.map +1 -1
- package/dist/generators/all.d.ts +4 -1
- package/dist/generators/all.d.ts.map +1 -1
- package/dist/generators/all.js +36 -316
- package/dist/generators/all.js.map +1 -1
- package/dist/generators/doc-parser.d.ts +18 -3
- package/dist/generators/doc-parser.d.ts.map +1 -1
- package/dist/generators/doc-parser.js +81 -10
- package/dist/generators/doc-parser.js.map +1 -1
- package/dist/generators/frontend-design-analyzer.d.ts +30 -0
- package/dist/generators/frontend-design-analyzer.d.ts.map +1 -0
- package/dist/generators/frontend-design-analyzer.js +208 -0
- package/dist/generators/frontend-design-analyzer.js.map +1 -0
- package/dist/generators/shared-packages.d.ts +45 -0
- package/dist/generators/shared-packages.d.ts.map +1 -0
- package/dist/generators/shared-packages.js +456 -0
- package/dist/generators/shared-packages.js.map +1 -0
- package/dist/generators/templates/index.d.ts +4 -0
- package/dist/generators/templates/index.d.ts.map +1 -1
- package/dist/generators/templates/index.js +4 -0
- package/dist/generators/templates/index.js.map +1 -1
- package/dist/generators/templates/website-components.d.ts.map +1 -1
- package/dist/generators/templates/website-components.js +36 -11
- package/dist/generators/templates/website-components.js.map +1 -1
- package/dist/generators/templates/website-config.d.ts +15 -1
- package/dist/generators/templates/website-config.d.ts.map +1 -1
- package/dist/generators/templates/website-config.js +155 -13
- package/dist/generators/templates/website-config.js.map +1 -1
- package/dist/generators/templates/website-landing.d.ts +24 -0
- package/dist/generators/templates/website-landing.d.ts.map +1 -0
- package/dist/generators/templates/website-landing.js +276 -0
- package/dist/generators/templates/website-landing.js.map +1 -0
- package/dist/generators/templates/website-layout.d.ts +42 -0
- package/dist/generators/templates/website-layout.d.ts.map +1 -0
- package/dist/generators/templates/website-layout.js +408 -0
- package/dist/generators/templates/website-layout.js.map +1 -0
- package/dist/generators/templates/website-pricing.d.ts +11 -0
- package/dist/generators/templates/website-pricing.d.ts.map +1 -0
- package/dist/generators/templates/website-pricing.js +313 -0
- package/dist/generators/templates/website-pricing.js.map +1 -0
- package/dist/generators/templates/website-sections.d.ts +102 -0
- package/dist/generators/templates/website-sections.d.ts.map +1 -0
- package/dist/generators/templates/website-sections.js +444 -0
- package/dist/generators/templates/website-sections.js.map +1 -0
- package/dist/generators/templates/website.d.ts +10 -50
- package/dist/generators/templates/website.d.ts.map +1 -1
- package/dist/generators/templates/website.js +12 -788
- package/dist/generators/templates/website.js.map +1 -1
- package/dist/generators/website-content-scanner.d.ts +37 -0
- package/dist/generators/website-content-scanner.d.ts.map +1 -0
- package/dist/generators/website-content-scanner.js +165 -0
- package/dist/generators/website-content-scanner.js.map +1 -0
- package/dist/generators/website-context.d.ts +38 -2
- package/dist/generators/website-context.d.ts.map +1 -1
- package/dist/generators/website-context.js +179 -19
- package/dist/generators/website-context.js.map +1 -1
- package/dist/generators/website-debug.d.ts +68 -0
- package/dist/generators/website-debug.d.ts.map +1 -0
- package/dist/generators/website-debug.js +93 -0
- package/dist/generators/website-debug.js.map +1 -0
- package/dist/generators/website.d.ts +2 -0
- package/dist/generators/website.d.ts.map +1 -1
- package/dist/generators/website.js +66 -4
- package/dist/generators/website.js.map +1 -1
- package/dist/generators/workspace-root.d.ts +27 -0
- package/dist/generators/workspace-root.d.ts.map +1 -0
- package/dist/generators/workspace-root.js +100 -0
- package/dist/generators/workspace-root.js.map +1 -0
- package/dist/state/index.d.ts +8 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +10 -0
- package/dist/state/index.js.map +1 -1
- package/dist/types/workflow.d.ts +6 -0
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +2 -0
- package/dist/types/workflow.js.map +1 -1
- package/dist/upgrade/handlers.d.ts +15 -0
- package/dist/upgrade/handlers.d.ts.map +1 -1
- package/dist/upgrade/handlers.js +52 -0
- package/dist/upgrade/handlers.js.map +1 -1
- package/dist/workflow/auto-fix-bundler.d.ts +37 -0
- package/dist/workflow/auto-fix-bundler.d.ts.map +1 -0
- package/dist/workflow/auto-fix-bundler.js +320 -0
- package/dist/workflow/auto-fix-bundler.js.map +1 -0
- package/dist/workflow/auto-fix.d.ts.map +1 -1
- package/dist/workflow/auto-fix.js +10 -3
- package/dist/workflow/auto-fix.js.map +1 -1
- package/dist/workflow/index.d.ts +1 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +12 -0
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/overview.d.ts.map +1 -1
- package/dist/workflow/overview.js +4 -0
- package/dist/workflow/overview.js.map +1 -1
- package/dist/workflow/plan-mode.d.ts +4 -3
- package/dist/workflow/plan-mode.d.ts.map +1 -1
- package/dist/workflow/plan-mode.js +69 -5
- package/dist/workflow/plan-mode.js.map +1 -1
- package/dist/workflow/website-strategy.d.ts +9 -0
- package/dist/workflow/website-strategy.d.ts.map +1 -1
- package/dist/workflow/website-strategy.js +73 -1
- package/dist/workflow/website-strategy.js.map +1 -1
- package/dist/workflow/website-updater.d.ts.map +1 -1
- package/dist/workflow/website-updater.js +15 -4
- package/dist/workflow/website-updater.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/create.ts +58 -4
- package/src/cli/interactive.ts +96 -7
- package/src/generators/all.ts +44 -332
- package/src/generators/doc-parser.ts +87 -10
- package/src/generators/frontend-design-analyzer.ts +261 -0
- package/src/generators/shared-packages.ts +500 -0
- package/src/generators/templates/index.ts +4 -0
- package/src/generators/templates/website-components.ts +36 -11
- package/src/generators/templates/website-config.ts +166 -13
- package/src/generators/templates/website-landing.ts +331 -0
- package/src/generators/templates/website-layout.ts +443 -0
- package/src/generators/templates/website-pricing.ts +330 -0
- package/src/generators/templates/website-sections.ts +541 -0
- package/src/generators/templates/website.ts +38 -851
- package/src/generators/website-content-scanner.ts +208 -0
- package/src/generators/website-context.ts +248 -20
- package/src/generators/website-debug.ts +130 -0
- package/src/generators/website.ts +71 -3
- package/src/generators/workspace-root.ts +113 -0
- package/src/state/index.ts +14 -0
- package/src/types/workflow.ts +6 -0
- package/src/upgrade/handlers.ts +65 -0
- package/src/workflow/auto-fix-bundler.ts +392 -0
- package/src/workflow/auto-fix.ts +11 -3
- package/src/workflow/index.ts +12 -0
- package/src/workflow/overview.ts +6 -0
- package/src/workflow/plan-mode.ts +81 -7
- package/src/workflow/website-strategy.ts +75 -1
- package/src/workflow/website-updater.ts +17 -6
- package/tests/cli/project-naming.test.ts +136 -0
- package/tests/generators/doc-parser.test.ts +121 -0
- package/tests/generators/frontend-design-analyzer.test.ts +90 -0
- package/tests/generators/quality-gate.test.ts +183 -0
- package/tests/generators/shared-packages.test.ts +83 -0
- package/tests/generators/website-components.test.ts +1 -1
- package/tests/generators/website-config.test.ts +84 -0
- package/tests/generators/website-content-scanner.test.ts +181 -0
- package/tests/generators/website-context.test.ts +109 -0
- package/tests/generators/website-debug.test.ts +77 -0
- package/tests/generators/website-landing.test.ts +188 -0
- package/tests/generators/website-pricing.test.ts +98 -0
- package/tests/generators/website-sections.test.ts +245 -0
- package/tests/generators/workspace-root.test.ts +105 -0
- package/tests/upgrade/handlers.test.ts +162 -0
- package/tests/workflow/auto-fix-bundler.test.ts +242 -0
- package/tests/workflow/plan-mode.test.ts +111 -1
- package/tests/workflow/website-strategy.test.ts +55 -0
|
@@ -1,790 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Website content templates
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*/
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
.join(' ');
|
|
15
|
-
const strategy = context?.strategy;
|
|
16
|
-
const displayName = context?.productName || title;
|
|
17
|
-
const desc = strategy?.messaging.longDescription
|
|
18
|
-
|| context?.description
|
|
19
|
-
|| `${displayName} - Your modern web application`;
|
|
20
|
-
// SEO keywords from strategy or defaults
|
|
21
|
-
const keywords = strategy?.seoStrategy.primaryKeywords
|
|
22
|
-
? strategy.seoStrategy.primaryKeywords.map(k => `'${escapeJsx(k)}'`).join(', ')
|
|
23
|
-
: `'${projectName}', 'web app', 'nextjs'`;
|
|
24
|
-
return `import type { Metadata } from 'next';
|
|
25
|
-
import { Inter } from 'next/font/google';
|
|
26
|
-
import './globals.css';
|
|
27
|
-
|
|
28
|
-
const inter = Inter({
|
|
29
|
-
subsets: ['latin'],
|
|
30
|
-
variable: '--font-inter',
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com';
|
|
34
|
-
|
|
35
|
-
export const metadata: Metadata = {
|
|
36
|
-
metadataBase: new URL(BASE_URL),
|
|
37
|
-
title: {
|
|
38
|
-
default: '${escapeJsx(displayName)}',
|
|
39
|
-
template: '%s | ${escapeJsx(displayName)}',
|
|
40
|
-
},
|
|
41
|
-
description: '${escapeJsx(desc)}',
|
|
42
|
-
keywords: [${keywords}],
|
|
43
|
-
authors: [{ name: '${escapeJsx(displayName)} Team' }],
|
|
44
|
-
creator: '${escapeJsx(displayName)}',
|
|
45
|
-
openGraph: {
|
|
46
|
-
type: 'website',
|
|
47
|
-
locale: 'en_US',
|
|
48
|
-
url: BASE_URL,
|
|
49
|
-
siteName: '${escapeJsx(displayName)}',
|
|
50
|
-
title: '${escapeJsx(displayName)}',
|
|
51
|
-
description: '${escapeJsx(desc)}',
|
|
52
|
-
},
|
|
53
|
-
twitter: {
|
|
54
|
-
card: 'summary_large_image',
|
|
55
|
-
title: '${escapeJsx(displayName)}',
|
|
56
|
-
description: '${escapeJsx(desc)}',
|
|
57
|
-
},
|
|
58
|
-
robots: {
|
|
59
|
-
index: true,
|
|
60
|
-
follow: true,
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
export default function RootLayout({
|
|
65
|
-
children,
|
|
66
|
-
}: {
|
|
67
|
-
children: React.ReactNode;
|
|
68
|
-
}) {
|
|
69
|
-
return (
|
|
70
|
-
<html lang="en" className={inter.variable}>
|
|
71
|
-
<body className="min-h-screen bg-white antialiased">
|
|
72
|
-
{children}
|
|
73
|
-
</body>
|
|
74
|
-
</html>
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
`;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Generate globals.css with optional brand colors
|
|
81
|
-
*/
|
|
82
|
-
export function generateWebsiteGlobalsCss(context) {
|
|
83
|
-
// Convert hex to HSL for CSS custom properties if brand color provided
|
|
84
|
-
const primaryHsl = context?.brand?.primaryColor
|
|
85
|
-
? hexToHslString(context.brand.primaryColor)
|
|
86
|
-
: '199 89% 48%';
|
|
87
|
-
return `@tailwind base;
|
|
88
|
-
@tailwind components;
|
|
89
|
-
@tailwind utilities;
|
|
90
|
-
|
|
91
|
-
@layer base {
|
|
92
|
-
:root {
|
|
93
|
-
--background: 0 0% 100%;
|
|
94
|
-
--foreground: 222.2 84% 4.9%;
|
|
95
|
-
--primary: ${primaryHsl};
|
|
96
|
-
--primary-foreground: 210 40% 98%;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
body {
|
|
100
|
-
@apply bg-background text-foreground;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
@layer components {
|
|
105
|
-
.container {
|
|
106
|
-
@apply mx-auto max-w-7xl px-4 sm:px-6 lg:px-8;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
`;
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Generate landing page.tsx with optional context-driven content
|
|
113
|
-
* When strategy is available, uses strategy messaging, trust signals, and CTAs
|
|
114
|
-
*/
|
|
115
|
-
export function generateWebsiteLandingPage(projectName, context) {
|
|
116
|
-
const title = projectName
|
|
117
|
-
.split('-')
|
|
118
|
-
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
119
|
-
.join(' ');
|
|
120
|
-
const strategy = context?.strategy;
|
|
121
|
-
const displayName = context?.productName || title;
|
|
122
|
-
// Strategy-driven or context-driven hero
|
|
123
|
-
const headline = strategy?.messaging.headline || displayName;
|
|
124
|
-
const subheadline = strategy?.messaging.subheadline || '';
|
|
125
|
-
const heroText = strategy?.messaging.longDescription
|
|
126
|
-
? escapeJsx(strategy.messaging.longDescription)
|
|
127
|
-
: context?.description
|
|
128
|
-
? escapeJsx(context.description)
|
|
129
|
-
: null;
|
|
130
|
-
const features = context?.features && context.features.length > 0
|
|
131
|
-
? context.features.slice(0, 6)
|
|
132
|
-
: null;
|
|
133
|
-
// CTAs from strategy or defaults
|
|
134
|
-
const primaryCtaText = strategy?.conversionStrategy.primaryCta.text || 'Get started';
|
|
135
|
-
const primaryCtaHref = strategy?.conversionStrategy.primaryCta.href || '/pricing';
|
|
136
|
-
const secondaryCtaText = strategy?.conversionStrategy.secondaryCta.text || 'Learn more';
|
|
137
|
-
const secondaryCtaHref = strategy?.conversionStrategy.secondaryCta.href || '/docs';
|
|
138
|
-
// Build hero paragraph
|
|
139
|
-
const heroParagraph = heroText
|
|
140
|
-
? ` ${heroText}`
|
|
141
|
-
: ` {/* TODO: populate from project specification */}`;
|
|
142
|
-
// Build features array
|
|
143
|
-
const featuresBlock = features
|
|
144
|
-
? features.map((f) => ` {\n title: '${escapeJsx(f.title)}',\n description: '${escapeJsx(f.description)}',\n }`).join(',\n')
|
|
145
|
-
: ` {\n title: 'Feature 1',\n description: '/* TODO: populate from project specification */',\n },\n {\n title: 'Feature 2',\n description: '/* TODO: populate from project specification */',\n },\n {\n title: 'Feature 3',\n description: '/* TODO: populate from project specification */',\n }`;
|
|
146
|
-
// Trust signals from strategy
|
|
147
|
-
const trustSignals = strategy?.conversionStrategy.trustSignals || [];
|
|
148
|
-
const trustSignalsBlock = trustSignals.length > 0
|
|
149
|
-
? trustSignals.map(s => ` '${escapeJsx(s)}'`).join(',\n')
|
|
150
|
-
: '';
|
|
151
|
-
// Social proof from strategy
|
|
152
|
-
const socialProof = strategy?.conversionStrategy.socialProof || [];
|
|
153
|
-
const socialProofBlock = socialProof.length > 0
|
|
154
|
-
? socialProof.map(s => ` '${escapeJsx(s)}'`).join(',\n')
|
|
155
|
-
: '';
|
|
156
|
-
// Build optional sections
|
|
157
|
-
const trustSection = trustSignals.length > 0 ? `
|
|
158
|
-
{/* Trust Signals */}
|
|
159
|
-
<section className="border-y border-gray-100 bg-gray-50 py-12">
|
|
160
|
-
<div className="container">
|
|
161
|
-
<div className="flex flex-wrap items-center justify-center gap-x-8 gap-y-4">
|
|
162
|
-
{[
|
|
163
|
-
${trustSignalsBlock}
|
|
164
|
-
].map((signal) => (
|
|
165
|
-
<p key={signal} className="text-sm font-medium text-gray-600">{signal}</p>
|
|
166
|
-
))}
|
|
167
|
-
</div>
|
|
168
|
-
</div>
|
|
169
|
-
</section>
|
|
170
|
-
` : '';
|
|
171
|
-
const socialProofSection = socialProof.length > 0 ? `
|
|
172
|
-
{/* Social Proof */}
|
|
173
|
-
<section className="py-16 sm:py-24">
|
|
174
|
-
<div className="container">
|
|
175
|
-
<h2 className="text-center text-3xl font-bold tracking-tight text-gray-900">
|
|
176
|
-
Trusted by teams everywhere
|
|
177
|
-
</h2>
|
|
178
|
-
<div className="mx-auto mt-12 grid max-w-4xl grid-cols-1 gap-8 md:grid-cols-2">
|
|
179
|
-
{[
|
|
180
|
-
${socialProofBlock}
|
|
181
|
-
].map((quote, i) => (
|
|
182
|
-
<blockquote key={i} className="rounded-2xl border border-gray-200 p-6">
|
|
183
|
-
<p className="text-gray-700">“{quote}”</p>
|
|
184
|
-
</blockquote>
|
|
185
|
-
))}
|
|
186
|
-
</div>
|
|
187
|
-
</div>
|
|
188
|
-
</section>
|
|
189
|
-
` : '';
|
|
190
|
-
// Metadata: strategy-driven or default
|
|
191
|
-
const metaTitle = strategy?.seoStrategy.titleTemplates?.home || 'Welcome';
|
|
192
|
-
const metaDesc = strategy?.seoStrategy.metaDescriptions?.home || `Welcome to ${displayName}`;
|
|
193
|
-
return `import type { Metadata } from 'next';
|
|
194
|
-
import Link from 'next/link';
|
|
195
|
-
import Header from '@/components/Header';
|
|
196
|
-
import Footer from '@/components/Footer';
|
|
197
|
-
import JsonLd from '@/components/JsonLd';
|
|
198
|
-
|
|
199
|
-
export const metadata: Metadata = {
|
|
200
|
-
title: '${escapeJsx(metaTitle)}',
|
|
201
|
-
description: '${escapeJsx(metaDesc)}',
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const ORG_SCHEMA = {
|
|
205
|
-
'@context': 'https://schema.org',
|
|
206
|
-
'@type': 'Organization',
|
|
207
|
-
name: '${escapeJsx(displayName)}',
|
|
208
|
-
url: process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com',
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
const PRODUCT_SCHEMA = {
|
|
212
|
-
'@context': 'https://schema.org',
|
|
213
|
-
'@type': 'SoftwareApplication',
|
|
214
|
-
name: '${escapeJsx(displayName)}',
|
|
215
|
-
applicationCategory: 'BusinessApplication',
|
|
216
|
-
operatingSystem: 'Web',
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
export default function HomePage() {
|
|
220
|
-
return (
|
|
221
|
-
<>
|
|
222
|
-
<Header />
|
|
223
|
-
<JsonLd schema={ORG_SCHEMA} />
|
|
224
|
-
<JsonLd schema={PRODUCT_SCHEMA} />
|
|
225
|
-
<main className="flex min-h-screen flex-col">
|
|
226
|
-
{/* Hero Section */}
|
|
227
|
-
<section className="relative overflow-hidden bg-gradient-to-b from-primary-50 to-white py-20 sm:py-32">
|
|
228
|
-
<div className="container">
|
|
229
|
-
<div className="mx-auto max-w-2xl text-center">
|
|
230
|
-
<h1 className="text-4xl font-bold tracking-tight text-gray-900 sm:text-6xl">
|
|
231
|
-
${escapeJsx(headline)}
|
|
232
|
-
</h1>
|
|
233
|
-
${subheadline ? ` <p className="mt-4 text-xl font-medium text-primary-600">\n ${escapeJsx(subheadline)}\n </p>` : ''}
|
|
234
|
-
<p className="mt-6 text-lg leading-8 text-gray-600">
|
|
235
|
-
${heroParagraph}
|
|
236
|
-
</p>
|
|
237
|
-
<div className="mt-10 flex items-center justify-center gap-x-6">
|
|
238
|
-
<Link
|
|
239
|
-
href="${escapeJsx(primaryCtaHref)}"
|
|
240
|
-
className="rounded-md bg-primary-600 px-6 py-3 text-sm font-semibold text-white shadow-sm hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600"
|
|
241
|
-
>
|
|
242
|
-
${escapeJsx(primaryCtaText)}
|
|
243
|
-
</Link>
|
|
244
|
-
<Link
|
|
245
|
-
href="${escapeJsx(secondaryCtaHref)}"
|
|
246
|
-
className="text-sm font-semibold leading-6 text-gray-900 hover:text-primary-600"
|
|
247
|
-
>
|
|
248
|
-
${escapeJsx(secondaryCtaText)} <span aria-hidden="true">-></span>
|
|
249
|
-
</Link>
|
|
250
|
-
</div>
|
|
251
|
-
</div>
|
|
252
|
-
</div>
|
|
253
|
-
</section>
|
|
254
|
-
${trustSection}
|
|
255
|
-
{/* Features Section */}
|
|
256
|
-
<section id="features" className="py-20 sm:py-32">
|
|
257
|
-
<div className="container">
|
|
258
|
-
<div className="mx-auto max-w-2xl text-center">
|
|
259
|
-
<h2 className="text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl">
|
|
260
|
-
Everything you need
|
|
261
|
-
</h2>
|
|
262
|
-
<p className="mt-4 text-lg text-gray-600">
|
|
263
|
-
{/* TODO: populate section subtitle from project specification */}
|
|
264
|
-
</p>
|
|
265
|
-
</div>
|
|
266
|
-
<div className="mx-auto mt-16 max-w-5xl">
|
|
267
|
-
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
|
|
268
|
-
{[
|
|
269
|
-
${featuresBlock}
|
|
270
|
-
].map((feature) => (
|
|
271
|
-
<div
|
|
272
|
-
key={feature.title}
|
|
273
|
-
className="rounded-2xl border border-gray-200 p-8"
|
|
274
|
-
>
|
|
275
|
-
<h3 className="text-lg font-semibold text-gray-900">
|
|
276
|
-
{feature.title}
|
|
277
|
-
</h3>
|
|
278
|
-
<p className="mt-2 text-gray-600">{feature.description}</p>
|
|
279
|
-
</div>
|
|
280
|
-
))}
|
|
281
|
-
</div>
|
|
282
|
-
</div>
|
|
283
|
-
</div>
|
|
284
|
-
</section>
|
|
285
|
-
${socialProofSection}
|
|
286
|
-
{/* CTA Section */}
|
|
287
|
-
<section className="bg-primary-600 py-16 sm:py-24">
|
|
288
|
-
<div className="container text-center">
|
|
289
|
-
<h2 className="text-3xl font-bold tracking-tight text-white sm:text-4xl">
|
|
290
|
-
Ready to get started?
|
|
291
|
-
</h2>
|
|
292
|
-
<p className="mt-4 text-lg text-primary-100">
|
|
293
|
-
${strategy?.messaging.elevatorPitch ? escapeJsx(strategy.messaging.elevatorPitch) : 'Start building today.'}
|
|
294
|
-
</p>
|
|
295
|
-
<div className="mt-8">
|
|
296
|
-
<Link
|
|
297
|
-
href="${escapeJsx(primaryCtaHref)}"
|
|
298
|
-
className="rounded-md bg-white px-6 py-3 text-sm font-semibold text-primary-600 shadow-sm hover:bg-primary-50"
|
|
299
|
-
>
|
|
300
|
-
${escapeJsx(primaryCtaText)}
|
|
301
|
-
</Link>
|
|
302
|
-
</div>
|
|
303
|
-
</div>
|
|
304
|
-
</section>
|
|
305
|
-
</main>
|
|
306
|
-
<Footer />
|
|
307
|
-
</>
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
`;
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Generate pricing page with optional context-driven tiers and FAQ
|
|
314
|
-
*/
|
|
315
|
-
export function generateWebsitePricingPage(projectName, context) {
|
|
316
|
-
const title = projectName
|
|
317
|
-
.split('-')
|
|
318
|
-
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
319
|
-
.join(' ');
|
|
320
|
-
const strategy = context?.strategy;
|
|
321
|
-
const displayName = context?.productName || title;
|
|
322
|
-
const tiers = context?.pricing && context.pricing.length > 0
|
|
323
|
-
? context.pricing
|
|
324
|
-
: null;
|
|
325
|
-
// Build tiers array
|
|
326
|
-
const tiersBlock = tiers
|
|
327
|
-
? tiers.map((t) => {
|
|
328
|
-
const featuresStr = t.features.map((f) => ` '${escapeJsx(f)}'`).join(',\n');
|
|
329
|
-
return ` {
|
|
330
|
-
name: '${escapeJsx(t.name)}',
|
|
331
|
-
price: '${escapeJsx(t.price)}',
|
|
332
|
-
description: '${escapeJsx(t.description)}',
|
|
333
|
-
features: [
|
|
334
|
-
${featuresStr}
|
|
335
|
-
],
|
|
336
|
-
cta: '${escapeJsx(t.cta)}',
|
|
337
|
-
featured: ${t.featured ? 'true' : 'false'},
|
|
338
|
-
}`;
|
|
339
|
-
}).join(',\n')
|
|
340
|
-
: ` {
|
|
341
|
-
name: '/* TODO: tier name */',
|
|
342
|
-
price: '/* TODO */',
|
|
343
|
-
description: '/* TODO: populate from project specification */',
|
|
344
|
-
features: ['/* TODO: populate from project specification */'],
|
|
345
|
-
cta: 'Get started',
|
|
346
|
-
featured: false,
|
|
347
|
-
},
|
|
348
|
-
{
|
|
349
|
-
name: '/* TODO: tier name */',
|
|
350
|
-
price: '/* TODO */',
|
|
351
|
-
description: '/* TODO: populate from project specification */',
|
|
352
|
-
features: ['/* TODO: populate from project specification */'],
|
|
353
|
-
cta: 'Start free trial',
|
|
354
|
-
featured: true,
|
|
355
|
-
},
|
|
356
|
-
{
|
|
357
|
-
name: '/* TODO: tier name */',
|
|
358
|
-
price: '/* TODO */',
|
|
359
|
-
description: '/* TODO: populate from project specification */',
|
|
360
|
-
features: ['/* TODO: populate from project specification */'],
|
|
361
|
-
cta: 'Contact sales',
|
|
362
|
-
featured: false,
|
|
363
|
-
}`;
|
|
364
|
-
// Pricing metadata from strategy or defaults
|
|
365
|
-
const metaTitle = strategy?.seoStrategy.titleTemplates?.pricing || 'Pricing';
|
|
366
|
-
const metaDesc = strategy?.seoStrategy.metaDescriptions?.pricing || `Choose the perfect plan for your needs - ${displayName}`;
|
|
367
|
-
// Enterprise CTA from strategy
|
|
368
|
-
const enterpriseCtaText = strategy?.conversionStrategy.primaryCta.text || 'Contact Sales';
|
|
369
|
-
return `import type { Metadata } from 'next';
|
|
370
|
-
import Link from 'next/link';
|
|
371
|
-
import Header from '@/components/Header';
|
|
372
|
-
import Footer from '@/components/Footer';
|
|
373
|
-
|
|
374
|
-
export const metadata: Metadata = {
|
|
375
|
-
title: '${escapeJsx(metaTitle)}',
|
|
376
|
-
description: '${escapeJsx(metaDesc)}',
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
const tiers = [
|
|
380
|
-
${tiersBlock}
|
|
381
|
-
];
|
|
382
|
-
|
|
383
|
-
export default function PricingPage() {
|
|
384
|
-
return (
|
|
385
|
-
<>
|
|
386
|
-
<Header />
|
|
387
|
-
<main className="py-20 sm:py-32">
|
|
388
|
-
<div className="container">
|
|
389
|
-
<div className="mx-auto max-w-2xl text-center">
|
|
390
|
-
<h1 className="text-4xl font-bold tracking-tight text-gray-900 sm:text-5xl">
|
|
391
|
-
Simple, transparent pricing
|
|
392
|
-
</h1>
|
|
393
|
-
<p className="mt-6 text-lg text-gray-600">
|
|
394
|
-
Choose the plan that works best for you.
|
|
395
|
-
</p>
|
|
396
|
-
</div>
|
|
397
|
-
|
|
398
|
-
<div className="mx-auto mt-16 grid max-w-lg grid-cols-1 gap-8 lg:max-w-5xl lg:grid-cols-3">
|
|
399
|
-
{tiers.map((tier) => (
|
|
400
|
-
<div
|
|
401
|
-
key={tier.name}
|
|
402
|
-
className={\`rounded-2xl p-8 \${
|
|
403
|
-
tier.featured
|
|
404
|
-
? 'bg-primary-600 text-white ring-2 ring-primary-600'
|
|
405
|
-
: 'border border-gray-200 bg-white'
|
|
406
|
-
}\`}
|
|
407
|
-
>
|
|
408
|
-
<h2
|
|
409
|
-
className={\`text-lg font-semibold \${
|
|
410
|
-
tier.featured ? 'text-white' : 'text-gray-900'
|
|
411
|
-
}\`}
|
|
412
|
-
>
|
|
413
|
-
{tier.name}
|
|
414
|
-
</h2>
|
|
415
|
-
<p
|
|
416
|
-
className={\`mt-2 text-sm \${
|
|
417
|
-
tier.featured ? 'text-primary-100' : 'text-gray-600'
|
|
418
|
-
}\`}
|
|
419
|
-
>
|
|
420
|
-
{tier.description}
|
|
421
|
-
</p>
|
|
422
|
-
<p className="mt-6">
|
|
423
|
-
<span
|
|
424
|
-
className={\`text-4xl font-bold \${
|
|
425
|
-
tier.featured ? 'text-white' : 'text-gray-900'
|
|
426
|
-
}\`}
|
|
427
|
-
>
|
|
428
|
-
{tier.price}
|
|
429
|
-
</span>
|
|
430
|
-
{tier.price !== 'Custom' && (
|
|
431
|
-
<span
|
|
432
|
-
className={\`text-sm \${
|
|
433
|
-
tier.featured ? 'text-primary-100' : 'text-gray-600'
|
|
434
|
-
}\`}
|
|
435
|
-
>
|
|
436
|
-
/month
|
|
437
|
-
</span>
|
|
438
|
-
)}
|
|
439
|
-
</p>
|
|
440
|
-
<ul className="mt-8 space-y-4">
|
|
441
|
-
{tier.features.map((feature) => (
|
|
442
|
-
<li
|
|
443
|
-
key={feature}
|
|
444
|
-
className={\`flex text-sm \${
|
|
445
|
-
tier.featured ? 'text-primary-100' : 'text-gray-600'
|
|
446
|
-
}\`}
|
|
447
|
-
>
|
|
448
|
-
<svg
|
|
449
|
-
className={\`h-5 w-5 flex-shrink-0 \${
|
|
450
|
-
tier.featured ? 'text-white' : 'text-primary-600'
|
|
451
|
-
}\`}
|
|
452
|
-
viewBox="0 0 20 20"
|
|
453
|
-
fill="currentColor"
|
|
454
|
-
>
|
|
455
|
-
<path
|
|
456
|
-
fillRule="evenodd"
|
|
457
|
-
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
|
458
|
-
clipRule="evenodd"
|
|
459
|
-
/>
|
|
460
|
-
</svg>
|
|
461
|
-
<span className="ml-3">{feature}</span>
|
|
462
|
-
</li>
|
|
463
|
-
))}
|
|
464
|
-
</ul>
|
|
465
|
-
<button
|
|
466
|
-
className={\`mt-8 w-full rounded-md px-4 py-2 text-sm font-semibold \${
|
|
467
|
-
tier.featured
|
|
468
|
-
? 'bg-white text-primary-600 hover:bg-primary-50'
|
|
469
|
-
: 'bg-primary-600 text-white hover:bg-primary-500'
|
|
470
|
-
}\`}
|
|
471
|
-
>
|
|
472
|
-
{tier.cta}
|
|
473
|
-
</button>
|
|
474
|
-
</div>
|
|
475
|
-
))}
|
|
476
|
-
</div>
|
|
477
|
-
|
|
478
|
-
{/* Enterprise CTA */}
|
|
479
|
-
<div className="mx-auto mt-16 max-w-2xl text-center">
|
|
480
|
-
<h2 className="text-2xl font-bold text-gray-900">
|
|
481
|
-
Need a custom plan?
|
|
482
|
-
</h2>
|
|
483
|
-
<p className="mt-4 text-gray-600">
|
|
484
|
-
Contact our sales team for enterprise pricing and custom solutions.
|
|
485
|
-
</p>
|
|
486
|
-
<Link
|
|
487
|
-
href="/contact"
|
|
488
|
-
className="mt-6 inline-block rounded-md border border-primary-600 px-6 py-3 text-sm font-semibold text-primary-600 hover:bg-primary-50"
|
|
489
|
-
>
|
|
490
|
-
${escapeJsx(enterpriseCtaText)}
|
|
491
|
-
</Link>
|
|
492
|
-
</div>
|
|
493
|
-
</div>
|
|
494
|
-
</main>
|
|
495
|
-
<Footer />
|
|
496
|
-
</>
|
|
497
|
-
);
|
|
498
|
-
}
|
|
499
|
-
`;
|
|
500
|
-
}
|
|
501
|
-
/**
|
|
502
|
-
* Generate sitemap.ts
|
|
503
|
-
*/
|
|
504
|
-
export function generateWebsiteSitemap(projectName) {
|
|
505
|
-
return `import { MetadataRoute } from 'next';
|
|
506
|
-
|
|
507
|
-
export default function sitemap(): MetadataRoute.Sitemap {
|
|
508
|
-
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com';
|
|
509
|
-
|
|
510
|
-
return [
|
|
511
|
-
{
|
|
512
|
-
url: baseUrl,
|
|
513
|
-
lastModified: new Date(),
|
|
514
|
-
changeFrequency: 'weekly',
|
|
515
|
-
priority: 1,
|
|
516
|
-
},
|
|
517
|
-
{
|
|
518
|
-
url: \`\${baseUrl}/pricing\`,
|
|
519
|
-
lastModified: new Date(),
|
|
520
|
-
changeFrequency: 'monthly',
|
|
521
|
-
priority: 0.8,
|
|
522
|
-
},
|
|
523
|
-
{
|
|
524
|
-
url: \`\${baseUrl}/docs\`,
|
|
525
|
-
lastModified: new Date(),
|
|
526
|
-
changeFrequency: 'weekly',
|
|
527
|
-
priority: 0.8,
|
|
528
|
-
},
|
|
529
|
-
{
|
|
530
|
-
url: \`\${baseUrl}/blog\`,
|
|
531
|
-
lastModified: new Date(),
|
|
532
|
-
changeFrequency: 'daily',
|
|
533
|
-
priority: 0.7,
|
|
534
|
-
},
|
|
535
|
-
];
|
|
536
|
-
}
|
|
537
|
-
`;
|
|
538
|
-
}
|
|
539
|
-
/**
|
|
540
|
-
* Generate robots.ts
|
|
541
|
-
*/
|
|
542
|
-
export function generateWebsiteRobots(projectName) {
|
|
543
|
-
return `import { MetadataRoute } from 'next';
|
|
544
|
-
|
|
545
|
-
export default function robots(): MetadataRoute.Robots {
|
|
546
|
-
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com';
|
|
547
|
-
|
|
548
|
-
return {
|
|
549
|
-
rules: [
|
|
550
|
-
{
|
|
551
|
-
userAgent: '*',
|
|
552
|
-
allow: '/',
|
|
553
|
-
disallow: ['/api/', '/admin/'],
|
|
554
|
-
},
|
|
555
|
-
],
|
|
556
|
-
sitemap: \`\${baseUrl}/sitemap.xml\`,
|
|
557
|
-
};
|
|
558
|
-
}
|
|
559
|
-
`;
|
|
560
|
-
}
|
|
561
|
-
/**
|
|
562
|
-
* Generate website README
|
|
563
|
-
*/
|
|
564
|
-
export function generateWebsiteReadme(projectName) {
|
|
565
|
-
const title = projectName
|
|
566
|
-
.split('-')
|
|
567
|
-
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
568
|
-
.join(' ');
|
|
569
|
-
return `# ${title} Website
|
|
570
|
-
|
|
571
|
-
Next.js marketing website with SEO optimization.
|
|
572
|
-
|
|
573
|
-
## Getting Started
|
|
574
|
-
|
|
575
|
-
\`\`\`bash
|
|
576
|
-
# Install dependencies
|
|
577
|
-
npm install
|
|
578
|
-
|
|
579
|
-
# Run development server (port 3001)
|
|
580
|
-
npm run dev
|
|
581
|
-
|
|
582
|
-
# Build for production
|
|
583
|
-
npm run build
|
|
584
|
-
|
|
585
|
-
# Run production server
|
|
586
|
-
npm start
|
|
587
|
-
\`\`\`
|
|
588
|
-
|
|
589
|
-
## SEO Features
|
|
590
|
-
|
|
591
|
-
- Server-side rendering (SSR)
|
|
592
|
-
- Auto-generated sitemap
|
|
593
|
-
- robots.txt configuration
|
|
594
|
-
- OpenGraph and Twitter meta tags
|
|
595
|
-
- Structured data support
|
|
596
|
-
|
|
597
|
-
## Project Structure
|
|
598
|
-
|
|
599
|
-
\`\`\`
|
|
600
|
-
src/
|
|
601
|
-
app/
|
|
602
|
-
layout.tsx # Root layout with metadata
|
|
603
|
-
page.tsx # Landing page
|
|
604
|
-
pricing/ # Pricing page
|
|
605
|
-
docs/ # Documentation
|
|
606
|
-
blog/ # Blog
|
|
607
|
-
sitemap.ts # Auto-generated sitemap
|
|
608
|
-
robots.ts # robots.txt config
|
|
609
|
-
components/ # UI components
|
|
610
|
-
lib/ # Utilities
|
|
611
|
-
content/
|
|
612
|
-
blog/ # MDX blog posts
|
|
613
|
-
docs/ # MDX documentation
|
|
614
|
-
\`\`\`
|
|
615
|
-
|
|
616
|
-
## Development
|
|
617
|
-
|
|
618
|
-
- Port: 3001 (to avoid conflicts with frontend on 5173)
|
|
619
|
-
- API URL: Configure via NEXT_PUBLIC_APP_URL
|
|
620
|
-
`;
|
|
621
|
-
}
|
|
622
|
-
/**
|
|
623
|
-
* Generate website spec JSON with optional context
|
|
624
|
-
*/
|
|
625
|
-
export function generateWebsiteSpec(projectName, context) {
|
|
626
|
-
const title = projectName
|
|
627
|
-
.split('-')
|
|
628
|
-
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
629
|
-
.join(' ');
|
|
630
|
-
const displayName = context?.productName || title;
|
|
631
|
-
const tagline = context?.tagline || context?.description || 'Build something amazing';
|
|
632
|
-
const primaryColor = context?.brand?.primaryColor || '#0ea5e9';
|
|
633
|
-
return JSON.stringify({
|
|
634
|
-
version: '1.0',
|
|
635
|
-
brand: {
|
|
636
|
-
name: displayName,
|
|
637
|
-
tagline,
|
|
638
|
-
colors: {
|
|
639
|
-
primary: primaryColor,
|
|
640
|
-
secondary: '#64748b',
|
|
641
|
-
accent: '#f59e0b',
|
|
642
|
-
background: '#ffffff',
|
|
643
|
-
foreground: '#0f172a',
|
|
644
|
-
},
|
|
645
|
-
typography: {
|
|
646
|
-
headingFont: 'Inter',
|
|
647
|
-
bodyFont: 'Inter',
|
|
648
|
-
},
|
|
649
|
-
},
|
|
650
|
-
seo: {
|
|
651
|
-
title: displayName,
|
|
652
|
-
description: context?.description || `${displayName} - Your modern web application`,
|
|
653
|
-
keywords: [projectName, 'web app', 'nextjs', 'saas'],
|
|
654
|
-
locale: 'en_US',
|
|
655
|
-
},
|
|
656
|
-
pages: [
|
|
657
|
-
{ name: 'Home', path: '/', type: 'landing' },
|
|
658
|
-
{ name: 'Pricing', path: '/pricing', type: 'pricing' },
|
|
659
|
-
{ name: 'Documentation', path: '/docs', type: 'docs' },
|
|
660
|
-
{ name: 'Blog', path: '/blog', type: 'blog' },
|
|
661
|
-
],
|
|
662
|
-
cta: {
|
|
663
|
-
primary: { text: 'Get Started', href: '/pricing' },
|
|
664
|
-
secondary: { text: 'Learn More', href: '/docs' },
|
|
665
|
-
},
|
|
666
|
-
features: {
|
|
667
|
-
analytics: true,
|
|
668
|
-
newsletter: false,
|
|
669
|
-
mdxBlog: true,
|
|
670
|
-
docsSearch: false,
|
|
671
|
-
},
|
|
672
|
-
}, null, 2);
|
|
673
|
-
}
|
|
674
|
-
/**
|
|
675
|
-
* Generate sample test for website
|
|
676
|
-
*/
|
|
677
|
-
export function generateWebsiteTest(projectName) {
|
|
678
|
-
const title = projectName
|
|
679
|
-
.split('-')
|
|
680
|
-
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
681
|
-
.join(' ');
|
|
682
|
-
return `import { describe, it, expect } from 'vitest';
|
|
683
|
-
import { render, screen } from '@testing-library/react';
|
|
684
|
-
import HomePage from '@/app/page';
|
|
685
|
-
|
|
686
|
-
describe('HomePage', () => {
|
|
687
|
-
it('renders the title', () => {
|
|
688
|
-
render(<HomePage />);
|
|
689
|
-
expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent('${title}');
|
|
690
|
-
});
|
|
691
|
-
|
|
692
|
-
it('renders the call-to-action buttons', () => {
|
|
693
|
-
render(<HomePage />);
|
|
694
|
-
expect(screen.getByRole('link', { name: /get started/i })).toBeInTheDocument();
|
|
695
|
-
expect(screen.getByRole('link', { name: /learn more/i })).toBeInTheDocument();
|
|
696
|
-
});
|
|
697
|
-
});
|
|
698
|
-
`;
|
|
699
|
-
}
|
|
700
|
-
/**
|
|
701
|
-
* Generate docs page placeholder
|
|
702
|
-
*/
|
|
703
|
-
export function generateWebsiteDocsPage() {
|
|
704
|
-
return `import type { Metadata } from 'next';
|
|
705
|
-
|
|
706
|
-
export const metadata: Metadata = {
|
|
707
|
-
title: 'Documentation',
|
|
708
|
-
description: 'Learn how to use our platform with comprehensive documentation.',
|
|
709
|
-
};
|
|
710
|
-
|
|
711
|
-
export default function DocsPage() {
|
|
712
|
-
return (
|
|
713
|
-
<main className="py-20">
|
|
714
|
-
<div className="container">
|
|
715
|
-
<h1 className="text-4xl font-bold text-gray-900">Documentation</h1>
|
|
716
|
-
<p className="mt-4 text-lg text-gray-600">
|
|
717
|
-
Documentation coming soon...
|
|
718
|
-
</p>
|
|
719
|
-
</div>
|
|
720
|
-
</main>
|
|
721
|
-
);
|
|
722
|
-
}
|
|
723
|
-
`;
|
|
724
|
-
}
|
|
725
|
-
/**
|
|
726
|
-
* Generate blog listing page placeholder
|
|
727
|
-
*/
|
|
728
|
-
export function generateWebsiteBlogPage() {
|
|
729
|
-
return `import type { Metadata } from 'next';
|
|
730
|
-
|
|
731
|
-
export const metadata: Metadata = {
|
|
732
|
-
title: 'Blog',
|
|
733
|
-
description: 'Latest news, updates, and insights from our team.',
|
|
734
|
-
};
|
|
735
|
-
|
|
736
|
-
export default function BlogPage() {
|
|
737
|
-
return (
|
|
738
|
-
<main className="py-20">
|
|
739
|
-
<div className="container">
|
|
740
|
-
<h1 className="text-4xl font-bold text-gray-900">Blog</h1>
|
|
741
|
-
<p className="mt-4 text-lg text-gray-600">
|
|
742
|
-
Blog posts coming soon...
|
|
743
|
-
</p>
|
|
744
|
-
</div>
|
|
745
|
-
</main>
|
|
746
|
-
);
|
|
747
|
-
}
|
|
748
|
-
`;
|
|
749
|
-
}
|
|
750
|
-
/**
|
|
751
|
-
* Escape a string for safe use inside JSX template literals
|
|
752
|
-
*/
|
|
753
|
-
function escapeJsx(str) {
|
|
754
|
-
return str
|
|
755
|
-
.replace(/\\/g, '\\\\')
|
|
756
|
-
.replace(/'/g, "\\'")
|
|
757
|
-
.replace(/`/g, '\\`')
|
|
758
|
-
.replace(/\$/g, '\\$');
|
|
759
|
-
}
|
|
760
|
-
/**
|
|
761
|
-
* Convert hex color to HSL string for CSS custom properties
|
|
762
|
-
* Returns format: "H S% L%"
|
|
763
|
-
*/
|
|
764
|
-
function hexToHslString(hex) {
|
|
765
|
-
// Remove # prefix
|
|
766
|
-
const h = hex.replace('#', '');
|
|
767
|
-
const r = parseInt(h.substring(0, 2), 16) / 255;
|
|
768
|
-
const g = parseInt(h.substring(2, 4), 16) / 255;
|
|
769
|
-
const b = parseInt(h.substring(4, 6), 16) / 255;
|
|
770
|
-
const max = Math.max(r, g, b);
|
|
771
|
-
const min = Math.min(r, g, b);
|
|
772
|
-
const l = (max + min) / 2;
|
|
773
|
-
if (max === min) {
|
|
774
|
-
return `0 0% ${Math.round(l * 100)}%`;
|
|
775
|
-
}
|
|
776
|
-
const d = max - min;
|
|
777
|
-
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
778
|
-
let hue = 0;
|
|
779
|
-
if (max === r) {
|
|
780
|
-
hue = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
781
|
-
}
|
|
782
|
-
else if (max === g) {
|
|
783
|
-
hue = ((b - r) / d + 2) / 6;
|
|
784
|
-
}
|
|
785
|
-
else {
|
|
786
|
-
hue = ((r - g) / d + 4) / 6;
|
|
787
|
-
}
|
|
788
|
-
return `${Math.round(hue * 360)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%`;
|
|
789
|
-
}
|
|
2
|
+
* Website content templates - re-export module
|
|
3
|
+
* Backward compatibility: all functions are available from their new homes
|
|
4
|
+
* but can still be imported from this file
|
|
5
|
+
*/
|
|
6
|
+
// Landing page (10-section redesign)
|
|
7
|
+
export { generateWebsiteLandingPage, generateWebsiteLandingPageWithInfo } from './website-landing.js';
|
|
8
|
+
// Pricing page (with toggle + comparison)
|
|
9
|
+
export { generateWebsitePricingPage } from './website-pricing.js';
|
|
10
|
+
// Layout, CSS, utility pages
|
|
11
|
+
export { generateWebsiteLayout, generateWebsiteGlobalsCss, generateWebsiteSitemap, generateWebsiteRobots, generateWebsiteReadme, generateWebsiteSpec, generateWebsiteTest, generateWebsiteDocsPage, generateWebsiteBlogPage, } from './website-layout.js';
|
|
12
|
+
// Reusable section generators
|
|
13
|
+
export { mapFeatureIcon, isNumericMetric, generatePainPointsSection, generateDifferentiatorsSection, generateHowItWorksSection, generateStatsSection, generateSocialProofSection, generatePricingTeaserSection, generateFaqSection, } from './website-sections.js';
|
|
790
14
|
//# sourceMappingURL=website.js.map
|