isolate functionality

use zustand to share state rather than prop drilling

isolate more functionality away into a separate component
This commit is contained in:
Abi Raja 2024-05-30 09:51:44 -04:00
parent 9e2cbdce94
commit 9cfcf928d0
5 changed files with 68 additions and 21 deletions

View File

@ -40,6 +40,7 @@ import ModelSettingsSection from "./components/ModelSettingsSection";
import { extractHtml } from "./components/preview/extractHtml"; import { extractHtml } from "./components/preview/extractHtml";
import useBrowserTabIndicator from "./hooks/useBrowserTabIndicator"; import useBrowserTabIndicator from "./hooks/useBrowserTabIndicator";
import TipLink from "./components/core/TipLink"; import TipLink from "./components/core/TipLink";
import SelectAndEditModeToggleButton from "./components/select-and-edit/SelectAndEditModeToggleButton";
const IS_OPENAI_DOWN = false; const IS_OPENAI_DOWN = false;
@ -93,6 +94,10 @@ function App() {
selectedCodeGenerationModel !== CodeGenerationModel.GPT_4O_2024_05_13 && selectedCodeGenerationModel !== CodeGenerationModel.GPT_4O_2024_05_13 &&
appState === AppState.INITIAL; appState === AppState.INITIAL;
const showSelectAndEditFeature =
selectedCodeGenerationModel === CodeGenerationModel.GPT_4O_2024_05_13 &&
settings.generatedCodeConfig === Stack.HTML_TAILWIND;
// Indicate coding state using the browser tab's favicon and title // Indicate coding state using the browser tab's favicon and title
useBrowserTabIndicator(appState === AppState.CODING); useBrowserTabIndicator(appState === AppState.CODING);
@ -236,7 +241,9 @@ function App() {
parentIndex: parentVersion, parentIndex: parentVersion,
code, code,
inputs: { inputs: {
prompt: updateInstruction, prompt: params.history
? params.history[params.history.length - 1]
: updateInstruction,
}, },
}, },
]; ];
@ -278,7 +285,10 @@ function App() {
} }
// Subsequent updates // Subsequent updates
async function doUpdate() { async function doUpdate(
updateInstruction: string,
selectedElement?: HTMLElement
) {
if (currentVersion === null) { if (currentVersion === null) {
toast.error( toast.error(
"No current version set. Contact support or open a Github issue." "No current version set. Contact support or open a Github issue."
@ -296,7 +306,17 @@ function App() {
return; return;
} }
const updatedHistory = [...historyTree, updateInstruction]; let modifiedUpdateInstruction = updateInstruction;
// Send in a reference to the selected element if it exists
if (selectedElement) {
modifiedUpdateInstruction =
updateInstruction +
" referring to this element specifically: " +
selectedElement.outerHTML;
}
const updatedHistory = [...historyTree, modifiedUpdateInstruction];
if (shouldIncludeResultImage) { if (shouldIncludeResultImage) {
const resultImage = await takeScreenshot(); const resultImage = await takeScreenshot();
@ -378,10 +398,7 @@ function App() {
/> />
)} )}
<div className="lg:fixed lg:inset-y-0 lg:z-40 lg:flex lg:w-96 lg:flex-col"> <div className="lg:fixed lg:inset-y-0 lg:z-40 lg:flex lg:w-96 lg:flex-col">
<div <div className="flex grow flex-col gap-y-2 overflow-y-auto border-r border-gray-200 bg-white px-6 dark:bg-zinc-950 dark:text-white">
className="flex grow flex-col gap-y-2 overflow-y-auto border-r
border-gray-200 bg-white px-6 dark:bg-zinc-950 dark:text-white"
>
<div className="flex items-center justify-between mt-10 mb-2"> <div className="flex items-center justify-between mt-10 mb-2">
<h1 className="text-2xl ">Screenshot to Code</h1> <h1 className="text-2xl ">Screenshot to Code</h1>
<SettingsDialog settings={settings} setSettings={setSettings} /> <SettingsDialog settings={settings} setSettings={setSettings} />
@ -484,7 +501,7 @@ function App() {
/> />
</div> </div>
<Button <Button
onClick={doUpdate} onClick={() => doUpdate(updateInstruction)}
className="dark:text-white dark:bg-gray-700 update-btn" className="dark:text-white dark:bg-gray-700 update-btn"
> >
Update Update
@ -497,6 +514,9 @@ function App() {
> >
🔄 Regenerate 🔄 Regenerate
</Button> </Button>
{showSelectAndEditFeature && (
<SelectAndEditModeToggleButton />
)}
</div> </div>
<div className="flex justify-end items-center mt-2"> <div className="flex justify-end items-center mt-2">
<TipLink /> <TipLink />
@ -625,10 +645,18 @@ function App() {
</div> </div>
</div> </div>
<TabsContent value="desktop"> <TabsContent value="desktop">
<Preview code={previewCode} device="desktop" /> <Preview
code={previewCode}
device="desktop"
doUpdate={doUpdate}
/>
</TabsContent> </TabsContent>
<TabsContent value="mobile"> <TabsContent value="mobile">
<Preview code={previewCode} device="mobile" /> <Preview
code={previewCode}
device="mobile"
doUpdate={doUpdate}
/>
</TabsContent> </TabsContent>
<TabsContent value="code"> <TabsContent value="code">
<CodeTab <CodeTab

View File

@ -2,18 +2,17 @@ import { useEffect, useRef, useState } from "react";
import classNames from "classnames"; import classNames from "classnames";
import useThrottle from "../hooks/useThrottle"; import useThrottle from "../hooks/useThrottle";
import EditPopup from "./select-and-edit/EditPopup"; import EditPopup from "./select-and-edit/EditPopup";
import { useAppStore } from "../store/app-store";
interface Props { interface Props {
code: string; code: string;
device: "mobile" | "desktop"; device: "mobile" | "desktop";
doUpdate: ( doUpdate: (updateInstruction: string, selectedElement?: HTMLElement) => void;
selectedUpdateInstruction?: string,
selectedElement?: HTMLElement
) => void;
inSelectAndEditMode: boolean;
} }
function Preview({ code, device, doUpdate, inSelectAndEditMode }: Props) { function Preview({ code, device, doUpdate }: Props) {
const { inSelectAndEditMode } = useAppStore();
const iframeRef = useRef<HTMLIFrameElement | null>(null); const iframeRef = useRef<HTMLIFrameElement | null>(null);
// Don't update code more often than every 200ms. // Don't update code more often than every 200ms.

View File

@ -6,10 +6,7 @@ import { addHighlight, getAdjustedCoordinates, removeHighlight } from "./utils";
interface EditPopupProps { interface EditPopupProps {
event: MouseEvent | null; event: MouseEvent | null;
inSelectAndEditMode: boolean; inSelectAndEditMode: boolean;
doUpdate: ( doUpdate: (updateInstruction: string, selectedElement?: HTMLElement) => void;
selectedUpdateInstruction?: string,
selectedElement?: HTMLElement
) => void;
iframeRef: React.RefObject<HTMLIFrameElement>; iframeRef: React.RefObject<HTMLIFrameElement>;
} }

View File

@ -0,0 +1,18 @@
import { useAppStore } from "../../store/app-store";
import { Button } from "../ui/button";
function SelectAndEditModeToggleButton() {
const { inSelectAndEditMode, toggleInSelectAndEditMode } = useAppStore();
return (
<Button
onClick={toggleInSelectAndEditMode}
className="flex items-center gap-x-2 dark:text-white dark:bg-gray-700 regenerate-btn"
variant={inSelectAndEditMode ? "destructive" : "default"}
>
{inSelectAndEditMode ? "Exit selection mode" : "🖱️ Select and Edit"}
</Button>
);
}
export default SelectAndEditModeToggleButton;

View File

@ -2,9 +2,14 @@ import { create } from "zustand";
// Store for app-wide state // Store for app-wide state
interface AppStore { interface AppStore {
inSelectAndEditMode: boolean;
inputMode: "image" | "video"; inputMode: "image" | "video";
toggleInSelectAndEditMode: () => void;
} }
export const useStore = create<AppStore>(() => ({ export const useAppStore = create<AppStore>((set) => ({
inputMode: "image", inputMode: "image",
inSelectAndEditMode: false,
toggleInSelectAndEditMode: () =>
set((state) => ({ inSelectAndEditMode: !state.inSelectAndEditMode })),
})); }));