import { useForm } from "@mantine/form" import { useMutation } from "@tanstack/react-query" import React from "react" import useDynamicTextareaSize from "~/hooks/useDynamicTextareaSize" import { useMessage } from "~/hooks/useMessage" import { toBase64 } from "~/libs/to-base64" import { Checkbox, Dropdown, Image, Tooltip } from "antd" import { useSpeechRecognition } from "~/hooks/useSpeechRecognition" import { useWebUI } from "~/store/webui" import { defaultEmbeddingModelForRag } from "~/services/ollama" import { ImageIcon, MicIcon, StopCircleIcon, X } from "lucide-react" import { useTranslation } from "react-i18next" import { ModelSelect } from "@/components/Common/ModelSelect" type Props = { dropedFile: File | undefined } export const SidepanelForm = ({ dropedFile }: Props) => { const textareaRef = React.useRef(null) const inputRef = React.useRef(null) const { sendWhenEnter, setSendWhenEnter } = useWebUI() const [typing, setTyping] = React.useState(false) const { t } = useTranslation(["playground", "common"]) const textAreaFocus = () => { if (textareaRef.current) { textareaRef.current.focus() } } const form = useForm({ initialValues: { message: "", image: "" } }) const onInputChange = async ( e: React.ChangeEvent | File ) => { if (e instanceof File) { const base64 = await toBase64(e) form.setFieldValue("image", base64) } else { if (e.target.files) { const base64 = await toBase64(e.target.files[0]) form.setFieldValue("image", base64) } } } React.useEffect(() => { if (dropedFile) { onInputChange(dropedFile) } }, [dropedFile]) useDynamicTextareaSize(textareaRef, form.values.message, 120) const { onSubmit, selectedModel, chatMode, speechToTextLanguage, stopStreamingRequest } = useMessage() const { isListening, start, stop, transcript } = useSpeechRecognition() React.useEffect(() => { if (isListening) { form.setFieldValue("message", transcript) } }, [transcript]) const { mutateAsync: sendMessage, isPending: isSending } = useMutation({ mutationFn: onSubmit, onSuccess: () => { textAreaFocus() }, onError: (error) => { textAreaFocus() } }) const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Process" || e.key === "229") return if ( e.key === "Enter" && !e.shiftKey && !isSending && sendWhenEnter && !typing ) { e.preventDefault() form.onSubmit(async (value) => { if (value.message.trim().length === 0) { return } if (!selectedModel || selectedModel.length === 0) { form.setFieldError("message", t("formError.noModel")) return } if (chatMode === "rag") { const defaultEM = await defaultEmbeddingModelForRag() if (!defaultEM) { form.setFieldError("message", t("formError.noEmbeddingModel")) return } } form.reset() textAreaFocus() await sendMessage({ image: value.image, message: value.message.trim() }) })() } } return (
Uploaded Image
{ if (!selectedModel || selectedModel.length === 0) { form.setFieldError("message", t("formError.noModel")) return } if (chatMode === "rag") { const defaultEM = await defaultEmbeddingModelForRag() if (!defaultEM) { form.setFieldError("message", t("formError.noEmbeddingModel")) return } } form.reset() textAreaFocus() await sendMessage({ image: value.image, message: value.message.trim() }) })} className="shrink-0 flex-grow flex flex-col items-center ">