index.tsx 5.6 KB

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