file uploader

This commit is contained in:
StyleZhang 2024-08-08 10:27:21 +08:00
parent e2962da1b8
commit 26bca75884
10 changed files with 103 additions and 43 deletions

View File

@ -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>
) )
} }

View File

@ -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} />
)} )}

View File

@ -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)

View File

@ -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

View File

@ -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)}
/> />
) )
} }

View File

@ -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>

View File

@ -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) : {}}
> >
{ {

View File

@ -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: [

View File

@ -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%);
} }

View File

@ -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%);
} }