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 useBrowserTabIndicator from "./hooks/useBrowserTabIndicator";
import TipLink from "./components/core/TipLink";
import SelectAndEditModeToggleButton from "./components/select-and-edit/SelectAndEditModeToggleButton";
const IS_OPENAI_DOWN = false;
@ -93,6 +94,10 @@ function App() {
selectedCodeGenerationModel !== CodeGenerationModel.GPT_4O_2024_05_13 &&
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
useBrowserTabIndicator(appState === AppState.CODING);
@ -236,7 +241,9 @@ function App() {
parentIndex: parentVersion,
code,
inputs: {
prompt: updateInstruction,
prompt: params.history
? params.history[params.history.length - 1]
: updateInstruction,
},
},
];
@ -278,7 +285,10 @@ function App() {
}
// Subsequent updates
async function doUpdate() {
async function doUpdate(
updateInstruction: string,
selectedElement?: HTMLElement
) {
if (currentVersion === null) {
toast.error(
"No current version set. Contact support or open a Github issue."
@ -296,7 +306,17 @@ function App() {
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) {
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="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 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">
<h1 className="text-2xl ">Screenshot to Code</h1>
<SettingsDialog settings={settings} setSettings={setSettings} />
@ -484,7 +501,7 @@ function App() {
/>
</div>
<Button
onClick={doUpdate}
onClick={() => doUpdate(updateInstruction)}
className="dark:text-white dark:bg-gray-700 update-btn"
>
Update
@ -497,6 +514,9 @@ function App() {
>
🔄 Regenerate
</Button>
{showSelectAndEditFeature && (
<SelectAndEditModeToggleButton />
)}
</div>
<div className="flex justify-end items-center mt-2">
<TipLink />
@ -625,10 +645,18 @@ function App() {
</div>
</div>
<TabsContent value="desktop">
<Preview code={previewCode} device="desktop" />
<Preview
code={previewCode}
device="desktop"
doUpdate={doUpdate}
/>
</TabsContent>
<TabsContent value="mobile">
<Preview code={previewCode} device="mobile" />
<Preview
code={previewCode}
device="mobile"
doUpdate={doUpdate}
/>
</TabsContent>
<TabsContent value="code">
<CodeTab

View File

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

View File

@ -6,10 +6,7 @@ import { addHighlight, getAdjustedCoordinates, removeHighlight } from "./utils";
interface EditPopupProps {
event: MouseEvent | null;
inSelectAndEditMode: boolean;
doUpdate: (
selectedUpdateInstruction?: string,
selectedElement?: HTMLElement
) => void;
doUpdate: (updateInstruction: string, selectedElement?: HTMLElement) => void;
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
interface AppStore {
inSelectAndEditMode: boolean;
inputMode: "image" | "video";
toggleInSelectAndEditMode: () => void;
}
export const useStore = create<AppStore>(() => ({
export const useAppStore = create<AppStore>((set) => ({
inputMode: "image",
inSelectAndEditMode: false,
toggleInSelectAndEditMode: () =>
set((state) => ({ inSelectAndEditMode: !state.inSelectAndEditMode })),
}));