mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-19 19:59:10 +08:00
file uploader
This commit is contained in:
parent
e2962da1b8
commit
26bca75884
@ -129,23 +129,27 @@ const ChatWrapper = () => {
|
||||
])
|
||||
|
||||
return (
|
||||
<Chat
|
||||
appData={appData}
|
||||
config={appConfig}
|
||||
chatList={chatList}
|
||||
isResponding={isResponding}
|
||||
chatContainerInnerClassName={`mx-auto pt-6 w-full max-w-[720px] ${isMobile && 'px-4'}`}
|
||||
chatFooterClassName='pb-4'
|
||||
chatFooterInnerClassName={`mx-auto w-full max-w-[720px] ${isMobile && 'px-4'}`}
|
||||
onSend={doSend}
|
||||
onStopResponding={handleStop}
|
||||
chatNode={chatNode}
|
||||
allToolIcons={appMeta?.tool_icons || {}}
|
||||
onFeedback={handleFeedback}
|
||||
suggestedQuestions={suggestedQuestions}
|
||||
hideProcessDetail
|
||||
themeBuilder={themeBuilder}
|
||||
/>
|
||||
<div
|
||||
className='h-full bg-chatbot-bg overflow-hidden'
|
||||
>
|
||||
<Chat
|
||||
appData={appData}
|
||||
config={appConfig}
|
||||
chatList={chatList}
|
||||
isResponding={isResponding}
|
||||
chatContainerInnerClassName={`mx-auto pt-6 w-full max-w-[720px] ${isMobile && 'px-4'}`}
|
||||
chatFooterClassName='pb-4'
|
||||
chatFooterInnerClassName={`mx-auto w-full max-w-[720px] ${isMobile && 'px-4'}`}
|
||||
onSend={doSend}
|
||||
onStopResponding={handleStop}
|
||||
chatNode={chatNode}
|
||||
allToolIcons={appMeta?.tool_icons || {}}
|
||||
onFeedback={handleFeedback}
|
||||
suggestedQuestions={suggestedQuestions}
|
||||
hideProcessDetail
|
||||
themeBuilder={themeBuilder}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ const AgentContent: FC<AgentContentProps> = ({
|
||||
return (
|
||||
<div>
|
||||
{agent_thoughts?.map((thought, index) => (
|
||||
<div key={index}>
|
||||
<div key={index} className='px-2 py-1'>
|
||||
{thought.thought && (
|
||||
<Markdown content={thought.thought} />
|
||||
)}
|
||||
|
@ -2,6 +2,7 @@ import type { FC } from 'react'
|
||||
import { memo } from 'react'
|
||||
import type { ChatItem } from '../../types'
|
||||
import { Markdown } from '@/app/components/base/markdown'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type BasicContentProps = {
|
||||
item: ChatItem
|
||||
@ -15,9 +16,17 @@ const BasicContent: FC<BasicContentProps> = ({
|
||||
} = item
|
||||
|
||||
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)
|
||||
|
@ -15,13 +15,13 @@ import BasicContent from './basic-content'
|
||||
import SuggestedQuestions from './suggested-questions'
|
||||
import More from './more'
|
||||
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 LoadingAnim from '@/app/components/base/chat/chat/loading-anim'
|
||||
import Citation from '@/app/components/base/chat/chat/citation'
|
||||
import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item'
|
||||
import type { Emoji } from '@/app/components/tools/types'
|
||||
import type { AppData } from '@/models/share'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type AnswerProps = {
|
||||
item: ChatItem
|
||||
@ -105,13 +105,12 @@ const Answer: FC<AnswerProps> = ({
|
||||
</div>
|
||||
<div className='chat-answer-container group grow w-0 ml-4' ref={containerRef}>
|
||||
<div className={`group relative pr-10 ${chatAnswerContainerInner}`}>
|
||||
<AnswerTriangle className='absolute -left-2 top-0 w-2 h-3 text-gray-100' />
|
||||
<div
|
||||
ref={contentRef}
|
||||
className={`
|
||||
relative inline-block px-4 py-3 max-w-full bg-gray-100 rounded-b-2xl rounded-tr-2xl text-sm text-gray-900
|
||||
${workflowProcess && 'w-full'}
|
||||
`}
|
||||
className={cn(
|
||||
'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',
|
||||
)}
|
||||
>
|
||||
{annotation?.id && (
|
||||
<div
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {
|
||||
memo,
|
||||
useCallback,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import Textarea from 'rc-textarea'
|
||||
@ -15,7 +16,7 @@ import type { Theme } from '../../embedded-chatbot/theme/theme-context'
|
||||
import { useTextAreaHeight } from './hooks'
|
||||
import Operation from './operation'
|
||||
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 { useToastContext } from '@/app/components/base/toast'
|
||||
|
||||
@ -41,9 +42,38 @@ const ChatInputArea = ({
|
||||
handleTextareaResize,
|
||||
isMultipleLine,
|
||||
} = useTextAreaHeight()
|
||||
const [value, setValue] = useState('')
|
||||
const [query, setQuery] = useState('')
|
||||
const isUseInputMethod = useRef(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(() => {
|
||||
(Recorder as any).getPermission().then(() => {
|
||||
setShowVoiceInput(true)
|
||||
@ -63,11 +93,11 @@ const ChatInputArea = ({
|
||||
return (
|
||||
<div
|
||||
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'>
|
||||
<FileListFlexOperation />
|
||||
<div className='relative px-[9px] max-h-[158px] overflow-x-hidden overflow-y-auto'>
|
||||
{/* <FileListFlexOperation /> */}
|
||||
<div
|
||||
ref={wrapperRef}
|
||||
className='flex items-center justify-between'
|
||||
@ -77,7 +107,7 @@ const ChatInputArea = ({
|
||||
ref={textValueRef}
|
||||
className='absolute w-auto h-auto p-1 leading-6 body-lg-regular pointer-events-none whitespace-pre invisible'
|
||||
>
|
||||
{value}
|
||||
{query}
|
||||
</div>
|
||||
<Textarea
|
||||
ref={textareaRef}
|
||||
@ -85,11 +115,13 @@ const ChatInputArea = ({
|
||||
placeholder='Enter message...'
|
||||
autoSize={{ minRows: 1 }}
|
||||
onResize={handleTextareaResize}
|
||||
value={value}
|
||||
value={query}
|
||||
onChange={(e) => {
|
||||
setValue(e.target.value)
|
||||
setQuery(e.target.value)
|
||||
handleTextareaResize()
|
||||
}}
|
||||
onKeyUp={handleKeyUp}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
@ -100,7 +132,7 @@ const ChatInputArea = ({
|
||||
showVoiceInput && (
|
||||
<VoiceInput
|
||||
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 Question from './question'
|
||||
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 { ChatContextProvider } from './context'
|
||||
import classNames from '@/utils/classnames'
|
||||
@ -261,7 +262,7 @@ const Chat: FC<ChatProps> = ({
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
{/* {
|
||||
!noChatInput && (
|
||||
<ChatInput
|
||||
visionConfig={config?.file_upload?.image}
|
||||
@ -270,6 +271,16 @@ const Chat: FC<ChatProps> = ({
|
||||
theme={themeBuilder?.theme}
|
||||
/>
|
||||
)
|
||||
} */}
|
||||
{
|
||||
!noChatInput && (
|
||||
<ChatInputArea
|
||||
visionConfig={config?.file_upload?.image}
|
||||
speechToTextConfig={config?.speech_to_text}
|
||||
onSend={onSend}
|
||||
theme={themeBuilder?.theme}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,7 +8,6 @@ import {
|
||||
import type { ChatItem } from '../types'
|
||||
import type { Theme } from '../embedded-chatbot/theme/theme-context'
|
||||
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 { Markdown } from '@/app/components/base/markdown'
|
||||
import ImageGallery from '@/app/components/base/image-gallery'
|
||||
@ -32,12 +31,8 @@ const Question: FC<QuestionProps> = ({
|
||||
return (
|
||||
<div className='flex justify-end mb-2 last:mb-0 pl-10'>
|
||||
<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
|
||||
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) : {}}
|
||||
>
|
||||
{
|
||||
|
@ -88,6 +88,10 @@ module.exports = {
|
||||
fontSize: {
|
||||
'2xs': '0.625rem',
|
||||
},
|
||||
backgroundImage: {
|
||||
'chatbot-bg': 'var(--color-chatbot-bg)',
|
||||
'chat-bubble-bg': 'var(--color-chat-bubble-bg)',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
|
@ -559,4 +559,7 @@ html[data-theme="dark"] {
|
||||
|
||||
--color-third-party-LangChain: #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-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