Repository: SawyerHood/draw-a-ui Branch: main Commit: 1aca63b4d54a Files: 19 Total size: 24.4 KB Directory structure: gitextract_w27hu8_s/ ├── .eslintrc.json ├── .github/ │ └── workflows/ │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── app/ │ ├── api/ │ │ └── toHtml/ │ │ └── route.ts │ ├── globals.css │ ├── layout.tsx │ └── page.tsx ├── components/ │ └── PreviewModal.tsx ├── lib/ │ ├── blobToBase64.ts │ ├── getBrowserCanvasMaxSize.ts │ ├── getSvgAsImage.ts │ └── png.ts ├── next.config.js ├── package.json ├── postcss.config.js ├── tailwind.config.ts └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintrc.json ================================================ { "extends": "next/core-web-vitals" } ================================================ FILE: .github/workflows/ci.yml ================================================ name: Build and Lint on: push: branches: [main] pull_request: branches: [main] jobs: build-and-lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Node.js uses: actions/setup-node@v2 with: node-version: "20" # Specify the Node.js version - name: Install dependencies run: npm install - name: Run build run: npm run build - name: Run lint run: npm run lint ================================================ FILE: .gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js .yarn/install-state.gz # testing /coverage # next.js /.next/ /out/ # production /build # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* # local env files .env*.local # vercel .vercel # typescript *.tsbuildinfo next-env.d.ts ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2023 Sawyer Hood Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # draw-a-ui This is an app that uses tldraw and the gpt-4-vision api to generate html based on a wireframe you draw. > The spiritual successor to this project is [Terragon Labs](https://terragonlabs.com).  This works by just taking the current canvas SVG, converting it to a PNG, and sending that png to gpt-4-vision with instructions to return a single html file with tailwind. > Disclaimer: This is a demo and is not intended for production use. It doesn't have any auth so you will go broke if you deploy it. ## Getting Started This is a Next.js app. To get started run the following commands in the root directory of the project. You will need an OpenAI API key with access to the GPT-4 Vision API. > Note this uses Next.js 14 and requires a version of `node` greater than 18.17. [Read more here](https://nextjs.org/docs/pages/building-your-application/upgrading/version-14). ```bash echo "OPENAI_API_KEY=sk-your-key" > .env.local npm install npm run dev ``` Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. ================================================ FILE: app/api/toHtml/route.ts ================================================ import { OpenAI } from "openai"; const systemPrompt = `You are an expert tailwind developer. A user will provide you with a low-fidelity wireframe of an application and you will return a single html file that uses tailwind to create the website. Use creative license to make the application more fleshed out. if you need to insert an image, use placehold.co to create a placeholder image. Respond only with the html file.`; export async function POST(request: Request) { const openai = new OpenAI(); const { image } = await request.json(); const resp = await openai.chat.completions.create({ model: "gpt-4o", max_tokens: 4096, messages: [ { role: "system", content: systemPrompt, }, { role: "user", content: [ { type: "image_url", image_url: { url: image, detail: "high" }, }, { type: "text", text: "Turn this into a single html file using tailwind.", }, ], }, ], }); return new Response(JSON.stringify(resp), { headers: { "content-type": "application/json; charset=UTF-8", }, }); } ================================================ FILE: app/globals.css ================================================ @tailwind base; @tailwind components; @tailwind utilities; .tlui-help-menu { display: none !important; } .tlui-debug-panel { display: none !important; } ================================================ FILE: app/layout.tsx ================================================ import type { Metadata } from "next"; import { Inter } from "next/font/google"; import "./globals.css"; const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { title: "Create Next App", description: "Generated by create next app", }; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return (
{children} ); } ================================================ FILE: app/page.tsx ================================================ "use client"; import dynamic from "next/dynamic"; import "@tldraw/tldraw/tldraw.css"; import { useEditor } from "@tldraw/tldraw"; import { getSvgAsImage } from "@/lib/getSvgAsImage"; import { blobToBase64 } from "@/lib/blobToBase64"; import React, { useEffect, useState } from "react"; import ReactDOM from "react-dom"; import { PreviewModal } from "@/components/PreviewModal"; const Tldraw = dynamic(async () => (await import("@tldraw/tldraw")).Tldraw, { ssr: false, }); export default function Home() { const [html, setHtml] = useState