Skip to content

Commit 33cbbe3

Browse files
author
Juan Castaño
committed
Code generation working. Need to figure out e2b
1 parent 8a8aa3f commit 33cbbe3

26 files changed

+2531
-146
lines changed

.env.example

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# OpenAI API Key for component generation
2+
OPENAI_API_KEY=sk-xxx
3+
4+
# Benchify API Key for code repair
5+
BENCHIFY_API_KEY=bnch_xxx
6+
7+
# E2B API Key for component preview
8+
E2B_API_KEY=e2b_xxx

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@ node_modules/
22
.next/
33
dist/
44
build/
5+
6+
# Environment Variables
7+
.env
8+
.env*.local

app/api/generate/route.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// app/api/generate/route.ts
2+
import { NextRequest, NextResponse } from 'next/server';
3+
import { generateApp } from '@/lib/openai';
4+
import { repairCode } from '@/lib/benchify';
5+
import { createSandbox, prepareVueEnvironment, deployApp } from '@/lib/e2b';
6+
import { componentSchema } from '@/lib/schemas';
7+
import { benchifyFileSchema } from '@/lib/schemas';
8+
9+
export async function POST(request: NextRequest) {
10+
try {
11+
const body = await request.json();
12+
13+
// Validate the request using Zod schema
14+
const validationResult = componentSchema.safeParse(body);
15+
16+
if (!validationResult.success) {
17+
return NextResponse.json(
18+
{ error: 'Invalid request format', details: validationResult.error.format() },
19+
{ status: 400 }
20+
);
21+
}
22+
23+
const { description } = validationResult.data;
24+
25+
// Generate the Vue app using OpenAI
26+
const generatedFiles = await generateApp(description);
27+
28+
// // Parse through schema before passing to repair
29+
// const validatedFiles = benchifyFileSchema.parse(generatedFiles);
30+
31+
// // Repair the generated code using Benchify's API
32+
// const repairedFiles = await repairCode(validatedFiles);
33+
34+
// // Set up E2B sandbox for preview
35+
let previewUrl = undefined;
36+
try {
37+
const sandbox = await createSandbox();
38+
await prepareVueEnvironment(sandbox);
39+
const { previewUrl: url } = await deployApp(sandbox, generatedFiles);
40+
previewUrl = url;
41+
} catch (error) {
42+
console.error('Error setting up preview:', error);
43+
}
44+
45+
console.log("Preview URL: ", previewUrl);
46+
47+
// Return the results to the client
48+
return NextResponse.json({
49+
originalFiles: generatedFiles,
50+
// repairedFiles: repairedFiles,
51+
buildOutput: '', // We don't get build output from Benchify in our current setup
52+
previewUrl,
53+
});
54+
} catch (error) {
55+
console.error('Error generating app:', error);
56+
return NextResponse.json(
57+
{
58+
error: 'Failed to generate app',
59+
message: error instanceof Error ? error.message : String(error)
60+
},
61+
{ status: 500 }
62+
);
63+
}
64+
}

app/globals.css

Lines changed: 109 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,122 @@
11
@import "tailwindcss";
2+
@import "tw-animate-css";
23

3-
:root {
4-
--background: #ffffff;
5-
--foreground: #171717;
6-
}
4+
@custom-variant dark (&:is(.dark *));
75

86
@theme inline {
97
--color-background: var(--background);
108
--color-foreground: var(--foreground);
119
--font-sans: var(--font-geist-sans);
1210
--font-mono: var(--font-geist-mono);
11+
--color-sidebar-ring: var(--sidebar-ring);
12+
--color-sidebar-border: var(--sidebar-border);
13+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
14+
--color-sidebar-accent: var(--sidebar-accent);
15+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
16+
--color-sidebar-primary: var(--sidebar-primary);
17+
--color-sidebar-foreground: var(--sidebar-foreground);
18+
--color-sidebar: var(--sidebar);
19+
--color-chart-5: var(--chart-5);
20+
--color-chart-4: var(--chart-4);
21+
--color-chart-3: var(--chart-3);
22+
--color-chart-2: var(--chart-2);
23+
--color-chart-1: var(--chart-1);
24+
--color-ring: var(--ring);
25+
--color-input: var(--input);
26+
--color-border: var(--border);
27+
--color-destructive: var(--destructive);
28+
--color-accent-foreground: var(--accent-foreground);
29+
--color-accent: var(--accent);
30+
--color-muted-foreground: var(--muted-foreground);
31+
--color-muted: var(--muted);
32+
--color-secondary-foreground: var(--secondary-foreground);
33+
--color-secondary: var(--secondary);
34+
--color-primary-foreground: var(--primary-foreground);
35+
--color-primary: var(--primary);
36+
--color-popover-foreground: var(--popover-foreground);
37+
--color-popover: var(--popover);
38+
--color-card-foreground: var(--card-foreground);
39+
--color-card: var(--card);
40+
--radius-sm: calc(var(--radius) - 4px);
41+
--radius-md: calc(var(--radius) - 2px);
42+
--radius-lg: var(--radius);
43+
--radius-xl: calc(var(--radius) + 4px);
1344
}
1445

15-
@media (prefers-color-scheme: dark) {
16-
:root {
17-
--background: #0a0a0a;
18-
--foreground: #ededed;
19-
}
46+
:root {
47+
--radius: 0.625rem;
48+
--background: oklch(1 0 0);
49+
--foreground: oklch(0.141 0.005 285.823);
50+
--card: oklch(1 0 0);
51+
--card-foreground: oklch(0.141 0.005 285.823);
52+
--popover: oklch(1 0 0);
53+
--popover-foreground: oklch(0.141 0.005 285.823);
54+
--primary: oklch(0.21 0.006 285.885);
55+
--primary-foreground: oklch(0.985 0 0);
56+
--secondary: oklch(0.967 0.001 286.375);
57+
--secondary-foreground: oklch(0.21 0.006 285.885);
58+
--muted: oklch(0.967 0.001 286.375);
59+
--muted-foreground: oklch(0.552 0.016 285.938);
60+
--accent: oklch(0.967 0.001 286.375);
61+
--accent-foreground: oklch(0.21 0.006 285.885);
62+
--destructive: oklch(0.577 0.245 27.325);
63+
--border: oklch(0.92 0.004 286.32);
64+
--input: oklch(0.92 0.004 286.32);
65+
--ring: oklch(0.705 0.015 286.067);
66+
--chart-1: oklch(0.646 0.222 41.116);
67+
--chart-2: oklch(0.6 0.118 184.704);
68+
--chart-3: oklch(0.398 0.07 227.392);
69+
--chart-4: oklch(0.828 0.189 84.429);
70+
--chart-5: oklch(0.769 0.188 70.08);
71+
--sidebar: oklch(0.985 0 0);
72+
--sidebar-foreground: oklch(0.141 0.005 285.823);
73+
--sidebar-primary: oklch(0.21 0.006 285.885);
74+
--sidebar-primary-foreground: oklch(0.985 0 0);
75+
--sidebar-accent: oklch(0.967 0.001 286.375);
76+
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
77+
--sidebar-border: oklch(0.92 0.004 286.32);
78+
--sidebar-ring: oklch(0.705 0.015 286.067);
2079
}
2180

22-
body {
23-
background: var(--background);
24-
color: var(--foreground);
25-
font-family: Arial, Helvetica, sans-serif;
81+
.dark {
82+
--background: oklch(0.141 0.005 285.823);
83+
--foreground: oklch(0.985 0 0);
84+
--card: oklch(0.21 0.006 285.885);
85+
--card-foreground: oklch(0.985 0 0);
86+
--popover: oklch(0.21 0.006 285.885);
87+
--popover-foreground: oklch(0.985 0 0);
88+
--primary: oklch(0.92 0.004 286.32);
89+
--primary-foreground: oklch(0.21 0.006 285.885);
90+
--secondary: oklch(0.274 0.006 286.033);
91+
--secondary-foreground: oklch(0.985 0 0);
92+
--muted: oklch(0.274 0.006 286.033);
93+
--muted-foreground: oklch(0.705 0.015 286.067);
94+
--accent: oklch(0.274 0.006 286.033);
95+
--accent-foreground: oklch(0.985 0 0);
96+
--destructive: oklch(0.704 0.191 22.216);
97+
--border: oklch(1 0 0 / 10%);
98+
--input: oklch(1 0 0 / 15%);
99+
--ring: oklch(0.552 0.016 285.938);
100+
--chart-1: oklch(0.488 0.243 264.376);
101+
--chart-2: oklch(0.696 0.17 162.48);
102+
--chart-3: oklch(0.769 0.188 70.08);
103+
--chart-4: oklch(0.627 0.265 303.9);
104+
--chart-5: oklch(0.645 0.246 16.439);
105+
--sidebar: oklch(0.21 0.006 285.885);
106+
--sidebar-foreground: oklch(0.985 0 0);
107+
--sidebar-primary: oklch(0.488 0.243 264.376);
108+
--sidebar-primary-foreground: oklch(0.985 0 0);
109+
--sidebar-accent: oklch(0.274 0.006 286.033);
110+
--sidebar-accent-foreground: oklch(0.985 0 0);
111+
--sidebar-border: oklch(1 0 0 / 10%);
112+
--sidebar-ring: oklch(0.552 0.016 285.938);
113+
}
114+
115+
@layer base {
116+
* {
117+
@apply border-border outline-ring/50;
118+
}
119+
body {
120+
@apply bg-background text-foreground;
121+
}
26122
}

app/page.tsx

Lines changed: 24 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,28 @@
1-
import Image from "next/image";
1+
// app/page.tsx
2+
'use client';
3+
4+
import { useState } from 'react';
5+
import { PromptForm } from '@/components/ui-builder/prompt-form';
6+
import { Card, CardContent } from '@/components/ui/card';
27

38
export default function Home() {
4-
return (
5-
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
6-
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
7-
<Image
8-
className="dark:invert"
9-
src="/next.svg"
10-
alt="Next.js logo"
11-
width={180}
12-
height={38}
13-
priority
14-
/>
15-
<ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
16-
<li className="mb-2 tracking-[-.01em]">
17-
Get started by editing{" "}
18-
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
19-
app/page.tsx
20-
</code>
21-
.
22-
</li>
23-
<li className="tracking-[-.01em]">
24-
Save and see your changes instantly.
25-
</li>
26-
</ol>
9+
const [result, setResult] = useState<any>(null);
2710

28-
<div className="flex gap-4 items-center flex-col sm:flex-row">
29-
<a
30-
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
31-
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
32-
target="_blank"
33-
rel="noopener noreferrer"
34-
>
35-
<Image
36-
className="dark:invert"
37-
src="/vercel.svg"
38-
alt="Vercel logomark"
39-
width={20}
40-
height={20}
41-
/>
42-
Deploy now
43-
</a>
44-
<a
45-
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
46-
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
47-
target="_blank"
48-
rel="noopener noreferrer"
49-
>
50-
Read our docs
51-
</a>
52-
</div>
53-
</main>
54-
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
55-
<a
56-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
57-
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
58-
target="_blank"
59-
rel="noopener noreferrer"
60-
>
61-
<Image
62-
aria-hidden
63-
src="/file.svg"
64-
alt="File icon"
65-
width={16}
66-
height={16}
67-
/>
68-
Learn
69-
</a>
70-
<a
71-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
72-
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
73-
target="_blank"
74-
rel="noopener noreferrer"
75-
>
76-
<Image
77-
aria-hidden
78-
src="/window.svg"
79-
alt="Window icon"
80-
width={16}
81-
height={16}
82-
/>
83-
Examples
84-
</a>
85-
<a
86-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
87-
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
88-
target="_blank"
89-
rel="noopener noreferrer"
90-
>
91-
<Image
92-
aria-hidden
93-
src="/globe.svg"
94-
alt="Globe icon"
95-
width={16}
96-
height={16}
97-
/>
98-
Go to nextjs.org →
99-
</a>
100-
</footer>
101-
</div>
11+
return (
12+
<main className="min-h-screen flex flex-col items-center justify-center bg-background">
13+
<div className="w-full max-w-3xl mx-auto">
14+
<h1 className="text-4xl md:text-6xl font-bold mb-4 text-center">
15+
UI App Builder
16+
</h1>
17+
<p className="text-lg text-muted-foreground mb-8 text-center">
18+
Generate UI components with AI and automatically repair issues with Benchify
19+
</p>
20+
<Card className="border-border bg-card">
21+
<CardContent className="pt-6">
22+
<PromptForm onGenerate={setResult} />
23+
</CardContent>
24+
</Card>
25+
</div>
26+
</main>
10227
);
103-
}
28+
}

components.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"$schema": "https://ui.shadcn.com/schema.json",
3+
"style": "new-york",
4+
"rsc": true,
5+
"tsx": true,
6+
"tailwind": {
7+
"config": "",
8+
"css": "app/globals.css",
9+
"baseColor": "zinc",
10+
"cssVariables": true,
11+
"prefix": ""
12+
},
13+
"aliases": {
14+
"components": "@/components",
15+
"utils": "@/lib/utils",
16+
"ui": "@/components/ui",
17+
"lib": "@/lib",
18+
"hooks": "@/hooks"
19+
},
20+
"iconLibrary": "lucide"
21+
}

0 commit comments

Comments
 (0)