diff --git a/backend/main.py b/backend/main.py index fc41cfd..4b75f71 100644 --- a/backend/main.py +++ b/backend/main.py @@ -70,9 +70,11 @@ async def stream_code(websocket: WebSocket): print("Received params") # Read the output settings from the request. Fall back to default if not provided. - output_settings = {"css": "tailwind"} + output_settings = {"css": "tailwind", "js": "vanilla"} if params["outputSettings"] and params["outputSettings"]["css"]: output_settings["css"] = params["outputSettings"]["css"] + if params["outputSettings"] and params["outputSettings"]["js"]: + output_settings["js"] = params["outputSettings"]["js"] print("Using output settings:", output_settings) # Get the OpenAI API key from the request. Fall back to environment variable if not provided. diff --git a/backend/prompts.py b/backend/prompts.py index 7a4b426..18dde89 100644 --- a/backend/prompts.py +++ b/backend/prompts.py @@ -48,6 +48,35 @@ Return only the full code in tags. Do not include markdown "```" or "```html" at the start or end. """ +REACT_TAILWIND_SYSTEM_PROMPT = """ +You are an expert React/Tailwind developer +You take screenshots of a reference web page from the user, and then build single page apps +using React and Tailwind CSS. +You might also be given a screenshot(The second image) of a web page that you have already built, and asked to +update it to look more like the reference image(The first image). + +- Make sure the app looks exactly like the screenshot. +- Pay close attention to background color, text color, font size, font family, +padding, margin, border, etc. Match the colors and sizes exactly. +- Use the exact text from the screenshot. +- Do not add comments in the code such as "" and "" in place of writing the full code. WRITE THE FULL CODE. +- Repeat elements as needed to match the screenshot. For example, if there are 15 items, the code should have 15 items. DO NOT LEAVE comments like "" or bad things will happen. +- For images, use placeholder images from https://placehold.co and include a detailed description of the image in the alt text so that an image generation AI can generate the image later. + +In terms of libraries, + +- Use these script to include React so that it can run on a standalone page: + + + +- Use this script to include Tailwind: +- You can use Google Fonts +- Font Awesome for icons: + +Return only the full code in tags. +Do not include markdown "```" or "```html" at the start or end. +""" + USER_PROMPT = """ Generate code for a web page that looks exactly like this. """ @@ -58,6 +87,8 @@ def assemble_prompt(image_data_url, output_settings: dict, result_image_data_url system_content = TAILWIND_SYSTEM_PROMPT if output_settings["css"] == "bootstrap": system_content = BOOTSTRAP_SYSTEM_PROMPT + if output_settings["js"] == "react": + system_content = REACT_TAILWIND_SYSTEM_PROMPT user_content = [ { diff --git a/backend/test_prompts.py b/backend/test_prompts.py index f6c9526..2eaaaf4 100644 --- a/backend/test_prompts.py +++ b/backend/test_prompts.py @@ -50,14 +50,48 @@ Return only the full code in tags. Do not include markdown "```" or "```html" at the start or end. """ +REACT_TAILWIND_SYSTEM_PROMPT = """ +You are an expert React/Tailwind developer +You take screenshots of a reference web page from the user, and then build single page apps +using React and Tailwind CSS. +You might also be given a screenshot(The second image) of a web page that you have already built, and asked to +update it to look more like the reference image(The first image). + +- Make sure the app looks exactly like the screenshot. +- Pay close attention to background color, text color, font size, font family, +padding, margin, border, etc. Match the colors and sizes exactly. +- Use the exact text from the screenshot. +- Do not add comments in the code such as "" and "" in place of writing the full code. WRITE THE FULL CODE. +- Repeat elements as needed to match the screenshot. For example, if there are 15 items, the code should have 15 items. DO NOT LEAVE comments like "" or bad things will happen. +- For images, use placeholder images from https://placehold.co and include a detailed description of the image in the alt text so that an image generation AI can generate the image later. + +In terms of libraries, + +- Use these script to include React so that it can run on a standalone page: + + + +- Use this script to include Tailwind: +- You can use Google Fonts +- Font Awesome for icons: + +Return only the full code in tags. +Do not include markdown "```" or "```html" at the start or end. +""" + def test_prompts(): tailwind_prompt = assemble_prompt( - "image_data_url", {"css": "tailwind"}, "result_image_data_url" + "image_data_url", {"css": "tailwind", "js": "vanilla"}, "result_image_data_url" ) assert tailwind_prompt[0]["content"] == TAILWIND_SYSTEM_PROMPT bootstrap_prompt = assemble_prompt( - "image_data_url", {"css": "bootstrap"}, "result_image_data_url" + "image_data_url", {"css": "bootstrap", "js": "vanilla"}, "result_image_data_url" ) assert bootstrap_prompt[0]["content"] == BOOTSTRAP_SYSTEM_PROMPT + + react_tailwind_prompt = assemble_prompt( + "image_data_url", {"css": "tailwind", "js": "react"}, "result_image_data_url" + ) + assert react_tailwind_prompt[0]["content"] == REACT_TAILWIND_SYSTEM_PROMPT diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b40d8a9..5f65f96 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -24,6 +24,7 @@ import { AppState, CSSOption, OutputSettings, + JSFrameworkOption, } from "./types"; import { IS_RUNNING_ON_CLOUD } from "./config"; import { PicoBadge } from "./components/PicoBadge"; @@ -55,6 +56,7 @@ function App() { ); const [outputSettings, setOutputSettings] = useState({ css: CSSOption.TAILWIND, + js: JSFrameworkOption.VANILLA, }); const [shouldIncludeResultImage, setShouldIncludeResultImage] = useState(false); diff --git a/frontend/src/components/OutputSettingsSection.tsx b/frontend/src/components/OutputSettingsSection.tsx index 5806c34..32f9979 100644 --- a/frontend/src/components/OutputSettingsSection.tsx +++ b/frontend/src/components/OutputSettingsSection.tsx @@ -5,13 +5,15 @@ import { SelectItem, SelectTrigger, } from "./ui/select"; -import { CSSOption, OutputSettings } from "../types"; +import { CSSOption, JSFrameworkOption, OutputSettings } from "../types"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "./ui/accordion"; +import { capitalize } from "../lib/utils"; +import toast from "react-hot-toast"; function displayCSSOption(option: CSSOption) { switch (option) { @@ -37,25 +39,53 @@ function convertStringToCSSOption(option: string) { interface Props { outputSettings: OutputSettings; - setOutputSettings: (outputSettings: OutputSettings) => void; + setOutputSettings: React.Dispatch>; } function OutputSettingsSection({ outputSettings, setOutputSettings }: Props) { + const onCSSValueChange = (value: string) => { + setOutputSettings((prev) => { + if (prev.js === JSFrameworkOption.REACT) { + if (value !== CSSOption.TAILWIND) { + toast.error( + "React only supports Tailwind CSS. Change JS framework to Vanilla to use Bootstrap." + ); + } + return { + css: CSSOption.TAILWIND, + js: JSFrameworkOption.REACT, + }; + } else { + return { + ...prev, + css: convertStringToCSSOption(value), + }; + } + }); + }; + + const onJsFrameworkChange = (value: string) => { + if (value === JSFrameworkOption.REACT) { + setOutputSettings(() => ({ + css: CSSOption.TAILWIND, + js: value as JSFrameworkOption, + })); + } else { + setOutputSettings((prev) => ({ + ...prev, + js: value as JSFrameworkOption, + })); + } + }; + return ( Output Settings - -
+ +
CSS - {displayCSSOption(outputSettings.css)} @@ -67,6 +97,25 @@ function OutputSettingsSection({ outputSettings, setOutputSettings }: Props) {
+
+ JS Framework + +
diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 9c30d0b..964ad5a 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -8,8 +8,15 @@ export enum CSSOption { BOOTSTRAP = "bootstrap", } +export enum JSFrameworkOption { + VANILLA = "vanilla", + REACT = "react", + VUE = "vue", +} + export interface OutputSettings { css: CSSOption; + js: JSFrameworkOption; } export interface Settings {