mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-20 02:29:05 +08:00
file uploader
This commit is contained in:
parent
e2962da1b8
commit
26bca75884
@ -129,23 +129,27 @@ const ChatWrapper = () => {
|
|||||||
])
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Chat
|
<div
|
||||||
appData={appData}
|
className='h-full bg-chatbot-bg overflow-hidden'
|
||||||
config={appConfig}
|
>
|
||||||
chatList={chatList}
|
<Chat
|
||||||
isResponding={isResponding}
|
appData={appData}
|
||||||
chatContainerInnerClassName={`mx-auto pt-6 w-full max-w-[720px] ${isMobile && 'px-4'}`}
|
config={appConfig}
|
||||||
chatFooterClassName='pb-4'
|
chatList={chatList}
|
||||||
chatFooterInnerClassName={`mx-auto w-full max-w-[720px] ${isMobile && 'px-4'}`}
|
isResponding={isResponding}
|
||||||
onSend={doSend}
|
chatContainerInnerClassName={`mx-auto pt-6 w-full max-w-[720px] ${isMobile && 'px-4'}`}
|
||||||
onStopResponding={handleStop}
|
chatFooterClassName='pb-4'
|
||||||
chatNode={chatNode}
|
chatFooterInnerClassName={`mx-auto w-full max-w-[720px] ${isMobile && 'px-4'}`}
|
||||||
allToolIcons={appMeta?.tool_icons || {}}
|
onSend={doSend}
|
||||||
onFeedback={handleFeedback}
|
onStopResponding={handleStop}
|
||||||
suggestedQuestions={suggestedQuestions}
|
chatNode={chatNode}
|
||||||
hideProcessDetail
|
allToolIcons={appMeta?.tool_icons || {}}
|
||||||
themeBuilder={themeBuilder}
|
onFeedback={handleFeedback}
|
||||||
/>
|
suggestedQuestions={suggestedQuestions}
|
||||||
|
hideProcessDetail
|
||||||
|
themeBuilder={themeBuilder}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ const AgentContent: FC<AgentContentProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{agent_thoughts?.map((thought, index) => (
|
{agent_thoughts?.map((thought, index) => (
|
||||||
<div key={index}>
|
<div key={index} className='px-2 py-1'>
|
||||||
{thought.thought && (
|
{thought.thought && (
|
||||||
<Markdown content={thought.thought} />
|
<Markdown content={thought.thought} />
|
||||||
)}
|
)}
|
||||||
|
@ -2,6 +2,7 @@ import type { FC } from 'react'
|
|||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
import type { ChatItem } from '../../types'
|
import type { ChatItem } from '../../types'
|
||||||
import { Markdown } from '@/app/components/base/markdown'
|
import { Markdown } from '@/app/components/base/markdown'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
type BasicContentProps = {
|
type BasicContentProps = {
|
||||||
item: ChatItem
|
item: ChatItem
|
||||||
@ -15,9 +16,17 @@ const BasicContent: FC<BasicContentProps> = ({
|
|||||||
} = item
|
} = item
|
||||||
|
|
||||||
if (annotation?.logAnnotation)
|
if (annotation?.logAnnotation)
|
||||||
return <Markdown content={annotation?.logAnnotation.content || ''} />
|
return <Markdown content={annotation?.logAnnotation.content || ''} className='px-2 py-1' />
|
||||||
|
|
||||||
return <Markdown content={content} className={`${item.isError && '!text-[#F04438]'}`} />
|
return (
|
||||||
|
<Markdown
|
||||||
|
className={cn(
|
||||||
|
'px-2 py-1',
|
||||||
|
item.isError && '!text-[#F04438]',
|
||||||
|
)}
|
||||||
|
content={content}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(BasicContent)
|
export default memo(BasicContent)
|
||||||
|
@ -15,13 +15,13 @@ import BasicContent from './basic-content'
|
|||||||
import SuggestedQuestions from './suggested-questions'
|
import SuggestedQuestions from './suggested-questions'
|
||||||
import More from './more'
|
import More from './more'
|
||||||
import WorkflowProcess from './workflow-process'
|
import WorkflowProcess from './workflow-process'
|
||||||
import { AnswerTriangle } from '@/app/components/base/icons/src/vender/solid/general'
|
|
||||||
import { MessageFast } from '@/app/components/base/icons/src/vender/solid/communication'
|
import { MessageFast } from '@/app/components/base/icons/src/vender/solid/communication'
|
||||||
import LoadingAnim from '@/app/components/base/chat/chat/loading-anim'
|
import LoadingAnim from '@/app/components/base/chat/chat/loading-anim'
|
||||||
import Citation from '@/app/components/base/chat/chat/citation'
|
import Citation from '@/app/components/base/chat/chat/citation'
|
||||||
import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item'
|
import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item'
|
||||||
import type { Emoji } from '@/app/components/tools/types'
|
import type { Emoji } from '@/app/components/tools/types'
|
||||||
import type { AppData } from '@/models/share'
|
import type { AppData } from '@/models/share'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
type AnswerProps = {
|
type AnswerProps = {
|
||||||
item: ChatItem
|
item: ChatItem
|
||||||
@ -105,13 +105,12 @@ const Answer: FC<AnswerProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className='chat-answer-container group grow w-0 ml-4' ref={containerRef}>
|
<div className='chat-answer-container group grow w-0 ml-4' ref={containerRef}>
|
||||||
<div className={`group relative pr-10 ${chatAnswerContainerInner}`}>
|
<div className={`group relative pr-10 ${chatAnswerContainerInner}`}>
|
||||||
<AnswerTriangle className='absolute -left-2 top-0 w-2 h-3 text-gray-100' />
|
|
||||||
<div
|
<div
|
||||||
ref={contentRef}
|
ref={contentRef}
|
||||||
className={`
|
className={cn(
|
||||||
relative inline-block px-4 py-3 max-w-full bg-gray-100 rounded-b-2xl rounded-tr-2xl text-sm text-gray-900
|
'relative inline-block p-2 max-w-full bg-chat-bubble-bg rounded-2xl border-t border-t-divider-subtle text-sm text-gray-900',
|
||||||
${workflowProcess && 'w-full'}
|
workflowProcess && 'w-full',
|
||||||
`}
|
)}
|
||||||
>
|
>
|
||||||
{annotation?.id && (
|
{annotation?.id && (
|
||||||
<div
|
<div
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
memo,
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import Textarea from 'rc-textarea'
|
import Textarea from 'rc-textarea'
|
||||||
@ -15,7 +16,7 @@ import type { Theme } from '../../embedded-chatbot/theme/theme-context'
|
|||||||
import { useTextAreaHeight } from './hooks'
|
import { useTextAreaHeight } from './hooks'
|
||||||
import Operation from './operation'
|
import Operation from './operation'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import { FileListFlexOperation } from '@/app/components/base/file-uploader'
|
// import { FileListFlexOperation } from '@/app/components/base/file-uploader'
|
||||||
import VoiceInput from '@/app/components/base/voice-input'
|
import VoiceInput from '@/app/components/base/voice-input'
|
||||||
import { useToastContext } from '@/app/components/base/toast'
|
import { useToastContext } from '@/app/components/base/toast'
|
||||||
|
|
||||||
@ -41,9 +42,38 @@ const ChatInputArea = ({
|
|||||||
handleTextareaResize,
|
handleTextareaResize,
|
||||||
isMultipleLine,
|
isMultipleLine,
|
||||||
} = useTextAreaHeight()
|
} = useTextAreaHeight()
|
||||||
const [value, setValue] = useState('')
|
const [query, setQuery] = useState('')
|
||||||
|
const isUseInputMethod = useRef(false)
|
||||||
const [showVoiceInput, setShowVoiceInput] = useState(false)
|
const [showVoiceInput, setShowVoiceInput] = useState(false)
|
||||||
|
|
||||||
|
const handleSend = () => {
|
||||||
|
if (onSend) {
|
||||||
|
if (!query || !query.trim()) {
|
||||||
|
notify({ type: 'info', message: t('appAnnotation.errorMessage.queryRequired') })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
onSend(query)
|
||||||
|
setQuery('')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleKeyUp = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
|
if (e.code === '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.code === 'Enter' && !e.shiftKey) {
|
||||||
|
setQuery(query.replace(/\n$/, ''))
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleShowVoiceInput = useCallback(() => {
|
const handleShowVoiceInput = useCallback(() => {
|
||||||
(Recorder as any).getPermission().then(() => {
|
(Recorder as any).getPermission().then(() => {
|
||||||
setShowVoiceInput(true)
|
setShowVoiceInput(true)
|
||||||
@ -63,11 +93,11 @@ const ChatInputArea = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'py-[9px] bg-components-panel-bg-blur border border-blue-300 rounded-xl shadow-md',
|
'py-[9px] bg-components-panel-bg-blur border border-components-chat-input-border rounded-xl shadow-md',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className='relative px-[9px] max-h-[158px] overflow-y-auto'>
|
<div className='relative px-[9px] max-h-[158px] overflow-x-hidden overflow-y-auto'>
|
||||||
<FileListFlexOperation />
|
{/* <FileListFlexOperation /> */}
|
||||||
<div
|
<div
|
||||||
ref={wrapperRef}
|
ref={wrapperRef}
|
||||||
className='flex items-center justify-between'
|
className='flex items-center justify-between'
|
||||||
@ -77,7 +107,7 @@ const ChatInputArea = ({
|
|||||||
ref={textValueRef}
|
ref={textValueRef}
|
||||||
className='absolute w-auto h-auto p-1 leading-6 body-lg-regular pointer-events-none whitespace-pre invisible'
|
className='absolute w-auto h-auto p-1 leading-6 body-lg-regular pointer-events-none whitespace-pre invisible'
|
||||||
>
|
>
|
||||||
{value}
|
{query}
|
||||||
</div>
|
</div>
|
||||||
<Textarea
|
<Textarea
|
||||||
ref={textareaRef}
|
ref={textareaRef}
|
||||||
@ -85,11 +115,13 @@ const ChatInputArea = ({
|
|||||||
placeholder='Enter message...'
|
placeholder='Enter message...'
|
||||||
autoSize={{ minRows: 1 }}
|
autoSize={{ minRows: 1 }}
|
||||||
onResize={handleTextareaResize}
|
onResize={handleTextareaResize}
|
||||||
value={value}
|
value={query}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setValue(e.target.value)
|
setQuery(e.target.value)
|
||||||
handleTextareaResize()
|
handleTextareaResize()
|
||||||
}}
|
}}
|
||||||
|
onKeyUp={handleKeyUp}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
@ -100,7 +132,7 @@ const ChatInputArea = ({
|
|||||||
showVoiceInput && (
|
showVoiceInput && (
|
||||||
<VoiceInput
|
<VoiceInput
|
||||||
onCancel={() => setShowVoiceInput(false)}
|
onCancel={() => setShowVoiceInput(false)}
|
||||||
onConverted={text => setValue(text)}
|
onConverted={text => setQuery(text)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,8 @@ 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 ChatInput from './chat-input'
|
||||||
|
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'
|
||||||
import classNames from '@/utils/classnames'
|
import classNames from '@/utils/classnames'
|
||||||
@ -261,7 +262,7 @@ const Chat: FC<ChatProps> = ({
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{/* {
|
||||||
!noChatInput && (
|
!noChatInput && (
|
||||||
<ChatInput
|
<ChatInput
|
||||||
visionConfig={config?.file_upload?.image}
|
visionConfig={config?.file_upload?.image}
|
||||||
@ -270,6 +271,16 @@ const Chat: FC<ChatProps> = ({
|
|||||||
theme={themeBuilder?.theme}
|
theme={themeBuilder?.theme}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
} */}
|
||||||
|
{
|
||||||
|
!noChatInput && (
|
||||||
|
<ChatInputArea
|
||||||
|
visionConfig={config?.file_upload?.image}
|
||||||
|
speechToTextConfig={config?.speech_to_text}
|
||||||
|
onSend={onSend}
|
||||||
|
theme={themeBuilder?.theme}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,7 +8,6 @@ import {
|
|||||||
import type { ChatItem } from '../types'
|
import type { ChatItem } from '../types'
|
||||||
import type { Theme } from '../embedded-chatbot/theme/theme-context'
|
import type { Theme } from '../embedded-chatbot/theme/theme-context'
|
||||||
import { CssTransform } from '../embedded-chatbot/theme/utils'
|
import { CssTransform } from '../embedded-chatbot/theme/utils'
|
||||||
import { QuestionTriangle } from '@/app/components/base/icons/src/vender/solid/general'
|
|
||||||
import { User } from '@/app/components/base/icons/src/public/avatar'
|
import { User } from '@/app/components/base/icons/src/public/avatar'
|
||||||
import { Markdown } from '@/app/components/base/markdown'
|
import { Markdown } from '@/app/components/base/markdown'
|
||||||
import ImageGallery from '@/app/components/base/image-gallery'
|
import ImageGallery from '@/app/components/base/image-gallery'
|
||||||
@ -32,12 +31,8 @@ const Question: FC<QuestionProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className='flex justify-end mb-2 last:mb-0 pl-10'>
|
<div className='flex justify-end mb-2 last:mb-0 pl-10'>
|
||||||
<div className='group relative mr-4'>
|
<div className='group relative mr-4'>
|
||||||
<QuestionTriangle
|
|
||||||
className='absolute -right-2 top-0 w-2 h-3 text-[#D1E9FF]/50'
|
|
||||||
style={theme ? { color: theme.chatBubbleColor } : {}}
|
|
||||||
/>
|
|
||||||
<div
|
<div
|
||||||
className='px-4 py-3 bg-[#D1E9FF]/50 rounded-b-2xl rounded-tl-2xl text-sm text-gray-900'
|
className='px-4 py-3 bg-util-colors-blue-brand-blue-brand-500 rounded-2xl text-sm text-gray-900'
|
||||||
style={theme?.chatBubbleColorStyle ? CssTransform(theme.chatBubbleColorStyle) : {}}
|
style={theme?.chatBubbleColorStyle ? CssTransform(theme.chatBubbleColorStyle) : {}}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
|
@ -88,6 +88,10 @@ module.exports = {
|
|||||||
fontSize: {
|
fontSize: {
|
||||||
'2xs': '0.625rem',
|
'2xs': '0.625rem',
|
||||||
},
|
},
|
||||||
|
backgroundImage: {
|
||||||
|
'chatbot-bg': 'var(--color-chatbot-bg)',
|
||||||
|
'chat-bubble-bg': 'var(--color-chat-bubble-bg)',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -559,4 +559,7 @@ html[data-theme="dark"] {
|
|||||||
|
|
||||||
--color-third-party-LangChain: #FFFFFF;
|
--color-third-party-LangChain: #FFFFFF;
|
||||||
--color-third-party-Langfuse: #FFFFFF;
|
--color-third-party-Langfuse: #FFFFFF;
|
||||||
|
|
||||||
|
--color-chatbot-bg: linear-gradient(180deg, rgba(34, 34, 37, 0.90) 0%, rgba(29, 29, 32, 0.90) 90.48%);
|
||||||
|
--color-chat-bubble-bg: linear-gradient(180deg, rgba(200, 206, 218, 0.08) 0%, rgba(200, 206, 218, 0.02) 100%);
|
||||||
}
|
}
|
@ -559,4 +559,7 @@ html[data-theme="light"] {
|
|||||||
|
|
||||||
--color-third-party-LangChain: #1C3C3C;
|
--color-third-party-LangChain: #1C3C3C;
|
||||||
--color-third-party-Langfuse: #000000;
|
--color-third-party-Langfuse: #000000;
|
||||||
|
|
||||||
|
--color-chatbot-bg: linear-gradient(180deg, rgba(249, 250, 251, 0.90) 0%, rgba(242, 244, 247, 0.90) 90.48%);
|
||||||
|
--color-chat-bubble-bg: linear-gradient(180deg, #FFF 0%, rgba(255, 255, 255, 0.60) 100%);
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user