From 7d2097648de97ebf1d59deb59409664374b2431d Mon Sep 17 00:00:00 2001 From: Himansh raj Date: Thu, 8 Aug 2024 07:49:35 +0530 Subject: [PATCH 1/6] Added support for multiple files and routing --- app/api/generateCode/route.ts | 8 +- app/page.tsx | 99 +++++++++----- package-lock.json | 240 +++++++++++++++++----------------- 3 files changed, 192 insertions(+), 155 deletions(-) diff --git a/app/api/generateCode/route.ts b/app/api/generateCode/route.ts index 0cadb7cf0..94fd9235c 100644 --- a/app/api/generateCode/route.ts +++ b/app/api/generateCode/route.ts @@ -8,13 +8,17 @@ export const maxDuration = 60; const systemPrompt = ` You are an expert frontend React engineer who is also a great UI/UX designer. Follow the instructions carefully, I will tip you $1 million if you do a good job: -- Create a React component for whatever the user asked you to create and make sure it can run by itself by using a default export +- Create React components for whatever the user asked you to create and make sure it can run by itself by using a default export - Make sure the React app is interactive and functional by creating state when needed and having no required props - Use TypeScript as the language for the React component - Use Tailwind classes for styling. DO NOT USE ARBITRARY VALUES (e.g. \`h-[600px]\`). Make sure to use a consistent color palette. - ONLY IF the user asks for a dashboard, graph or chart, the recharts library is available to be imported, e.g. \`import { LineChart, XAxis, ... } from "recharts"\` & \` ...\`. Please only use this when needed. -- NO OTHER LIBRARIES (e.g. zod, hookform) ARE INSTALLED OR ABLE TO BE IMPORTED. +- NO OTHER LIBRARIES (e.g. zod, hookform) ARE INSTALLED OR ABLE TO BE IMPORTED EXCEPT "react-router-dom". - Please ONLY return the full React code starting with the imports, nothing else. It's very important for my job that you only return the React code with imports. DO NOT START WITH \`\`\`typescript or \`\`\`javascript or \`\`\`tsx or \`\`\`. +- You can also create many files if needed, but make sure to export the main component from the main file. +- Make sure to write the name of File and Component in the comment at the top of the file, e.g. \`//Button.tsx\` +- Separate the files by using the following structure: -%-%- (e.g. \`//Button.tsx MultilineContents... -%-%- //ButtonGroup.tsx MultilineContents...\`) +- Always make sure to Start app from 'App.tsx' (then main file which will be mounted in 'index.tsx') `; export async function POST(req: Request) { diff --git a/app/page.tsx b/app/page.tsx index 267871f51..eadac474c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -22,19 +22,46 @@ import { AnimatePresence, motion } from "framer-motion"; import { FormEvent, useEffect, useState } from "react"; import LoadingDots from "../components/loading-dots"; +const CODES = { + "App.tsx": "", + "/public/index.html": ` + + + + + Document + + + +
+ + `, +}; + export default function Home() { let [status, setStatus] = useState< "initial" | "creating" | "created" | "updating" | "updated" >("initial"); let [generatedCode, setGeneratedCode] = useState(""); + let [CodeFiles, setCodeFiles] = useState(CODES); let [modelUsedForInitialCode, setModelUsedForInitialCode] = useState(""); let [ref, scrollTo] = useScrollTo(); + let [activeFile, setActiveFile] = useState("App.tsx"); let [messages, setMessages] = useState<{ role: string; content: string }[]>( - [], + [] ); let loading = status === "creating" || status === "updating"; + function SetCodesFiless(data: any) { + try { + const text = JSON.parse(data).text ?? ""; + setGeneratedCode((prev) => prev + text); + } catch (e) { + console.error(e); + } + } + async function generateCode(e: FormEvent) { e.preventDefault(); @@ -67,7 +94,6 @@ export default function Home() { throw new Error(chatRes.statusText); } - // This data is a ReadableStream const data = chatRes.body; if (!data) { return; @@ -75,16 +101,10 @@ export default function Home() { const onParse = (event: ParsedEvent | ReconnectInterval) => { if (event.type === "event") { const data = event.data; - try { - const text = JSON.parse(data).text ?? ""; - setGeneratedCode((prev) => prev + text); - } catch (e) { - console.error(e); - } + SetCodesFiless(data); } }; - // https://web.dev/streams/#the-getreader-and-read-methods const reader = data.getReader(); const decoder = new TextDecoder(); const parser = createParser(onParse); @@ -134,7 +154,6 @@ export default function Home() { throw new Error(chatRes.statusText); } - // This data is a ReadableStream const data = chatRes.body; if (!data) { return; @@ -142,16 +161,10 @@ export default function Home() { const onParse = (event: ParsedEvent | ReconnectInterval) => { if (event.type === "event") { const data = event.data; - try { - const text = JSON.parse(data).text ?? ""; - setGeneratedCode((prev) => prev + text); - } catch (e) { - console.error(e); - } + SetCodesFiless(data); } }; - // https://web.dev/streams/#the-getreader-and-read-methods const reader = data.getReader(); const decoder = new TextDecoder(); const parser = createParser(onParse); @@ -179,8 +192,36 @@ export default function Home() { let end = el.scrollHeight - el.clientHeight; el.scrollTo({ top: end }); } + let activeFile_="App.tsx"; + try { + const codeFiles = generatedCode.split("-%-%-"); + const newCodeFiles: { [key: string]: string } = {}; + for (let i = 0; i < codeFiles.length; i++) { + const codeFile = codeFiles[i]; + const regex = /\/\/(.*).tsx/g; + const match = regex.exec(codeFile); + if (match) { + const fileName = match[1]; + const fileContent = codeFile.replace(match[0], ""); + + const fullName=fileName.trim() + ".tsx"; + newCodeFiles[fullName] = fileContent; + activeFile_=fullName; + } + } + setCodeFiles((prevCodeFiles) => ({ + ...CODES, + ...newCodeFiles, + })); + setActiveFile(activeFile_); + } catch (e) { + console.error(e); + } }, [loading, generatedCode]); + useEffect(() => { + console.log("CodeFiles",CodeFiles); + },[CodeFiles]); return (
@@ -359,6 +400,7 @@ export default function Home() {
+ - - - - - Document - - - -
- - `, + showTabs: true, + // @ts-ignore + activeFile:activeFile, + + }} + files={CodeFiles} template="react-ts" customSetup={{ dependencies: { "lucide-react": "latest", + "react-router-dom": "latest", recharts: "2.9.0", }, }} diff --git a/package-lock.json b/package-lock.json index 392c43be6..b846875ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -572,6 +572,126 @@ "node": ">= 10" } }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.5.tgz", + "integrity": "sha512-vXHOPCwfDe9qLDuq7U1OYM2wUY+KQ4Ex6ozwsKxp26BlJ6XXbHleOUldenM67JRyBfVjv371oneEvYd3H2gNSA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.5.tgz", + "integrity": "sha512-vlhB8wI+lj8q1ExFW8lbWutA4M2ZazQNvMWuEDqZcuJJc78iUnLdPPunBPX8rC4IgT6lIx/adB+Cwrl99MzNaA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.5.tgz", + "integrity": "sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.5.tgz", + "integrity": "sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.5.tgz", + "integrity": "sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.5.tgz", + "integrity": "sha512-1GpG2VhbspO+aYoMOQPQiqc/tG3LzmsdBH0LhnDS3JrtDx2QmzXe0B6mSZZiN3Bq7IOMXxv1nlsjzoS1+9mzZw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.5.tgz", + "integrity": "sha512-Igh9ZlxwvCDsu6438FXlQTHlRno4gFpJzqPjSIBZooD22tKeI4fE/YMRoHVJHmrQ2P5YL1DoZ0qaOKkbeFWeMg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.5.tgz", + "integrity": "sha512-tEQ7oinq1/CjSG9uSTerca3v4AZ+dFa+4Yu6ihaG8Ud8ddqLQgFGcnwYls13H5X5CPDPZJdYxyeMui6muOLd4g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -6270,126 +6390,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.5.tgz", - "integrity": "sha512-vXHOPCwfDe9qLDuq7U1OYM2wUY+KQ4Ex6ozwsKxp26BlJ6XXbHleOUldenM67JRyBfVjv371oneEvYd3H2gNSA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.5.tgz", - "integrity": "sha512-vlhB8wI+lj8q1ExFW8lbWutA4M2ZazQNvMWuEDqZcuJJc78iUnLdPPunBPX8rC4IgT6lIx/adB+Cwrl99MzNaA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.5.tgz", - "integrity": "sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.5.tgz", - "integrity": "sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.5.tgz", - "integrity": "sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.5.tgz", - "integrity": "sha512-1GpG2VhbspO+aYoMOQPQiqc/tG3LzmsdBH0LhnDS3JrtDx2QmzXe0B6mSZZiN3Bq7IOMXxv1nlsjzoS1+9mzZw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.5.tgz", - "integrity": "sha512-Igh9ZlxwvCDsu6438FXlQTHlRno4gFpJzqPjSIBZooD22tKeI4fE/YMRoHVJHmrQ2P5YL1DoZ0qaOKkbeFWeMg==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.5.tgz", - "integrity": "sha512-tEQ7oinq1/CjSG9uSTerca3v4AZ+dFa+4Yu6ihaG8Ud8ddqLQgFGcnwYls13H5X5CPDPZJdYxyeMui6muOLd4g==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } } } } From 651947ec253eda2bb7ef1539bfc818f3ac04ddb6 Mon Sep 17 00:00:00 2001 From: Himansh raj Date: Thu, 8 Aug 2024 09:15:32 +0530 Subject: [PATCH 2/6] feat: Add feture to export --- README.md | 3 +- app/api/generateCode/route.ts | 3 + app/page.tsx | 53 ++++++++++++--- package-lock.json | 122 ++++++++++++++++++++++++++++++++-- package.json | 6 +- utils/ExportToZip.ts | 21 ++++++ utils/IndexFile.ts | 16 +++++ utils/ReactAllFiles.ts | 103 ++++++++++++++++++++++++++++ utils/SrcCode.ts | 17 +++++ utils/packageJson.ts | 48 +++++++++++++ 10 files changed, 376 insertions(+), 16 deletions(-) create mode 100644 utils/ExportToZip.ts create mode 100644 utils/IndexFile.ts create mode 100644 utils/ReactAllFiles.ts create mode 100644 utils/SrcCode.ts create mode 100644 utils/packageJson.ts diff --git a/README.md b/README.md index d33d96d5b..d7ef3da3f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,8 @@ ## Future Tasks -- [ ] Look into a way to export/deploy the app in a single click. Can try to do it myself with a dynamic route + some hashing, or try to use Replit/Vercel +- [✅] Look into a way to export the app. +- [ ] Look into a way to deploy the app in a single click. Can try to do it myself with a dynamic route + some hashing, or try to use Replit/Vercel - [ ] Save previous versions so people can go back and forth between the generated ones - [ ] Could be nice to show a "featured apps" route on the site on `/featured`. Have a `/id/${prompt}` dynamic route that can display a bunch of nice example apps in the sandbox ready to go - [ ] Support more languages starting with Python, check out E2B diff --git a/app/api/generateCode/route.ts b/app/api/generateCode/route.ts index 94fd9235c..9557c45c8 100644 --- a/app/api/generateCode/route.ts +++ b/app/api/generateCode/route.ts @@ -19,6 +19,9 @@ You are an expert frontend React engineer who is also a great UI/UX designer. Fo - Make sure to write the name of File and Component in the comment at the top of the file, e.g. \`//Button.tsx\` - Separate the files by using the following structure: -%-%- (e.g. \`//Button.tsx MultilineContents... -%-%- //ButtonGroup.tsx MultilineContents...\`) - Always make sure to Start app from 'App.tsx' (then main file which will be mounted in 'index.tsx') +- Add .tsx extension to all files when importing in another file +- You can generate as many files as you need, but make sure to export the component from the file. +- You can genrate any text-based files. For example, if you need to generate a CSS file, you can generate a CSS file with the content you need. `; export async function POST(req: Request) { diff --git a/app/page.tsx b/app/page.tsx index eadac474c..250101808 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -20,8 +20,10 @@ import { } from "eventsource-parser"; import { AnimatePresence, motion } from "framer-motion"; import { FormEvent, useEffect, useState } from "react"; -import LoadingDots from "../components/loading-dots"; - +import LoadingDots from "@/components/loading-dots"; +import ExportToZip from "@/utils/ExportToZip" +import ReactAllFiles from "@/utils/ReactAllFiles"; +import sendToSrc from "@/utils/SrcCode"; const CODES = { "App.tsx": "", "/public/index.html": ` @@ -50,7 +52,13 @@ export default function Home() { let [messages, setMessages] = useState<{ role: string; content: string }[]>( [] ); - + const [downloadloading, setDownloadLoading] = useState(false); + // TODO: Dynamically add dependencies based on the model + const dependencies = { + "lucide-react": "latest", + "react-router-dom": "latest", + recharts: "2.9.0", + } let loading = status === "creating" || status === "updating"; function SetCodesFiless(data: any) { @@ -126,7 +134,30 @@ export default function Home() { setMessages(newMessages); setStatus("created"); } + async function handleExport() { + setDownloadLoading(true); + let name="App"; + for (let i = 0; i < messages.length; i++) { + if (messages[i].role === "user") { + name = messages[i].content; + } + } + const updetedName=name.replaceAll(" ","-").replaceAll(".","-").replaceAll("/","-").replaceAll("\\","-").replaceAll(":","-").replaceAll("*","-").replaceAll("?","-").replaceAll("\"","-").replaceAll("<","-").replaceAll(">","-").replaceAll("|","-") + const files={ + ...sendToSrc(CodeFiles), + ...ReactAllFiles(updetedName.toLowerCase(), name, dependencies), + } + console.log(files); + try{ + await ExportToZip(files,updetedName); + + }catch{ + await ExportToZip(files,"App"); + } + setDownloadLoading(false); + +} async function modifyCode(e: FormEvent) { e.preventDefault(); @@ -203,7 +234,9 @@ export default function Home() { if (match) { const fileName = match[1]; const fileContent = codeFile.replace(match[0], ""); - + if( fileName.trim() === "index") { + continue; + } const fullName=fileName.trim() + ".tsx"; newCodeFiles[fullName] = fileContent; activeFile_=fullName; @@ -418,13 +451,15 @@ export default function Home() { files={CodeFiles} template="react-ts" customSetup={{ - dependencies: { - "lucide-react": "latest", - "react-router-dom": "latest", - recharts: "2.9.0", - }, + dependencies: dependencies, }} /> + {!loading&&}
diff --git a/package-lock.json b/package-lock.json index b846875ed..1e724538f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,8 +13,12 @@ "@heroicons/react": "^2.1.5", "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-tooltip": "^1.1.2", + "@types/file-saver": "^2.0.7", + "@types/jszip": "^3.4.1", "eventsource-parser": "^1.1.2", + "file-saver": "^2.0.5", "framer-motion": "^11.3.19", + "jszip": "^3.10.1", "next": "14.2.5", "next-plausible": "^3.12.0", "react": "^18", @@ -30,7 +34,7 @@ "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.5", "tailwindcss": "^3.4.1", - "typescript": "^5" + "typescript": "^5.5.4" } }, "node_modules/@alloc/quick-lru": { @@ -1295,12 +1299,28 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/file-saver": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.7.tgz", + "integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==", + "license": "MIT" + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/jszip": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@types/jszip/-/jszip-3.4.1.tgz", + "integrity": "sha512-TezXjmf3lj+zQ651r6hPqvSScqBLvyPI9FxdXBqpEwBijNGQ2NXpaFW/7joGzveYkKQUil7iiDHLo6LV71Pc0A==", + "deprecated": "This is a stub types definition. jszip provides its own type definitions, so you do not need this installed.", + "license": "MIT", + "dependencies": { + "jszip": "*" + } + }, "node_modules/@types/node": { "version": "20.14.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.13.tgz", @@ -2032,6 +2052,12 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/crelt": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", @@ -3171,6 +3197,12 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==", + "license": "MIT" + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -3595,6 +3627,12 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -3634,8 +3672,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/internal-slot": { "version": "1.0.7", @@ -4161,6 +4198,18 @@ "node": ">=4.0" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4201,6 +4250,15 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -4671,6 +4729,12 @@ "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", "dev": true }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5038,6 +5102,12 @@ } } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5196,6 +5266,27 @@ "pify": "^2.3.0" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -5392,6 +5483,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/safe-regex-test": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", @@ -5461,6 +5558,12 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5565,6 +5668,15 @@ "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.4.6.tgz", "integrity": "sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==" }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -6077,6 +6189,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6159,8 +6272,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/w3c-keyname": { "version": "2.2.8", diff --git a/package.json b/package.json index a7d7364c4..222942019 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,12 @@ "@heroicons/react": "^2.1.5", "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-tooltip": "^1.1.2", + "@types/file-saver": "^2.0.7", + "@types/jszip": "^3.4.1", "eventsource-parser": "^1.1.2", + "file-saver": "^2.0.5", "framer-motion": "^11.3.19", + "jszip": "^3.10.1", "next": "14.2.5", "next-plausible": "^3.12.0", "react": "^18", @@ -31,6 +35,6 @@ "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.5", "tailwindcss": "^3.4.1", - "typescript": "^5" + "typescript": "^5.5.4" } } diff --git a/utils/ExportToZip.ts b/utils/ExportToZip.ts new file mode 100644 index 000000000..b523049f7 --- /dev/null +++ b/utils/ExportToZip.ts @@ -0,0 +1,21 @@ +import JSZip from 'jszip'; +import { saveAs } from 'file-saver'; + +interface FilesObject { + [fileName: string]: string; +} + +const ExportCode = async (files: FilesObject,Filename:String="files"): Promise => { + const zip = new JSZip(); + + // Iterate over the object and add files to the zip + Object.keys(files).forEach(fileName => { + zip.file(fileName, files[fileName]); + }); + + // Generate the zip file and trigger download + const content = await zip.generateAsync({ type: 'blob' }); + saveAs(content, Filename.trim()+'.zip'); +}; + +export default ExportCode; \ No newline at end of file diff --git a/utils/IndexFile.ts b/utils/IndexFile.ts new file mode 100644 index 000000000..b67593bbc --- /dev/null +++ b/utils/IndexFile.ts @@ -0,0 +1,16 @@ + + +const ReactAppIndexFile = ()=>{ + return `import React from 'react'; + import ReactDOM from 'react-dom'; + import App from './App.tsx'; + + ReactDOM.render( + + + , + document.getElementById('root') + ); + `; +} +export default ReactAppIndexFile; \ No newline at end of file diff --git a/utils/ReactAllFiles.ts b/utils/ReactAllFiles.ts new file mode 100644 index 000000000..2de713089 --- /dev/null +++ b/utils/ReactAllFiles.ts @@ -0,0 +1,103 @@ +import packageJson from "./packageJson"; +import IndexFile from "./IndexFile"; + + +const ReactAllFiles = (name: string, desc: string, dependencies: any) => { + return { + "package.json": packageJson(name, desc, dependencies), + "src/index.tsx": IndexFile(), + "README.md": ` + # ${name} + + This is a simple React application. + + ## Table of Contents + + - [Prerequisites](#prerequisites) + - [Installation](#installation) + - [Running the App](#running-the-app) + - [Building the App](#building-the-app) + - [Deployment](#deployment) + + ## Prerequisites + + Before you begin, ensure you have met the following requirements: + + - You have installed [Node.js](https://nodejs.org/en/) and [npm](https://www.npmjs.com/) (Node Package Manager). + - You have a terminal or command prompt to run commands. + + ## Installation + + To install the dependencies and set up the project, follow these steps: + + 1. Navigate to the project directory (if you haven't done so already). + + 2. Install the dependencies: + + \`\`\`bash + npm install + \`\`\` + + ## Running the App + + To run the app locally, follow these steps: + + 1. Start the development server: + + \`\`\`bash + npm start + \`\`\` + + 2. Open your browser and navigate to: + + \`\`\` + http://localhost:3000 + \`\`\` + + ## Building the App + + To create a production build of the app, follow these steps: + + 1. Build the app: + + \`\`\`bash + npm run build + \`\`\` + + 2. The production-ready files will be in the \`build\` directory. + + ## Deployment + + To deploy the app, you can use any static site hosting service such as [Netlify](https://www.netlify.com/), [Vercel](https://vercel.com/), or [GitHub Pages](https://pages.github.com/). + + ### Deploying to Replit + + To deploy this React app to Replit, follow these steps: + + 1. Create a new Repl on [Replit](https://replit.com/). + + 2. Choose the Node.js template. + + 3. Link your GitHub repository or upload the project files. + + 4. Install the dependencies: + + \`\`\`bash + npm install + \`\`\` + + 5. Start the server: + + \`\`\`bash + npm start + \`\`\` + + 6. Your app should now be running on Replit. You can share the Replit URL with others. + + + `, + }; +}; + + +export default ReactAllFiles; \ No newline at end of file diff --git a/utils/SrcCode.ts b/utils/SrcCode.ts new file mode 100644 index 000000000..80ec54a54 --- /dev/null +++ b/utils/SrcCode.ts @@ -0,0 +1,17 @@ + + + +const sendToSrc=(CodeFiles: {[key: string]: String})=>{ + let newCodeFiles: {[key: string]: String} = {}; + for(let key in CodeFiles){ + if(key.includes("src/")) + newCodeFiles[key]=CodeFiles[key]; + else if (key.includes("public/")) + newCodeFiles[key]=CodeFiles[key]; + else + newCodeFiles["src/"+key]=CodeFiles[key]; + } + return newCodeFiles; +} + +export default sendToSrc; \ No newline at end of file diff --git a/utils/packageJson.ts b/utils/packageJson.ts new file mode 100644 index 000000000..de13b5d1a --- /dev/null +++ b/utils/packageJson.ts @@ -0,0 +1,48 @@ + + +const packageJson =(name:String="my-react-app",desc:String="React App",dependencies={})=>{ + return JSON.stringify({ + "name": name, + "version": "1.0.0", + "description": desc, + "main": "index.tsx", + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "keywords": [ + "react", + "typescript", + "application" + ], + "author": "LlamaCoder", + "license": "MIT", + "dependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-scripts": "5.0.0", + "typescript": "^4.0.0", + ...dependencies + }, + "devDependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } + }); +} + +export default packageJson; \ No newline at end of file From bd5bcb33c9b8f823c3334210afd30636713fa30d Mon Sep 17 00:00:00 2001 From: Himansh raj Date: Fri, 9 Aug 2024 11:52:22 +0530 Subject: [PATCH 3/6] feat: Update repository URL in codebase --- README.md | 3 +-- components/Footer.tsx | 2 +- components/Header.tsx | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d7ef3da3f..f8276d4c3 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,12 @@ ## Cloning & running -1. Clone the repo: `git clone https://github.com/Nutlope/llamacoder` +1. Clone the repo: `git clone https://github.com/iamthehimansh/llamacoder` 2. Create a `.env` file and add your [Together AI API key](https://dub.sh/together-ai): `TOGETHER_API_KEY=` 3. Run `npm install` and `npm run dev` to install dependencies and run locally ## Future Tasks -- [✅] Look into a way to export the app. - [ ] Look into a way to deploy the app in a single click. Can try to do it myself with a dynamic route + some hashing, or try to use Replit/Vercel - [ ] Save previous versions so people can go back and forth between the generated ones - [ ] Could be nice to show a "featured apps" route on the site on `/featured`. Have a `/id/${prompt}` dynamic route that can display a bunch of nice example apps in the sandbox ready to go diff --git a/components/Footer.tsx b/components/Footer.tsx index 8ad6797f5..55f46210b 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -36,7 +36,7 @@ export default function Footer() { diff --git a/components/Header.tsx b/components/Header.tsx index c22fdeb6d..359139efc 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -13,7 +13,7 @@ export default function Header() { From aa8cb168ccce9271bfa252c1c96cc7cbfba4439e Mon Sep 17 00:00:00 2001 From: Himansh raj Date: Fri, 9 Aug 2024 15:36:25 +0530 Subject: [PATCH 4/6] chore: Update system prompt and add support for few ui libraries --- app/api/generateCode/route.ts | 25 ++++++++++++++++++++++++- app/page.tsx | 21 +++++++++++++++++++++ utils/TogetherAIStream.ts | 1 + 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/app/api/generateCode/route.ts b/app/api/generateCode/route.ts index 9557c45c8..a922e0e8c 100644 --- a/app/api/generateCode/route.ts +++ b/app/api/generateCode/route.ts @@ -6,7 +6,7 @@ import { export const maxDuration = 60; const systemPrompt = ` -You are an expert frontend React engineer who is also a great UI/UX designer. Follow the instructions carefully, I will tip you $1 million if you do a good job: +You are an expert frontend React engineer(15 year of exprience) who is also a great UI/UX designer. Follow the instructions carefully, I will tip you $1 million if you do a good job: - Create React components for whatever the user asked you to create and make sure it can run by itself by using a default export - Make sure the React app is interactive and functional by creating state when needed and having no required props @@ -22,6 +22,28 @@ You are an expert frontend React engineer who is also a great UI/UX designer. Fo - Add .tsx extension to all files when importing in another file - You can generate as many files as you need, but make sure to export the component from the file. - You can genrate any text-based files. For example, if you need to generate a CSS file, you can generate a CSS file with the content you need. +- We have installed the following libraries for you: + - lucide-react: For icons + - react-router-dom: For routing + - recharts: For charts + - @mui/material: For UI components + - @emotion/react: For styling + - @emotion/styled: For styling + - @mui/icons-material: For icons + - @headlessui/react: For UI components + - @heroicons/react: For icons + - @radix-ui/react-tooltip: For tooltips + - @radix-ui/react-select: For select components + - framer-motion: For animations + - react-icons: For icons + - react-spring: For animations + You can use these libraries in your app if needed. +- Always make ui beautiful and responsive and animated(if not mentioned by user) +- Remember, you are an expert frontend React engineer(15 year of exprience) who is also a great UI/UX designer. So, make sure to write the code as an expert frontend React engineer(15 year of exprience) who is also a great UI/UX designer. +- Use component from the libraries mentioned above if needed. +- Do not use any other libraries. +- Do not use Link from react-router-dom, until unless you defined routes and the component should be inside the provider. +- For images, you can use any image from the internet (do not use local one). `; export async function POST(req: Request) { @@ -44,6 +66,7 @@ export async function POST(req: Request) { ], stream: true, temperature: 0.2, + max_tokens: 4097, }; const stream = await TogetherAIStream(payload); diff --git a/app/page.tsx b/app/page.tsx index 250101808..00f9e2992 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -58,7 +58,25 @@ export default function Home() { "lucide-react": "latest", "react-router-dom": "latest", recharts: "2.9.0", + "@mui/material": "latest", + "@emotion/react":"latest", + "@emotion/styled":"latest", + "@mui/icons-material":"latest", + // other ui libraries + "@headlessui/react": "latest", + "@heroicons/react": "latest", + "@radix-ui/react-tooltip": "latest", + "@radix-ui/react-select": "latest", + "framer-motion": "latest", + "react-icons": "latest", + // "@chakra-ui/react": "latest", + "react-spring": "latest", + "@fortawesome/react-fontawesome": "latest", + + + } + let loading = status === "creating" || status === "updating"; function SetCodesFiless(data: any) { @@ -244,6 +262,7 @@ export default function Home() { } setCodeFiles((prevCodeFiles) => ({ ...CODES, + ...prevCodeFiles, ...newCodeFiles, })); setActiveFile(activeFile_); @@ -440,6 +459,7 @@ export default function Home() { showNavigator: true, externalResources: [ "https://unpkg.com/@tailwindcss/ui/dist/tailwind-ui.min.css", + ], editorHeight: "80vh", showTabs: true, @@ -453,6 +473,7 @@ export default function Home() { customSetup={{ dependencies: dependencies, }} + /> {!loading&&