Compare commits

...

1 Commits

Author SHA1 Message Date
jZonG
b4a07d9e96 fix scroll in mobile
suggest questions display

tooltip for start a new chat

reset chat input position

fix webapp UI issues
2025-03-12 16:07:34 +08:00
8 changed files with 52 additions and 12 deletions

View File

@ -19,6 +19,7 @@ import {
} from '@/service/share' } from '@/service/share'
import AppIcon from '@/app/components/base/app-icon' import AppIcon from '@/app/components/base/app-icon'
import AnswerIcon from '@/app/components/base/answer-icon' import AnswerIcon from '@/app/components/base/answer-icon'
import SuggestedQuestions from '@/app/components/base/chat/chat/answer/suggested-questions'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
const ChatWrapper = () => { const ChatWrapper = () => {
@ -39,6 +40,7 @@ const ChatWrapper = () => {
currentChatInstanceRef, currentChatInstanceRef,
appData, appData,
themeBuilder, themeBuilder,
sidebarCollapseState,
} = useChatWithHistoryContext() } = useChatWithHistoryContext()
const appConfig = useMemo(() => { const appConfig = useMemo(() => {
const config = appParams || {} const config = appParams || {}
@ -144,10 +146,10 @@ const ChatWrapper = () => {
}, [chatList, doSend]) }, [chatList, doSend])
const messageList = useMemo(() => { const messageList = useMemo(() => {
if (currentConversationId) if (currentConversationId || isResponding)
return chatList return chatList
return chatList.filter(item => !item.isOpeningStatement) return chatList.filter(item => !item.isOpeningStatement)
}, [chatList, currentConversationId]) }, [chatList, currentConversationId, isResponding])
const [collapsed, setCollapsed] = useState(!!currentConversationId) const [collapsed, setCollapsed] = useState(!!currentConversationId)
@ -166,12 +168,33 @@ const ChatWrapper = () => {
const welcome = useMemo(() => { const welcome = useMemo(() => {
const welcomeMessage = chatList.find(item => item.isOpeningStatement) const welcomeMessage = chatList.find(item => item.isOpeningStatement)
if (isResponding)
return null
if (currentConversationId) if (currentConversationId)
return null return null
if (!welcomeMessage) if (!welcomeMessage)
return null return null
if (!collapsed && inputsForms.length > 0) if (!collapsed && inputsForms.length > 0)
return null return null
if (welcomeMessage.suggestedQuestions && welcomeMessage.suggestedQuestions?.length > 0) {
return (
<div className='h-[50vh] py-12 px-4 flex items-center justify-center'>
<div className='grow max-w-[720px] flex gap-4'>
<AppIcon
size='xl'
iconType={appData?.site.icon_type}
icon={appData?.site.icon}
background={appData?.site.icon_background}
imageUrl={appData?.site.icon_url}
/>
<div className='grow px-4 py-3 bg-chat-bubble-bg text-text-primary rounded-2xl body-lg-regular'>
{welcomeMessage.content}
<SuggestedQuestions item={welcomeMessage} />
</div>
</div>
</div>
)
}
return ( return (
<div className={cn('h-[50vh] py-12 flex flex-col items-center justify-center gap-3')}> <div className={cn('h-[50vh] py-12 flex flex-col items-center justify-center gap-3')}>
<AppIcon <AppIcon
@ -184,7 +207,7 @@ const ChatWrapper = () => {
<div className='text-text-tertiary body-2xl-regular'>{welcomeMessage.content}</div> <div className='text-text-tertiary body-2xl-regular'>{welcomeMessage.content}</div>
</div> </div>
) )
}, [appData?.site.icon, appData?.site.icon_background, appData?.site.icon_type, appData?.site.icon_url, chatList, collapsed, currentConversationId, inputsForms.length]) }, [appData?.site.icon, appData?.site.icon_background, appData?.site.icon_type, appData?.site.icon_url, chatList, collapsed, currentConversationId, inputsForms.length, isResponding])
const answerIcon = (appData?.site && appData.site.use_icon_as_answer_icon) const answerIcon = (appData?.site && appData.site.use_icon_as_answer_icon)
? <AnswerIcon ? <AnswerIcon
@ -227,6 +250,7 @@ const ChatWrapper = () => {
switchSibling={siblingMessageId => setTargetMessageId(siblingMessageId)} switchSibling={siblingMessageId => setTargetMessageId(siblingMessageId)}
inputDisabled={inputDisabled} inputDisabled={inputDisabled}
isMobile={isMobile} isMobile={isMobile}
sidebarCollapseState={sidebarCollapseState}
/> />
</div> </div>
) )

View File

@ -9,7 +9,7 @@ import {
useChatWithHistoryContext, useChatWithHistoryContext,
} from '../context' } from '../context'
import Operation from './operation' import Operation from './operation'
import ActionButton from '@/app/components/base/action-button' import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
import AppIcon from '@/app/components/base/app-icon' import AppIcon from '@/app/components/base/app-icon'
import Tooltip from '@/app/components/base/tooltip' import Tooltip from '@/app/components/base/tooltip'
import ViewFormDropdown from '@/app/components/base/chat/chat-with-history/inputs-form/view-form-dropdown' import ViewFormDropdown from '@/app/components/base/chat/chat-with-history/inputs-form/view-form-dropdown'
@ -106,9 +106,16 @@ const Header = () => {
<div className='h-[14px] w-px bg-divider-regular'></div> <div className='h-[14px] w-px bg-divider-regular'></div>
</div> </div>
{isSidebarCollapsed && ( {isSidebarCollapsed && (
<ActionButton size='l' onClick={handleNewConversation}> <Tooltip
<RiEditBoxLine className='w-[18px] h-[18px]' /> disabled={!!currentConversationId}
</ActionButton> popupContent={t('share.chat.newChatTip')}
>
<div>
<ActionButton size='l' state={!currentConversationId ? ActionButtonState.Disabled : ActionButtonState.Default} disabled={!currentConversationId} onClick={handleNewConversation}>
<RiEditBoxLine className='w-[18px] h-[18px]' />
</ActionButton>
</div>
</Tooltip>
)} )}
</div> </div>
<div className='flex items-center gap-1'> <div className='flex items-center gap-1'>

View File

@ -82,7 +82,7 @@ const ChatWithHistory: FC<ChatWithHistoryProps> = ({
{isMobile && ( {isMobile && (
<HeaderInMobile /> <HeaderInMobile />
)} )}
<div className={cn('relative grow p-2')}> <div className={cn('relative grow p-2', isMobile && 'h-[calc(100%_-_56px)] p-0')}>
{isSidebarCollapsed && ( {isSidebarCollapsed && (
<div <div
className={cn( className={cn(
@ -95,7 +95,7 @@ const ChatWithHistory: FC<ChatWithHistoryProps> = ({
<Sidebar isPanel /> <Sidebar isPanel />
</div> </div>
)} )}
<div className='h-full flex flex-col bg-chatbot-bg rounded-2xl border-[0,5px] border-components-panel-border-subtle overflow-hidden'> <div className={cn('h-full flex flex-col bg-chatbot-bg border-[0,5px] border-components-panel-border-subtle overflow-hidden', isMobile ? 'rounded-t-2xl' : 'rounded-2xl')}>
{!isMobile && <Header />} {!isMobile && <Header />}
{appChatListDataLoading && ( {appChatListDataLoading && (
<Loading type='app' /> <Loading type='app' />

View File

@ -110,7 +110,7 @@ const Answer: FC<AnswerProps> = ({
</div> </div>
)} )}
</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 pb-4' ref={containerRef}>
<div className={cn('group relative pr-10', chatAnswerContainerInner)}> <div className={cn('group relative pr-10', chatAnswerContainerInner)}>
<div <div
ref={contentRef} ref={contentRef}

View File

@ -72,6 +72,7 @@ export type ChatProps = {
noSpacing?: boolean noSpacing?: boolean
inputDisabled?: boolean inputDisabled?: boolean
isMobile?: boolean isMobile?: boolean
sidebarCollapseState?: boolean
} }
const Chat: FC<ChatProps> = ({ const Chat: FC<ChatProps> = ({
@ -110,6 +111,7 @@ const Chat: FC<ChatProps> = ({
noSpacing, noSpacing,
inputDisabled, inputDisabled,
isMobile, isMobile,
sidebarCollapseState,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const { currentLogItem, setCurrentLogItem, showPromptLogModal, setShowPromptLogModal, showAgentLogModal, setShowAgentLogModal } = useAppStore(useShallow(state => ({ const { currentLogItem, setCurrentLogItem, showPromptLogModal, setShowPromptLogModal, showAgentLogModal, setShowAgentLogModal } = useAppStore(useShallow(state => ({
@ -193,6 +195,11 @@ const Chat: FC<ChatProps> = ({
} }
}, []) }, [])
useEffect(() => {
if (!sidebarCollapseState)
setTimeout(() => handleWindowResize(), 200)
}, [sidebarCollapseState])
const hasTryToAsk = config?.suggested_questions_after_answer?.enabled && !!suggestedQuestions?.length && onSend const hasTryToAsk = config?.suggested_questions_after_answer?.enabled && !!suggestedQuestions?.length && onSend
return ( return (
@ -255,7 +262,7 @@ const Chat: FC<ChatProps> = ({
</div> </div>
</div> </div>
<div <div
className={`absolute bottom-0 bg-chat-input-mask ${(hasTryToAsk || !noChatInput || !noStopResponding) && chatFooterClassName}`} className={`absolute bottom-0 bg-chat-input-mask flex justify-center ${(hasTryToAsk || !noChatInput || !noStopResponding) && chatFooterClassName}`}
ref={chatFooterRef} ref={chatFooterRef}
> >
<div <div

View File

@ -204,7 +204,7 @@ const ChatWrapper = () => {
config={appConfig} config={appConfig}
chatList={messageList} chatList={messageList}
isResponding={isResponding} isResponding={isResponding}
chatContainerInnerClassName={cn('mx-auto w-full max-w-full tablet:px-4', isMobile && 'px-4')} chatContainerInnerClassName={cn('mx-auto w-full max-w-full pt-4 tablet:px-4', isMobile && 'px-4')}
chatFooterClassName={cn('pb-4', !isMobile && 'rounded-b-2xl')} chatFooterClassName={cn('pb-4', !isMobile && 'rounded-b-2xl')}
chatFooterInnerClassName={cn('mx-auto w-full max-w-full tablet:px-4', isMobile && 'px-2')} chatFooterInnerClassName={cn('mx-auto w-full max-w-full tablet:px-4', isMobile && 'px-2')}
onSend={doSend} onSend={doSend}

View File

@ -6,6 +6,7 @@ const translation = {
}, },
chat: { chat: {
newChat: 'Start New chat', newChat: 'Start New chat',
newChatTip: 'Already in a new chat',
chatSettingsTitle: 'New chat setup', chatSettingsTitle: 'New chat setup',
chatFormTip: 'Chat settings cannot be modified after the chat has started.', chatFormTip: 'Chat settings cannot be modified after the chat has started.',
pinnedTitle: 'Pinned', pinnedTitle: 'Pinned',

View File

@ -6,6 +6,7 @@ const translation = {
}, },
chat: { chat: {
newChat: '开启新对话', newChat: '开启新对话',
newChatTip: '已在新对话中',
chatSettingsTitle: '新对话设置', chatSettingsTitle: '新对话设置',
chatFormTip: '对话开始后,对话设置将无法修改。', chatFormTip: '对话开始后,对话设置将无法修改。',
pinnedTitle: '已置顶', pinnedTitle: '已置顶',