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.
- package/CHANGELOG.md +54 -0
- package/README.md +264 -63
- package/dist/adapters/gemini.d.ts +1 -0
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +9 -4
- package/dist/adapters/gemini.js.map +1 -1
- package/dist/adapters/grok.d.ts +1 -0
- package/dist/adapters/grok.d.ts.map +1 -1
- package/dist/adapters/grok.js +9 -4
- package/dist/adapters/grok.js.map +1 -1
- package/dist/adapters/openai.d.ts +1 -1
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +35 -9
- package/dist/adapters/openai.js.map +1 -1
- 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 +132 -7
- package/dist/cli/interactive.js.map +1 -1
- package/dist/generators/all.d.ts +8 -2
- package/dist/generators/all.d.ts.map +1 -1
- package/dist/generators/all.js +37 -316
- package/dist/generators/all.js.map +1 -1
- package/dist/generators/doc-parser.d.ts +64 -0
- package/dist/generators/doc-parser.d.ts.map +1 -0
- package/dist/generators/doc-parser.js +407 -0
- package/dist/generators/doc-parser.js.map +1 -0
- 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 +8 -0
- package/dist/generators/templates/index.d.ts.map +1 -1
- package/dist/generators/templates/index.js +8 -0
- package/dist/generators/templates/index.js.map +1 -1
- package/dist/generators/templates/website-components.d.ts +33 -0
- package/dist/generators/templates/website-components.d.ts.map +1 -0
- package/dist/generators/templates/website-components.js +303 -0
- package/dist/generators/templates/website-components.js.map +1 -0
- package/dist/generators/templates/website-config.d.ts +55 -0
- package/dist/generators/templates/website-config.d.ts.map +1 -0
- package/dist/generators/templates/website-config.js +425 -0
- package/dist/generators/templates/website-config.js.map +1 -0
- package/dist/generators/templates/website-conversion.d.ts +27 -0
- package/dist/generators/templates/website-conversion.d.ts.map +1 -0
- package/dist/generators/templates/website-conversion.js +326 -0
- package/dist/generators/templates/website-conversion.js.map +1 -0
- 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-seo.d.ts +76 -0
- package/dist/generators/templates/website-seo.d.ts.map +1 -0
- package/dist/generators/templates/website-seo.js +326 -0
- package/dist/generators/templates/website-seo.js.map +1 -0
- package/dist/generators/templates/website.d.ts +10 -83
- package/dist/generators/templates/website.d.ts.map +1 -1
- package/dist/generators/templates/website.js +12 -875
- 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 +119 -0
- package/dist/generators/website-context.d.ts.map +1 -0
- package/dist/generators/website-context.js +350 -0
- package/dist/generators/website-context.js.map +1 -0
- 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 +5 -0
- package/dist/generators/website.d.ts.map +1 -1
- package/dist/generators/website.js +136 -11
- 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 +35 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +40 -0
- package/dist/state/index.js.map +1 -1
- package/dist/types/consensus.d.ts +3 -0
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js +1 -0
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/website-strategy.d.ts +263 -0
- package/dist/types/website-strategy.d.ts.map +1 -0
- package/dist/types/website-strategy.js +105 -0
- package/dist/types/website-strategy.js.map +1 -0
- package/dist/types/workflow.d.ts +21 -0
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +8 -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/consensus.d.ts.map +1 -1
- package/dist/workflow/consensus.js +2 -0
- package/dist/workflow/consensus.js.map +1 -1
- package/dist/workflow/execution-mode.d.ts.map +1 -1
- package/dist/workflow/execution-mode.js +18 -0
- package/dist/workflow/execution-mode.js.map +1 -1
- package/dist/workflow/index.d.ts +4 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +37 -0
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/overview.d.ts +89 -0
- package/dist/workflow/overview.d.ts.map +1 -0
- package/dist/workflow/overview.js +358 -0
- package/dist/workflow/overview.js.map +1 -0
- package/dist/workflow/plan-mode.d.ts +6 -4
- package/dist/workflow/plan-mode.d.ts.map +1 -1
- package/dist/workflow/plan-mode.js +148 -6
- package/dist/workflow/plan-mode.js.map +1 -1
- package/dist/workflow/website-strategy.d.ts +79 -0
- package/dist/workflow/website-strategy.d.ts.map +1 -0
- package/dist/workflow/website-strategy.js +310 -0
- package/dist/workflow/website-strategy.js.map +1 -0
- package/dist/workflow/website-updater.d.ts +17 -0
- package/dist/workflow/website-updater.d.ts.map +1 -0
- package/dist/workflow/website-updater.js +116 -0
- package/dist/workflow/website-updater.js.map +1 -0
- package/dist/workflow/workflow-logger.d.ts +1 -1
- package/dist/workflow/workflow-logger.d.ts.map +1 -1
- package/dist/workflow/workflow-logger.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/gemini.ts +10 -4
- package/src/adapters/grok.ts +10 -4
- package/src/adapters/openai.ts +38 -6
- package/src/cli/commands/create.ts +58 -4
- package/src/cli/interactive.ts +143 -7
- package/src/generators/all.ts +49 -332
- package/src/generators/doc-parser.ts +449 -0
- package/src/generators/frontend-design-analyzer.ts +261 -0
- package/src/generators/shared-packages.ts +500 -0
- package/src/generators/templates/index.ts +8 -0
- package/src/generators/templates/website-components.ts +330 -0
- package/src/generators/templates/website-config.ts +444 -0
- package/src/generators/templates/website-conversion.ts +341 -0
- 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-seo.ts +370 -0
- package/src/generators/templates/website.ts +38 -905
- package/src/generators/website-content-scanner.ts +208 -0
- package/src/generators/website-context.ts +493 -0
- package/src/generators/website-debug.ts +130 -0
- package/src/generators/website.ts +178 -20
- package/src/generators/workspace-root.ts +113 -0
- package/src/state/index.ts +56 -0
- package/src/types/consensus.ts +3 -0
- package/src/types/website-strategy.ts +243 -0
- package/src/types/workflow.ts +21 -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/consensus.ts +2 -0
- package/src/workflow/execution-mode.ts +21 -0
- package/src/workflow/index.ts +37 -0
- package/src/workflow/overview.ts +475 -0
- package/src/workflow/plan-mode.ts +193 -8
- package/src/workflow/website-strategy.ts +379 -0
- package/src/workflow/website-updater.ts +142 -0
- package/src/workflow/workflow-logger.ts +1 -0
- package/tests/adapters/persona-switching.test.ts +63 -0
- 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 +159 -0
- 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 +331 -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/website-seo-quality.test.ts +246 -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/overview.test.ts +392 -0
- package/tests/workflow/plan-mode.test.ts +111 -1
- package/tests/workflow/website-strategy.test.ts +246 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Website configuration templates (non-content)
|
|
3
|
+
* Package configs, build tools, Docker, vitest, and env declarations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generate Next.js package.json
|
|
8
|
+
*/
|
|
9
|
+
export function generateWebsitePackageJson(projectName: string): string {
|
|
10
|
+
return `{
|
|
11
|
+
"name": "${projectName}-website",
|
|
12
|
+
"version": "1.0.0",
|
|
13
|
+
"private": true,
|
|
14
|
+
"scripts": {
|
|
15
|
+
"dev": "next dev -p 3001",
|
|
16
|
+
"build": "next build",
|
|
17
|
+
"start": "next start -p 3001",
|
|
18
|
+
"lint": "next lint",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"test:watch": "vitest",
|
|
21
|
+
"typecheck": "tsc --noEmit"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"next": "^14.1.0",
|
|
25
|
+
"react": "^18.2.0",
|
|
26
|
+
"react-dom": "^18.2.0",
|
|
27
|
+
"lucide-react": "^0.312.0",
|
|
28
|
+
"clsx": "^2.1.0",
|
|
29
|
+
"tailwind-merge": "^2.2.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^20.11.0",
|
|
33
|
+
"@types/react": "^18.2.0",
|
|
34
|
+
"@types/react-dom": "^18.2.0",
|
|
35
|
+
"autoprefixer": "^10.4.17",
|
|
36
|
+
"postcss": "^8.4.33",
|
|
37
|
+
"tailwindcss": "^3.4.1",
|
|
38
|
+
"typescript": "^5.3.3",
|
|
39
|
+
"@testing-library/react": "^14.1.2",
|
|
40
|
+
"@vitejs/plugin-react": "^4.2.1",
|
|
41
|
+
"vitest": "^1.2.0",
|
|
42
|
+
"jsdom": "^24.0.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Generate Next.js config
|
|
50
|
+
*/
|
|
51
|
+
export function generateNextConfig(): string {
|
|
52
|
+
return `/** @type {import('next').NextConfig} */
|
|
53
|
+
const nextConfig = {
|
|
54
|
+
// Enable React Strict Mode for better development
|
|
55
|
+
reactStrictMode: true,
|
|
56
|
+
|
|
57
|
+
// Image optimization
|
|
58
|
+
images: {
|
|
59
|
+
domains: [],
|
|
60
|
+
formats: ['image/avif', 'image/webp'],
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
// Disable x-powered-by header
|
|
64
|
+
poweredByHeader: false,
|
|
65
|
+
|
|
66
|
+
// Trailing slash config
|
|
67
|
+
trailingSlash: false,
|
|
68
|
+
|
|
69
|
+
// Headers for security
|
|
70
|
+
async headers() {
|
|
71
|
+
return [
|
|
72
|
+
{
|
|
73
|
+
source: '/:path*',
|
|
74
|
+
headers: [
|
|
75
|
+
{
|
|
76
|
+
key: 'X-DNS-Prefetch-Control',
|
|
77
|
+
value: 'on',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
key: 'X-Content-Type-Options',
|
|
81
|
+
value: 'nosniff',
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
module.exports = nextConfig;
|
|
90
|
+
`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Generate website tsconfig.json
|
|
95
|
+
*/
|
|
96
|
+
export function generateWebsiteTsconfig(): string {
|
|
97
|
+
return `{
|
|
98
|
+
"compilerOptions": {
|
|
99
|
+
"target": "ES2017",
|
|
100
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
101
|
+
"allowJs": true,
|
|
102
|
+
"skipLibCheck": true,
|
|
103
|
+
"strict": true,
|
|
104
|
+
"noEmit": true,
|
|
105
|
+
"esModuleInterop": true,
|
|
106
|
+
"module": "esnext",
|
|
107
|
+
"moduleResolution": "bundler",
|
|
108
|
+
"resolveJsonModule": true,
|
|
109
|
+
"isolatedModules": true,
|
|
110
|
+
"jsx": "preserve",
|
|
111
|
+
"incremental": true,
|
|
112
|
+
"plugins": [
|
|
113
|
+
{
|
|
114
|
+
"name": "next"
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
"paths": {
|
|
118
|
+
"@/*": ["./src/*"]
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
122
|
+
"exclude": ["node_modules"]
|
|
123
|
+
}
|
|
124
|
+
`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Options for generating the Tailwind config
|
|
129
|
+
*/
|
|
130
|
+
export interface TailwindConfigOptions {
|
|
131
|
+
/** Primary brand color (hex) to generate color scale from */
|
|
132
|
+
primaryColor?: string;
|
|
133
|
+
/** Whether to import workspace design-tokens preset */
|
|
134
|
+
workspaceMode?: boolean;
|
|
135
|
+
/** Project name (for workspace preset import path) */
|
|
136
|
+
projectName?: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Generate Tailwind config for website
|
|
141
|
+
*
|
|
142
|
+
* @param options - Optional configuration for brand colors and workspace mode
|
|
143
|
+
* @returns Tailwind config source code
|
|
144
|
+
*/
|
|
145
|
+
export function generateWebsiteTailwindConfig(options?: TailwindConfigOptions): string {
|
|
146
|
+
const colorScale = options?.primaryColor
|
|
147
|
+
? generateInlineColorScale(options.primaryColor)
|
|
148
|
+
: defaultColorScale();
|
|
149
|
+
|
|
150
|
+
const presetImport = options?.workspaceMode && options?.projectName
|
|
151
|
+
? `import designPreset from '@${options.projectName}/design-tokens/tailwind';\n`
|
|
152
|
+
: '';
|
|
153
|
+
|
|
154
|
+
const presetsBlock = options?.workspaceMode && options?.projectName
|
|
155
|
+
? ` presets: [designPreset],\n`
|
|
156
|
+
: '';
|
|
157
|
+
|
|
158
|
+
return `import type { Config } from 'tailwindcss';
|
|
159
|
+
${presetImport}
|
|
160
|
+
const config: Config = {
|
|
161
|
+
${presetsBlock} content: [
|
|
162
|
+
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
|
|
163
|
+
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
|
|
164
|
+
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
|
|
165
|
+
],
|
|
166
|
+
theme: {
|
|
167
|
+
extend: {
|
|
168
|
+
colors: {
|
|
169
|
+
primary: {
|
|
170
|
+
${colorScale}
|
|
171
|
+
},
|
|
172
|
+
background: 'hsl(var(--background))',
|
|
173
|
+
foreground: 'hsl(var(--foreground))',
|
|
174
|
+
muted: {
|
|
175
|
+
DEFAULT: 'hsl(var(--muted))',
|
|
176
|
+
foreground: 'hsl(var(--muted-foreground))',
|
|
177
|
+
},
|
|
178
|
+
accent: {
|
|
179
|
+
DEFAULT: 'hsl(var(--accent))',
|
|
180
|
+
foreground: 'hsl(var(--accent-foreground))',
|
|
181
|
+
},
|
|
182
|
+
card: {
|
|
183
|
+
DEFAULT: 'hsl(var(--card))',
|
|
184
|
+
foreground: 'hsl(var(--card-foreground))',
|
|
185
|
+
},
|
|
186
|
+
border: 'hsl(var(--border))',
|
|
187
|
+
ring: 'hsl(var(--ring))',
|
|
188
|
+
},
|
|
189
|
+
fontFamily: {
|
|
190
|
+
sans: ['var(--font-inter)', 'system-ui', 'sans-serif'],
|
|
191
|
+
},
|
|
192
|
+
borderColor: {
|
|
193
|
+
DEFAULT: 'hsl(var(--border))',
|
|
194
|
+
},
|
|
195
|
+
borderRadius: {
|
|
196
|
+
lg: 'var(--radius)',
|
|
197
|
+
md: 'calc(var(--radius) - 2px)',
|
|
198
|
+
sm: 'calc(var(--radius) - 4px)',
|
|
199
|
+
},
|
|
200
|
+
keyframes: {
|
|
201
|
+
fadeIn: {
|
|
202
|
+
'0%': { opacity: '0' },
|
|
203
|
+
'100%': { opacity: '1' },
|
|
204
|
+
},
|
|
205
|
+
slideUp: {
|
|
206
|
+
'0%': { opacity: '0', transform: 'translateY(20px)' },
|
|
207
|
+
'100%': { opacity: '1', transform: 'translateY(0)' },
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
animation: {
|
|
211
|
+
fadeIn: 'fadeIn 0.5s ease-out',
|
|
212
|
+
slideUp: 'slideUp 0.5s ease-out',
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
plugins: [],
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
export default config;
|
|
220
|
+
`;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Generate inline color scale entries from a hex color
|
|
225
|
+
*/
|
|
226
|
+
function generateInlineColorScale(hex: string): string {
|
|
227
|
+
const rgb = hexToRgb(hex);
|
|
228
|
+
if (!rgb) return defaultColorScale();
|
|
229
|
+
|
|
230
|
+
const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
|
|
231
|
+
const stops = [
|
|
232
|
+
{ key: '50', lightness: 0.95 },
|
|
233
|
+
{ key: '100', lightness: 0.90 },
|
|
234
|
+
{ key: '200', lightness: 0.80 },
|
|
235
|
+
{ key: '300', lightness: 0.68 },
|
|
236
|
+
{ key: '400', lightness: 0.56 },
|
|
237
|
+
{ key: '500', lightness: 0.48 },
|
|
238
|
+
{ key: '600', lightness: 0.40 },
|
|
239
|
+
{ key: '700', lightness: 0.32 },
|
|
240
|
+
{ key: '800', lightness: 0.24 },
|
|
241
|
+
{ key: '900', lightness: 0.15 },
|
|
242
|
+
];
|
|
243
|
+
|
|
244
|
+
return stops
|
|
245
|
+
.map(stop => ` ${stop.key}: '${hslToHex(hsl.h, hsl.s, stop.lightness)}',`)
|
|
246
|
+
.join('\n');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function defaultColorScale(): string {
|
|
250
|
+
return ` 50: '#f0f9ff',
|
|
251
|
+
100: '#e0f2fe',
|
|
252
|
+
200: '#bae6fd',
|
|
253
|
+
300: '#7dd3fc',
|
|
254
|
+
400: '#38bdf8',
|
|
255
|
+
500: '#0ea5e9',
|
|
256
|
+
600: '#0284c7',
|
|
257
|
+
700: '#0369a1',
|
|
258
|
+
800: '#075985',
|
|
259
|
+
900: '#0c4a6e',`;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// --- Color conversion helpers (inline to avoid circular deps) ---
|
|
263
|
+
|
|
264
|
+
function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
|
|
265
|
+
const cleaned = hex.replace(/^#/, '');
|
|
266
|
+
if (cleaned.length !== 6) return null;
|
|
267
|
+
const r = parseInt(cleaned.substring(0, 2), 16);
|
|
268
|
+
const g = parseInt(cleaned.substring(2, 4), 16);
|
|
269
|
+
const b = parseInt(cleaned.substring(4, 6), 16);
|
|
270
|
+
if (isNaN(r) || isNaN(g) || isNaN(b)) return null;
|
|
271
|
+
return { r, g, b };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function rgbToHsl(r: number, g: number, b: number): { h: number; s: number; l: number } {
|
|
275
|
+
const rn = r / 255;
|
|
276
|
+
const gn = g / 255;
|
|
277
|
+
const bn = b / 255;
|
|
278
|
+
const max = Math.max(rn, gn, bn);
|
|
279
|
+
const min = Math.min(rn, gn, bn);
|
|
280
|
+
const l = (max + min) / 2;
|
|
281
|
+
let h = 0;
|
|
282
|
+
let s = 0;
|
|
283
|
+
if (max !== min) {
|
|
284
|
+
const d = max - min;
|
|
285
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
286
|
+
if (max === rn) h = ((gn - bn) / d + (gn < bn ? 6 : 0)) / 6;
|
|
287
|
+
else if (max === gn) h = ((bn - rn) / d + 2) / 6;
|
|
288
|
+
else h = ((rn - gn) / d + 4) / 6;
|
|
289
|
+
}
|
|
290
|
+
return { h, s, l };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function hslToHex(h: number, s: number, l: number): string {
|
|
294
|
+
const hue2rgb = (p: number, q: number, t: number): number => {
|
|
295
|
+
let tn = t;
|
|
296
|
+
if (tn < 0) tn += 1;
|
|
297
|
+
if (tn > 1) tn -= 1;
|
|
298
|
+
if (tn < 1 / 6) return p + (q - p) * 6 * tn;
|
|
299
|
+
if (tn < 1 / 2) return q;
|
|
300
|
+
if (tn < 2 / 3) return p + (q - p) * (2 / 3 - tn) * 6;
|
|
301
|
+
return p;
|
|
302
|
+
};
|
|
303
|
+
let r: number, g: number, b: number;
|
|
304
|
+
if (s === 0) {
|
|
305
|
+
r = g = b = l;
|
|
306
|
+
} else {
|
|
307
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
308
|
+
const p = 2 * l - q;
|
|
309
|
+
r = hue2rgb(p, q, h + 1 / 3);
|
|
310
|
+
g = hue2rgb(p, q, h);
|
|
311
|
+
b = hue2rgb(p, q, h - 1 / 3);
|
|
312
|
+
}
|
|
313
|
+
const toHex = (n: number): string => {
|
|
314
|
+
const val = Math.round(n * 255).toString(16);
|
|
315
|
+
return val.length === 1 ? '0' + val : val;
|
|
316
|
+
};
|
|
317
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Generate PostCSS config for website
|
|
322
|
+
*/
|
|
323
|
+
export function generateWebsitePostcssConfig(): string {
|
|
324
|
+
return `module.exports = {
|
|
325
|
+
plugins: {
|
|
326
|
+
tailwindcss: {},
|
|
327
|
+
autoprefixer: {},
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
`;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Generate website Dockerfile
|
|
335
|
+
*/
|
|
336
|
+
export function generateWebsiteDockerfile(): string {
|
|
337
|
+
return `# Build stage
|
|
338
|
+
FROM node:20-alpine AS builder
|
|
339
|
+
|
|
340
|
+
WORKDIR /app
|
|
341
|
+
|
|
342
|
+
# Copy package files
|
|
343
|
+
COPY package*.json ./
|
|
344
|
+
|
|
345
|
+
# Install dependencies
|
|
346
|
+
RUN npm ci
|
|
347
|
+
|
|
348
|
+
# Copy source
|
|
349
|
+
COPY . .
|
|
350
|
+
|
|
351
|
+
# Build
|
|
352
|
+
RUN npm run build
|
|
353
|
+
|
|
354
|
+
# Production stage
|
|
355
|
+
FROM node:20-alpine AS runner
|
|
356
|
+
|
|
357
|
+
WORKDIR /app
|
|
358
|
+
|
|
359
|
+
ENV NODE_ENV=production
|
|
360
|
+
ENV NEXT_TELEMETRY_DISABLED=1
|
|
361
|
+
|
|
362
|
+
# Create non-root user
|
|
363
|
+
RUN addgroup --system --gid 1001 nodejs
|
|
364
|
+
RUN adduser --system --uid 1001 nextjs
|
|
365
|
+
|
|
366
|
+
# Copy built assets
|
|
367
|
+
COPY --from=builder /app/public ./public
|
|
368
|
+
COPY --from=builder /app/.next/standalone ./
|
|
369
|
+
COPY --from=builder /app/.next/static ./.next/static
|
|
370
|
+
|
|
371
|
+
USER nextjs
|
|
372
|
+
|
|
373
|
+
EXPOSE 3000
|
|
374
|
+
|
|
375
|
+
ENV PORT=3000
|
|
376
|
+
ENV HOSTNAME="0.0.0.0"
|
|
377
|
+
|
|
378
|
+
CMD ["node", "server.js"]
|
|
379
|
+
`;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Generate vitest config for website
|
|
384
|
+
*/
|
|
385
|
+
export function generateWebsiteVitestConfig(): string {
|
|
386
|
+
return `import { defineConfig } from 'vitest/config';
|
|
387
|
+
import react from '@vitejs/plugin-react';
|
|
388
|
+
import path from 'path';
|
|
389
|
+
|
|
390
|
+
export default defineConfig({
|
|
391
|
+
plugins: [react()],
|
|
392
|
+
test: {
|
|
393
|
+
environment: 'jsdom',
|
|
394
|
+
include: ['**/*.test.{ts,tsx}'],
|
|
395
|
+
globals: true,
|
|
396
|
+
setupFiles: ['./tests/setup.ts'],
|
|
397
|
+
},
|
|
398
|
+
resolve: {
|
|
399
|
+
alias: {
|
|
400
|
+
'@': path.resolve(__dirname, './src'),
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
`;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Generate vitest setup for website
|
|
409
|
+
*/
|
|
410
|
+
export function generateWebsiteVitestSetup(): string {
|
|
411
|
+
return `import '@testing-library/jest-dom';
|
|
412
|
+
|
|
413
|
+
// Mock next/navigation
|
|
414
|
+
vi.mock('next/navigation', () => ({
|
|
415
|
+
useRouter: () => ({
|
|
416
|
+
push: vi.fn(),
|
|
417
|
+
replace: vi.fn(),
|
|
418
|
+
prefetch: vi.fn(),
|
|
419
|
+
}),
|
|
420
|
+
useSearchParams: () => new URLSearchParams(),
|
|
421
|
+
usePathname: () => '/',
|
|
422
|
+
}));
|
|
423
|
+
|
|
424
|
+
// Mock next/image
|
|
425
|
+
vi.mock('next/image', () => ({
|
|
426
|
+
default: (props: Record<string, unknown>) => {
|
|
427
|
+
// eslint-disable-next-line @next/next/no-img-element, jsx-a11y/alt-text
|
|
428
|
+
return <img {...props} />;
|
|
429
|
+
},
|
|
430
|
+
}));
|
|
431
|
+
`;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Generate Next.js environment declaration
|
|
436
|
+
*/
|
|
437
|
+
export function generateWebsiteNextEnv(): string {
|
|
438
|
+
return `/// <reference types="next" />
|
|
439
|
+
/// <reference types="next/image-types/global" />
|
|
440
|
+
|
|
441
|
+
// NOTE: This file should not be edited
|
|
442
|
+
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
|
443
|
+
`;
|
|
444
|
+
}
|