import { useRef, useState } from "react"; import ImageUpload from "./components/ImageUpload"; import CodePreview from "./components/CodePreview"; import Preview from "./components/Preview"; import { CodeGenerationParams, generateCode } from "./generateCode"; import Spinner from "./components/Spinner"; import classNames from "classnames"; import { FaCode, FaDesktop, FaDownload, FaMobile, FaUndo, } from "react-icons/fa"; import { Switch } from "./components/ui/switch"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs"; import SettingsDialog from "./components/SettingsDialog"; import { Settings, EditorTheme, AppState, CSSOption, OutputSettings, JSFrameworkOption, } from "./types"; import { IS_RUNNING_ON_CLOUD } from "./config"; import { PicoBadge } from "./components/PicoBadge"; import { OnboardingNote } from "./components/OnboardingNote"; import { usePersistedState } from "./hooks/usePersistedState"; import { UrlInputSection } from "./components/UrlInputSection"; import TermsOfServiceDialog from "./components/TermsOfServiceDialog"; import html2canvas from "html2canvas"; import { USER_CLOSE_WEB_SOCKET_CODE } from "./constants"; import CodeTab from "./components/CodeTab"; import OutputSettingsSection from "./components/OutputSettingsSection"; function App() { const [appState, setAppState] = useState(AppState.INITIAL); const [generatedCode, setGeneratedCode] = useState(""); const [referenceImages, setReferenceImages] = useState([]); const [executionConsole, setExecutionConsole] = useState([]); const [updateInstruction, setUpdateInstruction] = useState(""); const [history, setHistory] = useState([]); const [settings, setSettings] = usePersistedState( { openAiApiKey: null, screenshotOneApiKey: null, isImageGenerationEnabled: true, editorTheme: EditorTheme.COBALT, isTermOfServiceAccepted: false, accessCode: null, }, "setting" ); const [outputSettings, setOutputSettings] = useState({ css: CSSOption.TAILWIND, js: JSFrameworkOption.NO_FRAMEWORK, }); const [shouldIncludeResultImage, setShouldIncludeResultImage] = useState(false); const wsRef = useRef(null); const takeScreenshot = async (): Promise => { const iframeElement = document.querySelector( "#preview-desktop" ) as HTMLIFrameElement; if (!iframeElement?.contentWindow?.document.body) { return ""; } const canvas = await html2canvas(iframeElement.contentWindow.document.body); const png = canvas.toDataURL("image/png"); return png; }; const downloadCode = () => { try { window.plausible("Download"); } catch { // Ignore } // Create a blob from the generated code const blob = new Blob([generatedCode], { type: "text/html" }); const url = URL.createObjectURL(blob); // Create an anchor element and set properties for download const a = document.createElement("a"); a.href = url; a.download = "index.html"; // Set the file name for download document.body.appendChild(a); // Append to the document a.click(); // Programmatically click the anchor to trigger download // Clean up by removing the anchor and revoking the Blob URL document.body.removeChild(a); URL.revokeObjectURL(url); }; const reset = () => { setAppState(AppState.INITIAL); setGeneratedCode(""); setReferenceImages([]); setExecutionConsole([]); setHistory([]); }; const stop = () => { wsRef.current?.close?.(USER_CLOSE_WEB_SOCKET_CODE); // make sure stop can correct the state even if the websocket is already closed setAppState(AppState.CODE_READY); }; function doGenerateCode(params: CodeGenerationParams) { setExecutionConsole([]); setAppState(AppState.CODING); // Merge settings with params const updatedParams = { ...params, ...settings, outputSettings }; generateCode( wsRef, updatedParams, (token) => setGeneratedCode((prev) => prev + token), (code) => setGeneratedCode(code), (line) => setExecutionConsole((prev) => [...prev, line]), () => setAppState(AppState.CODE_READY) ); } // Initial version creation function doCreate(referenceImages: string[]) { // Reset any existing state reset(); setReferenceImages(referenceImages); if (referenceImages.length > 0) { doGenerateCode({ generationType: "create", image: referenceImages[0], }); } } // Subsequent updates async function doUpdate() { const updatedHistory = [...history, generatedCode, updateInstruction]; if (shouldIncludeResultImage) { const resultImage = await takeScreenshot(); doGenerateCode({ generationType: "update", image: referenceImages[0], resultImage: resultImage, history: updatedHistory, }); } else { doGenerateCode({ generationType: "update", image: referenceImages[0], history: updatedHistory, }); } setHistory(updatedHistory); setGeneratedCode(""); setUpdateInstruction(""); } const handleTermDialogOpenChange = (open: boolean) => { setSettings((s) => ({ ...s, isTermOfServiceAccepted: !open, })); }; return (
{IS_RUNNING_ON_CLOUD && } {IS_RUNNING_ON_CLOUD && ( )}

Screenshot to Code

{IS_RUNNING_ON_CLOUD && !(settings.openAiApiKey || settings.accessCode) && ( )} {(appState === AppState.CODING || appState === AppState.CODE_READY) && ( <> {/* Show code preview only when coding */} {appState === AppState.CODING && (
{executionConsole.slice(-1)[0]}
)} {appState === AppState.CODE_READY && (