Merge branch 'main' into hosted

This commit is contained in:
Abi Raja 2023-12-11 18:56:29 -05:00
commit 1cdfd7d1ac
5 changed files with 53 additions and 19 deletions

View File

@ -12,6 +12,7 @@ See the [Examples](#-examples) section below for more demos.
## 🌟 Recent Updates ## 🌟 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 - 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 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 - Nov 28 - 🔥 🔥 🔥 Customize your stack: React or Bootstrap or TailwindCSS

5
design-docs.md Normal file
View File

@ -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)

View File

@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from "react";
import ImageUpload from "./components/ImageUpload"; import ImageUpload from "./components/ImageUpload";
import CodePreview from "./components/CodePreview"; import CodePreview from "./components/CodePreview";
import Preview from "./components/Preview"; import Preview from "./components/Preview";
import { CodeGenerationParams, generateCode } from "./generateCode"; import { generateCode } from "./generateCode";
import Spinner from "./components/Spinner"; import Spinner from "./components/Spinner";
import classNames from "classnames"; import classNames from "classnames";
import { import {
@ -18,7 +18,13 @@ import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs";
import SettingsDialog from "./components/SettingsDialog"; 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 { IS_RUNNING_ON_CLOUD } from "./config";
import { PicoBadge } from "./components/PicoBadge"; import { PicoBadge } from "./components/PicoBadge";
import { OnboardingNote } from "./components/OnboardingNote"; import { OnboardingNote } from "./components/OnboardingNote";
@ -127,10 +133,21 @@ function App() {
setIsImportedFromCode(false); setIsImportedFromCode(false);
}; };
const stop = () => { const cancelCodeGeneration = () => {
wsRef.current?.close?.(USER_CLOSE_WEB_SOCKET_CODE); wsRef.current?.close?.(USER_CLOSE_WEB_SOCKET_CODE);
// make sure stop can correct the state even if the websocket is already closed // 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( function doGenerateCode(
@ -187,6 +204,11 @@ function App() {
} }
}, },
(line) => setExecutionConsole((prev) => [...prev, line]), (line) => setExecutionConsole((prev) => [...prev, line]),
// On cancel
() => {
cancelCodeGenerationAndReset();
},
// On complete
() => { () => {
setAppState(AppState.CODE_READY); setAppState(AppState.CODE_READY);
} }
@ -343,10 +365,10 @@ function App() {
</div> </div>
<div className="flex mt-4 w-full"> <div className="flex mt-4 w-full">
<Button <Button
onClick={stop} onClick={cancelCodeGeneration}
className="w-full dark:text-white dark:bg-gray-700" className="w-full dark:text-white dark:bg-gray-700"
> >
Stop Cancel
</Button> </Button>
</div> </div>
<CodePreview code={generatedCode} /> <CodePreview code={generatedCode} />

View File

@ -1,27 +1,20 @@
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { WS_BACKEND_URL } from "./config"; import { WS_BACKEND_URL } from "./config";
import { USER_CLOSE_WEB_SOCKET_CODE } from "./constants"; import { USER_CLOSE_WEB_SOCKET_CODE } from "./constants";
import { FullGenerationSettings } from "./types";
const ERROR_MESSAGE = const ERROR_MESSAGE =
"Error generating code. Check the Developer Console AND the backend logs for details. Feel free to open a Github issue."; "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"; const CANCEL_MESSAGE = "Code generation cancelled";
export interface CodeGenerationParams {
generationType: "create" | "update";
image: string;
resultImage?: string;
history?: string[];
isImportedFromCode?: boolean;
// isImageGenerationEnabled: boolean; // TODO: Merge with Settings type in types.ts
}
export function generateCode( export function generateCode(
wsRef: React.MutableRefObject<WebSocket | null>, wsRef: React.MutableRefObject<WebSocket | null>,
params: CodeGenerationParams, params: FullGenerationSettings,
onChange: (chunk: string) => void, onChange: (chunk: string) => void,
onSetCode: (code: string) => void, onSetCode: (code: string) => void,
onStatusUpdate: (status: string) => void, onStatusUpdate: (status: string) => void,
onCancel: () => void,
onComplete: () => void onComplete: () => void
) { ) {
const wsUrl = `${WS_BACKEND_URL}/generate-code`; const wsUrl = `${WS_BACKEND_URL}/generate-code`;
@ -47,15 +40,18 @@ export function generateCode(
toast.error(response.value); toast.error(response.value);
} }
}); });
ws.addEventListener("close", (event) => { ws.addEventListener("close", (event) => {
console.log("Connection closed", event.code, event.reason); console.log("Connection closed", event.code, event.reason);
if (event.code === USER_CLOSE_WEB_SOCKET_CODE) { if (event.code === USER_CLOSE_WEB_SOCKET_CODE) {
toast.success(STOP_MESSAGE); toast.success(CANCEL_MESSAGE);
onCancel();
} else if (event.code !== 1000) { } else if (event.code !== 1000) {
console.error("WebSocket error code", event); console.error("WebSocket error code", event);
toast.error(ERROR_MESSAGE); toast.error(ERROR_MESSAGE);
} else {
onComplete();
} }
onComplete();
}); });
ws.addEventListener("error", (error) => { ws.addEventListener("error", (error) => {

View File

@ -28,3 +28,13 @@ export enum AppState {
CODING = "CODING", CODING = "CODING",
CODE_READY = "CODE_READY", CODE_READY = "CODE_READY",
} }
export interface CodeGenerationParams {
generationType: "create" | "update";
image: string;
resultImage?: string;
history?: string[];
isImportedFromCode?: boolean;
}
export type FullGenerationSettings = CodeGenerationParams & Settings;