| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 | import { useCallback, useEffect, useMemo } from 'react'import Chat from '../chat'import type {  ChatConfig,  ChatItem,  OnSend,} from '../types'import { useChat } from '../chat/hooks'import { getLastAnswer } from '../utils'import { useEmbeddedChatbotContext } from './context'import ConfigPanel from './config-panel'import { isDify } from './utils'import cn from '@/utils/classnames'import {  fetchSuggestedQuestions,  getUrl,  stopChatMessageResponding,} from '@/service/share'import LogoAvatar from '@/app/components/base/logo/logo-embedded-chat-avatar'import AnswerIcon from '@/app/components/base/answer-icon'const ChatWrapper = () => {  const {    appData,    appParams,    appPrevChatList,    currentConversationId,    currentConversationItem,    inputsForms,    newConversationInputs,    handleNewConversationCompleted,    isMobile,    isInstalledApp,    appId,    appMeta,    handleFeedback,    currentChatInstanceRef,    themeBuilder,  } = useEmbeddedChatbotContext()  const appConfig = useMemo(() => {    const config = appParams || {}    return {      ...config,      file_upload: {        ...(config as any).file_upload,        fileUploadConfig: (config as any).system_parameters,      },      supportFeedback: true,      opening_statement: currentConversationId ? currentConversationItem?.introduction : (config as any).opening_statement,    } as ChatConfig  }, [appParams, currentConversationItem?.introduction, currentConversationId])  const {    chatListRef,    chatList,    handleSend,    handleStop,    isResponding,    suggestedQuestions,    handleUpdateChatList,  } = useChat(    appConfig,    {      inputs: (currentConversationId ? currentConversationItem?.inputs : newConversationInputs) as any,      inputsForm: inputsForms,    },    appPrevChatList,    taskId => stopChatMessageResponding('', taskId, isInstalledApp, appId),  )  useEffect(() => {    if (currentChatInstanceRef.current)      currentChatInstanceRef.current.handleStop = handleStop  }, [])  const doSend: OnSend = useCallback((message, files, last_answer) => {    const data: any = {      query: message,      files,      inputs: currentConversationId ? currentConversationItem?.inputs : newConversationInputs,      conversation_id: currentConversationId,      parent_message_id: last_answer?.id || getLastAnswer(chatListRef.current)?.id || null,    }    handleSend(      getUrl('chat-messages', isInstalledApp, appId || ''),      data,      {        onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, isInstalledApp, appId),        onConversationComplete: currentConversationId ? undefined : handleNewConversationCompleted,        isPublicAPI: !isInstalledApp,      },    )  }, [    chatListRef,    appConfig,    currentConversationId,    currentConversationItem,    handleSend,    newConversationInputs,    handleNewConversationCompleted,    isInstalledApp,    appId,  ])  const doRegenerate = useCallback((chatItem: ChatItem) => {    const index = chatList.findIndex(item => item.id === chatItem.id)    if (index === -1)      return    const prevMessages = chatList.slice(0, index)    const question = prevMessages.pop()    const lastAnswer = getLastAnswer(prevMessages)    if (!question)      return    handleUpdateChatList(prevMessages)    doSend(question.content, question.message_files, lastAnswer)  }, [chatList, handleUpdateChatList, doSend])  const chatNode = useMemo(() => {    if (inputsForms.length) {      return (        <>          {!currentConversationId && (            <div className={cn('mx-auto w-full max-w-full tablet:px-4', isMobile && 'px-4')}>              <div className='mb-6' />              <ConfigPanel />              <div                className='my-6 h-[1px]'                style={{ background: 'linear-gradient(90deg, rgba(242, 244, 247, 0.00) 0%, #F2F4F7 49.17%, rgba(242, 244, 247, 0.00) 100%)' }}              />            </div>          )}        </>      )    }    return null  }, [currentConversationId, inputsForms, isMobile])  const answerIcon = isDify()    ? <LogoAvatar className='relative shrink-0' />    : (appData?.site && appData.site.use_icon_as_answer_icon)      ? <AnswerIcon        iconType={appData.site.icon_type}        icon={appData.site.icon}        background={appData.site.icon_background}        imageUrl={appData.site.icon_url}      />      : null  return (    <Chat      appData={appData}      config={appConfig}      chatList={chatList}      isResponding={isResponding}      chatContainerInnerClassName={cn('mx-auto w-full max-w-full tablet:px-4', isMobile && 'px-4')}      chatFooterClassName='pb-4'      chatFooterInnerClassName={cn('mx-auto w-full max-w-full tablet:px-4', isMobile && 'px-4')}      onSend={doSend}      inputs={currentConversationId ? currentConversationItem?.inputs as any : newConversationInputs}      inputsForm={inputsForms}      onRegenerate={doRegenerate}      onStopResponding={handleStop}      chatNode={chatNode}      allToolIcons={appMeta?.tool_icons || {}}      onFeedback={handleFeedback}      suggestedQuestions={suggestedQuestions}      answerIcon={answerIcon}      hideProcessDetail      themeBuilder={themeBuilder}    />  )}export default ChatWrapper
 |