mirror of
http://112.124.100.131/huang.ze/ebiz-dify-ai.git
synced 2025-12-10 11:26:52 +08:00
Signed-off-by: yihong0618 <zouzou0208@gmail.com> Signed-off-by: -LAN- <laipz8200@outlook.com> Signed-off-by: xhe <xw897002528@gmail.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: takatost <takatost@gmail.com> Co-authored-by: kurokobo <kuro664@gmail.com> Co-authored-by: Novice Lee <novicelee@NoviPro.local> Co-authored-by: zxhlyh <jasonapring2015@outlook.com> Co-authored-by: AkaraChen <akarachen@outlook.com> Co-authored-by: Yi <yxiaoisme@gmail.com> Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: JzoNg <jzongcode@gmail.com> Co-authored-by: twwu <twwu@dify.ai> Co-authored-by: Hiroshi Fujita <fujita-h@users.noreply.github.com> Co-authored-by: AkaraChen <85140972+AkaraChen@users.noreply.github.com> Co-authored-by: NFish <douxc512@gmail.com> Co-authored-by: Wu Tianwei <30284043+WTW0313@users.noreply.github.com> Co-authored-by: 非法操作 <hjlarry@163.com> Co-authored-by: Novice <857526207@qq.com> Co-authored-by: Hiroki Nagai <82458324+nagaihiroki-git@users.noreply.github.com> Co-authored-by: Gen Sato <52241300+halogen22@users.noreply.github.com> Co-authored-by: eux <euxuuu@gmail.com> Co-authored-by: huangzhuo1949 <167434202+huangzhuo1949@users.noreply.github.com> Co-authored-by: huangzhuo <huangzhuo1@xiaomi.com> Co-authored-by: lotsik <lotsik@mail.ru> Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com> Co-authored-by: nite-knite <nkCoding@gmail.com> Co-authored-by: Jyong <76649700+JohnJyong@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: gakkiyomi <gakkiyomi@aliyun.com> Co-authored-by: CN-P5 <heibai2006@gmail.com> Co-authored-by: CN-P5 <heibai2006@qq.com> Co-authored-by: Chuehnone <1897025+chuehnone@users.noreply.github.com> Co-authored-by: yihong <zouzou0208@gmail.com> Co-authored-by: Kevin9703 <51311316+Kevin9703@users.noreply.github.com> Co-authored-by: -LAN- <laipz8200@outlook.com> Co-authored-by: Boris Feld <lothiraldan@gmail.com> Co-authored-by: mbo <himabo@gmail.com> Co-authored-by: mabo <mabo@aeyes.ai> Co-authored-by: Warren Chen <warren.chen830@gmail.com> Co-authored-by: JzoNgKVO <27049666+JzoNgKVO@users.noreply.github.com> Co-authored-by: jiandanfeng <chenjh3@wangsu.com> Co-authored-by: zhu-an <70234959+xhdd123321@users.noreply.github.com> Co-authored-by: zhaoqingyu.1075 <zhaoqingyu.1075@bytedance.com> Co-authored-by: 海狸大師 <86974027+yenslife@users.noreply.github.com> Co-authored-by: Xu Song <xusong.vip@gmail.com> Co-authored-by: rayshaw001 <396301947@163.com> Co-authored-by: Ding Jiatong <dingjiatong@gmail.com> Co-authored-by: Bowen Liang <liangbowen@gf.com.cn> Co-authored-by: JasonVV <jasonwangiii@outlook.com> Co-authored-by: le0zh <newlight@qq.com> Co-authored-by: zhuxinliang <zhuxinliang@didiglobal.com> Co-authored-by: k-zaku <zaku99@outlook.jp> Co-authored-by: luckylhb90 <luckylhb90@gmail.com> Co-authored-by: hobo.l <hobo.l@binance.com> Co-authored-by: jiangbo721 <365065261@qq.com> Co-authored-by: 刘江波 <jiangbo721@163.com> Co-authored-by: Shun Miyazawa <34241526+miya@users.noreply.github.com> Co-authored-by: EricPan <30651140+Egfly@users.noreply.github.com> Co-authored-by: crazywoola <427733928@qq.com> Co-authored-by: sino <sino2322@gmail.com> Co-authored-by: Jhvcc <37662342+Jhvcc@users.noreply.github.com> Co-authored-by: lowell <lowell.hu@zkteco.in> Co-authored-by: Boris Polonsky <BorisPolonsky@users.noreply.github.com> Co-authored-by: Ademílson Tonato <ademilsonft@outlook.com> Co-authored-by: Ademílson Tonato <ademilson.tonato@refurbed.com> Co-authored-by: IWAI, Masaharu <iwaim.sub@gmail.com> Co-authored-by: Yueh-Po Peng (Yabi) <94939112+y10ab1@users.noreply.github.com> Co-authored-by: Jason <ggbbddjm@gmail.com> Co-authored-by: Xin Zhang <sjhpzx@gmail.com> Co-authored-by: yjc980121 <3898524+yjc980121@users.noreply.github.com> Co-authored-by: heyszt <36215648+hieheihei@users.noreply.github.com> Co-authored-by: Abdullah AlOsaimi <osaimiacc@gmail.com> Co-authored-by: Abdullah AlOsaimi <189027247+osaimi@users.noreply.github.com> Co-authored-by: Yingchun Lai <laiyingchun@apache.org> Co-authored-by: Hash Brown <hi@xzd.me> Co-authored-by: zuodongxu <192560071+zuodongxu@users.noreply.github.com> Co-authored-by: Masashi Tomooka <tmokmss@users.noreply.github.com> Co-authored-by: aplio <ryo.091219@gmail.com> Co-authored-by: Obada Khalili <54270856+obadakhalili@users.noreply.github.com> Co-authored-by: Nam Vu <zuzoovn@gmail.com> Co-authored-by: Kei YAMAZAKI <1715090+kei-yamazaki@users.noreply.github.com> Co-authored-by: TechnoHouse <13776377+deephbz@users.noreply.github.com> Co-authored-by: Riddhimaan-Senapati <114703025+Riddhimaan-Senapati@users.noreply.github.com> Co-authored-by: MaFee921 <31881301+2284730142@users.noreply.github.com> Co-authored-by: te-chan <t-nakanome@sakura-is.co.jp> Co-authored-by: HQidea <HQidea@users.noreply.github.com> Co-authored-by: Joshbly <36315710+Joshbly@users.noreply.github.com> Co-authored-by: xhe <xw897002528@gmail.com> Co-authored-by: weiwenyan-dev <154779315+weiwenyan-dev@users.noreply.github.com> Co-authored-by: ex_wenyan.wei <ex_wenyan.wei@tcl.com> Co-authored-by: engchina <12236799+engchina@users.noreply.github.com> Co-authored-by: engchina <atjapan2015@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: 呆萌闷油瓶 <253605712@qq.com> Co-authored-by: Kemal <kemalmeler@outlook.com> Co-authored-by: Lazy_Frog <4590648+lazyFrogLOL@users.noreply.github.com> Co-authored-by: Yi Xiao <54782454+YIXIAO0@users.noreply.github.com> Co-authored-by: Steven sun <98230804+Tuyohai@users.noreply.github.com> Co-authored-by: steven <sunzwj@digitalchina.com> Co-authored-by: Kalo Chin <91766386+fdb02983rhy@users.noreply.github.com> Co-authored-by: Katy Tao <34019945+KatyTao@users.noreply.github.com> Co-authored-by: depy <42985524+h4ckdepy@users.noreply.github.com> Co-authored-by: 胡春东 <gycm520@gmail.com> Co-authored-by: Junjie.M <118170653@qq.com> Co-authored-by: MuYu <mr.muzea@gmail.com> Co-authored-by: Naoki Takashima <39912547+takatea@users.noreply.github.com> Co-authored-by: Summer-Gu <37869445+gubinjie@users.noreply.github.com> Co-authored-by: Fei He <droxer.he@gmail.com> Co-authored-by: ybalbert001 <120714773+ybalbert001@users.noreply.github.com> Co-authored-by: Yuanbo Li <ybalbert@amazon.com> Co-authored-by: douxc <7553076+douxc@users.noreply.github.com> Co-authored-by: liuzhenghua <1090179900@qq.com> Co-authored-by: Wu Jiayang <62842862+Wu-Jiayang@users.noreply.github.com> Co-authored-by: Your Name <you@example.com> Co-authored-by: kimjion <45935338+kimjion@users.noreply.github.com> Co-authored-by: AugNSo <song.tiankai@icloud.com> Co-authored-by: llinvokerl <38915183+llinvokerl@users.noreply.github.com> Co-authored-by: liusurong.lsr <liusurong.lsr@alibaba-inc.com> Co-authored-by: Vasu Negi <vasu-negi@users.noreply.github.com> Co-authored-by: Hundredwz <1808096180@qq.com> Co-authored-by: Xiyuan Chen <52963600+GareArc@users.noreply.github.com>
228 lines
7.2 KiB
TypeScript
228 lines
7.2 KiB
TypeScript
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'
|
|
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<string, any>
|
|
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)) {
|
|
onSend(query, files)
|
|
setQuery('')
|
|
setFiles([])
|
|
}
|
|
}
|
|
}
|
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
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 = (
|
|
<Operation
|
|
ref={holdSpaceRef}
|
|
fileConfig={visionConfig}
|
|
speechToTextConfig={speechToTextConfig}
|
|
onShowVoiceInput={handleShowVoiceInput}
|
|
onSend={handleSend}
|
|
theme={theme}
|
|
/>
|
|
)
|
|
|
|
return (
|
|
<>
|
|
<div
|
|
className={cn(
|
|
'relative pb-[9px] bg-components-panel-bg-blur border border-components-chat-input-border rounded-xl shadow-md z-10',
|
|
isDragActive && 'border border-dashed border-components-option-card-option-selected-border',
|
|
)}
|
|
>
|
|
<div className='relative px-[9px] pt-[9px] max-h-[158px] overflow-x-hidden overflow-y-auto'>
|
|
<FileListInChatInput fileConfig={visionConfig!} />
|
|
<div
|
|
ref={wrapperRef}
|
|
className='flex items-center justify-between'
|
|
>
|
|
<div className='flex items-center relative grow w-full'>
|
|
<div
|
|
ref={textValueRef}
|
|
className='absolute w-auto h-auto p-1 leading-6 body-lg-regular pointer-events-none whitespace-pre invisible'
|
|
>
|
|
{query}
|
|
</div>
|
|
<Textarea
|
|
ref={textareaRef}
|
|
className={cn(
|
|
'p-1 w-full leading-6 body-lg-regular text-text-tertiary bg-transparent outline-none',
|
|
)}
|
|
placeholder={t('common.chat.inputPlaceholder') || ''}
|
|
autoFocus
|
|
autoSize={{ minRows: 1 }}
|
|
onResize={handleTextareaResize}
|
|
value={query}
|
|
onChange={(e) => {
|
|
setQuery(e.target.value)
|
|
handleTextareaResize()
|
|
}}
|
|
onKeyDown={handleKeyDown}
|
|
onPaste={handleClipboardPasteFile}
|
|
onDragEnter={handleDragFileEnter}
|
|
onDragLeave={handleDragFileLeave}
|
|
onDragOver={handleDragFileOver}
|
|
onDrop={handleDropFile}
|
|
/>
|
|
</div>
|
|
{
|
|
!isMultipleLine && operation
|
|
}
|
|
</div>
|
|
{
|
|
showVoiceInput && (
|
|
<VoiceInput
|
|
onCancel={() => setShowVoiceInput(false)}
|
|
onConverted={text => setQuery(text)}
|
|
/>
|
|
)
|
|
}
|
|
</div>
|
|
{
|
|
isMultipleLine && (
|
|
<div className='px-[9px]'>{operation}</div>
|
|
)
|
|
}
|
|
</div>
|
|
{showFeatureBar && <FeatureBar showFileUpload={showFileUpload} disabled={featureBarDisabled} onFeatureBarClick={onFeatureBarClick} />}
|
|
</>
|
|
)
|
|
}
|
|
|
|
const ChatInputAreaWrapper = (props: ChatInputAreaProps) => {
|
|
return (
|
|
<FileContextProvider>
|
|
<ChatInputArea {...props} />
|
|
</FileContextProvider>
|
|
)
|
|
}
|
|
|
|
export default ChatInputAreaWrapper
|