pixelcrew 0.2.2 → 0.2.3
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/package.json +1 -1
- package/src/orchestrator/code-generator.js +994 -0
- package/src/orchestrator/engine.js +11 -1
- package/src/orchestrator/oneshot.js +80 -1277
- package/src/orchestrator/planner.js +329 -0
- package/src/orchestrator/skills-registry.js +194 -0
- package/src/orchestrator/task-queue.js +128 -0
- package/src/server/server.js +9 -2
|
@@ -0,0 +1,994 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PIXEL CREW — Dynamic Code Generator
|
|
3
|
+
*
|
|
4
|
+
* Synthesizes production-ready Next.js 14/15 App Router + TypeScript codebase
|
|
5
|
+
* and standalone live preview from a Project Specification.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export class CodeGenerator {
|
|
9
|
+
/**
|
|
10
|
+
* Generates complete multi-file project tree and standalone preview
|
|
11
|
+
*/
|
|
12
|
+
generateProject(spec) {
|
|
13
|
+
const files = {};
|
|
14
|
+
const p = spec.palette;
|
|
15
|
+
const f = spec.fonts;
|
|
16
|
+
|
|
17
|
+
// 1. package.json
|
|
18
|
+
files['package.json'] = JSON.stringify({
|
|
19
|
+
name: spec.companyName.toLowerCase().replace(/[^a-z0-9]/g, '-'),
|
|
20
|
+
version: "0.1.0",
|
|
21
|
+
private: true,
|
|
22
|
+
scripts: {
|
|
23
|
+
dev: "next dev",
|
|
24
|
+
build: "next build",
|
|
25
|
+
start: "next start",
|
|
26
|
+
lint: "next lint"
|
|
27
|
+
},
|
|
28
|
+
dependencies: {
|
|
29
|
+
next: "^14.2.15",
|
|
30
|
+
react: "^18.3.1",
|
|
31
|
+
"react-dom": "^18.3.1",
|
|
32
|
+
"lucide-react": "^0.453.0",
|
|
33
|
+
"clsx": "^2.1.1",
|
|
34
|
+
"tailwind-merge": "^2.5.4"
|
|
35
|
+
},
|
|
36
|
+
devDependencies: {
|
|
37
|
+
typescript: "^5.6.3",
|
|
38
|
+
"@types/node": "^20.17.0",
|
|
39
|
+
"@types/react": "^18.3.11",
|
|
40
|
+
"@types/react-dom": "^18.3.1",
|
|
41
|
+
postcss: "^8.4.47",
|
|
42
|
+
tailwindcss: "^3.4.14"
|
|
43
|
+
}
|
|
44
|
+
}, null, 2);
|
|
45
|
+
|
|
46
|
+
// 2. tsconfig.json
|
|
47
|
+
files['tsconfig.json'] = JSON.stringify({
|
|
48
|
+
compilerOptions: {
|
|
49
|
+
lib: ["dom", "dom.iterable", "esnext"],
|
|
50
|
+
allowJs: true,
|
|
51
|
+
skipLibCheck: true,
|
|
52
|
+
strict: true,
|
|
53
|
+
noEmit: true,
|
|
54
|
+
esModuleInterop: true,
|
|
55
|
+
module: "esnext",
|
|
56
|
+
moduleResolution: "bundler",
|
|
57
|
+
resolveJsonModule: true,
|
|
58
|
+
isolatedModules: true,
|
|
59
|
+
jsx: "preserve",
|
|
60
|
+
incremental: true,
|
|
61
|
+
plugins: [{ name: "next" }],
|
|
62
|
+
paths: { "@/*": ["./src/*"] }
|
|
63
|
+
},
|
|
64
|
+
include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
65
|
+
exclude: ["node_modules"]
|
|
66
|
+
}, null, 2);
|
|
67
|
+
|
|
68
|
+
// 3. tailwind.config.ts
|
|
69
|
+
files['tailwind.config.ts'] = `import type { Config } from "tailwindcss";
|
|
70
|
+
|
|
71
|
+
const config: Config = {
|
|
72
|
+
content: [
|
|
73
|
+
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
|
|
74
|
+
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
|
75
|
+
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
|
76
|
+
],
|
|
77
|
+
theme: {
|
|
78
|
+
extend: {
|
|
79
|
+
colors: {
|
|
80
|
+
brandBg: "${p.bg}",
|
|
81
|
+
brandSurface: "${p.surface}",
|
|
82
|
+
brandSurfaceRaised: "${p.surfaceRaised}",
|
|
83
|
+
brandBorder: "${p.border}",
|
|
84
|
+
brandAccent: "${p.accent}"
|
|
85
|
+
},
|
|
86
|
+
fontFamily: {
|
|
87
|
+
display: [${f.display}],
|
|
88
|
+
sans: [${f.body}],
|
|
89
|
+
mono: [${f.mono}]
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
plugins: [],
|
|
94
|
+
};
|
|
95
|
+
export default config;
|
|
96
|
+
`;
|
|
97
|
+
|
|
98
|
+
// 4. src/lib/utils.ts
|
|
99
|
+
files['src/lib/utils.ts'] = `import { clsx, type ClassValue } from "clsx";
|
|
100
|
+
import { twMerge } from "tailwind-merge";
|
|
101
|
+
|
|
102
|
+
export function cn(...inputs: ClassValue[]) {
|
|
103
|
+
return twMerge(clsx(inputs));
|
|
104
|
+
}
|
|
105
|
+
`;
|
|
106
|
+
|
|
107
|
+
// 5. src/types/index.ts
|
|
108
|
+
files['src/types/index.ts'] = `export interface DomainItem {
|
|
109
|
+
id: string;
|
|
110
|
+
title: string;
|
|
111
|
+
category: string;
|
|
112
|
+
tagline: string;
|
|
113
|
+
metrics?: string;
|
|
114
|
+
price?: string;
|
|
115
|
+
stack?: string[];
|
|
116
|
+
description: string;
|
|
117
|
+
link?: string;
|
|
118
|
+
featured?: boolean;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface SectionMeta {
|
|
122
|
+
id: string;
|
|
123
|
+
component: string;
|
|
124
|
+
title?: string;
|
|
125
|
+
headline?: string;
|
|
126
|
+
subheadline?: string;
|
|
127
|
+
}
|
|
128
|
+
`;
|
|
129
|
+
|
|
130
|
+
// 6. src/lib/data.ts
|
|
131
|
+
const sampleItems = this.generateDomainItems(spec.domain);
|
|
132
|
+
files['src/lib/data.ts'] = `import { DomainItem } from "@/types";
|
|
133
|
+
|
|
134
|
+
export const SITE_METADATA = {
|
|
135
|
+
name: "${spec.companyName}",
|
|
136
|
+
headline: "${spec.headline}",
|
|
137
|
+
domain: "${spec.domain}",
|
|
138
|
+
summary: "${spec.summary}"
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const DOMAIN_ITEMS: DomainItem[] = ${JSON.stringify(sampleItems, null, 2)};
|
|
142
|
+
`;
|
|
143
|
+
|
|
144
|
+
// 7. src/app/globals.css
|
|
145
|
+
files['src/app/globals.css'] = `@tailwind base;
|
|
146
|
+
@tailwind components;
|
|
147
|
+
@tailwind utilities;
|
|
148
|
+
|
|
149
|
+
:root {
|
|
150
|
+
--bg-primary: ${p.bg};
|
|
151
|
+
--surface-base: ${p.surface};
|
|
152
|
+
--surface-raised: ${p.surfaceRaised};
|
|
153
|
+
--border-subtle: ${p.border};
|
|
154
|
+
--border-hover: ${p.borderHover};
|
|
155
|
+
--text-primary: ${p.textPrimary};
|
|
156
|
+
--text-secondary: ${p.textSecondary};
|
|
157
|
+
--accent: ${p.accent};
|
|
158
|
+
--accent-glow: ${p.accentGlow};
|
|
159
|
+
--font-display: ${f.display};
|
|
160
|
+
--font-body: ${f.body};
|
|
161
|
+
--font-mono: ${f.mono};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
body {
|
|
165
|
+
background-color: var(--bg-primary);
|
|
166
|
+
color: var(--text-primary);
|
|
167
|
+
font-family: var(--font-body);
|
|
168
|
+
overflow-x: hidden;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.font-display { font-family: var(--font-display); }
|
|
172
|
+
.font-mono { font-family: var(--font-mono); }
|
|
173
|
+
|
|
174
|
+
.glass-panel {
|
|
175
|
+
background: var(--surface-base);
|
|
176
|
+
border: 1px solid var(--border-subtle);
|
|
177
|
+
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.glass-panel:hover {
|
|
181
|
+
border-color: var(--border-hover);
|
|
182
|
+
background: var(--surface-raised);
|
|
183
|
+
transform: translateY(-2px);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.terminal-window {
|
|
187
|
+
background: #08090d;
|
|
188
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
189
|
+
box-shadow: 0 20px 40px -15px rgba(0,0,0,0.7);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.filter-btn.active {
|
|
193
|
+
background-color: #ffffff;
|
|
194
|
+
color: #000000;
|
|
195
|
+
border-color: #ffffff;
|
|
196
|
+
}
|
|
197
|
+
`;
|
|
198
|
+
|
|
199
|
+
// 8. src/app/layout.tsx
|
|
200
|
+
files['src/app/layout.tsx'] = `import type { Metadata } from "next";
|
|
201
|
+
import "./globals.css";
|
|
202
|
+
|
|
203
|
+
export const metadata: Metadata = {
|
|
204
|
+
title: "${spec.companyName} — ${spec.headline}",
|
|
205
|
+
description: "${spec.summary}",
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export default function RootLayout({
|
|
209
|
+
children,
|
|
210
|
+
}: Readonly<{
|
|
211
|
+
children: React.ReactNode;
|
|
212
|
+
}>) {
|
|
213
|
+
return (
|
|
214
|
+
<html lang="en" className="scroll-smooth">
|
|
215
|
+
<head>
|
|
216
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
217
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" />
|
|
218
|
+
<link href="${f.googleFontsUrl}" rel="stylesheet" />
|
|
219
|
+
</head>
|
|
220
|
+
<body className="bg-brandBg text-neutral-100 min-h-screen antialiased selection:bg-neutral-200 selection:text-black">
|
|
221
|
+
{children}
|
|
222
|
+
</body>
|
|
223
|
+
</html>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
`;
|
|
227
|
+
|
|
228
|
+
// 9. Component files in src/components/sections/
|
|
229
|
+
this.generateSectionComponents(files, spec, sampleItems);
|
|
230
|
+
|
|
231
|
+
// 10. src/app/page.tsx
|
|
232
|
+
files['src/app/page.tsx'] = this.generatePageTsx(spec);
|
|
233
|
+
|
|
234
|
+
// 11. Backend API route handlers
|
|
235
|
+
files['src/app/api/contact/route.ts'] = `import { NextRequest, NextResponse } from "next/server";
|
|
236
|
+
|
|
237
|
+
export async function POST(req: NextRequest) {
|
|
238
|
+
try {
|
|
239
|
+
const body = await req.json();
|
|
240
|
+
const { name, email, message } = body;
|
|
241
|
+
|
|
242
|
+
if (!email || !message) {
|
|
243
|
+
return NextResponse.json(
|
|
244
|
+
{ error: "Invalid Request", detail: "Email and message are required fields." },
|
|
245
|
+
{ status: 400 }
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return NextResponse.json({
|
|
250
|
+
success: true,
|
|
251
|
+
message: "Message received successfully. We will follow up shortly.",
|
|
252
|
+
timestamp: new Date().toISOString()
|
|
253
|
+
});
|
|
254
|
+
} catch (error) {
|
|
255
|
+
return NextResponse.json(
|
|
256
|
+
{ error: "Internal Server Error", detail: "Failed to process inquiry submission." },
|
|
257
|
+
{ status: 500 }
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
`;
|
|
262
|
+
|
|
263
|
+
files['src/app/api/data/route.ts'] = `import { NextRequest, NextResponse } from "next/server";
|
|
264
|
+
import { DOMAIN_ITEMS, SITE_METADATA } from "@/lib/data";
|
|
265
|
+
|
|
266
|
+
export async function GET(req: NextRequest) {
|
|
267
|
+
const { searchParams } = new URL(req.url);
|
|
268
|
+
const category = searchParams.get("category");
|
|
269
|
+
|
|
270
|
+
if (category && category !== "All") {
|
|
271
|
+
const filtered = DOMAIN_ITEMS.filter(item => item.category.toLowerCase() === category.toLowerCase());
|
|
272
|
+
return NextResponse.json({ items: filtered, total: filtered.length, metadata: SITE_METADATA });
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return NextResponse.json({ items: DOMAIN_ITEMS, total: DOMAIN_ITEMS.length, metadata: SITE_METADATA });
|
|
276
|
+
}
|
|
277
|
+
`;
|
|
278
|
+
|
|
279
|
+
// 12. README.md
|
|
280
|
+
files['README.md'] = `# ${spec.companyName} — ${spec.headline}
|
|
281
|
+
|
|
282
|
+
Synthesized using **Pixel Crew** dynamic multi-agent orchestration.
|
|
283
|
+
|
|
284
|
+
## 🚀 Getting Started
|
|
285
|
+
|
|
286
|
+
\`\`\`bash
|
|
287
|
+
# 1. Install dependencies
|
|
288
|
+
npm install
|
|
289
|
+
|
|
290
|
+
# 2. Start the local Next.js development server
|
|
291
|
+
npm run dev
|
|
292
|
+
|
|
293
|
+
# 3. Open in your browser
|
|
294
|
+
http://localhost:3000
|
|
295
|
+
\`\`\`
|
|
296
|
+
|
|
297
|
+
## 🛠️ Stack & Standards
|
|
298
|
+
- **Framework**: Next.js 14/15 App Router
|
|
299
|
+
- **Language**: TypeScript (Strict Mode)
|
|
300
|
+
- **Styling**: Tailwind CSS + Fluid clamp() Design Tokens
|
|
301
|
+
- **Quality Standard**: Zero AI Slop, WCAG AA Accessibility
|
|
302
|
+
`;
|
|
303
|
+
|
|
304
|
+
// 13. Standalone Live Preview HTML
|
|
305
|
+
const previewHtml = this.generateStandalonePreview(spec, sampleItems);
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
files,
|
|
309
|
+
previewHtml,
|
|
310
|
+
fileCount: Object.keys(files).length,
|
|
311
|
+
entrypoint: 'src/app/page.tsx'
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Helper: Generates domain-specific items
|
|
317
|
+
*/
|
|
318
|
+
generateDomainItems(domain) {
|
|
319
|
+
if (domain === 'restaurant') {
|
|
320
|
+
return [
|
|
321
|
+
{
|
|
322
|
+
id: "dish-1",
|
|
323
|
+
title: "Charred Spruce & Wild Morels",
|
|
324
|
+
category: "First Course",
|
|
325
|
+
tagline: "Pine needle infusion, fermented birch reduction, hazelnut oil",
|
|
326
|
+
price: "$28",
|
|
327
|
+
description: "Foraged from high-elevation pine groves at dawn. Smoked over dried mountain heather.",
|
|
328
|
+
featured: true
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
id: "dish-2",
|
|
332
|
+
title: "Dry-Aged Duck Breast in Salt Crust",
|
|
333
|
+
category: "Main Course",
|
|
334
|
+
tagline: "28-day dry age, elderberry glaze, roasted sunchoke puree",
|
|
335
|
+
price: "$54",
|
|
336
|
+
description: "Slow-roasted over fruitwood coals. Served with preserved autumn quince.",
|
|
337
|
+
featured: true
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
id: "dish-3",
|
|
341
|
+
title: "Smoked Juniper & Honeycomb Cream",
|
|
342
|
+
category: "Dessert",
|
|
343
|
+
tagline: "Wild heather honey, crisp chamomile tuile, sea salt",
|
|
344
|
+
price: "$22",
|
|
345
|
+
description: "Chilled botanicals paired with raw mountain comb.",
|
|
346
|
+
featured: false
|
|
347
|
+
}
|
|
348
|
+
];
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Default portfolio / dev / agency items
|
|
352
|
+
return [
|
|
353
|
+
{
|
|
354
|
+
id: "proj-1",
|
|
355
|
+
title: "HyperFlow — Distributed Stream Processor",
|
|
356
|
+
category: "Distributed Systems",
|
|
357
|
+
tagline: "Low-latency stream engine with Raft consensus & Rust FFI",
|
|
358
|
+
metrics: "1.2M events/sec · < 3ms latency",
|
|
359
|
+
stack: ["Rust", "TypeScript", "Kafka", "PostgreSQL"],
|
|
360
|
+
description: "Engineered zero-copy memory buffers and SIMD-accelerated deserialization. Slashed cloud compute by 68%.",
|
|
361
|
+
link: "https://github.com",
|
|
362
|
+
featured: true
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
id: "proj-2",
|
|
366
|
+
title: "NeuralCanvas — Collaborative AI Studio",
|
|
367
|
+
category: "AI & RAG",
|
|
368
|
+
tagline: "Multimodal generative canvas with real-time latent space exploration",
|
|
369
|
+
metrics: "120k active creators · 60fps canvas",
|
|
370
|
+
stack: ["Next.js 15", "WebGPU", "WebSockets", "FastAPI"],
|
|
371
|
+
description: "Implemented client-side WebGPU shaders for real-time latent image upscaling.",
|
|
372
|
+
link: "https://github.com",
|
|
373
|
+
featured: true
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
id: "proj-3",
|
|
377
|
+
title: "AuraUI — Accessible Design System",
|
|
378
|
+
category: "Frontend Islands",
|
|
379
|
+
tagline: "Headless, keyboard-first primitive library with fluid typography mathematical scales",
|
|
380
|
+
metrics: "WCAG AAA · Zero runtime dependencies",
|
|
381
|
+
stack: ["React 19", "Tailwind CSS", "TypeScript"],
|
|
382
|
+
description: "Adopted by 450+ engineering teams with automated contrast verification.",
|
|
383
|
+
link: "https://github.com",
|
|
384
|
+
featured: false
|
|
385
|
+
}
|
|
386
|
+
];
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Helper: Generates React components
|
|
391
|
+
*/
|
|
392
|
+
generateSectionComponents(files, spec, items) {
|
|
393
|
+
const p = spec.palette;
|
|
394
|
+
const f = spec.fonts;
|
|
395
|
+
|
|
396
|
+
// Navbar.tsx
|
|
397
|
+
files['src/components/sections/Navbar.tsx'] = `'use client';
|
|
398
|
+
|
|
399
|
+
import React from 'react';
|
|
400
|
+
|
|
401
|
+
export function Navbar() {
|
|
402
|
+
return (
|
|
403
|
+
<header className="fixed top-0 left-0 right-0 z-50 bg-brandBg/80 backdrop-blur-md border-b border-white/5">
|
|
404
|
+
<div className="max-w-7xl mx-auto px-6 h-20 flex items-center justify-between">
|
|
405
|
+
<div className="flex items-center gap-3">
|
|
406
|
+
<div className="w-8 h-8 bg-white text-black font-mono font-bold text-xs flex items-center justify-center rounded-sm">
|
|
407
|
+
${spec.companyName.charAt(0)}
|
|
408
|
+
</div>
|
|
409
|
+
<span className="font-display font-semibold text-lg tracking-tight text-white">${spec.companyName}</span>
|
|
410
|
+
</div>
|
|
411
|
+
<nav className="hidden md:flex items-center gap-8 text-xs font-mono uppercase tracking-wider text-neutral-400">
|
|
412
|
+
<a href="#showcase" className="hover:text-white transition-colors">Showcase</a>
|
|
413
|
+
<a href="#interactive" className="hover:text-white transition-colors">Interactive</a>
|
|
414
|
+
<a href="#contact" className="px-3.5 py-1.5 bg-white text-black font-medium rounded-sm hover:bg-neutral-200 transition-colors">Contact</a>
|
|
415
|
+
</nav>
|
|
416
|
+
</div>
|
|
417
|
+
</header>
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
`;
|
|
421
|
+
|
|
422
|
+
// Hero.tsx
|
|
423
|
+
files['src/components/sections/Hero.tsx'] = `'use client';
|
|
424
|
+
|
|
425
|
+
import React from 'react';
|
|
426
|
+
import { ArrowUpRight, Sparkles } from 'lucide-react';
|
|
427
|
+
|
|
428
|
+
export function Hero() {
|
|
429
|
+
return (
|
|
430
|
+
<section className="relative pt-36 pb-20 px-6 max-w-7xl mx-auto">
|
|
431
|
+
<div className="flex items-center gap-2 mb-6">
|
|
432
|
+
<span className="inline-flex items-center gap-2 px-3 py-1 text-xs font-mono tracking-wider uppercase bg-white/5 border border-white/10 text-neutral-300 rounded-full">
|
|
433
|
+
<span className="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span>
|
|
434
|
+
BESPOKE DIGITAL PLATFORM
|
|
435
|
+
</span>
|
|
436
|
+
</div>
|
|
437
|
+
|
|
438
|
+
<h1 className="font-display text-4xl sm:text-6xl md:text-7xl font-normal tracking-tight text-white max-w-4xl leading-[1.08] mb-8">
|
|
439
|
+
${spec.headline}
|
|
440
|
+
</h1>
|
|
441
|
+
|
|
442
|
+
<p className="text-lg md:text-xl text-neutral-400 font-light max-w-2xl leading-relaxed mb-10">
|
|
443
|
+
${spec.summary}
|
|
444
|
+
</p>
|
|
445
|
+
|
|
446
|
+
<div className="flex flex-wrap items-center gap-4 mb-16">
|
|
447
|
+
<a href="#showcase" className="px-6 py-3.5 bg-white text-black font-medium text-sm rounded-sm hover:bg-neutral-200 transition-colors flex items-center gap-2">
|
|
448
|
+
Explore Showcase
|
|
449
|
+
<ArrowUpRight className="w-4 h-4" />
|
|
450
|
+
</a>
|
|
451
|
+
<a href="#interactive" className="px-6 py-3.5 bg-brandSurface border border-white/10 text-white font-mono text-sm rounded-sm hover:border-white/30 transition-colors">
|
|
452
|
+
Launch Interactive Sandbox
|
|
453
|
+
</a>
|
|
454
|
+
</div>
|
|
455
|
+
</section>
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
`;
|
|
459
|
+
|
|
460
|
+
// ShowcaseGrid.tsx
|
|
461
|
+
files['src/components/sections/ShowcaseGrid.tsx'] = `'use client';
|
|
462
|
+
|
|
463
|
+
import React, { useState } from 'react';
|
|
464
|
+
import { DOMAIN_ITEMS } from '@/lib/data';
|
|
465
|
+
import { ArrowUpRight } from 'lucide-react';
|
|
466
|
+
|
|
467
|
+
export function ShowcaseGrid() {
|
|
468
|
+
const [activeCat, setActiveCat] = useState('All');
|
|
469
|
+
const categories = ["All", ...Array.from(new Set(DOMAIN_ITEMS.map(i => i.category)))];
|
|
470
|
+
|
|
471
|
+
const filtered = activeCat === 'All'
|
|
472
|
+
? DOMAIN_ITEMS
|
|
473
|
+
: DOMAIN_ITEMS.filter(i => i.category === activeCat);
|
|
474
|
+
|
|
475
|
+
return (
|
|
476
|
+
<section id="showcase" className="py-24 px-6 max-w-7xl mx-auto border-t border-white/10">
|
|
477
|
+
<div className="flex flex-col md:flex-row md:items-end justify-between mb-12 gap-6">
|
|
478
|
+
<div>
|
|
479
|
+
<span className="font-mono text-xs text-neutral-400 uppercase tracking-widest block mb-2">01 // Selected Work</span>
|
|
480
|
+
<h2 className="font-display text-3xl md:text-4xl text-white font-normal">Featured Artifacts & Architectures</h2>
|
|
481
|
+
</div>
|
|
482
|
+
|
|
483
|
+
<div className="flex flex-wrap gap-2">
|
|
484
|
+
{categories.map((cat) => (
|
|
485
|
+
<button
|
|
486
|
+
key={cat}
|
|
487
|
+
onClick={() => setActiveCat(cat)}
|
|
488
|
+
className={\`px-3 py-1.5 text-xs font-mono rounded-sm transition-all border \${
|
|
489
|
+
activeCat === cat
|
|
490
|
+
? 'bg-white text-black border-white font-medium'
|
|
491
|
+
: 'bg-brandSurface text-neutral-400 border-white/10 hover:border-white/30'
|
|
492
|
+
}\`}
|
|
493
|
+
>
|
|
494
|
+
{cat}
|
|
495
|
+
</button>
|
|
496
|
+
))}
|
|
497
|
+
</div>
|
|
498
|
+
</div>
|
|
499
|
+
|
|
500
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
501
|
+
{filtered.map((item) => (
|
|
502
|
+
<div key={item.id} className="glass-panel p-8 rounded-sm flex flex-col justify-between group">
|
|
503
|
+
<div>
|
|
504
|
+
<div className="flex items-center justify-between gap-2 mb-4">
|
|
505
|
+
<span className="text-xs font-mono uppercase tracking-wider text-neutral-400 bg-white/5 px-2.5 py-1 rounded border border-white/5">
|
|
506
|
+
{item.category}
|
|
507
|
+
</span>
|
|
508
|
+
{item.metrics && (
|
|
509
|
+
<span className="font-mono text-xs text-emerald-400">{item.metrics}</span>
|
|
510
|
+
)}
|
|
511
|
+
{item.price && (
|
|
512
|
+
<span className="font-mono text-xs text-amber-300">{item.price}</span>
|
|
513
|
+
)}
|
|
514
|
+
</div>
|
|
515
|
+
<h3 className="font-display text-2xl text-white font-normal mb-3 group-hover:text-emerald-300 transition-colors">
|
|
516
|
+
{item.title}
|
|
517
|
+
</h3>
|
|
518
|
+
<p className="text-neutral-400 text-sm font-light leading-relaxed mb-6">
|
|
519
|
+
{item.description}
|
|
520
|
+
</p>
|
|
521
|
+
</div>
|
|
522
|
+
|
|
523
|
+
<div>
|
|
524
|
+
{item.stack && (
|
|
525
|
+
<div className="flex flex-wrap gap-1.5 mb-6">
|
|
526
|
+
{item.stack.map((t) => (
|
|
527
|
+
<span key={t} className="text-[11px] font-mono text-neutral-300 bg-neutral-900 px-2 py-0.5 rounded border border-white/10">
|
|
528
|
+
{t}
|
|
529
|
+
</span>
|
|
530
|
+
))}
|
|
531
|
+
</div>
|
|
532
|
+
)}
|
|
533
|
+
<div className="flex items-center gap-4 pt-4 border-t border-white/5 text-xs font-mono">
|
|
534
|
+
<a href="#contact" className="text-white hover:underline flex items-center gap-1">
|
|
535
|
+
Inquire Artifact <ArrowUpRight className="w-3.5 h-3.5" />
|
|
536
|
+
</a>
|
|
537
|
+
</div>
|
|
538
|
+
</div>
|
|
539
|
+
</div>
|
|
540
|
+
))}
|
|
541
|
+
</div>
|
|
542
|
+
</section>
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
`;
|
|
546
|
+
|
|
547
|
+
// InteractiveSection.tsx
|
|
548
|
+
files['src/components/sections/InteractiveSection.tsx'] = `'use client';
|
|
549
|
+
|
|
550
|
+
import React, { useState } from 'react';
|
|
551
|
+
import { Terminal, CornerDownLeft } from 'lucide-react';
|
|
552
|
+
|
|
553
|
+
export function InteractiveSection() {
|
|
554
|
+
const [input, setInput] = useState('');
|
|
555
|
+
const [logs, setLogs] = useState<Array<{ cmd: string; res: string }>>([
|
|
556
|
+
{ cmd: "status", res: "System Architecture: Operational\\nRendering: Next.js 14 App Router\\nAnti-AI Critic: Passed (Score >= 8.5/10)" }
|
|
557
|
+
]);
|
|
558
|
+
|
|
559
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
560
|
+
e.preventDefault();
|
|
561
|
+
const raw = input.trim();
|
|
562
|
+
if (!raw) return;
|
|
563
|
+
|
|
564
|
+
let res = \`Execution result for '\${raw}': OK. All systems operational.\`;
|
|
565
|
+
if (raw.toLowerCase() === 'help') res = "Available commands: status, architecture, stack, clear";
|
|
566
|
+
if (raw.toLowerCase() === 'clear') {
|
|
567
|
+
setLogs([]);
|
|
568
|
+
setInput('');
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
setLogs(prev => [...prev, { cmd: raw, res }]);
|
|
573
|
+
setInput('');
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
return (
|
|
577
|
+
<section id="interactive" className="py-20 px-6 max-w-7xl mx-auto border-t border-white/10">
|
|
578
|
+
<div className="mb-8">
|
|
579
|
+
<span className="font-mono text-xs text-neutral-400 uppercase tracking-widest block mb-2">02 // Interactive Console</span>
|
|
580
|
+
<h2 className="font-display text-3xl text-white font-normal">Runtime Telemetry Shell</h2>
|
|
581
|
+
</div>
|
|
582
|
+
|
|
583
|
+
<div className="terminal-window rounded-sm overflow-hidden font-mono text-xs">
|
|
584
|
+
<div className="bg-[#12141a] px-4 py-2.5 border-b border-white/10 flex items-center justify-between">
|
|
585
|
+
<div className="flex items-center gap-2">
|
|
586
|
+
<span className="w-2.5 h-2.5 rounded-full bg-rose-500"></span>
|
|
587
|
+
<span className="w-2.5 h-2.5 rounded-full bg-amber-500"></span>
|
|
588
|
+
<span className="w-2.5 h-2.5 rounded-full bg-emerald-500"></span>
|
|
589
|
+
<span className="text-[11px] text-neutral-400 ml-2">guest@pixelcrew.system:~</span>
|
|
590
|
+
</div>
|
|
591
|
+
<span className="text-[10px] text-neutral-500">NEXTJS 14</span>
|
|
592
|
+
</div>
|
|
593
|
+
|
|
594
|
+
<div className="p-6 text-neutral-300 min-h-[200px] max-h-[340px] overflow-y-auto space-y-3">
|
|
595
|
+
{logs.map((l, i) => (
|
|
596
|
+
<div key={i} className="space-y-1">
|
|
597
|
+
<div className="flex items-center gap-2 text-emerald-400">
|
|
598
|
+
<span>$</span>
|
|
599
|
+
<span>{l.cmd}</span>
|
|
600
|
+
</div>
|
|
601
|
+
<div className="text-neutral-400 whitespace-pre-line pl-4 border-l border-white/10">
|
|
602
|
+
{l.res}
|
|
603
|
+
</div>
|
|
604
|
+
</div>
|
|
605
|
+
))}
|
|
606
|
+
|
|
607
|
+
<form onSubmit={handleSubmit} className="flex items-center gap-2 pt-2">
|
|
608
|
+
<span className="text-emerald-400 font-bold">$</span>
|
|
609
|
+
<input
|
|
610
|
+
type="text"
|
|
611
|
+
value={input}
|
|
612
|
+
onChange={(e) => setInput(e.target.value)}
|
|
613
|
+
placeholder="type 'status' or 'help'..."
|
|
614
|
+
className="bg-transparent text-white focus:outline-none flex-1 font-mono text-xs placeholder:text-neutral-600"
|
|
615
|
+
/>
|
|
616
|
+
<button type="submit" className="text-neutral-500 hover:text-white">
|
|
617
|
+
<CornerDownLeft className="w-3.5 h-3.5" />
|
|
618
|
+
</button>
|
|
619
|
+
</form>
|
|
620
|
+
</div>
|
|
621
|
+
</div>
|
|
622
|
+
</section>
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
`;
|
|
626
|
+
|
|
627
|
+
// ContactSection.tsx
|
|
628
|
+
files['src/components/sections/ContactSection.tsx'] = `'use client';
|
|
629
|
+
|
|
630
|
+
import React, { useState } from 'react';
|
|
631
|
+
|
|
632
|
+
export function ContactSection() {
|
|
633
|
+
const [email, setEmail] = useState('');
|
|
634
|
+
const [message, setMessage] = useState('');
|
|
635
|
+
const [sent, setSent] = useState(false);
|
|
636
|
+
|
|
637
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
638
|
+
e.preventDefault();
|
|
639
|
+
if (!email || !message) return;
|
|
640
|
+
setSent(true);
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
return (
|
|
644
|
+
<section id="contact" className="py-24 px-6 max-w-7xl mx-auto border-t border-white/10">
|
|
645
|
+
<div className="max-w-2xl">
|
|
646
|
+
<span className="font-mono text-xs text-neutral-400 uppercase tracking-widest block mb-2">03 // Inquiries</span>
|
|
647
|
+
<h2 className="font-display text-3xl md:text-4xl text-white font-normal mb-6">Let's build something exceptional.</h2>
|
|
648
|
+
<p className="text-neutral-400 font-light text-sm mb-8">
|
|
649
|
+
Available for technical leadership, high-order digital architecture, and bespoke platform development.
|
|
650
|
+
</p>
|
|
651
|
+
|
|
652
|
+
{sent ? (
|
|
653
|
+
<div className="p-4 bg-emerald-500/10 border border-emerald-500/30 text-emerald-300 font-mono text-xs rounded">
|
|
654
|
+
✓ Thank you! Your inquiry has been received.
|
|
655
|
+
</div>
|
|
656
|
+
) : (
|
|
657
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
658
|
+
<div>
|
|
659
|
+
<input
|
|
660
|
+
type="email"
|
|
661
|
+
value={email}
|
|
662
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
663
|
+
placeholder="your.email@domain.com"
|
|
664
|
+
required
|
|
665
|
+
className="w-full px-4 py-3 bg-brandSurface border border-white/10 text-white font-mono text-xs rounded-sm focus:outline-none focus:border-white/40"
|
|
666
|
+
/>
|
|
667
|
+
</div>
|
|
668
|
+
<div>
|
|
669
|
+
<textarea
|
|
670
|
+
value={message}
|
|
671
|
+
onChange={(e) => setMessage(e.target.value)}
|
|
672
|
+
placeholder="Project brief, scope, and target timeline..."
|
|
673
|
+
rows={4}
|
|
674
|
+
required
|
|
675
|
+
className="w-full px-4 py-3 bg-brandSurface border border-white/10 text-white font-mono text-xs rounded-sm focus:outline-none focus:border-white/40"
|
|
676
|
+
/>
|
|
677
|
+
</div>
|
|
678
|
+
<button
|
|
679
|
+
type="submit"
|
|
680
|
+
className="px-6 py-3 bg-white text-black font-medium text-xs rounded-sm hover:bg-neutral-200 transition-colors font-mono"
|
|
681
|
+
>
|
|
682
|
+
Submit Inquiry →
|
|
683
|
+
</button>
|
|
684
|
+
</form>
|
|
685
|
+
)}
|
|
686
|
+
</div>
|
|
687
|
+
</section>
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
`;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Helper: Generates page.tsx
|
|
695
|
+
*/
|
|
696
|
+
generatePageTsx(spec) {
|
|
697
|
+
return `import { Navbar } from "@/components/sections/Navbar";
|
|
698
|
+
import { Hero } from "@/components/sections/Hero";
|
|
699
|
+
import { ShowcaseGrid } from "@/components/sections/ShowcaseGrid";
|
|
700
|
+
import { InteractiveSection } from "@/components/sections/InteractiveSection";
|
|
701
|
+
import { ContactSection } from "@/components/sections/ContactSection";
|
|
702
|
+
|
|
703
|
+
export default function Home() {
|
|
704
|
+
return (
|
|
705
|
+
<main className="min-h-screen">
|
|
706
|
+
<Navbar />
|
|
707
|
+
<Hero />
|
|
708
|
+
<ShowcaseGrid />
|
|
709
|
+
<InteractiveSection />
|
|
710
|
+
<ContactSection />
|
|
711
|
+
|
|
712
|
+
<footer className="py-16 px-6 border-t border-white/10 max-w-7xl mx-auto text-xs font-mono text-neutral-500 flex flex-col sm:flex-row justify-between items-center gap-4">
|
|
713
|
+
<div>© {new Date().getFullYear()} ${spec.companyName}. Synthesized with Pixel Crew design-first architecture.</div>
|
|
714
|
+
<div className="flex items-center gap-6 text-neutral-400">
|
|
715
|
+
<a href="https://github.com" className="hover:text-white">GitHub</a>
|
|
716
|
+
<a href="https://twitter.com" className="hover:text-white">Twitter</a>
|
|
717
|
+
</div>
|
|
718
|
+
</footer>
|
|
719
|
+
</main>
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
`;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Helper: Standalone HTML Live Preview Generator
|
|
727
|
+
*/
|
|
728
|
+
generateStandalonePreview(spec, items) {
|
|
729
|
+
const p = spec.palette;
|
|
730
|
+
const f = spec.fonts;
|
|
731
|
+
const categories = ["All", ...Array.from(new Set(items.map(i => i.category)))];
|
|
732
|
+
|
|
733
|
+
return `<!DOCTYPE html>
|
|
734
|
+
<html lang="en" class="scroll-smooth">
|
|
735
|
+
<head>
|
|
736
|
+
<meta charset="UTF-8">
|
|
737
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
738
|
+
<title>${spec.companyName} — ${spec.headline}</title>
|
|
739
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
740
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
741
|
+
<link href="${f.googleFontsUrl}" rel="stylesheet">
|
|
742
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
743
|
+
<script>
|
|
744
|
+
tailwind.config = {
|
|
745
|
+
theme: {
|
|
746
|
+
extend: {
|
|
747
|
+
colors: {
|
|
748
|
+
brandBg: '${p.bg}',
|
|
749
|
+
brandSurface: '${p.surface}',
|
|
750
|
+
brandSurfaceRaised: '${p.surfaceRaised}',
|
|
751
|
+
brandBorder: '${p.border}',
|
|
752
|
+
brandAccent: '${p.accent}'
|
|
753
|
+
},
|
|
754
|
+
fontFamily: {
|
|
755
|
+
display: [${f.display}],
|
|
756
|
+
sans: [${f.body}],
|
|
757
|
+
mono: [${f.mono}]
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
</script>
|
|
763
|
+
<style>
|
|
764
|
+
:root {
|
|
765
|
+
--bg-primary: ${p.bg};
|
|
766
|
+
--surface-base: ${p.surface};
|
|
767
|
+
--surface-raised: ${p.surfaceRaised};
|
|
768
|
+
--border-subtle: ${p.border};
|
|
769
|
+
--border-hover: ${p.borderHover};
|
|
770
|
+
--text-primary: ${p.textPrimary};
|
|
771
|
+
--text-secondary: ${p.textSecondary};
|
|
772
|
+
--font-display: ${f.display};
|
|
773
|
+
--font-body: ${f.body};
|
|
774
|
+
--font-mono: ${f.mono};
|
|
775
|
+
}
|
|
776
|
+
body { background-color: var(--bg-primary); color: var(--text-primary); font-family: var(--font-body); }
|
|
777
|
+
.font-display { font-family: var(--font-display); }
|
|
778
|
+
.font-mono { font-family: var(--font-mono); }
|
|
779
|
+
.glass-panel { background: var(--surface-base); border: 1px solid var(--border-subtle); transition: all 0.25s ease; }
|
|
780
|
+
.glass-panel:hover { border-color: var(--border-hover); background: var(--surface-raised); transform: translateY(-2px); }
|
|
781
|
+
.terminal-window { background: #08090d; border: 1px solid rgba(255, 255, 255, 0.12); }
|
|
782
|
+
.filter-btn.active { background-color: #ffffff; color: #000000; border-color: #ffffff; }
|
|
783
|
+
</style>
|
|
784
|
+
</head>
|
|
785
|
+
<body class="bg-brandBg text-neutral-100 min-h-screen selection:bg-neutral-200 selection:text-black">
|
|
786
|
+
|
|
787
|
+
<!-- Header Navigation -->
|
|
788
|
+
<header class="fixed top-0 left-0 right-0 z-50 bg-brandBg/85 backdrop-blur-md border-b border-white/5">
|
|
789
|
+
<div class="max-w-7xl mx-auto px-6 h-20 flex items-center justify-between">
|
|
790
|
+
<a href="#" class="flex items-center gap-3">
|
|
791
|
+
<div class="w-8 h-8 bg-white text-black font-mono font-bold text-xs flex items-center justify-center rounded-sm">
|
|
792
|
+
${spec.companyName.charAt(0)}
|
|
793
|
+
</div>
|
|
794
|
+
<span class="font-display font-semibold text-lg tracking-tight text-white">${spec.companyName}</span>
|
|
795
|
+
</a>
|
|
796
|
+
<nav class="hidden md:flex items-center gap-8 text-xs font-mono uppercase tracking-wider text-neutral-400">
|
|
797
|
+
<a href="#showcase" class="hover:text-white transition-colors">Showcase</a>
|
|
798
|
+
<a href="#interactive" class="hover:text-white transition-colors">Interactive</a>
|
|
799
|
+
<a href="#contact" class="px-3.5 py-1.5 bg-white text-black font-medium rounded-sm hover:bg-neutral-200 transition-colors">Contact</a>
|
|
800
|
+
</nav>
|
|
801
|
+
</div>
|
|
802
|
+
</header>
|
|
803
|
+
|
|
804
|
+
<!-- Hero Section -->
|
|
805
|
+
<section class="relative pt-36 pb-20 px-6 max-w-7xl mx-auto">
|
|
806
|
+
<div class="flex items-center gap-2 mb-6">
|
|
807
|
+
<span class="inline-flex items-center gap-2 px-3 py-1 text-xs font-mono tracking-wider uppercase bg-white/5 border border-white/10 text-neutral-300 rounded-full">
|
|
808
|
+
<span class="w-2 h-2 rounded-full bg-emerald-400 animate-ping"></span>
|
|
809
|
+
BESPOKE SYSTEM
|
|
810
|
+
</span>
|
|
811
|
+
</div>
|
|
812
|
+
|
|
813
|
+
<h1 class="font-display text-4xl sm:text-6xl md:text-7xl font-normal tracking-tight text-white max-w-4xl leading-[1.08] mb-8">
|
|
814
|
+
${spec.headline}
|
|
815
|
+
</h1>
|
|
816
|
+
|
|
817
|
+
<p class="text-lg md:text-xl text-neutral-400 font-light max-w-2xl leading-relaxed mb-10">
|
|
818
|
+
${spec.summary}
|
|
819
|
+
</p>
|
|
820
|
+
|
|
821
|
+
<div class="flex flex-wrap items-center gap-4 mb-16">
|
|
822
|
+
<a href="#showcase" class="px-6 py-3.5 bg-white text-black font-medium text-sm rounded-sm hover:bg-neutral-200 transition-colors">
|
|
823
|
+
Explore Artifacts →
|
|
824
|
+
</a>
|
|
825
|
+
<a href="#interactive" class="px-6 py-3.5 bg-brandSurface border border-white/10 text-white font-mono text-sm rounded-sm hover:border-white/30 transition-colors">
|
|
826
|
+
$ Launch Terminal
|
|
827
|
+
</a>
|
|
828
|
+
</div>
|
|
829
|
+
</section>
|
|
830
|
+
|
|
831
|
+
<!-- Showcase Section with Live Filtering -->
|
|
832
|
+
<section id="showcase" class="py-24 px-6 max-w-7xl mx-auto border-t border-white/10">
|
|
833
|
+
<div class="flex flex-col md:flex-row md:items-end justify-between mb-12 gap-6">
|
|
834
|
+
<div>
|
|
835
|
+
<span class="font-mono text-xs text-neutral-400 uppercase tracking-widest block mb-2">01 // Selected Work</span>
|
|
836
|
+
<h2 class="font-display text-3xl md:text-4xl text-white font-normal">Featured Artifacts & Architectures</h2>
|
|
837
|
+
</div>
|
|
838
|
+
|
|
839
|
+
<div class="flex flex-wrap gap-2" id="filterBtnGroup">
|
|
840
|
+
${categories.map((cat, i) => `
|
|
841
|
+
<button
|
|
842
|
+
class="filter-btn px-3 py-1.5 text-xs font-mono rounded-sm transition-all border ${i === 0 ? 'active bg-white text-black font-medium' : 'bg-brandSurface text-neutral-400 border-white/10 hover:border-white/30'}"
|
|
843
|
+
data-category="${cat}"
|
|
844
|
+
>
|
|
845
|
+
${cat}
|
|
846
|
+
</button>
|
|
847
|
+
`).join('')}
|
|
848
|
+
</div>
|
|
849
|
+
</div>
|
|
850
|
+
|
|
851
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6" id="itemsContainer">
|
|
852
|
+
${items.map(item => `
|
|
853
|
+
<div class="showcase-card glass-panel p-8 rounded-sm flex flex-col justify-between" data-category="${item.category}">
|
|
854
|
+
<div>
|
|
855
|
+
<div class="flex items-center justify-between gap-2 mb-4">
|
|
856
|
+
<span class="text-xs font-mono uppercase tracking-wider text-neutral-400 bg-white/5 px-2.5 py-1 rounded border border-white/5">
|
|
857
|
+
${item.category}
|
|
858
|
+
</span>
|
|
859
|
+
${item.metrics ? `<span class="font-mono text-xs text-emerald-400">${item.metrics}</span>` : ''}
|
|
860
|
+
${item.price ? `<span class="font-mono text-xs text-amber-300">${item.price}</span>` : ''}
|
|
861
|
+
</div>
|
|
862
|
+
<h3 class="font-display text-2xl text-white font-normal mb-3">
|
|
863
|
+
${item.title}
|
|
864
|
+
</h3>
|
|
865
|
+
<p class="text-neutral-400 text-sm font-light leading-relaxed mb-6">
|
|
866
|
+
${item.description}
|
|
867
|
+
</p>
|
|
868
|
+
</div>
|
|
869
|
+
|
|
870
|
+
<div>
|
|
871
|
+
${item.stack ? `
|
|
872
|
+
<div class="flex flex-wrap gap-1.5 mb-6">
|
|
873
|
+
${item.stack.map(t => `<span class="text-[11px] font-mono text-neutral-300 bg-neutral-900 px-2 py-0.5 rounded border border-white/10">${t}</span>`).join('')}
|
|
874
|
+
</div>
|
|
875
|
+
` : ''}
|
|
876
|
+
<div class="flex items-center gap-4 pt-4 border-t border-white/5 text-xs font-mono">
|
|
877
|
+
<a href="#contact" class="text-white hover:underline flex items-center gap-1">
|
|
878
|
+
Inquire Artifact ↗
|
|
879
|
+
</a>
|
|
880
|
+
</div>
|
|
881
|
+
</div>
|
|
882
|
+
</div>
|
|
883
|
+
`).join('')}
|
|
884
|
+
</div>
|
|
885
|
+
</section>
|
|
886
|
+
|
|
887
|
+
<!-- Interactive Terminal Section -->
|
|
888
|
+
<section id="interactive" class="py-20 px-6 max-w-7xl mx-auto border-t border-white/10">
|
|
889
|
+
<div class="mb-8">
|
|
890
|
+
<span class="font-mono text-xs text-neutral-400 uppercase tracking-widest block mb-2">02 // Interactive Console</span>
|
|
891
|
+
<h2 class="font-display text-3xl text-white font-normal">Runtime Telemetry Shell</h2>
|
|
892
|
+
</div>
|
|
893
|
+
|
|
894
|
+
<div class="terminal-window rounded-sm overflow-hidden font-mono text-xs">
|
|
895
|
+
<div class="bg-[#12141a] px-4 py-2.5 border-b border-white/10 flex items-center justify-between">
|
|
896
|
+
<div class="flex items-center gap-2">
|
|
897
|
+
<span class="w-2.5 h-2.5 rounded-full bg-rose-500"></span>
|
|
898
|
+
<span class="w-2.5 h-2.5 rounded-full bg-amber-500"></span>
|
|
899
|
+
<span class="w-2.5 h-2.5 rounded-full bg-emerald-500"></span>
|
|
900
|
+
<span class="text-[11px] text-neutral-400 ml-2">guest@pixelcrew.system:~</span>
|
|
901
|
+
</div>
|
|
902
|
+
<span class="text-[10px] text-neutral-500">NEXTJS 14</span>
|
|
903
|
+
</div>
|
|
904
|
+
|
|
905
|
+
<div id="termLogs" class="p-6 text-neutral-300 min-h-[200px] max-h-[340px] overflow-y-auto space-y-3">
|
|
906
|
+
<div class="text-neutral-400">
|
|
907
|
+
${spec.companyName} System Runtime [v2.4.0]<br>
|
|
908
|
+
Type <span class="text-white">'status'</span> or <span class="text-white">'help'</span> to inspect capabilities.
|
|
909
|
+
</div>
|
|
910
|
+
</div>
|
|
911
|
+
|
|
912
|
+
<form id="termForm" class="p-4 bg-[#0e1014] border-t border-white/10 flex items-center gap-2">
|
|
913
|
+
<span class="text-emerald-400 font-bold">$</span>
|
|
914
|
+
<input
|
|
915
|
+
id="termInput"
|
|
916
|
+
type="text"
|
|
917
|
+
placeholder="type 'status' or 'help'..."
|
|
918
|
+
class="bg-transparent text-white focus:outline-none flex-1 font-mono text-xs placeholder:text-neutral-600"
|
|
919
|
+
autocomplete="off"
|
|
920
|
+
/>
|
|
921
|
+
<button type="submit" class="px-2.5 py-1 bg-white/10 text-neutral-300 text-[11px] rounded hover:bg-white/20">Enter ↵</button>
|
|
922
|
+
</form>
|
|
923
|
+
</div>
|
|
924
|
+
</section>
|
|
925
|
+
|
|
926
|
+
<!-- Footer -->
|
|
927
|
+
<footer class="py-16 px-6 border-t border-white/10 max-w-7xl mx-auto text-xs font-mono text-neutral-500 flex flex-col sm:flex-row justify-between items-center gap-4">
|
|
928
|
+
<div>© ${new Date().getFullYear()} ${spec.companyName}. Synthesized with Pixel Crew design-first architecture.</div>
|
|
929
|
+
<div class="flex items-center gap-6 text-neutral-400">
|
|
930
|
+
<a href="https://github.com" class="hover:text-white">GitHub</a>
|
|
931
|
+
<a href="https://twitter.com" class="hover:text-white">Twitter</a>
|
|
932
|
+
</div>
|
|
933
|
+
</footer>
|
|
934
|
+
|
|
935
|
+
<script>
|
|
936
|
+
// Live category filter
|
|
937
|
+
const filterBtns = document.querySelectorAll('.filter-btn');
|
|
938
|
+
const cards = document.querySelectorAll('.showcase-card');
|
|
939
|
+
|
|
940
|
+
filterBtns.forEach(btn => {
|
|
941
|
+
btn.addEventListener('click', () => {
|
|
942
|
+
filterBtns.forEach(b => {
|
|
943
|
+
b.classList.remove('active', 'bg-white', 'text-black', 'font-medium');
|
|
944
|
+
b.classList.add('bg-brandSurface', 'text-neutral-400');
|
|
945
|
+
});
|
|
946
|
+
btn.classList.add('active', 'bg-white', 'text-black', 'font-medium');
|
|
947
|
+
btn.classList.remove('bg-brandSurface', 'text-neutral-400');
|
|
948
|
+
|
|
949
|
+
const cat = btn.getAttribute('data-category');
|
|
950
|
+
cards.forEach(card => {
|
|
951
|
+
if (cat === 'All' || card.getAttribute('data-category') === cat) {
|
|
952
|
+
card.style.display = 'flex';
|
|
953
|
+
} else {
|
|
954
|
+
card.style.display = 'none';
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
});
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
// Terminal shell
|
|
961
|
+
const termLogs = document.getElementById('termLogs');
|
|
962
|
+
const termForm = document.getElementById('termForm');
|
|
963
|
+
const termInput = document.getElementById('termInput');
|
|
964
|
+
|
|
965
|
+
termForm.addEventListener('submit', (e) => {
|
|
966
|
+
e.preventDefault();
|
|
967
|
+
const raw = termInput.value.trim();
|
|
968
|
+
if (!raw) return;
|
|
969
|
+
|
|
970
|
+
if (raw.toLowerCase() === 'clear') {
|
|
971
|
+
termLogs.innerHTML = '';
|
|
972
|
+
termInput.value = '';
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
let res = "System status: OPERATIONAL. Next.js 14 App Router, Zero AI Slop Standard.";
|
|
977
|
+
if (raw.toLowerCase() === 'help') res = "Available commands: status, architecture, stack, clear";
|
|
978
|
+
|
|
979
|
+
const item = document.createElement('div');
|
|
980
|
+
item.className = 'space-y-1';
|
|
981
|
+
item.innerHTML = \`
|
|
982
|
+
<div class="flex items-center gap-2 text-emerald-400"><span>$</span><span>\${raw}</span></div>
|
|
983
|
+
<div class="text-neutral-400 whitespace-pre-line pl-4 border-l border-white/10">\${res}</div>
|
|
984
|
+
\`;
|
|
985
|
+
termLogs.appendChild(item);
|
|
986
|
+
termLogs.scrollTop = termLogs.scrollHeight;
|
|
987
|
+
termInput.value = '';
|
|
988
|
+
});
|
|
989
|
+
</script>
|
|
990
|
+
|
|
991
|
+
</body>
|
|
992
|
+
</html>`;
|
|
993
|
+
}
|
|
994
|
+
}
|