diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 396b3e8..654acf2 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -80,10 +80,12 @@ function App() { // Initial version creation function doCreate(referenceImages: string[]) { setReferenceImages(referenceImages); - doGenerateCode({ - generationType: "create", - image: referenceImages[0], - }); + if (referenceImages.length > 0) { + doGenerateCode({ + generationType: "create", + image: referenceImages[0], + }); + } } // Subsequent updates diff --git a/frontend/src/components/ImageUpload.tsx b/frontend/src/components/ImageUpload.tsx index 16b5373..2f0b059 100644 --- a/frontend/src/components/ImageUpload.tsx +++ b/frontend/src/components/ImageUpload.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useMemo } from "react"; +import { useState, useEffect, useMemo, useCallback } from "react"; import { useDropzone } from "react-dropzone"; // import { PromptImage } from "../../../types"; import { toast } from "react-hot-toast"; @@ -80,7 +80,7 @@ function ImageUpload({ setReferenceImages }: Props) { setReferenceImages(dataUrls.map((dataUrl) => dataUrl as string)); }) .catch((error) => { - // TODO: Display error to user + toast.error("Error reading files" + error); console.error("Error reading files:", error); }); }, @@ -89,6 +89,38 @@ function ImageUpload({ setReferenceImages }: Props) { }, }); + const pasteEvent = useCallback( + (event: ClipboardEvent) => { + const clipboardData = event.clipboardData; + if (!clipboardData) return; + + const items = clipboardData.items; + const files = []; + for (let i = 0; i < items.length; i++) { + const file = items[i].getAsFile(); + if (file && file.type.startsWith("image/")) { + files.push(file); + } + } + + // Convert images to data URLs and set the prompt images state + Promise.all(files.map((file) => fileToDataURL(file))) + .then((dataUrls) => { + setReferenceImages(dataUrls.map((dataUrl) => dataUrl as string)); + }) + .catch((error) => { + // TODO: Display error to user + console.error("Error reading files:", error); + }); + }, + [setReferenceImages] + ); + + // TODO: Make sure we don't listen to paste events in text input components + useEffect(() => { + window.addEventListener("paste", pasteEvent); + }, [pasteEvent]); + useEffect(() => { return () => files.forEach((file) => URL.revokeObjectURL(file.preview)); }, [files]); // Added files as a dependency @@ -108,7 +140,7 @@ function ImageUpload({ setReferenceImages }: Props) { {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
-

Drop a screenshot here, or click to select

+

Drop a screenshot here, paste from clipboard, or click to select

);