mirror of
https://github.com/langgenius/dify.git
synced 2026-04-27 02:36:29 +08:00
remove chat input
This commit is contained in:
parent
aa7ae4c5f1
commit
8bd9d8f6ba
@ -12,19 +12,14 @@ import {
|
|||||||
} from './context'
|
} from './context'
|
||||||
import type { DebugWithMultipleModelContextType } from './context'
|
import type { DebugWithMultipleModelContextType } from './context'
|
||||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||||
import ChatInput from '@/app/components/base/chat/chat/chat-input'
|
import ChatInputArea from '@/app/components/base/chat/chat/chat-input-area'
|
||||||
// import ChatInputArea from '@/app/components/base/chat/chat/chat-input-area'
|
|
||||||
import type { VisionFile } from '@/app/components/base/chat/types'
|
import type { VisionFile } from '@/app/components/base/chat/types'
|
||||||
import { useDebugConfigurationContext } from '@/context/debug-configuration'
|
import { useDebugConfigurationContext } from '@/context/debug-configuration'
|
||||||
import { useFeatures } from '@/app/components/base/features/hooks'
|
import { useFeatures } from '@/app/components/base/features/hooks'
|
||||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||||
import { Resolution, TransferMethod } from '@/types/app'
|
|
||||||
|
|
||||||
const DebugWithMultipleModel = () => {
|
const DebugWithMultipleModel = () => {
|
||||||
const {
|
const { mode } = useDebugConfigurationContext()
|
||||||
mode,
|
|
||||||
// isShowVisionConfig,
|
|
||||||
} = useDebugConfigurationContext()
|
|
||||||
const speech2text = useFeatures(s => s.features.speech2text)
|
const speech2text = useFeatures(s => s.features.speech2text)
|
||||||
const file = useFeatures(s => s.features.file)
|
const file = useFeatures(s => s.features.file)
|
||||||
const {
|
const {
|
||||||
@ -34,15 +29,6 @@ const DebugWithMultipleModel = () => {
|
|||||||
const { eventEmitter } = useEventEmitterContextContext()
|
const { eventEmitter } = useEventEmitterContextContext()
|
||||||
const isChatMode = mode === 'chat' || mode === 'agent-chat'
|
const isChatMode = mode === 'chat' || mode === 'agent-chat'
|
||||||
|
|
||||||
const visionConfig = useMemo(() => {
|
|
||||||
return {
|
|
||||||
enabled: file?.enabled || false,
|
|
||||||
detail: file?.image?.detail || Resolution.high,
|
|
||||||
number_limits: file?.number_limits || 3,
|
|
||||||
transfer_methods: file?.allowed_file_upload_methods || [TransferMethod.local_file, TransferMethod.remote_url],
|
|
||||||
}
|
|
||||||
}, [file])
|
|
||||||
|
|
||||||
const handleSend = useCallback((message: string, files?: VisionFile[]) => {
|
const handleSend = useCallback((message: string, files?: VisionFile[]) => {
|
||||||
if (checkCanSend && !checkCanSend())
|
if (checkCanSend && !checkCanSend())
|
||||||
return
|
return
|
||||||
@ -139,22 +125,14 @@ const DebugWithMultipleModel = () => {
|
|||||||
</div>
|
</div>
|
||||||
{isChatMode && (
|
{isChatMode && (
|
||||||
<div className='shrink-0 pb-0 px-6'>
|
<div className='shrink-0 pb-0 px-6'>
|
||||||
<ChatInput
|
<ChatInputArea
|
||||||
showFeatureBar
|
|
||||||
showFileUpload={false}
|
|
||||||
onFeatureBarClick={setShowAppConfigureFeaturesModal}
|
|
||||||
onSend={handleSend}
|
|
||||||
speechToTextConfig={speech2text as any}
|
|
||||||
visionConfig={visionConfig}
|
|
||||||
/>
|
|
||||||
{/* <ChatInputArea
|
|
||||||
showFeatureBar
|
showFeatureBar
|
||||||
showFileUpload={false}
|
showFileUpload={false}
|
||||||
onFeatureBarClick={setShowAppConfigureFeaturesModal}
|
onFeatureBarClick={setShowAppConfigureFeaturesModal}
|
||||||
onSend={handleSend}
|
onSend={handleSend}
|
||||||
speechToTextConfig={speech2text as any}
|
speechToTextConfig={speech2text as any}
|
||||||
visionConfig={file}
|
visionConfig={file}
|
||||||
/> */}
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,265 +0,0 @@
|
|||||||
import type { FC } from 'react'
|
|
||||||
import {
|
|
||||||
memo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react'
|
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Recorder from 'js-audio-recorder'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import Textarea from 'rc-textarea'
|
|
||||||
import type {
|
|
||||||
EnableType,
|
|
||||||
OnSend,
|
|
||||||
VisionConfig,
|
|
||||||
} from '../types'
|
|
||||||
import { TransferMethod } from '../types'
|
|
||||||
import { useChatWithHistoryContext } from '../chat-with-history/context'
|
|
||||||
import type { Theme } from '../embedded-chatbot/theme/theme-context'
|
|
||||||
import { CssTransform } from '../embedded-chatbot/theme/utils'
|
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
|
||||||
import { ToastContext } from '@/app/components/base/toast'
|
|
||||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
|
||||||
import VoiceInput from '@/app/components/base/voice-input'
|
|
||||||
import { Microphone01 } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
|
|
||||||
import { Microphone01 as Microphone01Solid } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
|
|
||||||
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
|
||||||
import { Send03 } from '@/app/components/base/icons/src/vender/solid/communication'
|
|
||||||
import ChatImageUploader from '@/app/components/base/image-uploader/chat-image-uploader'
|
|
||||||
import ImageList from '@/app/components/base/image-uploader/image-list'
|
|
||||||
import {
|
|
||||||
useClipboardUploader,
|
|
||||||
useDraggableUploader,
|
|
||||||
useImageFiles,
|
|
||||||
} from '@/app/components/base/image-uploader/hooks'
|
|
||||||
import FeatureBar from '@/app/components/base/features/new-feature-panel/feature-bar'
|
|
||||||
import cn from '@/utils/classnames'
|
|
||||||
|
|
||||||
type ChatInputProps = {
|
|
||||||
showFeatureBar?: boolean
|
|
||||||
showFileUpload?: boolean
|
|
||||||
featureBarDisabled?: boolean
|
|
||||||
onFeatureBarClick?: (state: boolean) => void
|
|
||||||
visionConfig?: VisionConfig
|
|
||||||
speechToTextConfig?: EnableType
|
|
||||||
onSend?: OnSend
|
|
||||||
theme?: Theme | null
|
|
||||||
noSpacing?: boolean
|
|
||||||
}
|
|
||||||
const ChatInput: FC<ChatInputProps> = ({
|
|
||||||
showFeatureBar,
|
|
||||||
showFileUpload,
|
|
||||||
featureBarDisabled,
|
|
||||||
onFeatureBarClick,
|
|
||||||
visionConfig,
|
|
||||||
speechToTextConfig,
|
|
||||||
onSend,
|
|
||||||
theme,
|
|
||||||
noSpacing,
|
|
||||||
}) => {
|
|
||||||
const { appData } = useChatWithHistoryContext()
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const { notify } = useContext(ToastContext)
|
|
||||||
const [voiceInputShow, setVoiceInputShow] = useState(false)
|
|
||||||
const textAreaRef = useRef<HTMLTextAreaElement>(null)
|
|
||||||
const {
|
|
||||||
files,
|
|
||||||
onUpload,
|
|
||||||
onRemove,
|
|
||||||
onReUpload,
|
|
||||||
onImageLinkLoadError,
|
|
||||||
onImageLinkLoadSuccess,
|
|
||||||
onClear,
|
|
||||||
} = useImageFiles()
|
|
||||||
const { onPaste } = useClipboardUploader({ onUpload, visionConfig, files })
|
|
||||||
const { onDragEnter, onDragLeave, onDragOver, onDrop, isDragActive } = useDraggableUploader<HTMLTextAreaElement>({ onUpload, files, visionConfig })
|
|
||||||
const isUseInputMethod = useRef(false)
|
|
||||||
const [query, setQuery] = useState('')
|
|
||||||
const handleContentChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
||||||
const value = e.target.value
|
|
||||||
setQuery(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSend = () => {
|
|
||||||
if (onSend) {
|
|
||||||
if (files.find(item => item.type === TransferMethod.local_file && !item.fileId)) {
|
|
||||||
notify({ type: 'info', message: t('appDebug.errorMessage.waitForImgUpload') })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!query || !query.trim()) {
|
|
||||||
notify({ type: 'info', message: t('appAnnotation.errorMessage.queryRequired') })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
onSend(query, files.filter(file => file.progress !== -1).map(fileItem => ({
|
|
||||||
type: 'image',
|
|
||||||
transfer_method: fileItem.type,
|
|
||||||
url: fileItem.url,
|
|
||||||
upload_file_id: fileItem.fileId,
|
|
||||||
})))
|
|
||||||
setQuery('')
|
|
||||||
onClear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleKeyUp = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
e.preventDefault()
|
|
||||||
// prevent send message when using input method enter
|
|
||||||
if (!e.shiftKey && !isUseInputMethod.current)
|
|
||||||
handleSend()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
||||||
isUseInputMethod.current = e.nativeEvent.isComposing
|
|
||||||
if (e.key === 'Enter' && !e.shiftKey) {
|
|
||||||
setQuery(query.replace(/\n$/, ''))
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const logError = (message: string) => {
|
|
||||||
notify({ type: 'error', message })
|
|
||||||
}
|
|
||||||
const handleVoiceInputShow = () => {
|
|
||||||
(Recorder as any).getPermission().then(() => {
|
|
||||||
setVoiceInputShow(true)
|
|
||||||
}, () => {
|
|
||||||
logError(t('common.voiceInput.notAllow'))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const [isActiveIconFocused, setActiveIconFocused] = useState(false)
|
|
||||||
|
|
||||||
const media = useBreakpoints()
|
|
||||||
const isMobile = media === MediaType.mobile
|
|
||||||
const sendIconThemeStyle = theme
|
|
||||||
? {
|
|
||||||
color: (isActiveIconFocused || query || (query.trim() !== '')) ? theme.primaryColor : '#d1d5db',
|
|
||||||
}
|
|
||||||
: {}
|
|
||||||
const sendBtn = (
|
|
||||||
<div
|
|
||||||
className='group flex items-center justify-center w-8 h-8 rounded-lg hover:bg-[#EBF5FF] cursor-pointer'
|
|
||||||
onMouseEnter={() => setActiveIconFocused(true)}
|
|
||||||
onMouseLeave={() => setActiveIconFocused(false)}
|
|
||||||
onClick={handleSend}
|
|
||||||
style={isActiveIconFocused ? CssTransform(theme?.chatBubbleColorStyle ?? '') : {}}
|
|
||||||
>
|
|
||||||
<Send03
|
|
||||||
style={sendIconThemeStyle}
|
|
||||||
className={`
|
|
||||||
w-5 h-5 text-gray-300 group-hover:text-primary-600
|
|
||||||
${!!query.trim() && 'text-primary-600'}
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className={cn('relative', !noSpacing && 'px-8', showFeatureBar && 'z-10')}>
|
|
||||||
<div
|
|
||||||
className={cn('p-[5.5px] max-h-[150px] bg-white border-[1.5px] border-gray-200 rounded-xl overflow-y-auto', isDragActive && 'border-primary-600', !showFeatureBar && 'mb-2')}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
visionConfig?.enabled && (
|
|
||||||
<>
|
|
||||||
<div className={cn('absolute bottom-2 flex items-center', noSpacing ? 'left-2' : 'left-10')}>
|
|
||||||
<ChatImageUploader
|
|
||||||
settings={visionConfig}
|
|
||||||
onUpload={onUpload}
|
|
||||||
disabled={files.length >= visionConfig.number_limits}
|
|
||||||
/>
|
|
||||||
<div className='mx-1 w-[1px] h-4 bg-black/5' />
|
|
||||||
</div>
|
|
||||||
<div className='pl-[52px]'>
|
|
||||||
<ImageList
|
|
||||||
list={files}
|
|
||||||
onRemove={onRemove}
|
|
||||||
onReUpload={onReUpload}
|
|
||||||
onImageLinkLoadSuccess={onImageLinkLoadSuccess}
|
|
||||||
onImageLinkLoadError={onImageLinkLoadError}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<Textarea
|
|
||||||
ref={textAreaRef as any}
|
|
||||||
className={`
|
|
||||||
block w-full px-2 pr-[118px] py-[7px] leading-5 max-h-none text-sm text-gray-700 outline-none appearance-none resize-none
|
|
||||||
${visionConfig?.enabled && 'pl-12'}
|
|
||||||
`}
|
|
||||||
value={query}
|
|
||||||
onChange={handleContentChange}
|
|
||||||
onKeyUp={handleKeyUp}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
onPaste={onPaste}
|
|
||||||
onDragEnter={onDragEnter}
|
|
||||||
onDragLeave={onDragLeave}
|
|
||||||
onDragOver={onDragOver}
|
|
||||||
onDrop={onDrop}
|
|
||||||
autoSize
|
|
||||||
/>
|
|
||||||
<div className={cn('absolute bottom-[7px] flex items-center h-8', noSpacing ? 'right-2' : 'right-10')}>
|
|
||||||
<div className='flex items-center px-1 h-5 rounded-md bg-gray-100 text-xs font-medium text-gray-500'>
|
|
||||||
{query.trim().length}
|
|
||||||
</div>
|
|
||||||
{
|
|
||||||
query
|
|
||||||
? (
|
|
||||||
<div className='flex justify-center items-center ml-2 w-8 h-8 cursor-pointer hover:bg-gray-100 rounded-lg' onClick={() => setQuery('')}>
|
|
||||||
<XCircle className='w-4 h-4 text-[#98A2B3]' />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
: speechToTextConfig?.enabled
|
|
||||||
? (
|
|
||||||
<div
|
|
||||||
className='group flex justify-center items-center ml-2 w-8 h-8 hover:bg-primary-50 rounded-lg cursor-pointer'
|
|
||||||
onClick={handleVoiceInputShow}
|
|
||||||
>
|
|
||||||
<Microphone01 className='block w-4 h-4 text-gray-500 group-hover:hidden' />
|
|
||||||
<Microphone01Solid className='hidden w-4 h-4 text-primary-600 group-hover:block' />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
<div className='mx-2 w-[1px] h-4 bg-black opacity-5' />
|
|
||||||
{isMobile
|
|
||||||
? sendBtn
|
|
||||||
: (
|
|
||||||
<Tooltip
|
|
||||||
popupContent={
|
|
||||||
<div>
|
|
||||||
<div>{t('common.operation.send')} Enter</div>
|
|
||||||
<div>{t('common.operation.lineBreak')} Shift Enter</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{sendBtn}
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{
|
|
||||||
voiceInputShow && (
|
|
||||||
<VoiceInput
|
|
||||||
onCancel={() => setVoiceInputShow(false)}
|
|
||||||
onConverted={(text) => {
|
|
||||||
setQuery(text)
|
|
||||||
textAreaRef.current?.focus()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{appData?.site?.custom_disclaimer && <div className='text-xs text-gray-500 mt-1 text-center'>
|
|
||||||
{appData.site.custom_disclaimer}
|
|
||||||
</div>}
|
|
||||||
{showFeatureBar && <FeatureBar showFileUpload={showFileUpload} disabled={featureBarDisabled} onFeatureBarClick={onFeatureBarClick} />}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(ChatInput)
|
|
||||||
@ -6,7 +6,6 @@ import {
|
|||||||
memo,
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
@ -22,7 +21,6 @@ import type {
|
|||||||
import type { ThemeBuilder } from '../embedded-chatbot/theme/theme-context'
|
import type { ThemeBuilder } from '../embedded-chatbot/theme/theme-context'
|
||||||
import Question from './question'
|
import Question from './question'
|
||||||
import Answer from './answer'
|
import Answer from './answer'
|
||||||
import ChatInput from './chat-input'
|
|
||||||
import ChatInputArea from './chat-input-area'
|
import ChatInputArea from './chat-input-area'
|
||||||
import TryToAsk from './try-to-ask'
|
import TryToAsk from './try-to-ask'
|
||||||
import { ChatContextProvider } from './context'
|
import { ChatContextProvider } from './context'
|
||||||
@ -34,7 +32,6 @@ import AgentLogModal from '@/app/components/base/agent-log-modal'
|
|||||||
import PromptLogModal from '@/app/components/base/prompt-log-modal'
|
import PromptLogModal from '@/app/components/base/prompt-log-modal'
|
||||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||||
import type { AppData } from '@/models/share'
|
import type { AppData } from '@/models/share'
|
||||||
import { Resolution, TransferMethod } from '@/types/app'
|
|
||||||
|
|
||||||
export type ChatProps = {
|
export type ChatProps = {
|
||||||
appData?: AppData
|
appData?: AppData
|
||||||
@ -184,15 +181,6 @@ const Chat: FC<ChatProps> = ({
|
|||||||
|
|
||||||
const hasTryToAsk = config?.suggested_questions_after_answer?.enabled && !!suggestedQuestions?.length && onSend
|
const hasTryToAsk = config?.suggested_questions_after_answer?.enabled && !!suggestedQuestions?.length && onSend
|
||||||
|
|
||||||
const visionConfig = useMemo(() => {
|
|
||||||
return {
|
|
||||||
enabled: (config?.file_upload as any)?.enabled || false,
|
|
||||||
detail: (config?.file_upload as any)?.image?.detail || Resolution.high,
|
|
||||||
number_limits: (config?.file_upload as any)?.number_limits || 3,
|
|
||||||
transfer_methods: (config?.file_upload as any)?.allowed_file_upload_methods || [TransferMethod.local_file, TransferMethod.remote_url],
|
|
||||||
}
|
|
||||||
}, [config?.file_upload])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChatContextProvider
|
<ChatContextProvider
|
||||||
config={config}
|
config={config}
|
||||||
@ -279,7 +267,7 @@ const Chat: FC<ChatProps> = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
!noChatInput && showFileUpload && (
|
!noChatInput && (
|
||||||
<ChatInputArea
|
<ChatInputArea
|
||||||
showFeatureBar={showFeatureBar}
|
showFeatureBar={showFeatureBar}
|
||||||
showFileUpload={showFileUpload}
|
showFileUpload={showFileUpload}
|
||||||
@ -292,18 +280,6 @@ const Chat: FC<ChatProps> = ({
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{!noChatInput && !showFileUpload && (
|
|
||||||
<ChatInput
|
|
||||||
showFeatureBar={showFeatureBar}
|
|
||||||
showFileUpload={showFileUpload}
|
|
||||||
featureBarDisabled={isResponding}
|
|
||||||
onFeatureBarClick={onFeatureBarClick}
|
|
||||||
onSend={onSend}
|
|
||||||
speechToTextConfig={config?.speech_to_text}
|
|
||||||
visionConfig={visionConfig}
|
|
||||||
noSpacing={noSpacing}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{showPromptLogModal && !hideLogModal && (
|
{showPromptLogModal && !hideLogModal && (
|
||||||
|
|||||||
@ -156,8 +156,6 @@ const FormItem: FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
{/* #TODO# file upload */}
|
|
||||||
{(type === InputVarType.singleFile || type === InputVarType.multiFiles) && (
|
{(type === InputVarType.singleFile || type === InputVarType.multiFiles) && (
|
||||||
<FileUploaderInAttachmentWrapper
|
<FileUploaderInAttachmentWrapper
|
||||||
onChange={files => onChange(files.filter(file => file.progress !== -1).map(fileItem => ({
|
onChange={files => onChange(files.filter(file => file.progress !== -1).map(fileItem => ({
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user