index.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import type {
  2. FC,
  3. ReactNode,
  4. } from 'react'
  5. import { memo, useEffect, useRef, useState } from 'react'
  6. import { useTranslation } from 'react-i18next'
  7. import type {
  8. ChatConfig,
  9. ChatItem,
  10. } from '../../types'
  11. import { useChatContext } from '../context'
  12. import Operation from './operation'
  13. import AgentContent from './agent-content'
  14. import BasicContent from './basic-content'
  15. import SuggestedQuestions from './suggested-questions'
  16. import More from './more'
  17. import WorkflowProcess from './workflow-process'
  18. import { AnswerTriangle } from '@/app/components/base/icons/src/vender/solid/general'
  19. import { MessageFast } from '@/app/components/base/icons/src/vender/solid/communication'
  20. import LoadingAnim from '@/app/components/base/chat/chat/loading-anim'
  21. import Citation from '@/app/components/base/chat/chat/citation'
  22. import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item'
  23. import type { Emoji } from '@/app/components/tools/types'
  24. import type { AppData } from '@/models/share'
  25. import AnswerIcon from '@/app/components/base/answer-icon'
  26. type AnswerProps = {
  27. item: ChatItem
  28. question: string
  29. index: number
  30. config?: ChatConfig
  31. answerIcon?: ReactNode
  32. responding?: boolean
  33. allToolIcons?: Record<string, string | Emoji>
  34. showPromptLog?: boolean
  35. chatAnswerContainerInner?: string
  36. hideProcessDetail?: boolean
  37. appData?: AppData
  38. }
  39. const Answer: FC<AnswerProps> = ({
  40. item,
  41. question,
  42. index,
  43. config,
  44. answerIcon,
  45. responding,
  46. allToolIcons,
  47. showPromptLog,
  48. chatAnswerContainerInner,
  49. hideProcessDetail,
  50. appData,
  51. }) => {
  52. const { t } = useTranslation()
  53. const {
  54. content,
  55. citation,
  56. agent_thoughts,
  57. more,
  58. annotation,
  59. workflowProcess,
  60. } = item
  61. const hasAgentThoughts = !!agent_thoughts?.length
  62. const [containerWidth] = useState(0)
  63. const [contentWidth, setContentWidth] = useState(0)
  64. const containerRef = useRef<HTMLDivElement>(null)
  65. const contentRef = useRef<HTMLDivElement>(null)
  66. const {
  67. config: chatContextConfig,
  68. } = useChatContext()
  69. const voiceRef = useRef(chatContextConfig?.text_to_speech?.voice)
  70. const getContentWidth = () => {
  71. if (contentRef.current)
  72. setContentWidth(contentRef.current?.clientWidth)
  73. }
  74. useEffect(() => {
  75. voiceRef.current = chatContextConfig?.text_to_speech?.voice
  76. }
  77. , [chatContextConfig?.text_to_speech?.voice])
  78. useEffect(() => {
  79. if (!responding)
  80. getContentWidth()
  81. }, [responding])
  82. return (
  83. <div className='flex mb-2 last:mb-0'>
  84. <div className='shrink-0 relative w-10 h-10'>
  85. {
  86. answerIcon || <AnswerIcon />
  87. }
  88. {
  89. responding && (
  90. <div className='absolute -top-[3px] -left-[3px] pl-[6px] flex items-center w-4 h-4 bg-white rounded-full shadow-xs border-[0.5px] border-gray-50'>
  91. <LoadingAnim type='avatar' />
  92. </div>
  93. )
  94. }
  95. </div>
  96. <div className='chat-answer-container group grow w-0 ml-4' ref={containerRef}>
  97. <div className={`group relative pr-10 ${chatAnswerContainerInner}`}>
  98. <AnswerTriangle className='absolute -left-2 top-0 w-2 h-3 text-gray-100' />
  99. <div
  100. ref={contentRef}
  101. className={`
  102. relative inline-block px-4 py-3 max-w-full bg-gray-100 rounded-b-2xl rounded-tr-2xl text-sm text-gray-900
  103. ${workflowProcess && 'w-full'}
  104. `}
  105. >
  106. {annotation?.id && (
  107. <div
  108. className='absolute -top-3.5 -right-3.5 box-border flex items-center justify-center h-7 w-7 p-0.5 rounded-lg bg-white cursor-pointer text-[#444CE7] shadow-md group-hover:hidden'
  109. >
  110. <div className='p-1 rounded-lg bg-[#EEF4FF] '>
  111. <MessageFast className='w-4 h-4' />
  112. </div>
  113. </div>
  114. )}
  115. {
  116. !responding && (
  117. <Operation
  118. hasWorkflowProcess={!!workflowProcess}
  119. maxSize={containerWidth - contentWidth - 4}
  120. contentWidth={contentWidth}
  121. item={item}
  122. question={question}
  123. index={index}
  124. showPromptLog={showPromptLog}
  125. />
  126. )
  127. }
  128. {/** Render the normal steps */}
  129. {
  130. workflowProcess && !hideProcessDetail && (
  131. <WorkflowProcess
  132. data={workflowProcess}
  133. item={item}
  134. hideInfo
  135. hideProcessDetail={hideProcessDetail}
  136. />
  137. )
  138. }
  139. {/** Hide workflow steps by it's settings in siteInfo */}
  140. {
  141. workflowProcess && hideProcessDetail && appData && appData.site.show_workflow_steps && (
  142. <WorkflowProcess
  143. data={workflowProcess}
  144. item={item}
  145. hideInfo
  146. hideProcessDetail={hideProcessDetail}
  147. />
  148. )
  149. }
  150. {
  151. responding && !content && !hasAgentThoughts && (
  152. <div className='flex items-center justify-center w-6 h-5'>
  153. <LoadingAnim type='text' />
  154. </div>
  155. )
  156. }
  157. {
  158. content && !hasAgentThoughts && (
  159. <BasicContent item={item} />
  160. )
  161. }
  162. {
  163. hasAgentThoughts && (
  164. <AgentContent
  165. item={item}
  166. responding={responding}
  167. allToolIcons={allToolIcons}
  168. />
  169. )
  170. }
  171. {
  172. annotation?.id && annotation.authorName && (
  173. <EditTitle
  174. className='mt-1'
  175. title={t('appAnnotation.editBy', { author: annotation.authorName })}
  176. />
  177. )
  178. }
  179. <SuggestedQuestions item={item} />
  180. {
  181. !!citation?.length && !responding && (
  182. <Citation data={citation} showHitInfo={config?.supportCitationHitInfo} />
  183. )
  184. }
  185. </div>
  186. </div>
  187. <More more={more} />
  188. </div>
  189. </div>
  190. )
  191. }
  192. export default memo(Answer)