Skip to content

Commit 1996524

Browse files
committed
add gpt vision capabilities
1 parent 30d18cd commit 1996524

File tree

11 files changed

+385
-16
lines changed

11 files changed

+385
-16
lines changed

app/api/v1/image/route.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import * as fs from "node:fs/promises";
2+
import { generatedCode } from "@/types/types";
3+
import { ChatOpenAI } from "@langchain/openai";
4+
import { HumanMessage } from "@langchain/core/messages";
5+
import axios from "axios";
6+
import { NextRequest, NextResponse } from "next/server";
7+
8+
const FIGMA_BASE_URL = "https://api.figma.com";
9+
10+
export async function POST(req: NextRequest, res: NextResponse) {
11+
try {
12+
const formData = await req.formData();
13+
const file = formData.get("file") as File;
14+
const buffer = Buffer.from(await file.arrayBuffer());
15+
const chat = new ChatOpenAI({
16+
model: "gpt-4o",
17+
maxTokens: 1024,
18+
});
19+
20+
const message = new HumanMessage({
21+
content: [
22+
{
23+
type: "text",
24+
text: ` You are a developer who needs to convert the image given into corresponding JSX code for a React component.
25+
The Figma JSON object includes information about various UI elements such as buttons, text fields, and containers.
26+
The task involves translating the structure and properties defined in the JSON object into JSX code, using Tailwind CSS for styling.
27+
28+
Determine which elements can be nested and make sure you use em or rem units for more responsiveness.
29+
Use flexbox for the layout and make sure positioning and alignment is correct.
30+
Only give the return value and omit the return keyword.
31+
Only JSX syntax should remain and remove new line characters`
32+
},
33+
{
34+
type: "image_url",
35+
image_url: {
36+
url: `data:image/png;base64,${buffer.toString("base64")}`,
37+
},
38+
},
39+
],
40+
});
41+
42+
const structuredLlm = chat.withStructuredOutput(generatedCode)
43+
const res = await structuredLlm.invoke([message])
44+
45+
return NextResponse.json({ data: res.code }, { status : 200 })
46+
47+
} catch (error) {
48+
console.error(error);
49+
return NextResponse.json({ data: error }, { status: 500 });
50+
}
51+
}

app/image/page.tsx

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"use client";
2+
import React, { useEffect, useState } from "react";
3+
import { Button, Form, Input, message, Upload, UploadProps } from "antd";
4+
import axios from "axios";
5+
import { LiveEditor, LiveError, LivePreview, LiveProvider } from "react-live";
6+
import * as prettier from "prettier";
7+
import { UploadOutlined } from "@ant-design/icons";
8+
import { renderToStaticMarkup } from "react-dom/server";
9+
import parse from 'html-react-parser';
10+
11+
const HomePage: React.FC = () => {
12+
const [designForm] = Form.useForm();
13+
const [codePreview, setCodePreview] = useState(null);
14+
const [isGeneratingUI, setIsGeneratingUI] = useState(false);
15+
const [jsxElement, setJsxElement] = useState(null);
16+
17+
const props: UploadProps = {
18+
name: "file",
19+
action: "/api/v1/image",
20+
onChange(info) {
21+
if (info.file.status !== "uploading") {
22+
console.log(info.file, info.fileList);
23+
}
24+
if (info.file.status === "done") {
25+
message.success(`${info.file.name} file uploaded successfully`);
26+
if (info.file.response?.data) {
27+
setCodePreview(info.file.response?.data);
28+
}
29+
} else if (info.file.status === "error") {
30+
message.error(`${info.file.name} file upload failed.`);
31+
}
32+
},
33+
};
34+
35+
return (
36+
<>
37+
<div className="flex flex-col h-screen w-full px-20 py-10">
38+
<Form
39+
form={designForm}
40+
layout="vertical"
41+
// onFinish={handleSubmit}
42+
className="mb-8"
43+
>
44+
{/* <Form.Item name="projectId" label="Figma Project ID" required>
45+
<Input />
46+
</Form.Item>
47+
<Form.Item name="nodeIds" label="Project Node ID" required>
48+
<Input />
49+
</Form.Item> */}
50+
<Upload {...props}>
51+
<Button icon={<UploadOutlined />}>Click to Upload</Button>
52+
</Upload>
53+
{/* <Button loading={isGeneratingUI} htmlType="submit">
54+
Generate
55+
</Button> */}
56+
</Form>
57+
<LiveProvider code={`${codePreview}`}>
58+
<p className="mb-4">Code Editor:</p>
59+
<LiveEditor />
60+
<p className="mt-4">Errors:</p>
61+
<LiveError />
62+
</LiveProvider>
63+
<p className="font-bold text-xl">Preview</p>
64+
<div className="w-full">
65+
{ codePreview && parse(codePreview) }
66+
</div>
67+
</div>
68+
</>
69+
);
70+
};
71+
72+
export default HomePage;

app/page.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React, { useState } from "react";
33
import { Button, Form, Input, Spin } from "antd";
44
import axios from "axios";
55
import { LiveEditor, LiveError, LivePreview, LiveProvider } from "react-live";
6-
import * as prettier from "prettier";
6+
import parse from 'html-react-parser';
77

88
const HomePage: React.FC = () => {
99
const [designForm] = Form.useForm();
@@ -53,9 +53,12 @@ const HomePage: React.FC = () => {
5353
<LiveEditor />
5454
<p className="mt-4">Errors:</p>
5555
<LiveError />
56-
<p className="mt-4">Preview:</p>
57-
<LivePreview />
56+
5857
</LiveProvider>
58+
<p className="mt-4">Preview:</p>
59+
<div className="w-full">
60+
{ codePreview && parse(codePreview) }
61+
</div>
5962
</div>
6063
</>
6164
);

app/test-route/page.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import React from 'react'
2+
3+
export default function page() {
4+
return (
5+
<div className='flex items-center justify-between bg-gray-200 p-4 gap-4'> <div className='bg-gray-400' style={{ width: '2rem', height: '2rem' }}></div> <div className='flex-1 flex justify-center gap-8'> <span>Item</span> <span>Item</span> <span>Item</span> </div> <button className='bg-blue-500 text-white px-4 py-2 rounded'>Log In</button> </div>
6+
)
7+
}

app/test/page.tsx

Lines changed: 0 additions & 9 deletions
This file was deleted.

assets/nav-img.png

7 KB
Loading

component-references/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { nav_one } from "./ref";
2+
3+
export { nav_one }

component-references/ref.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
export const nav_one = `<div
2+
className="flex flex-col items-center px-5"
3+
>
4+
<div
5+
className="flex justify-between items-center w-full h-40"
6+
style={{ backgroundColor: "rgba(255, 255, 255, 1)" }}
7+
>
8+
<div
9+
className="w-24 h-24"
10+
style={{ backgroundColor: "rgba(217, 217, 217, 1)" }}
11+
></div>
12+
<div className="flex space-x-8">
13+
<span
14+
className="text-black text-2xl"
15+
style={{
16+
fontFamily: "Inter",
17+
fontWeight: 400,
18+
lineHeight: "2.42rem",
19+
}}
20+
>
21+
Item
22+
</span>
23+
<span
24+
className="text-black text-2xl"
25+
style={{
26+
fontFamily: "Inter",
27+
fontWeight: 400,
28+
lineHeight: "2.42rem",
29+
}}
30+
>
31+
Item
32+
</span>
33+
<span
34+
className="text-black text-2xl"
35+
style={{
36+
fontFamily: "Inter",
37+
fontWeight: 400,
38+
lineHeight: "2.42rem",
39+
}}
40+
>
41+
Item
42+
</span>
43+
<span
44+
className="text-black text-2xl"
45+
style={{
46+
fontFamily: "Inter",
47+
fontWeight: 400,
48+
lineHeight: "2.42rem",
49+
}}
50+
>
51+
Item
52+
</span>
53+
</div>
54+
<div
55+
className="flex items-center justify-center w-50 h-21 rounded-full"
56+
style={{ backgroundColor: "rgba(81, 77, 250, 1)" }}
57+
>
58+
<span
59+
className="text-white text-2xl font-bold px-4 py-2"
60+
style={{ fontFamily: "Inter", lineHeight: "2.42rem" }}
61+
>
62+
Button
63+
</span>
64+
</div>
65+
</div>
66+
</div>`

components/GeneratedComponent.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from 'react';
2+
import DOMPurify from 'dompurify';
3+
4+
const GeneratedComponent = ({ jsxString }: { jsxString: string }) => {
5+
const sanitizedHtml = DOMPurify.sanitize(jsxString);
6+
7+
return (
8+
<div
9+
className="your-container-class"
10+
dangerouslySetInnerHTML={{ __html: sanitizedHtml }}
11+
/>
12+
);
13+
};
14+
15+
export default GeneratedComponent;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy