isolate functionality
use zustand to share state rather than prop drilling isolate more functionality away into a separate component
This commit is contained in:
parent
9e2cbdce94
commit
9cfcf928d0
@ -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
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
@ -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 })),
|
||||||
}));
|
}));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user