/* eslint-disable @typescript-eslint/no-explicit-any */ import { useEffect, useRef, useState } from 'react' import { MessageStatus } from '@janhq/core' import { TextArea, Button, Tooltip, useClickOutside, Badge } from '@janhq/joi' import { useAtom, useAtomValue } from 'jotai' import { FileTextIcon, ImageIcon, StopCircle, PaperclipIcon, SettingsIcon, ChevronUpIcon, Settings2Icon, } from 'lucide-react' import { twMerge } from 'tailwind-merge' import ModelDropdown from '@/containers/ModelDropdown' import { currentPromptAtom, fileUploadAtom } from '@/containers/Providers/Jotai' import { useActiveModel } from '@/hooks/useActiveModel' import useSendChatMessage from '@/hooks/useSendChatMessage' import FileUploadPreview from '../FileUploadPreview' import ImageUploadPreview from '../ImageUploadPreview' import { showRightPanelAtom } from '@/helpers/atoms/App.atom' import { experimentalFeatureEnabledAtom } from '@/helpers/atoms/AppConfig.atom' import { getCurrentChatMessagesAtom } from '@/helpers/atoms/ChatMessage.atom' import { spellCheckAtom } from '@/helpers/atoms/Setting.atom' import { activeSettingInputBoxAtom, activeThreadAtom, getActiveThreadIdAtom, isGeneratingResponseAtom, threadStatesAtom, waitingToSendMessage, } from '@/helpers/atoms/Thread.atom' import { activeTabThreadRightPanelAtom } from '@/helpers/atoms/ThreadRightPanel.atom' const ChatInput = () => { const activeThread = useAtomValue(activeThreadAtom) const { stateModel } = useActiveModel() const messages = useAtomValue(getCurrentChatMessagesAtom) // const [activeSetting, setActiveSetting] = useState(false) const spellCheck = useAtomValue(spellCheckAtom) const [currentPrompt, setCurrentPrompt] = useAtom(currentPromptAtom) const [activeSettingInputBox, setActiveSettingInputBox] = useAtom( activeSettingInputBoxAtom ) const { sendChatMessage } = useSendChatMessage() const activeThreadId = useAtomValue(getActiveThreadIdAtom) const [isWaitingToSend, setIsWaitingToSend] = useAtom(waitingToSendMessage) const [fileUpload, setFileUpload] = useAtom(fileUploadAtom) const [showAttacmentMenus, setShowAttacmentMenus] = useState(false) const textareaRef = useRef(null) const fileInputRef = useRef(null) const imageInputRef = useRef(null) const experimentalFeature = useAtomValue(experimentalFeatureEnabledAtom) const isGeneratingResponse = useAtomValue(isGeneratingResponseAtom) const threadStates = useAtomValue(threadStatesAtom) const { stopInference } = useActiveModel() const [activeTabThreadRightPanel, setActiveTabThreadRightPanel] = useAtom( activeTabThreadRightPanelAtom ) const isStreamingResponse = Object.values(threadStates).some( (threadState) => threadState.waitingForResponse ) const onPromptChange = (e: React.ChangeEvent) => { setCurrentPrompt(e.target.value) } const refAttachmentMenus = useClickOutside(() => setShowAttacmentMenus(false)) const [showRightPanel, setShowRightPanel] = useAtom(showRightPanelAtom) useEffect(() => { if (isWaitingToSend && activeThreadId) { setIsWaitingToSend(false) sendChatMessage(currentPrompt) } }, [ activeThreadId, isWaitingToSend, currentPrompt, setIsWaitingToSend, sendChatMessage, ]) useEffect(() => { if (textareaRef.current) { textareaRef.current.focus() } }, [activeThreadId]) useEffect(() => { if (textareaRef.current?.clientHeight) { textareaRef.current.style.height = activeSettingInputBox ? '100px' : '40px' textareaRef.current.style.height = textareaRef.current.scrollHeight + 'px' textareaRef.current.style.overflow = textareaRef.current.clientHeight >= 390 ? 'auto' : 'hidden' } }, [textareaRef.current?.clientHeight, currentPrompt, activeSettingInputBox]) const onKeyDown = async (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) { e.preventDefault() if (messages[messages.length - 1]?.status !== MessageStatus.Pending) sendChatMessage(currentPrompt) else onStopInferenceClick() } } const onStopInferenceClick = async () => { stopInference() } /** * Handles the change event of the extension file input element by setting the file name state. * Its to be used to display the extension file name of the selected file. * @param event - The change event object. */ const handleFileChange = (event: React.ChangeEvent) => { const file = event.target.files?.[0] if (!file) return setFileUpload([{ file: file, type: 'pdf' }]) } const handleImageChange = (event: React.ChangeEvent) => { const file = event.target.files?.[0] if (!file) return setFileUpload([{ file: file, type: 'image' }]) } const renderPreview = (fileUpload: any) => { if (fileUpload.length > 0) { if (fileUpload[0].type === 'image') { return } else { return } } } return (
{renderPreview(fileUpload)}