import { useForm } from "@mantine/form" import { useMutation, useQueryClient } from "@tanstack/react-query" import React from "react" import useDynamicTextareaSize from "~/hooks/useDynamicTextareaSize" import { toBase64 } from "~/libs/to-base64" import { useMessageOption } from "~/hooks/useMessageOption" import { Checkbox, Dropdown, Switch, Tooltip } from "antd" import { Image } from "antd" import { useWebUI } from "~/store/webui" import { defaultEmbeddingModelForRag } from "~/services/ollama" import { ImageIcon, MicIcon, StopCircleIcon, X } from "lucide-react" import { getVariable } from "~/utils/select-varaible" import { useTranslation } from "react-i18next" import { KnowledgeSelect } from "../Knowledge/KnowledgeSelect" import { useSpeechRecognition } from "@/hooks/useSpeechRecognition" import { PiGlobe } from "react-icons/pi" type Props = { dropedFile: File | undefined } export const PlaygroundForm = ({ dropedFile }: Props) => { const { t } = useTranslation(["playground", "common"]) const inputRef = React.useRef(null) const [typing, setTyping] = React.useState(false) const { onSubmit, selectedModel, chatMode, speechToTextLanguage, stopStreamingRequest, streaming: isSending, webSearch, setWebSearch, selectedQuickPrompt, textareaRef, setSelectedQuickPrompt, selectedKnowledge } = useMessageOption() const isMobile = () => { return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( navigator.userAgent ) } const textAreaFocus = () => { if (textareaRef.current) { if ( textareaRef.current.selectionStart === textareaRef.current.selectionEnd ) { if (!isMobile()) { textareaRef.current.focus() } else { textareaRef.current.blur() } } } } const form = useForm({ initialValues: { message: "", image: "" } }) React.useEffect(() => { textAreaFocus() }, []) 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) } } } const handlePaste = (e: React.ClipboardEvent) => { if (e.clipboardData.files.length > 0) { onInputChange(e.clipboardData.files[0]) } } React.useEffect(() => { if (dropedFile) { onInputChange(dropedFile) } }, [dropedFile]) useDynamicTextareaSize(textareaRef, form.values.message, 300) const { transcript, isListening, resetTranscript, start: startListening, stop: stopSpeechRecognition, supported: browserSupportsSpeechRecognition } = useSpeechRecognition() const { sendWhenEnter, setSendWhenEnter } = useWebUI() React.useEffect(() => { if (isListening) { form.setFieldValue("message", transcript) } }, [transcript]) React.useEffect(() => { if (selectedQuickPrompt) { const word = getVariable(selectedQuickPrompt) form.setFieldValue("message", selectedQuickPrompt) if (word) { textareaRef.current?.focus() const interval = setTimeout(() => { textareaRef.current?.setSelectionRange(word.start, word.end) setSelectedQuickPrompt(null) }, 100) return () => { clearInterval(interval) } } } }, [selectedQuickPrompt]) const queryClient = useQueryClient() const { mutateAsync: sendMessage } = useMutation({ mutationFn: onSubmit, onSuccess: () => { textAreaFocus() queryClient.invalidateQueries({ queryKey: ["fetchChatHistory"] }) }, onError: (error) => { textAreaFocus() } }) const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Process" || e.key === "229") return if ( !typing && e.key === "Enter" && !e.shiftKey && !isSending && sendWhenEnter ) { e.preventDefault() stopListening() form.onSubmit(async (value) => { if (value.message.trim().length === 0) { return } if (!selectedModel || selectedModel.length === 0) { form.setFieldError("message", t("formError.noModel")) return } if (webSearch) { 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() }) })() } } const stopListening = async () => { if (isListening) { stopSpeechRecognition() } } return (
Uploaded Image
{ stopListening() if (!selectedModel || selectedModel.length === 0) { form.setFieldError("message", t("formError.noModel")) return } if (webSearch) { 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 ">