diff --git a/README.md b/README.md index e4739a4..92e26d2 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ See the [Examples](#-examples) section below for more demos. ## 🌟 Recent Updates +- Dec 11 - Start a new project from existing code (allows you to come back to an older project) - Dec 7 - 🔥 🔥 🔥 View a history of your edits, and branch off them - Nov 30 - Dark mode, output code in Ionic (thanks [@dialmedu](https://github.com/dialmedu)), set OpenAI base URL - Nov 28 - 🔥 🔥 🔥 Customize your stack: React or Bootstrap or TailwindCSS diff --git a/design-docs.md b/design-docs.md new file mode 100644 index 0000000..eb38119 --- /dev/null +++ b/design-docs.md @@ -0,0 +1,5 @@ +## Version History + +Version history is stored as a tree on the client-side. + +![Screenshot to Code](https://github.com/abi/screenshot-to-code/assets/23818/e35644aa-b90a-4aa7-8027-b8732796fd7c) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index d7db9db..4f3afb2 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2,7 +2,7 @@ import { useEffect, 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 { generateCode } from "./generateCode"; import Spinner from "./components/Spinner"; import classNames from "classnames"; import { @@ -18,7 +18,13 @@ 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, GeneratedCodeConfig } from "./types"; +import { + AppState, + CodeGenerationParams, + EditorTheme, + GeneratedCodeConfig, + Settings, +} from "./types"; import { IS_RUNNING_ON_CLOUD } from "./config"; import { PicoBadge } from "./components/PicoBadge"; import { OnboardingNote } from "./components/OnboardingNote"; @@ -127,10 +133,21 @@ function App() { setIsImportedFromCode(false); }; - const stop = () => { + const cancelCodeGeneration = () => { 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); + cancelCodeGenerationAndReset(); + }; + + const cancelCodeGenerationAndReset = () => { + // When this is the first version, reset the entire app state + if (currentVersion === null) { + reset(); + } else { + // Otherwise, revert to the last version + setGeneratedCode(appHistory[currentVersion].code); + setAppState(AppState.CODE_READY); + } }; function doGenerateCode( @@ -187,6 +204,11 @@ function App() { } }, (line) => setExecutionConsole((prev) => [...prev, line]), + // On cancel + () => { + cancelCodeGenerationAndReset(); + }, + // On complete () => { setAppState(AppState.CODE_READY); } @@ -343,10 +365,10 @@ function App() {
diff --git a/frontend/src/generateCode.ts b/frontend/src/generateCode.ts index 5e826dd..55523c4 100644 --- a/frontend/src/generateCode.ts +++ b/frontend/src/generateCode.ts @@ -1,27 +1,20 @@ import toast from "react-hot-toast"; import { WS_BACKEND_URL } from "./config"; import { USER_CLOSE_WEB_SOCKET_CODE } from "./constants"; +import { FullGenerationSettings } from "./types"; const ERROR_MESSAGE = "Error generating code. Check the Developer Console AND the backend logs for details. Feel free to open a Github issue."; -const STOP_MESSAGE = "Code generation stopped"; - -export interface CodeGenerationParams { - generationType: "create" | "update"; - image: string; - resultImage?: string; - history?: string[]; - isImportedFromCode?: boolean; - // isImageGenerationEnabled: boolean; // TODO: Merge with Settings type in types.ts -} +const CANCEL_MESSAGE = "Code generation cancelled"; export function generateCode( wsRef: React.MutableRefObject, - params: CodeGenerationParams, + params: FullGenerationSettings, onChange: (chunk: string) => void, onSetCode: (code: string) => void, onStatusUpdate: (status: string) => void, + onCancel: () => void, onComplete: () => void ) { const wsUrl = `${WS_BACKEND_URL}/generate-code`; @@ -47,15 +40,18 @@ export function generateCode( toast.error(response.value); } }); + ws.addEventListener("close", (event) => { console.log("Connection closed", event.code, event.reason); if (event.code === USER_CLOSE_WEB_SOCKET_CODE) { - toast.success(STOP_MESSAGE); + toast.success(CANCEL_MESSAGE); + onCancel(); } else if (event.code !== 1000) { console.error("WebSocket error code", event); toast.error(ERROR_MESSAGE); + } else { + onComplete(); } - onComplete(); }); ws.addEventListener("error", (error) => { diff --git a/frontend/src/types.ts b/frontend/src/types.ts index deb370a..236116a 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -28,3 +28,13 @@ export enum AppState { CODING = "CODING", CODE_READY = "CODE_READY", } + +export interface CodeGenerationParams { + generationType: "create" | "update"; + image: string; + resultImage?: string; + history?: string[]; + isImportedFromCode?: boolean; +} + +export type FullGenerationSettings = CodeGenerationParams & Settings;