diff --git a/src/components/PromptForm/MobilePromptForm.tsx b/src/components/PromptForm/MobilePromptForm.tsx index 8edc5ae1..ae84a038 100644 --- a/src/components/PromptForm/MobilePromptForm.tsx +++ b/src/components/PromptForm/MobilePromptForm.tsx @@ -1,18 +1,19 @@ import { FormEvent, KeyboardEvent, useEffect, useState, type RefObject } from "react"; -import { Box, chakra, Flex } from "@chakra-ui/react"; +import { Box, chakra, Flex, Image, CloseButton } from "@chakra-ui/react"; import AutoResizingTextarea from "../AutoResizingTextarea"; import { useSettings } from "../../hooks/use-settings"; import OptionsButton from "../OptionsButton"; import MicIcon from "./MicIcon"; import { isTranscriptionSupported } from "../../lib/speech-recognition"; +import { useModels } from "../../hooks/use-models"; import PromptSendButton from "./PromptSendButton"; import AudioStatus from "./AudioStatus"; import { useKeyDownHandler } from "../../hooks/use-key-down-handler"; type MobilePromptFormProps = { forkUrl: string; - onSendClick: (prompt: string) => void; + onSendClick: (prompt: string, imageUrls: string[]) => void; inputPromptRef: RefObject; isLoading: boolean; previousMessage?: string; @@ -28,11 +29,14 @@ function MobilePromptForm({ const [prompt, setPrompt] = useState(""); // Has the user started typing? const [isDirty, setIsDirty] = useState(false); - const { settings } = useSettings(); + const { models } = useModels(); + const { settings, setSettings } = useSettings(); const [isRecording, setIsRecording] = useState(false); const [isTranscribing, setIsTranscribing] = useState(false); const [recordingSeconds, setRecordingSeconds] = useState(0); const inputType = isRecording || isTranscribing ? "audio" : "text"; + // Base64 images + const [inputImageUrls, setInputImageUrls] = useState([]); // If the user clears the prompt, allow up-arrow again useEffect(() => { @@ -67,12 +71,23 @@ function MobilePromptForm({ }; }, [isRecording, recordingSeconds]); + // Update model to the supported model when inputImages is not empty + useEffect(() => { + if (inputImageUrls?.length > 0) { + const visionModel = models.find((model) => model.supportsImages); + if (visionModel && visionModel.name != settings.model.name) { + setSettings({ ...settings, model: visionModel }); + } + } + }, [inputImageUrls, models, settings, setSettings]); + // Handle prompt form submission const handlePromptSubmit = (e: FormEvent) => { e.preventDefault(); - const value = prompt.trim(); + const textValue = prompt.trim(); setPrompt(""); - onSendClick(value); + setInputImageUrls([]); + onSendClick(textValue, inputImageUrls); }; const handleMetaEnter = useKeyDownHandler({ @@ -133,16 +148,62 @@ function MobilePromptForm({ setIsTranscribing(false); // Use this transcript as our prompt - onSendClick(transcription); + onSendClick(transcription, inputImageUrls); + setInputImageUrls([]); + }; + + const handleDeleteImage = (index: number) => { + const updatedImageUrls = [...inputImageUrls]; + updatedImageUrls.splice(index, 1); + setInputImageUrls(updatedImageUrls); }; return ( - + + setInputImageUrls((prevImageUrls) => [...prevImageUrls, base64String]) + } + /> + + {inputImageUrls.map((imageUrl, index) => ( + + {`Image# + + handleDeleteImage(index)} /> + + + ))} + {inputType === "audio" ? (