import { useCallback, useRef, useState, } from 'react' import Textarea from 'rc-textarea' import { useTranslation } from 'react-i18next' import Recorder from 'js-audio-recorder' import type { EnableType, OnSend, } from '../../types' import type { Theme } from '../embedded-chatbot/theme/theme-context' import type { InputForm } from '../type' import { useCheckInputsForms } from '../check-input-forms-hooks' import { useTextAreaHeight } from './hooks' import Operation from './operation' import cn from '@/utils/classnames' import { FileListInChatInput } from '@/app/components/base/file-uploader' import { useFile } from '@/app/components/base/file-uploader/hooks' import { FileContextProvider, useFileStore, } from '@/app/components/base/file-uploader/store' import VoiceInput from '@/app/components/base/voice-input' import { useToastContext } from '@/app/components/base/toast' // TODO mars // import FeatureBar from '@/app/components/base/features/new-feature-panel/feature-bar' import type { FileUpload } from '@/app/components/base/features/types' import { TransferMethod } from '@/types/app' type ChatInputAreaProps = { showFeatureBar?: boolean showFileUpload?: boolean featureBarDisabled?: boolean onFeatureBarClick?: (state: boolean) => void visionConfig?: FileUpload speechToTextConfig?: EnableType onSend?: OnSend inputs?: Record inputsForm?: InputForm[] theme?: Theme | null isResponding?: boolean } const ChatInputArea = ({ showFeatureBar, showFileUpload, featureBarDisabled, onFeatureBarClick, visionConfig, speechToTextConfig = { enabled: true }, onSend, inputs = {}, inputsForm = [], theme, isResponding, }: ChatInputAreaProps) => { const { t } = useTranslation() const { notify } = useToastContext() const { wrapperRef, textareaRef, textValueRef, holdSpaceRef, handleTextareaResize, isMultipleLine, } = useTextAreaHeight() const [query, setQuery] = useState('') const [showVoiceInput, setShowVoiceInput] = useState(false) const filesStore = useFileStore() const { handleDragFileEnter, handleDragFileLeave, handleDragFileOver, handleDropFile, handleClipboardPasteFile, isDragActive, } = useFile(visionConfig!) const { checkInputsForm } = useCheckInputsForms() const historyRef = useRef(['']) const [currentIndex, setCurrentIndex] = useState(-1) const handleSend = () => { if (isResponding) { notify({ type: 'info', message: t('appDebug.errorMessage.waitForResponse') }) return } if (onSend) { const { files, setFiles } = filesStore.getState() if (files.find(item => item.transferMethod === TransferMethod.local_file && !item.uploadedId)) { notify({ type: 'info', message: t('appDebug.errorMessage.waitForFileUpload') }) return } if (!query || !query.trim()) { notify({ type: 'info', message: t('appAnnotation.errorMessage.queryRequired') }) return } if (checkInputsForm(inputs, inputsForm)) { // debugger onSend(query, files) setQuery('') setFiles([]) } } } const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) { e.preventDefault() setQuery(query.replace(/\n$/, '')) historyRef.current.push(query) setCurrentIndex(historyRef.current.length) handleSend() } else if (e.key === 'ArrowUp' && !e.shiftKey && !e.nativeEvent.isComposing && e.metaKey) { // When the cmd + up key is pressed, output the previous element if (currentIndex > 0) { setCurrentIndex(currentIndex - 1) setQuery(historyRef.current[currentIndex - 1]) } } else if (e.key === 'ArrowDown' && !e.shiftKey && !e.nativeEvent.isComposing && e.metaKey) { // When the cmd + down key is pressed, output the next element if (currentIndex < historyRef.current.length - 1) { setCurrentIndex(currentIndex + 1) setQuery(historyRef.current[currentIndex + 1]) } else if (currentIndex === historyRef.current.length - 1) { // If it is the last element, clear the input box setCurrentIndex(historyRef.current.length) setQuery('') } } } const handleShowVoiceInput = useCallback(() => { (Recorder as any).getPermission().then(() => { setShowVoiceInput(true) }, () => { notify({ type: 'error', message: t('common.voiceInput.notAllow') }) }) }, [t, notify]) const operation = ( ) return ( <>
{query}