import { useForm } from "@mantine/form"
import { useMutation, useQueryClient } from "@tanstack/react-query"
import React, { useMemo } from "react"
import useDynamicTextareaSize from "~/hooks/useDynamicTextareaSize"
import { toBase64 } from "~/libs/to-base64"
import { useMessageOption } from "~/hooks/useMessageOption"
import {
Button,
Checkbox,
Dropdown,
Image,
MenuProps,
Switch,
Tooltip
} 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-variable"
import { useTranslation } from "react-i18next"
// import { KnowledgeSelect } from "../Knowledge/KnowledgeSelect"
import { useSpeechRecognition } from "@/hooks/useSpeechRecognition"
import { PiGlobe, PiNetwork } from "react-icons/pi"
import { handleChatInputKeyDown } from "@/utils/key-down"
import { getIsSimpleInternetSearch } from "@/services/search"
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,
iodSearch,
setIodSearch,
selectedQuickPrompt,
textareaRef,
setSelectedQuickPrompt,
selectedKnowledge,
temporaryChat,
useOCR,
setUseOCR,
defaultInternetSearchOn
} = 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()
if (defaultInternetSearchOn) {
setWebSearch(true)
}
}, [])
React.useEffect(() => {
if (defaultInternetSearchOn) {
setWebSearch(true)
}
}, [defaultInternetSearchOn])
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 (import.meta.env.BROWSER !== "firefox") {
if (e.key === "Process" || e.key === "229") return
}
if (
handleChatInputKeyDown({
e,
sendWhenEnter,
typing,
isSending
})
) {
e.preventDefault()
stopListening()
form.onSubmit(async (value) => {
if (value.message.trim().length === 0 && value.image.length === 0) {
return
}
if (!selectedModel || selectedModel.length === 0) {
form.setFieldError("message", t("formError.noModel"))
return
}
if (webSearch) {
const defaultEM = await defaultEmbeddingModelForRag()
const simpleSearch = await getIsSimpleInternetSearch()
if (!defaultEM && !simpleSearch) {
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()
}
}
const iodSearchItems = useMemo(() => {
return [
{
key: 0,
label: (
{
setIodSearch(true)
}}>
开
输出带数联网的回答
)
},
{
key: 1,
label: (
{
setIodSearch(false)
}}>
关闭
快速直接回答
)
}
]
}, [iodSearch])
return (
{" "}
{form.errors.message && (
{form.errors.message}
)}
)
}