Merge branch 'select-and-edit' into hosted
This commit is contained in:
commit
7d4d62aa41
@ -48,6 +48,7 @@ import useBrowserTabIndicator from "./hooks/useBrowserTabIndicator";
|
|||||||
import TipLink from "./components/core/TipLink";
|
import TipLink from "./components/core/TipLink";
|
||||||
import FeedbackCallNote from "./components/user-feedback/FeedbackCallNote";
|
import FeedbackCallNote from "./components/user-feedback/FeedbackCallNote";
|
||||||
import SelectAndEditModeToggleButton from "./components/select-and-edit/SelectAndEditModeToggleButton";
|
import SelectAndEditModeToggleButton from "./components/select-and-edit/SelectAndEditModeToggleButton";
|
||||||
|
import { useAppStore } from "./store/app-store";
|
||||||
|
|
||||||
const IS_OPENAI_DOWN = false;
|
const IS_OPENAI_DOWN = false;
|
||||||
|
|
||||||
@ -71,6 +72,8 @@ function App({ navbarComponent }: Props) {
|
|||||||
const { getToken } = useAuth();
|
const { getToken } = useAuth();
|
||||||
const subscriberTier = useStore((state) => state.subscriberTier);
|
const subscriberTier = useStore((state) => state.subscriberTier);
|
||||||
|
|
||||||
|
const { disableInSelectAndEditMode } = useAppStore();
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
const [settings, setSettings] = usePersistedState<Settings>(
|
const [settings, setSettings] = usePersistedState<Settings>(
|
||||||
{
|
{
|
||||||
@ -174,6 +177,7 @@ function App({ navbarComponent }: Props) {
|
|||||||
setAppHistory([]);
|
setAppHistory([]);
|
||||||
setCurrentVersion(null);
|
setCurrentVersion(null);
|
||||||
setShouldIncludeResultImage(false);
|
setShouldIncludeResultImage(false);
|
||||||
|
disableInSelectAndEditMode();
|
||||||
};
|
};
|
||||||
|
|
||||||
const regenerate = () => {
|
const regenerate = () => {
|
||||||
|
|||||||
@ -2,7 +2,6 @@ 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;
|
||||||
@ -11,8 +10,6 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Preview({ code, device, doUpdate }: 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.
|
||||||
@ -51,12 +48,7 @@ function Preview({ code, device, doUpdate }: Props) {
|
|||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
></iframe>
|
></iframe>
|
||||||
<EditPopup
|
<EditPopup event={clickEvent} iframeRef={iframeRef} doUpdate={doUpdate} />
|
||||||
event={clickEvent}
|
|
||||||
iframeRef={iframeRef}
|
|
||||||
inSelectAndEditMode={inSelectAndEditMode}
|
|
||||||
doUpdate={doUpdate}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,33 +2,40 @@ import React, { useEffect, useRef, useState } from "react";
|
|||||||
import { Textarea } from "../ui/textarea";
|
import { Textarea } from "../ui/textarea";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
import { addHighlight, getAdjustedCoordinates, removeHighlight } from "./utils";
|
import { addHighlight, getAdjustedCoordinates, removeHighlight } from "./utils";
|
||||||
|
import { useAppStore } from "../../store/app-store";
|
||||||
|
|
||||||
interface EditPopupProps {
|
interface EditPopupProps {
|
||||||
event: MouseEvent | null;
|
event: MouseEvent | null;
|
||||||
inSelectAndEditMode: boolean;
|
|
||||||
doUpdate: (updateInstruction: string, selectedElement?: HTMLElement) => void;
|
|
||||||
iframeRef: React.RefObject<HTMLIFrameElement>;
|
iframeRef: React.RefObject<HTMLIFrameElement>;
|
||||||
|
doUpdate: (updateInstruction: string, selectedElement?: HTMLElement) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditPopup: React.FC<EditPopupProps> = ({
|
const EditPopup: React.FC<EditPopupProps> = ({
|
||||||
event,
|
event,
|
||||||
inSelectAndEditMode,
|
|
||||||
doUpdate,
|
|
||||||
iframeRef,
|
iframeRef,
|
||||||
|
doUpdate,
|
||||||
}) => {
|
}) => {
|
||||||
// Edit state
|
// App state
|
||||||
const [selectedElement, setSelectedElement] = useState<
|
const { inSelectAndEditMode } = useAppStore();
|
||||||
HTMLElement | undefined
|
|
||||||
>(undefined);
|
// Create a wrapper ref to store inSelectAndEditMode so the value is not stale
|
||||||
const [updateText, setUpdateText] = useState("");
|
// in a event listener
|
||||||
|
const inSelectAndEditModeRef = useRef(inSelectAndEditMode);
|
||||||
|
|
||||||
|
// Update the ref whenever the state changes
|
||||||
|
useEffect(() => {
|
||||||
|
inSelectAndEditModeRef.current = inSelectAndEditMode;
|
||||||
|
}, [inSelectAndEditMode]);
|
||||||
|
|
||||||
// Popup state
|
// Popup state
|
||||||
const [popupVisible, setPopupVisible] = useState(false);
|
const [popupVisible, setPopupVisible] = useState(false);
|
||||||
const [popupPosition, setPopupPosition] = useState({ x: 0, y: 0 });
|
const [popupPosition, setPopupPosition] = useState({ x: 0, y: 0 });
|
||||||
|
|
||||||
// Create a wrapper ref to store inSelectAndEditMode so the value is not stale
|
// Edit state
|
||||||
// in a event listener
|
const [selectedElement, setSelectedElement] = useState<
|
||||||
const inSelectAndEditModeRef = useRef(inSelectAndEditMode);
|
HTMLElement | undefined
|
||||||
|
>(undefined);
|
||||||
|
const [updateText, setUpdateText] = useState("");
|
||||||
|
|
||||||
// Textarea ref for focusing
|
// Textarea ref for focusing
|
||||||
const textareaRef = useRef<HTMLTextAreaElement | null>(null);
|
const textareaRef = useRef<HTMLTextAreaElement | null>(null);
|
||||||
@ -47,11 +54,6 @@ const EditPopup: React.FC<EditPopupProps> = ({
|
|||||||
setPopupVisible(false);
|
setPopupVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the ref whenever the state changes
|
|
||||||
useEffect(() => {
|
|
||||||
inSelectAndEditModeRef.current = inSelectAndEditMode;
|
|
||||||
}, [inSelectAndEditMode]);
|
|
||||||
|
|
||||||
// Remove highlight and reset state when not in select and edit mode
|
// Remove highlight and reset state when not in select and edit mode
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!inSelectAndEditMode) {
|
if (!inSelectAndEditMode) {
|
||||||
@ -103,6 +105,15 @@ const EditPopup: React.FC<EditPopupProps> = ({
|
|||||||
textareaRef.current?.focus();
|
textareaRef.current?.focus();
|
||||||
}, [event, iframeRef]);
|
}, [event, iframeRef]);
|
||||||
|
|
||||||
|
// Focus the textarea when the popup is visible (we can't do this only when handling the click event
|
||||||
|
// because the textarea is not rendered yet)
|
||||||
|
// We need to also do it in the click event because popupVisible doesn't change values in that event
|
||||||
|
useEffect(() => {
|
||||||
|
if (popupVisible) {
|
||||||
|
textareaRef.current?.focus();
|
||||||
|
}
|
||||||
|
}, [popupVisible]);
|
||||||
|
|
||||||
if (!popupVisible) return;
|
if (!popupVisible) return;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -115,6 +126,12 @@ const EditPopup: React.FC<EditPopupProps> = ({
|
|||||||
value={updateText}
|
value={updateText}
|
||||||
onChange={(e) => setUpdateText(e.target.value)}
|
onChange={(e) => setUpdateText(e.target.value)}
|
||||||
placeholder="Tell the AI what to change about this element..."
|
placeholder="Tell the AI what to change about this element..."
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
onUpdate(updateText);
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="flex justify-end mt-2">
|
<div className="flex justify-end mt-2">
|
||||||
<Button onClick={() => onUpdate(updateText)}>Update</Button>
|
<Button onClick={() => onUpdate(updateText)}>Update</Button>
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { GiClick } from "react-icons/gi";
|
||||||
import { useAppStore } from "../../store/app-store";
|
import { useAppStore } from "../../store/app-store";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
|
|
||||||
@ -10,7 +11,10 @@ function SelectAndEditModeToggleButton() {
|
|||||||
className="flex items-center gap-x-2 dark:text-white dark:bg-gray-700 regenerate-btn"
|
className="flex items-center gap-x-2 dark:text-white dark:bg-gray-700 regenerate-btn"
|
||||||
variant={inSelectAndEditMode ? "destructive" : "default"}
|
variant={inSelectAndEditMode ? "destructive" : "default"}
|
||||||
>
|
>
|
||||||
{inSelectAndEditMode ? "Exit selection mode" : "🖱️ Select and Edit"}
|
<GiClick className="text-lg" />
|
||||||
|
<span>
|
||||||
|
{inSelectAndEditMode ? "Exit selection mode" : "Select and update"}
|
||||||
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ interface AppStore {
|
|||||||
inSelectAndEditMode: boolean;
|
inSelectAndEditMode: boolean;
|
||||||
inputMode: "image" | "video";
|
inputMode: "image" | "video";
|
||||||
toggleInSelectAndEditMode: () => void;
|
toggleInSelectAndEditMode: () => void;
|
||||||
|
disableInSelectAndEditMode: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useAppStore = create<AppStore>((set) => ({
|
export const useAppStore = create<AppStore>((set) => ({
|
||||||
@ -12,4 +13,5 @@ export const useAppStore = create<AppStore>((set) => ({
|
|||||||
inSelectAndEditMode: false,
|
inSelectAndEditMode: false,
|
||||||
toggleInSelectAndEditMode: () =>
|
toggleInSelectAndEditMode: () =>
|
||||||
set((state) => ({ inSelectAndEditMode: !state.inSelectAndEditMode })),
|
set((state) => ({ inSelectAndEditMode: !state.inSelectAndEditMode })),
|
||||||
|
disableInSelectAndEditMode: () => set({ inSelectAndEditMode: false }),
|
||||||
}));
|
}));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user