From 54b59c85d6f012f507c108e3501fb1b0a8902e17 Mon Sep 17 00:00:00 2001 From: Abi Raja Date: Fri, 19 Jul 2024 09:25:11 -0400 Subject: [PATCH 01/21] add eval runner for text prompt --- backend/run_evals.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/backend/run_evals.py b/backend/run_evals.py index 2324c51..00da70b 100644 --- a/backend/run_evals.py +++ b/backend/run_evals.py @@ -60,4 +60,48 @@ async def main(): file.write(content) +# async def text_main(): +# OUTPUT_DIR = EVALS_DIR + "/outputs" + +# GENERAL_TEXT_V1 = [ +# "Login form", +# "Simple notification", +# "button", +# "saas dashboard", +# "landing page for barber shop", +# ] + +# tasks: list[Coroutine[Any, Any, str]] = [] +# for prompt in GENERAL_TEXT_V1: +# for n in range(N): # Generate N tasks for each input +# if n == 0: +# task = generate_code_for_text( +# text=prompt, +# stack=STACK, +# model=Llm.CLAUDE_3_5_SONNET_2024_06_20, +# ) +# else: +# task = generate_code_for_text( +# text=prompt, stack=STACK, model=Llm.GPT_4O_2024_05_13 +# ) +# tasks.append(task) + +# print(f"Generating {len(tasks)} codes") + +# results = await asyncio.gather(*tasks) + +# os.makedirs(OUTPUT_DIR, exist_ok=True) + +# for i, content in enumerate(results): +# # Calculate index for filename and output number +# eval_index = i // N +# output_number = i % N +# filename = GENERAL_TEXT_V1[eval_index] +# # File name is derived from the original filename in evals with an added output number +# output_filename = f"{os.path.splitext(filename)[0]}_{output_number}.html" +# output_filepath = os.path.join(OUTPUT_DIR, output_filename) +# with open(output_filepath, "w") as file: +# file.write(content) + + asyncio.run(main()) From 3f6fcdea25bcc0c7651d40d7dff5579104b8f12d Mon Sep 17 00:00:00 2001 From: Abi Raja Date: Fri, 19 Jul 2024 16:01:30 -0400 Subject: [PATCH 02/21] remove unused UI --- frontend/src/App.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index e3b5b64..4fb97bf 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -43,8 +43,6 @@ import SelectAndEditModeToggleButton from "./components/select-and-edit/SelectAn import { useAppStore } from "./store/app-store"; import KeyboardShortcutBadge from "./components/core/KeyboardShortcutBadge"; -const IS_OPENAI_DOWN = false; - function App() { const [appState, setAppState] = useState(AppState.INITIAL); const [generatedCode, setGeneratedCode] = useState(""); @@ -464,13 +462,6 @@ function App() { {IS_RUNNING_ON_CLOUD && !settings.openAiApiKey && } - {IS_OPENAI_DOWN && ( -
- OpenAI API is currently down. Try back in 30 minutes or later. We - apologize for the inconvenience. -
- )} - {(appState === AppState.CODING || appState === AppState.CODE_READY) && ( <> From ba0d51a34f2e76138d0ace3d9f295ba819f84cb4 Mon Sep 17 00:00:00 2001 From: Abi Raja Date: Fri, 19 Jul 2024 16:11:46 -0400 Subject: [PATCH 03/21] remove react warning --- frontend/src/App.tsx | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4fb97bf..d667a0e 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -89,11 +89,6 @@ function App() { const wsRef = useRef(null); - const showReactWarning = - selectedCodeGenerationModel === - CodeGenerationModel.GPT_4_TURBO_2024_04_09 && - settings.generatedCodeConfig === Stack.REACT_TAILWIND; - const showBetterModelMessage = selectedCodeGenerationModel !== CodeGenerationModel.GPT_4O_2024_05_13 && selectedCodeGenerationModel !== @@ -442,13 +437,6 @@ function App() { } /> - {showReactWarning && ( -
- Sorry - React is not currently working with GPT-4 Turbo. Please - use GPT-4 Vision or Claude Sonnet. We are working on a fix. -
- )} - {showBetterModelMessage && (

@@ -578,7 +566,7 @@ function App() {

)} -
+

Console

From fd6a8f779d123ef454dfcff4ad2973ad8fa01aba Mon Sep 17 00:00:00 2001 From: Abi Raja Date: Fri, 19 Jul 2024 16:39:21 -0400 Subject: [PATCH 04/21] add comment --- frontend/src/App.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index d667a0e..1c642b3 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -615,6 +615,7 @@ function App() { )} {(appState === AppState.CODING || appState === AppState.CODE_READY) && ( + // Right side preview and code
From 98951e0382acb65e114190880520437aaf7702a6 Mon Sep 17 00:00:00 2001 From: Abi Raja Date: Thu, 25 Jul 2024 12:32:17 -0400 Subject: [PATCH 05/21] move some state over to zustand store --- frontend/src/App.tsx | 15 +++++++--- frontend/src/store/project-store.ts | 45 +++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 frontend/src/store/project-store.ts diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 1c642b3..fa0b485 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -42,17 +42,24 @@ import TipLink from "./components/core/TipLink"; import SelectAndEditModeToggleButton from "./components/select-and-edit/SelectAndEditModeToggleButton"; import { useAppStore } from "./store/app-store"; import KeyboardShortcutBadge from "./components/core/KeyboardShortcutBadge"; +import { useProjectStore } from "./store/project-store"; function App() { const [appState, setAppState] = useState(AppState.INITIAL); - const [generatedCode, setGeneratedCode] = useState(""); - const [inputMode, setInputMode] = useState<"image" | "video">("image"); + const { + inputMode, + setInputMode, + isImportedFromCode, + setIsImportedFromCode, + referenceImages, + setReferenceImages, + generatedCode, + setGeneratedCode, + } = useProjectStore(); - const [referenceImages, setReferenceImages] = useState([]); const [executionConsole, setExecutionConsole] = useState([]); const [updateInstruction, setUpdateInstruction] = useState(""); - const [isImportedFromCode, setIsImportedFromCode] = useState(false); const textareaRef = useRef(null); diff --git a/frontend/src/store/project-store.ts b/frontend/src/store/project-store.ts new file mode 100644 index 0000000..4b2c547 --- /dev/null +++ b/frontend/src/store/project-store.ts @@ -0,0 +1,45 @@ +import { create } from "zustand"; +import { History } from "../components/history/history_types"; + +// Store for app-wide state +interface ProjectStore { + // Inputs + inputMode: "image" | "video"; + setInputMode: (mode: "image" | "video") => void; + isImportedFromCode: boolean; + setIsImportedFromCode: (imported: boolean) => void; + referenceImages: string[]; + setReferenceImages: (images: string[]) => void; + + // Outputs and other state + generatedCode: string; + setGeneratedCode: ( + updater: string | ((currentCode: string) => string) + ) => void; + currentVersion: number | null; + setCurrentVersion: (version: number | null) => void; + appHistory: History[]; + setAppHistory: (history: History[]) => void; +} + +export const useProjectStore = create((set) => ({ + // Inputs and their setters + inputMode: "image", + setInputMode: (mode) => set({ inputMode: mode }), + isImportedFromCode: false, + setIsImportedFromCode: (imported) => set({ isImportedFromCode: imported }), + referenceImages: [], + setReferenceImages: (images) => set({ referenceImages: images }), + + // Outputs and other state + generatedCode: "", + setGeneratedCode: (updater) => + set((state) => ({ + generatedCode: + typeof updater === "function" ? updater(state.generatedCode) : updater, + })), + currentVersion: null, + setCurrentVersion: (version) => set({ currentVersion: version }), + appHistory: [], + setAppHistory: (history) => set({ appHistory: history }), +})); From 83f6f00b108ec5c172aee4c362d28fe6c78317f7 Mon Sep 17 00:00:00 2001 From: Abi Raja Date: Thu, 25 Jul 2024 12:33:31 -0400 Subject: [PATCH 06/21] also use currentVersion --- frontend/src/App.tsx | 4 ++-- frontend/src/store/project-store.ts | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index fa0b485..c85c09f 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -56,6 +56,8 @@ function App() { setReferenceImages, generatedCode, setGeneratedCode, + currentVersion, + setCurrentVersion, } = useProjectStore(); const [executionConsole, setExecutionConsole] = useState([]); @@ -88,8 +90,6 @@ function App() { // App history const [appHistory, setAppHistory] = useState([]); - // Tracks the currently shown version from app history - const [currentVersion, setCurrentVersion] = useState(null); const [shouldIncludeResultImage, setShouldIncludeResultImage] = useState(false); diff --git a/frontend/src/store/project-store.ts b/frontend/src/store/project-store.ts index 4b2c547..ec2510f 100644 --- a/frontend/src/store/project-store.ts +++ b/frontend/src/store/project-store.ts @@ -16,8 +16,12 @@ interface ProjectStore { setGeneratedCode: ( updater: string | ((currentCode: string) => string) ) => void; + + // Tracks the currently shown version from app history + // TODO: might want to move to appStore currentVersion: number | null; setCurrentVersion: (version: number | null) => void; + appHistory: History[]; setAppHistory: (history: History[]) => void; } From dcef298dba2a3c1910f1929ebd2582a19aae28b8 Mon Sep 17 00:00:00 2001 From: Abi Raja Date: Thu, 25 Jul 2024 12:37:06 -0400 Subject: [PATCH 07/21] also use appHistory --- frontend/src/App.tsx | 8 +++++--- frontend/src/store/project-store.ts | 12 +++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c85c09f..8c397e9 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -48,16 +48,21 @@ function App() { const [appState, setAppState] = useState(AppState.INITIAL); const { + // Inputs inputMode, setInputMode, isImportedFromCode, setIsImportedFromCode, referenceImages, setReferenceImages, + + // Outputs generatedCode, setGeneratedCode, currentVersion, setCurrentVersion, + appHistory, + setAppHistory, } = useProjectStore(); const [executionConsole, setExecutionConsole] = useState([]); @@ -88,9 +93,6 @@ function App() { const selectedCodeGenerationModel = settings.codeGenerationModel || CodeGenerationModel.GPT_4_VISION; - // App history - const [appHistory, setAppHistory] = useState([]); - const [shouldIncludeResultImage, setShouldIncludeResultImage] = useState(false); diff --git a/frontend/src/store/project-store.ts b/frontend/src/store/project-store.ts index ec2510f..c583cb2 100644 --- a/frontend/src/store/project-store.ts +++ b/frontend/src/store/project-store.ts @@ -22,8 +22,10 @@ interface ProjectStore { currentVersion: number | null; setCurrentVersion: (version: number | null) => void; - appHistory: History[]; - setAppHistory: (history: History[]) => void; + appHistory: History; + setAppHistory: ( + updater: History | ((currentHistory: History) => History) + ) => void; } export const useProjectStore = create((set) => ({ @@ -45,5 +47,9 @@ export const useProjectStore = create((set) => ({ currentVersion: null, setCurrentVersion: (version) => set({ currentVersion: version }), appHistory: [], - setAppHistory: (history) => set({ appHistory: history }), + setAppHistory: (updater) => + set((state) => ({ + appHistory: + typeof updater === "function" ? updater(state.appHistory) : updater, + })), })); From deb2375146768bd0cdf1efd3e8b5c7f6e7665113 Mon Sep 17 00:00:00 2001 From: Abi Raja Date: Thu, 25 Jul 2024 12:45:03 -0400 Subject: [PATCH 08/21] isolate history component by using projectStore directly --- frontend/src/App.tsx | 12 ---------- .../src/components/history/HistoryDisplay.tsx | 24 +++++++++++-------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 8c397e9..ae58b89 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -593,18 +593,6 @@ function App() { )} { { - if ( - index < 0 || - index >= appHistory.length || - !appHistory[index] - ) - return; - setCurrentVersion(index); - setGeneratedCode(appHistory[index].code); - }} shouldDisableReverts={appState === AppState.CODING} /> } diff --git a/frontend/src/components/history/HistoryDisplay.tsx b/frontend/src/components/history/HistoryDisplay.tsx index ce00db4..a3dcdb1 100644 --- a/frontend/src/components/history/HistoryDisplay.tsx +++ b/frontend/src/components/history/HistoryDisplay.tsx @@ -1,4 +1,3 @@ -import { History } from "./history_types"; import toast from "react-hot-toast"; import classNames from "classnames"; @@ -11,22 +10,27 @@ import { } from "../ui/collapsible"; import { Button } from "../ui/button"; import { CaretSortIcon } from "@radix-ui/react-icons"; +import { useProjectStore } from "../../store/project-store"; interface Props { - history: History; - currentVersion: number | null; - revertToVersion: (version: number) => void; shouldDisableReverts: boolean; } -export default function HistoryDisplay({ - history, - currentVersion, - revertToVersion, - shouldDisableReverts, -}: Props) { +export default function HistoryDisplay({ shouldDisableReverts }: Props) { + const { + appHistory: history, + currentVersion, + setCurrentVersion, + setGeneratedCode, + } = useProjectStore(); const renderedHistory = renderHistory(history, currentVersion); + const revertToVersion = (index: number) => { + if (index < 0 || index >= history.length || !history[index]) return; + setCurrentVersion(index); + setGeneratedCode(history[index].code); + }; + return renderedHistory.length === 0 ? null : (

Versions

From 993ff88e2be6b88d2343439a5e880c052ae57507 Mon Sep 17 00:00:00 2001 From: Abi Raja Date: Thu, 25 Jul 2024 14:35:04 -0400 Subject: [PATCH 09/21] abstract into more components --- frontend/src/App.tsx | 273 ++---------------- frontend/src/components/preview/Preview.tsx | 114 ++++++++ .../PreviewComponent.tsx} | 8 +- frontend/src/components/sidebar/Sidebar.tsx | 174 +++++++++++ frontend/src/store/app-store.ts | 21 ++ frontend/src/store/project-store.ts | 13 + 6 files changed, 346 insertions(+), 257 deletions(-) create mode 100644 frontend/src/components/preview/Preview.tsx rename frontend/src/components/{Preview.tsx => preview/PreviewComponent.tsx} (87%) create mode 100644 frontend/src/components/sidebar/Sidebar.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index ae58b89..bbd55f5 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,21 +1,6 @@ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef } from "react"; import ImageUpload from "./components/ImageUpload"; -import CodePreview from "./components/CodePreview"; -import Preview from "./components/Preview"; import { 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 { AppState, CodeGenerationParams, EditorTheme, Settings } from "./types"; import { IS_RUNNING_ON_CLOUD } from "./config"; @@ -26,7 +11,6 @@ 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"; import { History } from "./components/history/history_types"; import HistoryDisplay from "./components/history/HistoryDisplay"; @@ -36,17 +20,14 @@ import ImportCodeSection from "./components/ImportCodeSection"; import { Stack } from "./lib/stacks"; import { CodeGenerationModel } from "./lib/models"; import ModelSettingsSection from "./components/ModelSettingsSection"; -import { extractHtml } from "./components/preview/extractHtml"; import useBrowserTabIndicator from "./hooks/useBrowserTabIndicator"; import TipLink from "./components/core/TipLink"; -import SelectAndEditModeToggleButton from "./components/select-and-edit/SelectAndEditModeToggleButton"; import { useAppStore } from "./store/app-store"; -import KeyboardShortcutBadge from "./components/core/KeyboardShortcutBadge"; import { useProjectStore } from "./store/project-store"; +import Sidebar from "./components/sidebar/Sidebar"; +import Preview from "./components/preview/Preview"; function App() { - const [appState, setAppState] = useState(AppState.INITIAL); - const { // Inputs inputMode, @@ -57,20 +38,22 @@ function App() { setReferenceImages, // Outputs - generatedCode, setGeneratedCode, + setExecutionConsole, currentVersion, setCurrentVersion, appHistory, setAppHistory, } = useProjectStore(); - const [executionConsole, setExecutionConsole] = useState([]); - const [updateInstruction, setUpdateInstruction] = useState(""); - - const textareaRef = useRef(null); - - const { disableInSelectAndEditMode } = useAppStore(); + const { + disableInSelectAndEditMode, + setUpdateInstruction, + appState, + setAppState, + shouldIncludeResultImage, + setShouldIncludeResultImage, + } = useAppStore(); // Settings const [settings, setSettings] = usePersistedState( @@ -93,9 +76,6 @@ function App() { const selectedCodeGenerationModel = settings.codeGenerationModel || CodeGenerationModel.GPT_4_VISION; - const [shouldIncludeResultImage, setShouldIncludeResultImage] = - useState(false); - const wsRef = useRef(null); const showBetterModelMessage = @@ -139,23 +119,6 @@ function App() { return png; }; - const downloadCode = () => { - // 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(""); @@ -194,11 +157,6 @@ function App() { cancelCodeGenerationAndReset(); }; - const previewCode = - inputMode === "video" && appState === AppState.CODING - ? extractHtml(generatedCode) - : generatedCode; - const cancelCodeGenerationAndReset = () => { // When this is the first version, reset the entire app state if (currentVersion === null) { @@ -257,7 +215,7 @@ function App() { inputs: { prompt: params.history ? params.history[params.history.length - 1] - : updateInstruction, + : "", }, }, ]; @@ -407,13 +365,6 @@ function App() { setAppState(AppState.CODE_READY); } - // When coding is complete, focus on the update instruction textarea - useEffect(() => { - if (appState === AppState.CODE_READY && textareaRef.current) { - textareaRef.current.focus(); - } - }, [appState]); - return (
{IS_RUNNING_ON_CLOUD && } @@ -461,135 +412,12 @@ function App() { {(appState === AppState.CODING || appState === AppState.CODE_READY) && ( - <> - {/* Show code preview only when coding */} - {appState === AppState.CODING && ( -
- {/* Speed disclaimer for video mode */} - {inputMode === "video" && ( -
- Code generation from videos can take 3-4 minutes. We do - multiple passes to get the best result. Please be patient. -
- )} - -
- - {executionConsole.slice(-1)[0]} -
- - - -
- -
-
- )} - - {appState === AppState.CODE_READY && ( -
-
-